discourse/plugins/poll/spec/controllers/posts_controller_spec.rb
Sam 9e241e82e9
DEV: use HTML5 version of loofah (#21522)
https://meta.discourse.org/t/markdown-preview-and-result-differ/263878

The result of this markdown had different results in the composer preview and the post. This is solved by updating Loofah to the latest version and using html5 fragments like our user had reported. While the change was only needed in cooked_post_processor.rb for this fix, other areas also had to be updated due to various side effects.
2023-06-20 09:49:22 +08:00

479 lines
14 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: true
require "rails_helper"
RSpec.describe PostsController do
let!(:user) { log_in }
let!(:title) { "Testing Poll Plugin" }
before { SiteSetting.min_first_post_typing_time = 0 }
describe "polls" do
it "works" do
post :create, params: { title: title, raw: "[poll]\n- A\n- B\n[/poll]" }, format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["cooked"]).to match("data-poll-")
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
it "works on any post" do
post_1 = Fabricate(:post)
post :create,
params: {
topic_id: post_1.topic.id,
raw: "[poll]\n- A\n- B\n[/poll]",
},
format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["cooked"]).to match("data-poll-")
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
it "schedules auto-close job" do
freeze_time
name = "auto_close"
close_date = 1.month.from_now.round
expect do
post :create,
params: {
title: title,
raw: "[poll name=#{name} close=#{close_date.iso8601}]\n- A\n- B\n[/poll]",
},
format: :json
end.to change { Jobs::ClosePoll.jobs.size }.by(1) & change { Poll.count }.by(1)
expect(response.status).to eq(200)
json = response.parsed_body
post_id = json["id"]
expect(Poll.find_by(post_id: post_id).close_at).to eq_time(close_date)
job = Jobs::ClosePoll.jobs.first
job_args = job["args"].first
expect(job_args["post_id"]).to eq(post_id)
expect(job_args["poll_name"]).to eq(name)
end
it "should have different options" do
post :create, params: { title: title, raw: "[poll]\n- A\n- A\n[/poll]" }, format: :json
expect(response).not_to be_successful
json = response.parsed_body
expect(json["errors"][0]).to eq(I18n.t("poll.default_poll_must_have_different_options"))
end
it "accepts different Chinese options" do
SiteSetting.default_locale = "zh_CN"
post :create,
params: {
title: title,
raw: "[poll]\n- Microsoft Edge\n- Microsoft Edge\n[/poll]",
},
format: :json
expect(response).to be_successful
end
it "should have at least 1 options" do
post :create, params: { title: title, raw: "[poll]\n[/poll]" }, format: :json
expect(response).not_to be_successful
json = response.parsed_body
expect(json["errors"][0]).to eq(I18n.t("poll.default_poll_must_have_at_least_1_option"))
end
it "should have at most 'SiteSetting.poll_maximum_options' options" do
raw = +"[poll]\n"
(SiteSetting.poll_maximum_options + 1).times { |n| raw << "\n- #{n}" }
raw << "\n[/poll]"
post :create, params: { title: title, raw: raw }, format: :json
expect(response).not_to be_successful
json = response.parsed_body
expect(json["errors"][0]).to eq(
I18n.t("poll.default_poll_must_have_less_options", count: SiteSetting.poll_maximum_options),
)
end
it "should have valid parameters" do
post :create,
params: {
title: title,
raw: "[poll type=multiple min=5]\n- A\n- B\n[/poll]",
},
format: :json
expect(response).not_to be_successful
json = response.parsed_body
expect(json["errors"][0]).to eq(
I18n.t("poll.default_poll_with_multiple_choices_has_invalid_parameters"),
)
end
it "prevents self-xss" do
post :create,
params: {
title: title,
raw: "[poll name=<script>alert('xss')</script>]\n- A\n- B\n[/poll]",
},
format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["cooked"]).to include("data-poll-name=\"<script>alert('xss')</script>\"")
expect(Poll.find_by(post_id: json["id"]).name).to eq(
"&lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;",
)
end
it "also works when there is a link starting with '[poll'" do
post :create,
params: {
title: title,
raw: "[Polls are awesome](/foobar)\n[poll]\n- A\n- B\n[/poll]",
},
format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["cooked"]).to match("data-poll-")
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
it "prevents poll-inception" do
post :create,
params: {
title: title,
raw: "[poll name=1]\n- A\n[poll name=2]\n- B\n- C\n[/poll]\n- D\n[/poll]",
},
format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["cooked"]).to match("data-poll-")
expect(Poll.where(post_id: json["id"]).count).to eq(1)
end
it "accepts polls with titles" do
post :create,
params: {
title: title,
raw: "[poll]\n# What's up?\n- one\n[/poll]",
},
format: :json
expect(response).to be_successful
poll = Poll.last
expect(poll).to_not be_nil
expect(poll.title).to eq("Whats up?")
end
describe "edit window" do
describe "within the first 5 minutes" do
let(:post_id) do
freeze_time(4.minutes.ago) do
post :create, params: { title: title, raw: "[poll]\n- A\n- B\n[/poll]" }, format: :json
response.parsed_body["id"]
end
end
it "can be changed" do
put :update,
params: {
id: post_id,
post: {
raw: "[poll]\n- A\n- B\n- C\n[/poll]",
},
},
format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["post"]["polls"][0]["options"][2]["html"]).to eq("C")
end
it "resets the votes" do
DiscoursePoll::Poll.vote(user, post_id, "poll", ["5c24fc1df56d764b550ceae1b9319125"])
put :update,
params: {
id: post_id,
post: {
raw: "[poll]\n- A\n- B\n- C\n[/poll]",
},
},
format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["post"]["polls_votes"]).to_not be
end
end
describe "after the poll edit window has expired" do
let(:poll) { "[poll]\n- A\n- B\n[/poll]" }
let(:new_option) { "[poll]\n- A\n- C\n[/poll]" }
let(:updated) { "before\n\n[poll]\n- A\n- B\n[/poll]\n\nafter" }
let(:post_id) do
freeze_time(6.minutes.ago) do
post :create, params: { title: title, raw: poll }, format: :json
response.parsed_body["id"]
end
end
let(:poll_edit_window_mins) { 6 }
before { SiteSetting.poll_edit_window_mins = poll_edit_window_mins }
describe "with no vote" do
it "can change the options" do
put :update, params: { id: post_id, post: { raw: new_option } }, format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["post"]["polls"][0]["options"][1]["html"]).to eq("C")
end
it "support changes on the post" do
put :update, params: { id: post_id, post: { raw: updated } }, format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["post"]["cooked"]).to match("before")
end
end
describe "with at least one vote" do
before do
DiscoursePoll::Poll.vote(user, post_id, "poll", ["5c24fc1df56d764b550ceae1b9319125"])
end
it "cannot change the options" do
put :update, params: { id: post_id, post: { raw: new_option } }, format: :json
expect(response).not_to be_successful
json = response.parsed_body
expect(json["errors"][0]).to eq(
I18n.t(
"poll.edit_window_expired.cannot_edit_default_poll_with_votes",
minutes: poll_edit_window_mins,
),
)
end
it "support changes on the post" do
put :update, params: { id: post_id, post: { raw: updated } }, format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["post"]["cooked"]).to match("before")
end
end
end
end
end
describe "named polls" do
it "should have different options" do
post :create,
params: {
title: title,
raw:
"[poll name=" \
"foo" \
"]\n- A\n- A\n[/poll]",
},
format: :json
expect(response).not_to be_successful
json = response.parsed_body
expect(json["errors"][0]).to eq(
I18n.t("poll.named_poll_must_have_different_options", name: "foo"),
)
end
it "should have at least 1 option" do
post :create, params: { title: title, raw: "[poll name='foo']\n[/poll]" }, format: :json
expect(response).not_to be_successful
json = response.parsed_body
expect(json["errors"][0]).to eq(
I18n.t("poll.named_poll_must_have_at_least_1_option", name: "foo"),
)
end
end
describe "multiple polls" do
it "works" do
post :create,
params: {
title: title,
raw: "[poll]\n- A\n- B\n[/poll]\n[poll name=foo]\n- A\n- B\n[/poll]",
},
format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["cooked"]).to match("data-poll-")
expect(Poll.where(post_id: json["id"]).count).to eq(2)
end
it "should have a name" do
post :create,
params: {
title: title,
raw: "[poll]\n- A\n- B\n[/poll]\n[poll]\n- A\n- B\n[/poll]",
},
format: :json
expect(response).not_to be_successful
json = response.parsed_body
expect(json["errors"][0]).to eq(I18n.t("poll.multiple_polls_without_name"))
end
it "should have unique name" do
post :create,
params: {
title: title,
raw: "[poll name=foo]\n- A\n- B\n[/poll]\n[poll name=foo]\n- A\n- B\n[/poll]",
},
format: :json
expect(response).not_to be_successful
json = response.parsed_body
expect(json["errors"][0]).to eq(I18n.t("poll.multiple_polls_with_same_name", name: "foo"))
end
end
describe "disabled polls" do
before { SiteSetting.poll_enabled = false }
it "doesnt cook the poll" do
log_in_user(Fabricate(:user, admin: true, trust_level: 4))
post :create, params: { title: title, raw: "[poll]\n- A\n- B\n[/poll]" }, format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["cooked"]).to eq(
"<p>[poll]</p>\n<ul>\n<li>A</li>\n<li>B<br>\n[/poll]</li>\n</ul>",
)
end
end
describe "regular user with insufficient trust level" do
before { SiteSetting.poll_minimum_trust_level_to_create = 2 }
it "invalidates the post" do
log_in_user(Fabricate(:user, trust_level: 1))
post :create, params: { title: title, raw: "[poll]\n- A\n- B\n[/poll]" }, format: :json
expect(response).not_to be_successful
json = response.parsed_body
expect(json["errors"][0]).to eq(I18n.t("poll.insufficient_rights_to_create"))
end
it "skips the check in PMs with bots" do
user = Fabricate(:user, trust_level: 1)
topic =
Fabricate(
:private_message_topic,
topic_allowed_users: [
Fabricate.build(:topic_allowed_user, user: user),
Fabricate.build(:topic_allowed_user, user: Discourse.system_user),
],
)
Fabricate(:post, topic_id: topic.id, user_id: Discourse::SYSTEM_USER_ID)
log_in_user(user)
post :create, params: { topic_id: topic.id, raw: "[poll]\n- A\n- B\n[/poll]" }, format: :json
expect(response.parsed_body["errors"]).to eq(nil)
end
end
describe "regular user with equal trust level" do
before { SiteSetting.poll_minimum_trust_level_to_create = 2 }
it "validates the post" do
log_in_user(Fabricate(:user, trust_level: 2))
post :create, params: { title: title, raw: "[poll]\n- A\n- B\n[/poll]" }, format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["cooked"]).to match("data-poll-")
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
end
describe "regular user with superior trust level" do
before { SiteSetting.poll_minimum_trust_level_to_create = 2 }
it "validates the post" do
log_in_user(Fabricate(:user, trust_level: 3))
post :create, params: { title: title, raw: "[poll]\n- A\n- B\n[/poll]" }, format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["cooked"]).to match("data-poll-")
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
end
describe "staff with insufficient trust level" do
before { SiteSetting.poll_minimum_trust_level_to_create = 2 }
it "validates the post" do
log_in_user(Fabricate(:user, moderator: true, trust_level: 1))
post :create, params: { title: title, raw: "[poll]\n- A\n- B\n[/poll]" }, format: :json
expect(response.status).to eq(200)
json = response.parsed_body
expect(json["cooked"]).to match("data-poll-")
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
end
describe "staff editing posts of users with insufficient trust level" do
before { SiteSetting.poll_minimum_trust_level_to_create = 2 }
it "validates the post" do
log_in_user(Fabricate(:user, trust_level: 1))
post :create, params: { title: title, raw: title }, format: :json
expect(response.status).to eq(200)
post_id = response.parsed_body["id"]
log_in_user(Fabricate(:admin))
put :update,
params: {
id: post_id,
post: {
raw: "#{title}\n[poll]\n- A\n- B\n- C\n[/poll]",
},
},
format: :json
expect(response.status).to eq(200)
expect(response.parsed_body["post"]["polls"][0]["options"][2]["html"]).to eq("C")
end
end
end