mirror of
https://github.com/discourse/discourse.git
synced 2026-08-11 02:59:07 +08:00
Previously, there was no way to tell who closed (or reopened) a poll — neither the staff action log nor the poll itself recorded it, as raised in [meta](https://meta.discourse.org/t/who-closed-the-poll/405495). This change logs `poll_closed`/`poll_opened` staff actions (auto-close attributed to the system user) and stores `closed_by`/`closed_at` on the poll, surfacing "Closed by @user" in the poll UI and making the closer queryable. --- Also includes a small fix: the poll buttons dropdown rendered a `<dropdown.divider />` after every item, leaving a stray trailing border below the last option; it now renders dividers only between items. **Screenshot** <img width="1400" height="1200" alt="image" src="https://github.com/user-attachments/assets/fd04353a-eac6-448d-904e-47b8e5f8f14f" />
698 lines
21 KiB
Ruby
Vendored
698 lines
21 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
RSpec.describe DiscoursePoll::Poll do
|
|
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
|
|
fab!(:user_2, :user)
|
|
fab!(:user_3, :user)
|
|
|
|
fab!(:post_with_regular_poll) { Fabricate(:post, raw: <<~RAW) }
|
|
[poll]
|
|
* 1
|
|
* 2
|
|
[/poll]
|
|
RAW
|
|
|
|
fab!(:post_with_multiple_poll) { Fabricate(:post, raw: <<~RAW) }
|
|
[poll type=multiple min=2 max=3]
|
|
* 1
|
|
* 2
|
|
* 3
|
|
* 4
|
|
* 5
|
|
[/poll]
|
|
RAW
|
|
|
|
fab!(:post_with_ranked_choice_poll) { Fabricate(:post, raw: <<~RAW) }
|
|
[poll type=ranked_choice public=true]
|
|
* Red
|
|
* Blue
|
|
* Yellow
|
|
[/poll]
|
|
RAW
|
|
|
|
describe ".vote" do
|
|
it "should only allow one vote per user for a regular poll" do
|
|
poll = post_with_regular_poll.polls.first
|
|
|
|
expect do
|
|
DiscoursePoll::Poll.vote(
|
|
user,
|
|
post_with_regular_poll.id,
|
|
"poll",
|
|
poll.poll_options.map(&:digest),
|
|
)
|
|
end.to raise_error(DiscoursePoll::Error, I18n.t("poll.one_vote_per_user"))
|
|
end
|
|
|
|
it "should not allow a ranked vote with all abstentions" do
|
|
poll = post_with_ranked_choice_poll.polls.first
|
|
poll_options = poll.poll_options
|
|
|
|
expect do
|
|
DiscoursePoll::Poll.vote(
|
|
user,
|
|
post_with_ranked_choice_poll.id,
|
|
"poll",
|
|
{
|
|
"0": {
|
|
digest: poll_options.first.digest,
|
|
rank: "0",
|
|
},
|
|
"1": {
|
|
digest: poll_options.second.digest,
|
|
rank: "0",
|
|
},
|
|
"2": {
|
|
digest: poll_options.third.digest,
|
|
rank: "0",
|
|
},
|
|
},
|
|
)
|
|
end.to raise_error(
|
|
DiscoursePoll::Error,
|
|
I18n.t("poll.requires_that_at_least_one_option_is_ranked"),
|
|
)
|
|
end
|
|
|
|
it "should clean up bad votes for a regular poll" do
|
|
poll = post_with_regular_poll.polls.first
|
|
|
|
PollVote.create!(poll: poll, poll_option: poll.poll_options.first, user: user)
|
|
|
|
PollVote.create!(poll: poll, poll_option: poll.poll_options.last, user: user)
|
|
|
|
DiscoursePoll::Poll.vote(
|
|
user,
|
|
post_with_regular_poll.id,
|
|
"poll",
|
|
[poll.poll_options.first.digest],
|
|
)
|
|
|
|
expect(PollVote.where(poll: poll, user: user).pluck(:poll_option_id)).to contain_exactly(
|
|
poll.poll_options.first.id,
|
|
)
|
|
end
|
|
|
|
it "allows user to vote on multiple options correctly for a multiple poll" do
|
|
poll = post_with_multiple_poll.polls.first
|
|
poll_options = poll.poll_options
|
|
|
|
[poll_options.first, poll_options.second, poll_options.third].each do |poll_option|
|
|
PollVote.create!(poll: poll, poll_option: poll_option, user: user)
|
|
end
|
|
|
|
DiscoursePoll::Poll.vote(
|
|
user,
|
|
post_with_multiple_poll.id,
|
|
"poll",
|
|
[poll_options.first.digest, poll_options.second.digest],
|
|
)
|
|
|
|
DiscoursePoll::Poll.vote(
|
|
user_2,
|
|
post_with_multiple_poll.id,
|
|
"poll",
|
|
[poll_options.third.digest, poll_options.fourth.digest],
|
|
)
|
|
|
|
expect(PollVote.where(poll: poll, user: user).pluck(:poll_option_id)).to contain_exactly(
|
|
poll_options.first.id,
|
|
poll_options.second.id,
|
|
)
|
|
|
|
expect(PollVote.where(poll: poll, user: user_2).pluck(:poll_option_id)).to contain_exactly(
|
|
poll_options.third.id,
|
|
poll_options.fourth.id,
|
|
)
|
|
end
|
|
|
|
it "should respect the min/max votes per user for a multiple poll" do
|
|
poll = post_with_multiple_poll.polls.first
|
|
|
|
expect do
|
|
DiscoursePoll::Poll.vote(
|
|
user,
|
|
post_with_multiple_poll.id,
|
|
"poll",
|
|
poll.poll_options.map(&:digest),
|
|
)
|
|
end.to raise_error(DiscoursePoll::Error, I18n.t("poll.max_vote_per_user", count: poll.max))
|
|
|
|
expect do
|
|
DiscoursePoll::Poll.vote(
|
|
user,
|
|
post_with_multiple_poll.id,
|
|
"poll",
|
|
[poll.poll_options.first.digest],
|
|
)
|
|
end.to raise_error(DiscoursePoll::Error, I18n.t("poll.min_vote_per_user", count: poll.min))
|
|
end
|
|
|
|
it "should allow user to vote on a multiple poll even if min option is not configured" do
|
|
post_with_multiple_poll = Fabricate(:post, raw: <<~RAW)
|
|
[poll type=multiple max=3]
|
|
* 1
|
|
* 2
|
|
* 3
|
|
* 4
|
|
* 5
|
|
[/poll]
|
|
RAW
|
|
|
|
poll = post_with_multiple_poll.polls.first
|
|
|
|
DiscoursePoll::Poll.vote(
|
|
user,
|
|
post_with_multiple_poll.id,
|
|
"poll",
|
|
[poll.poll_options.first.digest],
|
|
)
|
|
|
|
expect(PollVote.where(poll: poll, user: user).pluck(:poll_option_id)).to contain_exactly(
|
|
poll.poll_options.first.id,
|
|
)
|
|
end
|
|
|
|
it "should allow user to vote on a multiple poll even if max option is not configured" do
|
|
post_with_multiple_poll = Fabricate(:post, raw: <<~RAW)
|
|
[poll type=multiple min=1]
|
|
* 1
|
|
* 2
|
|
* 3
|
|
* 4
|
|
* 5
|
|
[/poll]
|
|
RAW
|
|
|
|
poll = post_with_multiple_poll.polls.first
|
|
|
|
DiscoursePoll::Poll.vote(
|
|
user,
|
|
post_with_multiple_poll.id,
|
|
"poll",
|
|
[poll.poll_options.first.digest, poll.poll_options.second.digest],
|
|
)
|
|
|
|
expect(PollVote.where(poll: poll, user: user).pluck(:poll_option_id)).to contain_exactly(
|
|
poll.poll_options.first.id,
|
|
poll.poll_options.second.id,
|
|
)
|
|
end
|
|
|
|
it "allows user to vote on options correctly for a ranked choice poll and to vote again" do
|
|
poll = post_with_ranked_choice_poll.polls.first
|
|
poll_options = poll.poll_options
|
|
|
|
DiscoursePoll::Poll.vote(
|
|
user,
|
|
post_with_ranked_choice_poll.id,
|
|
"poll",
|
|
{
|
|
"0": {
|
|
digest: poll_options.first.digest,
|
|
rank: "2",
|
|
},
|
|
"1": {
|
|
digest: poll_options.second.digest,
|
|
rank: "1",
|
|
},
|
|
"2": {
|
|
digest: poll_options.third.digest,
|
|
rank: "0",
|
|
},
|
|
},
|
|
)
|
|
|
|
DiscoursePoll::Poll.vote(
|
|
user_2,
|
|
post_with_ranked_choice_poll.id,
|
|
"poll",
|
|
{
|
|
"0": {
|
|
digest: poll_options.first.digest,
|
|
rank: "0",
|
|
},
|
|
"1": {
|
|
digest: poll_options.second.digest,
|
|
rank: "2",
|
|
},
|
|
"2": {
|
|
digest: poll_options.third.digest,
|
|
rank: "1",
|
|
},
|
|
},
|
|
)
|
|
|
|
DiscoursePoll::Poll.vote(
|
|
user,
|
|
post_with_ranked_choice_poll.id,
|
|
"poll",
|
|
{
|
|
"0": {
|
|
digest: poll_options.first.digest,
|
|
rank: "1",
|
|
},
|
|
"1": {
|
|
digest: poll_options.second.digest,
|
|
rank: "2",
|
|
},
|
|
"2": {
|
|
digest: poll_options.third.digest,
|
|
rank: "0",
|
|
},
|
|
},
|
|
)
|
|
|
|
expect(PollVote.count).to eq(6)
|
|
|
|
expect(PollVote.where(poll: poll, user: user).pluck(:poll_option_id)).to contain_exactly(
|
|
poll_options.first.id,
|
|
poll_options.second.id,
|
|
poll_options.third.id,
|
|
)
|
|
|
|
expect(PollVote.where(poll: poll, user: user_2).pluck(:poll_option_id)).to contain_exactly(
|
|
poll_options.first.id,
|
|
poll_options.second.id,
|
|
poll_options.third.id,
|
|
)
|
|
end
|
|
end
|
|
|
|
describe ".toggle_status" do
|
|
fab!(:admin)
|
|
|
|
it "records who closed the poll and logs a staff action" do
|
|
DiscoursePoll::Poll.toggle_status(admin, post_with_regular_poll.id, "poll", "closed")
|
|
|
|
poll = post_with_regular_poll.polls.first
|
|
expect(poll.closed?).to eq(true)
|
|
expect(poll.closed_by_id).to eq(admin.id)
|
|
expect(poll.closed_at).to be_present
|
|
|
|
log = UserHistory.last
|
|
expect(log.action).to eq(UserHistory.actions[:custom_staff])
|
|
expect(log.custom_type).to eq("poll_closed")
|
|
expect(log.acting_user_id).to eq(admin.id)
|
|
expect(log.subject).to eq("poll")
|
|
end
|
|
|
|
it "logs a poll_opened staff action when a poll is reopened" do
|
|
DiscoursePoll::Poll.toggle_status(admin, post_with_regular_poll.id, "poll", "closed")
|
|
DiscoursePoll::Poll.toggle_status(admin, post_with_regular_poll.id, "poll", "open")
|
|
|
|
expect(UserHistory.last.custom_type).to eq("poll_opened")
|
|
end
|
|
|
|
it "clears the closer when the poll is reopened" do
|
|
DiscoursePoll::Poll.toggle_status(admin, post_with_regular_poll.id, "poll", "closed")
|
|
DiscoursePoll::Poll.toggle_status(admin, post_with_regular_poll.id, "poll", "open")
|
|
|
|
poll = post_with_regular_poll.polls.first
|
|
expect(poll.closed?).to eq(false)
|
|
expect(poll.closed_by_id).to eq(nil)
|
|
expect(poll.closed_at).to eq(nil)
|
|
end
|
|
|
|
it "always serializes closed_by and closed_at so reopened polls clear stale values" do
|
|
json =
|
|
PollSerializer.new(
|
|
post_with_regular_poll.polls.first,
|
|
root: false,
|
|
scope: Guardian.new,
|
|
).as_json
|
|
expect(json).to include(closed_by: nil, closed_at: nil)
|
|
end
|
|
|
|
it "does not surface the system user as the closer when a poll is automatically closed" do
|
|
DiscoursePoll::Poll.toggle_status(
|
|
Discourse.system_user,
|
|
post_with_regular_poll.id,
|
|
"poll",
|
|
"closed",
|
|
)
|
|
|
|
poll = post_with_regular_poll.polls.first
|
|
expect(poll.closed_by_id).to eq(Discourse.system_user.id)
|
|
|
|
json = PollSerializer.new(poll, root: false, scope: Guardian.new).as_json
|
|
expect(json[:closed_by]).to eq(nil)
|
|
expect(json[:closed_at]).to be_present
|
|
end
|
|
|
|
it "does not log when the status is unchanged" do
|
|
expect do
|
|
DiscoursePoll::Poll.toggle_status(admin, post_with_regular_poll.id, "poll", "open")
|
|
end.not_to change { UserHistory.count }
|
|
end
|
|
end
|
|
|
|
describe "post_created" do
|
|
it "publishes on message bus if a there are polls" do
|
|
first_post = Fabricate(:post)
|
|
topic = first_post.topic
|
|
creator = PostCreator.new(user, topic_id: topic.id, raw: <<~RAW)
|
|
[poll]
|
|
* 1
|
|
* 2
|
|
[/poll]
|
|
RAW
|
|
|
|
messages = MessageBus.track_publish("/polls/#{topic.id}") { creator.create! }
|
|
|
|
expect(messages.count).to eq(1)
|
|
end
|
|
|
|
it "does not publish on message bus when a post with no polls is created" do
|
|
first_post = Fabricate(:post)
|
|
topic = first_post.topic
|
|
creator =
|
|
PostCreator.new(user, topic_id: topic.id, raw: "Just a post with definitely no polls")
|
|
|
|
messages = MessageBus.track_publish("/polls/#{topic.id}") { creator.create! }
|
|
|
|
expect(messages.count).to eq(0)
|
|
end
|
|
end
|
|
|
|
describe ".extract" do
|
|
it "skips the polls inside quote" do
|
|
raw = <<~RAW
|
|
[quote="username, post:1, topic:2"]
|
|
[poll type=regular result=always]
|
|
* 1
|
|
* 2
|
|
[/poll]
|
|
[/quote]
|
|
|
|
[poll type=regular result=always]
|
|
* 3
|
|
* 4
|
|
[/poll]
|
|
|
|
Post with a poll and a quoted poll.
|
|
RAW
|
|
|
|
expect(DiscoursePoll::Poll.extract(raw, 2)).to contain_exactly(
|
|
{
|
|
"name" => "poll",
|
|
"options" => [
|
|
{ "html" => "3", "id" => "68b434ff88aeae7054e42cd05a4d9056" },
|
|
{ "html" => "4", "id" => "aa2393b424f2f395abb63bf785760a3b" },
|
|
],
|
|
"status" => "open",
|
|
"type" => "regular",
|
|
},
|
|
)
|
|
end
|
|
|
|
it "limits generated number poll options to one more than the configured maximum" do
|
|
SiteSetting.poll_maximum_options = 3
|
|
|
|
raw = <<~RAW
|
|
[poll type=number min=1 max=100 step=1]
|
|
[/poll]
|
|
RAW
|
|
|
|
poll = DiscoursePoll::Poll.extract(raw, 2).first
|
|
|
|
expect(poll["options"].map { |option| option["html"] }).to eq(%w[1 2 3 4])
|
|
end
|
|
|
|
it "extracts poll when there are multiple quotes in the post" do
|
|
raw = <<~RAW
|
|
[quote="user1, post:1, topic:123"]
|
|
First quote content
|
|
[/quote]
|
|
|
|
[poll type=regular results=always]
|
|
* Option A
|
|
* Option B
|
|
[/poll]
|
|
|
|
[quote="user2, post:2, topic:123"]
|
|
Second quote content
|
|
[/quote]
|
|
|
|
Some text after.
|
|
RAW
|
|
|
|
polls = DiscoursePoll::Poll.extract(raw, 2)
|
|
expect(polls.size).to eq(1)
|
|
expect(polls.first["name"]).to eq("poll")
|
|
expect(polls.first["type"]).to eq("regular")
|
|
expect(polls.first["results"]).to eq("always")
|
|
expect(polls.first["options"].map { |o| o["html"] }).to contain_exactly(
|
|
"Option A",
|
|
"Option B",
|
|
)
|
|
end
|
|
end
|
|
|
|
describe ".serialized_voters" do
|
|
context "with a regular poll" do
|
|
let(:post) { post_with_regular_poll }
|
|
let(:poll) { post.polls.first }
|
|
let(:poll_options) { poll.poll_options }
|
|
let(:votes) do
|
|
{
|
|
user => [poll_options.first.digest],
|
|
user_2 => [poll_options.second.digest],
|
|
user_3 => [poll_options.first.digest],
|
|
}
|
|
end
|
|
|
|
before do
|
|
votes.each_pair { |user, options| DiscoursePoll::Poll.vote(user, post.id, "poll", options) }
|
|
end
|
|
|
|
it "returns all serialized voters" do
|
|
voters = DiscoursePoll::Poll.serialized_voters(poll)
|
|
voters.transform_values! { |users| users.sort_by { |u| u[:id] } }
|
|
expect(voters).to eq(
|
|
{
|
|
poll_options.first.digest => [
|
|
UserNameSerializer.new(user).serializable_hash,
|
|
UserNameSerializer.new(user_3).serializable_hash,
|
|
],
|
|
poll_options.second.digest => [UserNameSerializer.new(user_2).serializable_hash],
|
|
},
|
|
)
|
|
end
|
|
|
|
it "correctly paginates voters" do
|
|
opts = { page: 1, limit: 2 }.with_indifferent_access
|
|
voters = DiscoursePoll::Poll.serialized_voters(poll, opts)
|
|
voters.transform_values! { |users| users.sort_by { |u| u[:id] } }
|
|
expect(voters).to eq(
|
|
{
|
|
poll_options.first.digest => [
|
|
UserNameSerializer.new(user).serializable_hash,
|
|
UserNameSerializer.new(user_3).serializable_hash,
|
|
],
|
|
poll_options.second.digest => [UserNameSerializer.new(user_2).serializable_hash],
|
|
},
|
|
)
|
|
|
|
opts = { page: 2, limit: 2 }.with_indifferent_access
|
|
voters = DiscoursePoll::Poll.serialized_voters(poll, opts)
|
|
expect(voters).to be_nil
|
|
end
|
|
end
|
|
|
|
context "with a multi-choice poll" do
|
|
let(:post) { post_with_multiple_poll }
|
|
let(:poll) { post.polls.first }
|
|
let(:poll_options) { poll.poll_options }
|
|
let(:votes) do
|
|
{
|
|
user => [poll_options.first.digest, poll_options.second.digest],
|
|
user_2 => [poll_options.second.digest, poll_options.third.digest],
|
|
user_3 => [
|
|
poll_options.second.digest,
|
|
poll_options.third.digest,
|
|
poll_options.fourth.digest,
|
|
],
|
|
}
|
|
end
|
|
|
|
before do
|
|
votes.each_pair { |user, options| DiscoursePoll::Poll.vote(user, post.id, "poll", options) }
|
|
end
|
|
|
|
it "returns all serialized voters" do
|
|
voters = DiscoursePoll::Poll.serialized_voters(poll)
|
|
voters.transform_values! { |users| users.sort_by { |u| u[:id] } }
|
|
expect(voters).to eq(
|
|
{
|
|
poll_options.first.digest => [UserNameSerializer.new(user).serializable_hash],
|
|
poll_options.second.digest => [
|
|
UserNameSerializer.new(user).serializable_hash,
|
|
UserNameSerializer.new(user_2).serializable_hash,
|
|
UserNameSerializer.new(user_3).serializable_hash,
|
|
],
|
|
poll_options.third.digest => [
|
|
UserNameSerializer.new(user_2).serializable_hash,
|
|
UserNameSerializer.new(user_3).serializable_hash,
|
|
],
|
|
poll_options.fourth.digest => [UserNameSerializer.new(user_3).serializable_hash],
|
|
},
|
|
)
|
|
end
|
|
|
|
it "correctly paginates voters" do
|
|
opts = { page: 1, limit: 2 }.with_indifferent_access
|
|
voters = DiscoursePoll::Poll.serialized_voters(poll, opts)
|
|
voters.transform_values! { |users| users.sort_by { |u| u[:id] } }
|
|
expect(voters).to eq(
|
|
{
|
|
poll_options.first.digest => [UserNameSerializer.new(user).serializable_hash],
|
|
poll_options.second.digest => [
|
|
UserNameSerializer.new(user).serializable_hash,
|
|
UserNameSerializer.new(user_2).serializable_hash,
|
|
],
|
|
poll_options.third.digest => [
|
|
UserNameSerializer.new(user_2).serializable_hash,
|
|
UserNameSerializer.new(user_3).serializable_hash,
|
|
],
|
|
poll_options.fourth.digest => [UserNameSerializer.new(user_3).serializable_hash],
|
|
},
|
|
)
|
|
|
|
opts = { page: 2, limit: 2 }.with_indifferent_access
|
|
voters = DiscoursePoll::Poll.serialized_voters(poll, opts)
|
|
voters.transform_values! { |users| users.sort_by { |u| u[:id] } }
|
|
expect(voters).to eq(
|
|
{ poll_options.second.digest => [UserNameSerializer.new(user_3).serializable_hash] },
|
|
)
|
|
|
|
opts = { page: 3, limit: 2 }.with_indifferent_access
|
|
voters = DiscoursePoll::Poll.serialized_voters(poll, opts)
|
|
expect(voters).to be_nil
|
|
end
|
|
end
|
|
|
|
context "with a ranked choice poll" do
|
|
let(:post) { post_with_ranked_choice_poll }
|
|
let(:poll) { post.polls.first }
|
|
let(:poll_options) { poll.poll_options }
|
|
let(:votes) do
|
|
{
|
|
user => {
|
|
"0": {
|
|
digest: poll_options.first.digest,
|
|
rank: "0",
|
|
},
|
|
"1": {
|
|
digest: poll_options.second.digest,
|
|
rank: "1",
|
|
},
|
|
"2": {
|
|
digest: poll_options.third.digest,
|
|
rank: "2",
|
|
},
|
|
},
|
|
user_2 => {
|
|
"0": {
|
|
digest: poll_options.second.digest,
|
|
rank: "0",
|
|
},
|
|
"1": {
|
|
digest: poll_options.third.digest,
|
|
rank: "1",
|
|
},
|
|
"2": {
|
|
digest: poll_options.first.digest,
|
|
rank: "2",
|
|
},
|
|
},
|
|
user_3 => {
|
|
"0": {
|
|
digest: poll_options.third.digest,
|
|
rank: "0",
|
|
},
|
|
"1": {
|
|
digest: poll_options.first.digest,
|
|
rank: "1",
|
|
},
|
|
"2": {
|
|
digest: poll_options.second.digest,
|
|
rank: "2",
|
|
},
|
|
},
|
|
}
|
|
end
|
|
|
|
before do
|
|
votes.each_pair { |user, options| DiscoursePoll::Poll.vote(user, post.id, "poll", options) }
|
|
end
|
|
|
|
it "returns all serialized voters" do
|
|
voters = DiscoursePoll::Poll.serialized_voters(poll)
|
|
voters.transform_values! { |users| users.sort_by { |ranked_u| ranked_u[:user][:id] } }
|
|
expect(voters).to eq(
|
|
{
|
|
poll_options.first.digest => [
|
|
{ user: UserNameSerializer.new(user).serializable_hash, rank: "Abstain" },
|
|
{ user: UserNameSerializer.new(user_2).serializable_hash, rank: "2" },
|
|
{ user: UserNameSerializer.new(user_3).serializable_hash, rank: "1" },
|
|
],
|
|
poll_options.second.digest => [
|
|
{ user: UserNameSerializer.new(user).serializable_hash, rank: "1" },
|
|
{ user: UserNameSerializer.new(user_2).serializable_hash, rank: "Abstain" },
|
|
{ user: UserNameSerializer.new(user_3).serializable_hash, rank: "2" },
|
|
],
|
|
poll_options.third.digest => [
|
|
{ user: UserNameSerializer.new(user).serializable_hash, rank: "2" },
|
|
{ user: UserNameSerializer.new(user_2).serializable_hash, rank: "1" },
|
|
{ user: UserNameSerializer.new(user_3).serializable_hash, rank: "Abstain" },
|
|
],
|
|
},
|
|
)
|
|
end
|
|
|
|
it "correctly paginates voters" do
|
|
opts = { page: 1, limit: 2 }.with_indifferent_access
|
|
voters = DiscoursePoll::Poll.serialized_voters(poll, opts)
|
|
voters.transform_values! { |users| users.sort_by { |ranked_u| ranked_u[:user][:id] } }
|
|
expect(voters).to eq(
|
|
{
|
|
poll_options.first.digest => [
|
|
{ user: UserNameSerializer.new(user).serializable_hash, rank: "Abstain" },
|
|
{ user: UserNameSerializer.new(user_2).serializable_hash, rank: "2" },
|
|
],
|
|
poll_options.second.digest => [
|
|
{ user: UserNameSerializer.new(user).serializable_hash, rank: "1" },
|
|
{ user: UserNameSerializer.new(user_2).serializable_hash, rank: "Abstain" },
|
|
],
|
|
poll_options.third.digest => [
|
|
{ user: UserNameSerializer.new(user).serializable_hash, rank: "2" },
|
|
{ user: UserNameSerializer.new(user_2).serializable_hash, rank: "1" },
|
|
],
|
|
},
|
|
)
|
|
|
|
opts = { page: 2, limit: 2 }.with_indifferent_access
|
|
voters = DiscoursePoll::Poll.serialized_voters(poll, opts)
|
|
voters.transform_values! { |users| users.sort_by { |ranked_u| ranked_u[:user][:id] } }
|
|
expect(voters).to eq(
|
|
{
|
|
poll_options.first.digest => [
|
|
{ user: UserNameSerializer.new(user_3).serializable_hash, rank: "1" },
|
|
],
|
|
poll_options.second.digest => [
|
|
{ user: UserNameSerializer.new(user_3).serializable_hash, rank: "2" },
|
|
],
|
|
poll_options.third.digest => [
|
|
{ user: UserNameSerializer.new(user_3).serializable_hash, rank: "Abstain" },
|
|
],
|
|
},
|
|
)
|
|
|
|
opts = { page: 3, limit: 2 }.with_indifferent_access
|
|
voters = DiscoursePoll::Poll.serialized_voters(poll, opts)
|
|
expect(voters).to be_nil
|
|
end
|
|
end
|
|
end
|
|
end
|