REFACTOR: use tables instead of custom fields for polls (#6359)

Co-authored-by: Guo Xiang Tan <tgx_world@hotmail.com>
This commit is contained in:
Régis Hanol
2018-11-19 14:50:00 +01:00
committed by GitHub
parent 86dafc1f25
commit 4459665dee
37 changed files with 1912 additions and 1573 deletions

View File

@ -1,5 +1,4 @@
require "rails_helper"
require_relative "../helpers"
describe ::DiscoursePoll::PollsController do
routes { ::DiscoursePoll::Engine.routes }
@ -8,6 +7,8 @@ describe ::DiscoursePoll::PollsController do
let(:topic) { Fabricate(:topic) }
let(:poll) { Fabricate(:post, topic: topic, user: user, raw: "[poll]\n- A\n- B\n[/poll]") }
let(:multi_poll) { Fabricate(:post, topic: topic, user: user, raw: "[poll min=1 max=2 type=multiple public=true]\n- A\n- B\n[/poll]") }
let(:public_poll_on_vote) { Fabricate(:post, topic: topic, user: user, raw: "[poll public=true results=on_vote]\n- A\n- B\n[/poll]") }
let(:public_poll_on_close) { Fabricate(:post, topic: topic, user: user, raw: "[poll public=true results=on_close]\n- A\n- B\n[/poll]") }
describe "#vote" do
@ -56,7 +57,7 @@ describe ::DiscoursePoll::PollsController do
expect(json["poll"]["options"][1]["votes"]).to eq(1)
end
it "works even if topic is closed" do
it "works on closed topics" do
topic.update_attribute(:closed, true)
put :vote, params: {
@ -102,16 +103,6 @@ describe ::DiscoursePoll::PollsController do
expect(json["errors"][0]).to eq(I18n.t("poll.user_cant_post_in_topic"))
end
it "ensures polls are associated with the post" do
put :vote, params: {
post_id: Fabricate(:post).id, poll_name: "foobar", options: ["A"]
}, format: :json
expect(response.status).not_to eq(200)
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(I18n.t("poll.no_polls_associated_with_this_post"))
end
it "checks the name of the poll" do
put :vote, params: {
post_id: poll.id, poll_name: "foobar", options: ["A"]
@ -135,8 +126,10 @@ describe ::DiscoursePoll::PollsController do
end
it "doesn't discard anonymous votes when someone votes" do
default_poll = poll.custom_fields["polls"]["poll"]
add_anonymous_votes(poll, default_poll, 17, "5c24fc1df56d764b550ceae1b9319125" => 11, "e89dec30bbd9bf50fabf6a05b4324edf" => 6)
the_poll = poll.polls.first
the_poll.update_attribute(:anonymous_voters, 17)
the_poll.poll_options[0].update_attribute(:anonymous_votes, 11)
the_poll.poll_options[1].update_attribute(:anonymous_votes, 6)
put :vote, params: {
post_id: poll.id, poll_name: "poll", options: ["5c24fc1df56d764b550ceae1b9319125"]
@ -149,57 +142,6 @@ describe ::DiscoursePoll::PollsController do
expect(json["poll"]["options"][0]["votes"]).to eq(12)
expect(json["poll"]["options"][1]["votes"]).to eq(6)
end
it "tracks the users ids for public polls" do
public_poll = Fabricate(:post, topic_id: topic.id, user_id: user.id, raw: "[poll public=true]\n- A\n- B\n[/poll]")
body = { post_id: public_poll.id, poll_name: "poll" }
message = MessageBus.track_publish do
put :vote,
params: body.merge(options: ["5c24fc1df56d764b550ceae1b9319125"]),
format: :json
end.first
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["poll"]["voters"]).to eq(1)
expect(json["poll"]["options"][0]["votes"]).to eq(1)
expect(json["poll"]["options"][1]["votes"]).to eq(0)
expect(json["poll"]["options"][0]["voter_ids"]).to eq([user.id])
expect(json["poll"]["options"][1]["voter_ids"]).to eq([])
expect(message.data[:post_id].to_i).to eq(public_poll.id)
expect(message.data[:user][:id].to_i).to eq(user.id)
put :vote,
params: body.merge(options: ["e89dec30bbd9bf50fabf6a05b4324edf"]),
format: :json
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["poll"]["voters"]).to eq(1)
expect(json["poll"]["options"][0]["votes"]).to eq(0)
expect(json["poll"]["options"][1]["votes"]).to eq(1)
expect(json["poll"]["options"][0]["voter_ids"]).to eq([])
expect(json["poll"]["options"][1]["voter_ids"]).to eq([user.id])
another_user = Fabricate(:user)
log_in_user(another_user)
put :vote,
params: body.merge(options: ["e89dec30bbd9bf50fabf6a05b4324edf", "5c24fc1df56d764b550ceae1b9319125"]),
format: :json
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["poll"]["voters"]).to eq(2)
expect(json["poll"]["options"][0]["votes"]).to eq(1)
expect(json["poll"]["options"][1]["votes"]).to eq(2)
expect(json["poll"]["options"][0]["voter_ids"]).to eq([another_user.id])
expect(json["poll"]["options"][1]["voter_ids"]).to eq([user.id, another_user.id])
end
end
describe "#toggle_status" do
@ -248,13 +190,12 @@ describe ::DiscoursePoll::PollsController do
end
describe "votes" do
describe "#voters" do
let(:first) { "5c24fc1df56d764b550ceae1b9319125" }
let(:second) { "e89dec30bbd9bf50fabf6a05b4324edf" }
it "correctly handles offset" do
first = "5c24fc1df56d764b550ceae1b9319125"
second = "e89dec30bbd9bf50fabf6a05b4324edf"
user1 = log_in
put :vote, params: {
@ -274,15 +215,13 @@ describe ::DiscoursePoll::PollsController do
user3 = log_in
put :vote, params: {
post_id: multi_poll.id,
poll_name: "poll",
options: [first, second]
post_id: multi_poll.id, poll_name: "poll", options: [first, second]
}, format: :json
expect(response.status).to eq(200)
get :voters, params: {
poll_name: 'poll', post_id: multi_poll.id, voter_limit: 2
poll_name: 'poll', post_id: multi_poll.id, limit: 2
}, format: :json
expect(response.status).to eq(200)
@ -290,25 +229,81 @@ describe ::DiscoursePoll::PollsController do
json = JSON.parse(response.body)
# no user3 cause voter_limit is 2
expect(json["poll"][first].map { |h| h["id"] }.sort).to eq([user1.id, user2.id])
expect(json["poll"][second].map { |h| h["id"] }).to eq([user3.id])
expect(json["voters"][first].map { |h| h["id"] }).to contain_exactly(user1.id, user2.id)
expect(json["voters"][second].map { |h| h["id"] }).to contain_exactly(user3.id)
end
reloaded = Post.find(multi_poll.id)
# break the custom poll and make sure we still return something sane here
# TODO: normalize this data so we don't store the information twice and there is a chance
# that somehow a bg job can cause both fields to be out-of-sync
poll_votes = reloaded.custom_fields[DiscoursePoll::VOTES_CUSTOM_FIELD]
poll_votes.delete user2.id.to_s
reloaded.save_custom_fields(true)
get :voters, params: {
poll_name: 'poll', post_id: multi_poll.id, voter_limit: 2
it "ensures voters can only be seen after casting a vote" do
put :vote, params: {
post_id: public_poll_on_vote.id, poll_name: "poll", options: [first]
}, format: :json
expect(response.status).to eq(200)
get :voters, params: {
poll_name: "poll", post_id: public_poll_on_vote.id
}, format: :json
expect(response.status).to eq(200)
json = JSON.parse(response.body)
expect(json["voters"][first].size).to eq(1)
user2 = log_in
get :voters, params: {
poll_name: "poll", post_id: public_poll_on_vote.id
}, format: :json
expect(response.status).to eq(422)
put :vote, params: {
post_id: public_poll_on_vote.id, poll_name: "poll", options: [second]
}, format: :json
expect(response.status).to eq(200)
get :voters, params: {
poll_name: "poll", post_id: public_poll_on_vote.id
}, format: :json
expect(response.status).to eq(200)
json = JSON.parse(response.body)
expect(json["voters"][first].size).to eq(1)
expect(json["voters"][second].size).to eq(1)
end
it "ensures voters can only be seen when poll is closed" do
put :vote, params: {
post_id: public_poll_on_close.id, poll_name: "poll", options: [first]
}, format: :json
expect(response.status).to eq(200)
get :voters, params: {
poll_name: "poll", post_id: public_poll_on_close.id
}, format: :json
expect(response.status).to eq(422)
put :toggle_status, params: {
post_id: public_poll_on_close.id, poll_name: "poll", status: "closed"
}, format: :json
expect(response.status).to eq(200)
get :voters, params: {
poll_name: "poll", post_id: public_poll_on_close.id
}, format: :json
expect(response.status).to eq(200)
json = JSON.parse(response.body)
expect(json["voters"][first].size).to eq(1)
end
end

View File

@ -1,5 +1,4 @@
require "rails_helper"
require_relative "../helpers"
describe PostsController do
let!(:user) { log_in }
@ -19,7 +18,7 @@ describe PostsController do
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["polls"]["poll"]).to be
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
it "works on any post" do
@ -32,7 +31,7 @@ describe PostsController do
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["polls"]["poll"]).to be
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
it "schedules auto-close job" do
@ -45,9 +44,8 @@ describe PostsController do
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["polls"][name]["close"]).to be
expect(Jobs.scheduled_for(:close_poll, post_id: Post.last.id, poll_name: name)).to be
expect(Poll.find_by(post_id: json["id"]).close_at).to be
expect(Jobs.scheduled_for(:close_poll, post_id: json["id"], poll_name: name)).to be
end
it "should have different options" do
@ -55,7 +53,7 @@ describe PostsController do
title: title, raw: "[poll]\n- A\n- A\n[/poll]"
}, format: :json
expect(response).not_to be_success
expect(response).not_to be_successful
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(I18n.t("poll.default_poll_must_have_different_options"))
end
@ -65,7 +63,7 @@ describe PostsController do
title: title, raw: "[poll]\n- A\n[/poll]"
}, format: :json
expect(response).not_to be_success
expect(response).not_to be_successful
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(I18n.t("poll.default_poll_must_have_at_least_2_options"))
end
@ -79,7 +77,7 @@ describe PostsController do
title: title, raw: raw
}, format: :json
expect(response).not_to be_success
expect(response).not_to be_successful
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(I18n.t("poll.default_poll_must_have_less_options", count: SiteSetting.poll_maximum_options))
end
@ -89,7 +87,7 @@ describe PostsController do
title: title, raw: "[poll type=multiple min=5]\n- A\n- B\n[/poll]"
}, format: :json
expect(response).not_to be_success
expect(response).not_to be_successful
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(I18n.t("poll.default_poll_with_multiple_choices_has_invalid_parameters"))
end
@ -103,7 +101,7 @@ describe PostsController do
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["cooked"]).to include("&lt;script&gt;")
expect(json["polls"]["&lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;"]).to be
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 whe there is a link starting with '[poll'" do
@ -114,7 +112,7 @@ describe PostsController do
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["polls"]).to be
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
it "prevents pollception" do
@ -125,8 +123,7 @@ describe PostsController do
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["polls"]["1"]).to_not be
expect(json["polls"]["2"]).to be
expect(Poll.where(post_id: json["id"]).count).to eq(1)
end
describe "edit window" do
@ -150,7 +147,7 @@ describe PostsController do
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["post"]["polls"]["poll"]["options"][2]["html"]).to eq("C")
expect(json["post"]["polls"][0]["options"][2]["html"]).to eq("C")
end
it "resets the votes" do
@ -191,26 +188,14 @@ describe PostsController do
describe "with no vote" do
it "OP can change the options" 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 = ::JSON.parse(response.body)
expect(json["post"]["polls"]["poll"]["options"][1]["html"]).to eq("C")
end
it "staff can change the options" do
log_in_user(Fabricate(:moderator))
put :update, params: {
id: post_id, post: { raw: new_option }
}, format: :json
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["post"]["polls"]["poll"]["options"][1]["html"]).to eq("C")
expect(json["post"]["polls"][0]["options"][1]["html"]).to eq("C")
end
it "support changes on the post" do
@ -228,54 +213,19 @@ describe PostsController do
DiscoursePoll::Poll.vote(post_id, "poll", ["5c24fc1df56d764b550ceae1b9319125"], user)
end
it "OP cannot change the options" do
it "cannot change the options" do
put :update, params: {
id: post_id, post: { raw: new_option }
}, format: :json
expect(response).not_to be_success
expect(response).not_to be_successful
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(I18n.t(
"poll.edit_window_expired.op_cannot_edit_options",
"poll.edit_window_expired.cannot_edit_default_poll_with_votes",
minutes: poll_edit_window_mins
))
end
it "staff can change the options and votes are merged" do
log_in_user(Fabricate(:moderator))
put :update, params: {
id: post_id, post: { raw: new_option }
}, format: :json
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["post"]["polls"]["poll"]["options"][1]["html"]).to eq("C")
expect(json["post"]["polls"]["poll"]["voters"]).to eq(1)
expect(json["post"]["polls"]["poll"]["options"][0]["votes"]).to eq(1)
expect(json["post"]["polls"]["poll"]["options"][1]["votes"]).to eq(0)
end
it "staff can change the options and anonymous votes are merged" do
post = Post.find_by(id: post_id)
default_poll = post.custom_fields["polls"]["poll"]
add_anonymous_votes(post, default_poll, 7, "5c24fc1df56d764b550ceae1b9319125" => 7)
log_in_user(Fabricate(:moderator))
put :update, params: {
id: post_id, post: { raw: new_option }
}, format: :json
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["post"]["polls"]["poll"]["options"][1]["html"]).to eq("C")
expect(json["post"]["polls"]["poll"]["voters"]).to eq(8)
expect(json["post"]["polls"]["poll"]["options"][0]["votes"]).to eq(8)
expect(json["post"]["polls"]["poll"]["options"][1]["votes"]).to eq(0)
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)
@ -298,7 +248,7 @@ describe PostsController do
title: title, raw: "[poll name=""foo""]\n- A\n- A\n[/poll]"
}, format: :json
expect(response).not_to be_success
expect(response).not_to be_successful
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(I18n.t("poll.named_poll_must_have_different_options", name: "foo"))
end
@ -308,7 +258,7 @@ describe PostsController do
title: title, raw: "[poll name='foo']\n- A\n[/poll]"
}, format: :json
expect(response).not_to be_success
expect(response).not_to be_successful
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(I18n.t("poll.named_poll_must_have_at_least_2_options", name: "foo"))
end
@ -325,8 +275,7 @@ describe PostsController do
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["polls"]["poll"]).to be
expect(json["polls"]["foo"]).to be
expect(Poll.where(post_id: json["id"]).count).to eq(2)
end
it "should have a name" do
@ -334,7 +283,7 @@ describe PostsController do
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_success
expect(response).not_to be_successful
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(I18n.t("poll.multiple_polls_without_name"))
end
@ -344,7 +293,7 @@ describe PostsController do
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_success
expect(response).not_to be_successful
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(I18n.t("poll.multiple_polls_with_same_name", name: "foo"))
end
@ -381,7 +330,7 @@ describe PostsController do
title: title, raw: "[poll]\n- A\n- B\n[/poll]"
}, format: :json
expect(response).not_to be_success
expect(response).not_to be_successful
json = ::JSON.parse(response.body)
expect(json["errors"][0]).to eq(I18n.t("poll.insufficient_rights_to_create"))
end
@ -402,7 +351,7 @@ describe PostsController do
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["polls"]["poll"]).to be
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
end
@ -421,7 +370,7 @@ describe PostsController do
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["polls"]["poll"]).to be
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
end
@ -440,7 +389,7 @@ describe PostsController do
expect(response.status).to eq(200)
json = ::JSON.parse(response.body)
expect(json["cooked"]).to match("data-poll-")
expect(json["polls"]["poll"]).to be
expect(Poll.exists?(post_id: json["id"])).to eq(true)
end
end
end