mirror of
https://github.com/discourse/discourse.git
synced 2026-08-04 10:39:43 +08:00
When topic voting vote limits are disabled, the "Notify me about new posts" prompt shown after voting never appeared. That prompt is part of the menu the vote button opens, but the menu was only rendered when vote limits were enabled — with limits off the button had no menu at all, so both the notification prompt and the remove-vote action were unreachable. The fix renders the menu for any signed-in user rather than gating it on vote limits. The remaining-votes rows stay tied to limits being enabled, while remove-vote and watch-topic show whenever the user has voted. Removing a vote with limits disabled now goes through the menu, matching the behavior when limits are enabled. Reported at https://meta.discourse.org/t/disabling-vote-limits-seems-to-break-notification-prompt-in-topic-voting/407512 The second commit is follow-up housekeeping on the plugin, kept separate from the fix: removing dead code and duplication in the vote components and initializers, counting a user's votes with `COUNT` instead of loading every row on each current-user serialization, and deleting orphaned i18n keys and a route-map filename left over from the plugin's former name. ### Testing - Added a system spec covering the notification prompt appearing after voting with vote limits disabled. - Existing topic-voting system and JS specs pass; the model spec still covers the trust-level-0 lock via `reached_voting_limit?`.
268 lines
10 KiB
Ruby
Vendored
268 lines
10 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
describe DiscourseTopicVoting do
|
|
let!(:user0) { Fabricate(:user) }
|
|
let!(:user1) { Fabricate(:user) }
|
|
|
|
let(:category1) { Fabricate(:category) }
|
|
let(:category2) { Fabricate(:category) }
|
|
|
|
let!(:topic0) { Fabricate(:topic, category: category1) }
|
|
let!(:topic1) { Fabricate(:topic, category: category2) }
|
|
|
|
before do
|
|
SiteSetting.topic_voting_enabled = true
|
|
SiteSetting.topic_voting_show_who_voted = true
|
|
end
|
|
|
|
it "doesn't allow users to vote more than they are allowed" do
|
|
SiteSetting.topic_voting_tl1_vote_limit = 1
|
|
user0.update!(trust_level: 1)
|
|
|
|
expect(user0.reached_voting_limit?).to eq(false)
|
|
|
|
DiscourseTopicVoting::Vote.create!(user: user0, topic: topic0)
|
|
|
|
expect(user0.reached_voting_limit?).to eq(true)
|
|
end
|
|
|
|
it "does not enforce vote limits when vote limits are disabled" do
|
|
SiteSetting.topic_voting_enable_vote_limits = false
|
|
SiteSetting.topic_voting_tl1_vote_limit = 0
|
|
user0.update!(trust_level: 1)
|
|
|
|
DiscourseTopicVoting::Vote.create!(user: user0, topic: topic0)
|
|
|
|
expect(user0.vote_limit).to be_nil
|
|
expect(user0.votes_left).to be_nil
|
|
expect(user0.can_vote?).to eq(true)
|
|
expect(user0.reached_voting_limit?).to eq(false)
|
|
expect(user0.alert_low_votes?).to eq(false)
|
|
end
|
|
|
|
it "doesn't allow users to vote if their trust level vote limit is 0" do
|
|
SiteSetting.topic_voting_tl1_vote_limit = 0
|
|
user0.update!(trust_level: 1)
|
|
|
|
expect(user0.reached_voting_limit?).to eq(true)
|
|
end
|
|
|
|
context "when a user has an empty string as the votes custom field" do
|
|
before { user0.votes.delete_all }
|
|
|
|
it "returns a vote count of zero" do
|
|
expect(user0.vote_count).to eq(0)
|
|
expect(user0.topics_with_archived_vote.pluck(:topic_id)).to eq([])
|
|
end
|
|
end
|
|
|
|
context "when topic status is changed" do
|
|
it "enqueues a job for releasing/reclaiming votes" do
|
|
blk = Proc.new { |topic| expect(topic).to be_instance_of(Topic) }
|
|
DiscourseEvent.on(:topic_status_updated, &blk)
|
|
|
|
topic1.update_status("closed", true, Discourse.system_user)
|
|
expect(Jobs::DiscourseTopicVoting::VoteRelease.jobs.first["args"].first["topic_id"]).to eq(
|
|
topic1.id,
|
|
)
|
|
|
|
topic1.update_status("closed", false, Discourse.system_user)
|
|
expect(Jobs::DiscourseTopicVoting::VoteReclaim.jobs.first["args"].first["topic_id"]).to eq(
|
|
topic1.id,
|
|
)
|
|
ensure
|
|
DiscourseEvent.off(:topic_status_updated, &blk)
|
|
end
|
|
|
|
it "doesn't enqueue a job for reclaiming votes if the topic is deleted" do
|
|
topic1.update_status("closed", true, Discourse.system_user)
|
|
expect(Jobs::DiscourseTopicVoting::VoteRelease.jobs.first["args"].first["topic_id"]).to eq(
|
|
topic1.id,
|
|
)
|
|
|
|
topic1.trash!
|
|
|
|
topic1.update_status("closed", false, Discourse.system_user)
|
|
expect(Jobs::DiscourseTopicVoting::VoteReclaim.jobs.length).to eq(0)
|
|
end
|
|
|
|
it "doesn't enqueue a job for reclaiming votes when opening an archived topic" do
|
|
topic1.update_status("closed", true, Discourse.system_user)
|
|
expect(Jobs::DiscourseTopicVoting::VoteRelease.jobs.first["args"].first["topic_id"]).to eq(
|
|
topic1.id,
|
|
)
|
|
|
|
topic1.update_status("archived", true, Discourse.system_user)
|
|
|
|
topic1.update_status("closed", false, Discourse.system_user)
|
|
expect(Jobs::DiscourseTopicVoting::VoteReclaim.jobs.length).to eq(0)
|
|
end
|
|
|
|
it "doesn't enqueue a job for reclaiming votes when un-archiving a closed topic" do
|
|
topic1.update_status("archived", true, Discourse.system_user)
|
|
expect(Jobs::DiscourseTopicVoting::VoteRelease.jobs.first["args"].first["topic_id"]).to eq(
|
|
topic1.id,
|
|
)
|
|
|
|
topic1.update_status("closed", true, Discourse.system_user)
|
|
|
|
topic1.update_status("archived", false, Discourse.system_user)
|
|
expect(Jobs::DiscourseTopicVoting::VoteReclaim.jobs.length).to eq(0)
|
|
end
|
|
|
|
it "creates notification that topic was completed" do
|
|
Jobs.run_immediately!
|
|
DiscourseTopicVoting::Vote.create!(user: user0, topic: topic1)
|
|
expect { topic1.update_status("closed", true, user0) }.to change {
|
|
user0.reload.notifications.count
|
|
}.by(1)
|
|
notification = user0.notifications.last
|
|
expect(notification.topic_id).to eq(topic1.id)
|
|
expect(JSON.parse(notification.data)["message"]).to eq("votes_released")
|
|
end
|
|
end
|
|
|
|
context "when a topic is trashed and then recovered" do
|
|
it "released the vote back to the user, then reclaims it on topic recovery" do
|
|
Jobs.run_immediately!
|
|
DiscourseTopicVoting::Vote.create!(user: user0, topic: topic1)
|
|
|
|
topic1.reload.trash!
|
|
expect(user0.reload.topics_with_vote.pluck(:topic_id)).to eq([])
|
|
expect(user0.notifications.count).to eq(0)
|
|
|
|
topic1.recover!
|
|
expect(user0.reload.topics_with_vote.pluck(:topic_id)).to eq([topic1.id])
|
|
end
|
|
end
|
|
|
|
context "when a topic is moved to a category" do
|
|
let(:admin) { Fabricate(:admin) }
|
|
let(:post0) { Fabricate(:post, topic: topic0, post_number: 1) }
|
|
let(:post1) { Fabricate(:post, topic: topic1, post_number: 1) }
|
|
|
|
before do
|
|
DiscourseTopicVoting::CategorySetting.create!(category: category1)
|
|
category1.save!
|
|
Category.reset_voting_cache
|
|
end
|
|
|
|
it "enqueues a job to reclaim votes if voting is enabled for the new category" do
|
|
user = post1.user
|
|
DiscourseTopicVoting::Vote.create!(user: user, topic: post1.topic, archive: true)
|
|
DiscourseTopicVoting::Vote.create!(user: user, topic_id: 456_456, archive: true)
|
|
|
|
PostRevisor.new(post1).revise!(admin, category_id: category1.id)
|
|
expect(Jobs::DiscourseTopicVoting::VoteReclaim.jobs.first["args"].first["topic_id"]).to eq(
|
|
post1.reload.topic_id,
|
|
)
|
|
|
|
Jobs::DiscourseTopicVoting::VoteReclaim.new.execute(topic_id: post1.topic_id)
|
|
user.reload
|
|
|
|
expect(user.topics_with_vote.pluck(:topic_id)).to eq([post1.topic_id])
|
|
expect(user.topics_with_archived_vote.pluck(:topic_id)).to eq([456_456])
|
|
end
|
|
|
|
it "doesn't enqueue a job to reclaim votes if category not changed" do
|
|
user = post1.user
|
|
DiscourseTopicVoting::Vote.create!(user: user, topic: post0.topic, archive: true)
|
|
DiscourseTopicVoting::Vote.create!(user: user, topic_id: 456_456, archive: true)
|
|
|
|
PostRevisor.new(post0).revise!(admin, title: "Updated #{post0.topic.title}")
|
|
expect(Jobs::DiscourseTopicVoting::VoteReclaim.jobs.length).to eq(0)
|
|
end
|
|
|
|
it "doesn't enqueue a job to reclaim votes if topic is closed" do
|
|
DiscourseTopicVoting::Vote.create!(user: post1.user, topic: post1.topic, archive: true)
|
|
post1.topic.update_status("closed", true, Discourse.system_user)
|
|
PostRevisor.new(post1).revise!(admin, category_id: category1.id)
|
|
expect(Jobs::DiscourseTopicVoting::VoteReclaim.jobs.length).to eq(0)
|
|
end
|
|
|
|
it "doesn't enqueue a job to reclaim votes if topic is archived" do
|
|
DiscourseTopicVoting::Vote.create!(user: post1.user, topic: post1.topic, archive: true)
|
|
post1.topic.update_status("archived", true, Discourse.system_user)
|
|
PostRevisor.new(post1).revise!(admin, category_id: category1.id)
|
|
expect(Jobs::DiscourseTopicVoting::VoteReclaim.jobs.length).to eq(0)
|
|
end
|
|
|
|
it "doesn't enqueue a job to reclaim votes if topic is trashed" do
|
|
DiscourseTopicVoting::Vote.create!(user: post1.user, topic: post1.topic, archive: true)
|
|
post1.topic.trash!
|
|
PostRevisor.new(post1).revise!(admin, category_id: category1.id)
|
|
expect(Jobs::DiscourseTopicVoting::VoteReclaim.jobs.length).to eq(0)
|
|
end
|
|
|
|
it "enqueues a job to release votes if voting is disabled for the new category" do
|
|
user = post0.user
|
|
DiscourseTopicVoting::Vote.create!(user: user, topic: post0.topic)
|
|
DiscourseTopicVoting::Vote.create!(user: user, topic_id: 456_456)
|
|
|
|
PostRevisor.new(post0).revise!(admin, category_id: category2.id)
|
|
expect(Jobs::DiscourseTopicVoting::VoteRelease.jobs.first["args"].first["topic_id"]).to eq(
|
|
post0.reload.topic_id,
|
|
)
|
|
|
|
Jobs::DiscourseTopicVoting::VoteRelease.new.execute(topic_id: post0.topic_id)
|
|
user.reload
|
|
|
|
expect(user.topics_with_archived_vote.pluck(:topic_id)).to eq([post0.topic_id])
|
|
expect(user.topics_with_vote.pluck(:topic_id)).to eq([456_456])
|
|
end
|
|
|
|
it "doesn't enqueue a job if the topic has no votes" do
|
|
PostRevisor.new(post0).revise!(admin, category_id: category2.id)
|
|
expect(Jobs::DiscourseTopicVoting::VoteRelease.jobs.size).to eq(0)
|
|
|
|
PostRevisor.new(post1).revise!(admin, category_id: category1.id)
|
|
expect(Jobs::DiscourseTopicVoting::VoteReclaim.jobs.size).to eq(0)
|
|
end
|
|
end
|
|
|
|
context "when a category has voting enabled/disabled" do
|
|
let(:category3) { Fabricate(:category) }
|
|
let(:topic2) { Fabricate(:topic, category: category3) }
|
|
|
|
before do
|
|
DiscourseTopicVoting::CategorySetting.create!(category: category1)
|
|
|
|
DiscourseTopicVoting::CategorySetting.create!(category: category2)
|
|
|
|
DiscourseTopicVoting::CategorySetting.destroy_by(category: category3)
|
|
|
|
DiscourseTopicVoting::Vote.create(user: user0, topic: topic0)
|
|
DiscourseTopicVoting::Vote.create(user: user0, topic: topic1)
|
|
DiscourseTopicVoting::Vote.create(user: user0, topic: topic2, archive: true)
|
|
end
|
|
|
|
it "reclaims votes when voting is disabled on a category" do
|
|
DiscourseTopicVoting::CategorySetting.destroy_by(category: category1)
|
|
|
|
user0.reload
|
|
|
|
expect(
|
|
DiscourseTopicVoting::Vote.where(user: user0, archive: false).map(&:topic_id),
|
|
).to contain_exactly(topic1.id)
|
|
expect(
|
|
DiscourseTopicVoting::Vote.where(user: user0, archive: true).map(&:topic_id),
|
|
).to contain_exactly(topic0.id, topic2.id)
|
|
end
|
|
|
|
it "restores votes when voting is enabled on a category" do
|
|
DiscourseTopicVoting::CategorySetting.create!(category: category3)
|
|
|
|
user0.reload
|
|
|
|
expect(
|
|
DiscourseTopicVoting::Vote.where(user: user0, archive: false).map(&:topic_id),
|
|
).to contain_exactly(topic0.id, topic1.id, topic2.id)
|
|
expect(DiscourseTopicVoting::Vote.where(user: user0, archive: true).map(&:topic_id)).to eq([])
|
|
end
|
|
|
|
it "is not erroring when topic without category" do
|
|
topic1.category.destroy
|
|
expect(topic1.reload.can_vote?).to be_falsey
|
|
end
|
|
end
|
|
end
|