mirror of
https://github.com/discourse/discourse.git
synced 2025-02-08 09:01:46 +08:00
![Jarek Radosz](/assets/img/avatar_default.png)
Adds an optional title attribute to polls. The rationale for this addition is that polls themselves didn't contain context/question and relied on post body to explain them. That context wasn't always obvious (e.g. when there are multiple polls in a single post) or available (e.g. when you display the poll breakdown - you see the answers, but not the question) As a side note, here's a word on how the poll plugin works: > We have a markdown poll renderer, which we use in the builder UI and the composer preview, but… when you submit a post, raw markdown is cooked into html (twice), then we extract data from the generated html and save it to the database. When it's render time, we first display the cooked html poll, and then extract some data from that html, get the data from the post's JSON (and identify that poll using the extracted html stuff) to then render the poll using widgets and the JSON data.
437 lines
13 KiB
Ruby
437 lines
13 KiB
Ruby
# frozen_string_literal: true
|
||
|
||
require "rails_helper"
|
||
|
||
describe PostsController do
|
||
let!(:user) { log_in }
|
||
let!(:title) { "Testing Poll Plugin" }
|
||
|
||
before do
|
||
SiteSetting.min_first_post_typing_time = 0
|
||
end
|
||
|
||
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 "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 match("data-poll-")
|
||
expect(json["cooked"]).to include("<script>")
|
||
expect(Poll.find_by(post_id: json["id"]).name).to eq("<script>alert('xss')</script>")
|
||
end
|
||
|
||
it "also works whe 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 pollception" 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("What’s 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(post_id, "poll", ["5c24fc1df56d764b550ceae1b9319125"], user)
|
||
|
||
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 do
|
||
SiteSetting.poll_edit_window_mins = poll_edit_window_mins
|
||
end
|
||
|
||
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(post_id, "poll", ["5c24fc1df56d764b550ceae1b9319125"], user)
|
||
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 do
|
||
SiteSetting.poll_enabled = false
|
||
end
|
||
|
||
it "doesn’t 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 do
|
||
SiteSetting.poll_minimum_trust_level_to_create = 2
|
||
end
|
||
|
||
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 do
|
||
SiteSetting.poll_minimum_trust_level_to_create = 2
|
||
end
|
||
|
||
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 do
|
||
SiteSetting.poll_minimum_trust_level_to_create = 2
|
||
end
|
||
|
||
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 do
|
||
SiteSetting.poll_minimum_trust_level_to_create = 2
|
||
end
|
||
|
||
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
|
||
end
|