mirror of
https://github.com/discourse/discourse.git
synced 2026-08-11 02:59:07 +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?`.
299 lines
11 KiB
Ruby
Vendored
299 lines
11 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
RSpec.describe "Topic voting" do
|
|
fab!(:user)
|
|
fab!(:admin) { Fabricate(:admin, trust_level: TrustLevel[4]) }
|
|
fab!(:category1, :category)
|
|
fab!(:category2, :category)
|
|
fab!(:voting_category) { Fabricate(:category, name: "voting category") }
|
|
fab!(:topic1) { Fabricate(:topic, category: category1) }
|
|
fab!(:topic2) { Fabricate(:topic, category: category1) }
|
|
fab!(:topic3) { Fabricate(:topic, category: category2) }
|
|
fab!(:voting_topic1) { Fabricate(:topic, category: voting_category) }
|
|
fab!(:voting_topic2) { Fabricate(:topic, category: voting_category) }
|
|
fab!(:voting_topic3) { Fabricate(:topic, category: voting_category) }
|
|
fab!(:voting_topic4) { Fabricate(:topic, category: voting_category) }
|
|
fab!(:post1) { Fabricate(:post, topic: topic1) }
|
|
fab!(:post2) { Fabricate(:post, topic: topic2) }
|
|
|
|
let(:category_page) { PageObjects::Pages::Category.new }
|
|
let(:banner) { PageObjects::Components::AdminChangesBanner.new }
|
|
let(:topic_page) { PageObjects::Pages::Topic.new }
|
|
let(:user_page) { PageObjects::Pages::User.new }
|
|
let(:admin_page) { PageObjects::Pages::AdminSiteSettings.new }
|
|
let(:form) { PageObjects::Components::FormKit.new(".form-kit") }
|
|
let(:toast) { PageObjects::Components::Toasts.new }
|
|
|
|
before do
|
|
SiteSetting.topic_voting_enabled = true
|
|
sign_in(admin)
|
|
end
|
|
|
|
it "enables voting in category topics and votes" do
|
|
category_page.visit(category1)
|
|
expect(category_page).to have_no_css(category_page.votes)
|
|
|
|
category_page.visit_general(category1)
|
|
category_type_selector = PageObjects::Components::DMenu.new(".category-type-selector")
|
|
category_type_selector.expand
|
|
category_type_selector.option(".category-type-selector__result.--category-type-ideas").click
|
|
banner.click_save
|
|
|
|
try_until_success { expect(Category.can_vote?(category1.id)).to eq(true) }
|
|
|
|
# make a vote
|
|
category_page.visit(category1)
|
|
expect(category_page).to have_css(category_page.votes)
|
|
category_page.select_topic(topic1)
|
|
|
|
expect(topic_page.vote_count).to have_text("0")
|
|
topic_page.vote
|
|
expect(topic_page.vote_popup).to have_text(
|
|
I18n.t("js.topic_voting.see_votes", count: 9, max: 10),
|
|
)
|
|
expect(topic_page.vote_count).to have_text("1")
|
|
|
|
# visit user activity page
|
|
topic_page.click_my_votes
|
|
expect(user_page.active_user_primary_navigation).to have_text("Activity")
|
|
expect(user_page.active_user_secondary_navigation).to have_text("Votes")
|
|
expect(page).to have_css(".topic-list-body tr[data-topic-id=\"#{topic1.id}\"]")
|
|
find(".topic-list-body tr[data-topic-id=\"#{topic1.id}\"] a.raw-link").click
|
|
|
|
# unvoting
|
|
topic_page.remove_vote
|
|
expect(topic_page.vote_count).to have_text("0")
|
|
end
|
|
|
|
context "when navigating between voting topics" do
|
|
fab!(:voting_post1) { Fabricate(:post, topic: voting_topic1) }
|
|
fab!(:voting_post2) { Fabricate(:post, topic: voting_topic2) }
|
|
fab!(:voting_post3) { Fabricate(:post, topic: voting_topic3) }
|
|
|
|
before { DiscourseTopicVoting::CategorySetting.create!(category: voting_category) }
|
|
|
|
it "resets vote UI state after route transitions" do
|
|
voting_topic3.update!(closed: true)
|
|
Fabricate(:post, topic: voting_topic3, raw: "Check out #{voting_topic1.url}")
|
|
Fabricate(:post, topic: voting_topic1, raw: "Check out #{voting_topic2.url}")
|
|
|
|
visit("/t/#{voting_topic3.slug}/#{voting_topic3.id}")
|
|
expect(page).to have_css("button.voting-wrapper__button[disabled]")
|
|
|
|
find("a[href='#{voting_topic1.url}']").click
|
|
expect(page).to have_no_css("button.voting-wrapper__button[disabled]")
|
|
|
|
topic_page.vote
|
|
expect(topic_page.vote_popup).to have_text(
|
|
I18n.t("js.topic_voting.see_votes", count: 9, max: 10),
|
|
)
|
|
|
|
find("a[href='#{voting_topic2.url}']").click
|
|
expect(page).to have_no_css("button.voting-wrapper__button[disabled]")
|
|
|
|
topic_page.vote
|
|
expect(topic_page.vote_popup).to have_text(
|
|
I18n.t("js.topic_voting.see_votes", count: 8, max: 10),
|
|
)
|
|
end
|
|
end
|
|
|
|
context "when toggling watch from the vote menu" do
|
|
fab!(:voting_post) { Fabricate(:post, topic: voting_topic1) }
|
|
|
|
before { DiscourseTopicVoting::CategorySetting.create!(category: voting_category) }
|
|
|
|
it "sets the topic to watching" do
|
|
visit("/t/#{voting_topic1.slug}/#{voting_topic1.id}")
|
|
|
|
topic_page.vote
|
|
expect(topic_page).to have_watch_toggle_off
|
|
|
|
topic_page.click_watch_toggle
|
|
expect(topic_page).to have_watch_toggle_on
|
|
expect(TopicUser.find_by(user: admin, topic: voting_topic1).notification_level).to eq(
|
|
TopicUser.notification_levels[:watching],
|
|
)
|
|
end
|
|
end
|
|
|
|
context "when viewing a closed voting topic without having voted" do
|
|
before { DiscourseTopicVoting::CategorySetting.create!(category: voting_category) }
|
|
|
|
it "does not show the remove vote button" do
|
|
voting_topic1.update!(closed: true)
|
|
Fabricate(:post, topic: voting_topic1)
|
|
|
|
visit("/t/#{voting_topic1.slug}/#{voting_topic1.id}")
|
|
expect(page).to have_css("button.voting-wrapper__button[disabled]")
|
|
expect(topic_page).to have_no_remove_vote_button
|
|
end
|
|
end
|
|
|
|
context "when no votes are left" do
|
|
before do
|
|
DiscourseTopicVoting::CategorySetting.create!(category: category1)
|
|
SiteSetting.topic_voting_tl4_vote_limit = 1
|
|
end
|
|
|
|
it "alerts the user" do
|
|
category_page.visit(category1).select_topic(topic1)
|
|
topic_page.vote
|
|
|
|
expect(topic_page.vote_popup).to have_text(
|
|
I18n.t("js.topic_voting.see_votes", count: 0, max: 1),
|
|
)
|
|
end
|
|
end
|
|
|
|
context "when viewing as anonymous user" do
|
|
fab!(:voting_post) { Fabricate(:post, topic: voting_topic1) }
|
|
|
|
before do
|
|
DiscourseTopicVoting::CategorySetting.create!(category: voting_category)
|
|
Capybara.reset_session!
|
|
end
|
|
|
|
it "redirects to login when clicking vote" do
|
|
visit("/t/#{voting_topic1.slug}/#{voting_topic1.id}")
|
|
find(".title-voting button.voting-wrapper__button").click
|
|
expect(page).to have_current_path("/login")
|
|
end
|
|
end
|
|
|
|
context "when scrolling down on a voting topic" do
|
|
fab!(:voting_posts) { Fabricate.times(21, :post, topic: voting_topic1) }
|
|
|
|
before { DiscourseTopicVoting::CategorySetting.create!(category: voting_category) }
|
|
|
|
it "shows voting in the docked header" do
|
|
sign_in(admin)
|
|
visit("/t/#{voting_topic1.slug}/#{voting_topic1.id}")
|
|
|
|
expect(page).to have_css(".title-voting")
|
|
expect(page).to have_no_css(".header-title-voting")
|
|
|
|
page.execute_script("document.querySelector('#post_4').scrollIntoView()")
|
|
|
|
expect(page).to have_css(".header-title-voting .voting-wrapper")
|
|
end
|
|
end
|
|
|
|
context "when viewing who voted" do
|
|
fab!(:voting_post) { Fabricate(:post, topic: voting_topic1) }
|
|
|
|
before do
|
|
DiscourseTopicVoting::CategorySetting.create!(category: voting_category)
|
|
SiteSetting.topic_voting_show_who_voted = true
|
|
DiscourseTopicVoting::Vote.create!(user: admin, topic: voting_topic1)
|
|
voting_topic1.update_vote_count
|
|
end
|
|
|
|
it "shows voter avatars in the popup" do
|
|
sign_in(admin)
|
|
visit("/t/#{voting_topic1.slug}/#{voting_topic1.id}")
|
|
|
|
find(".title-voting .voting-wrapper__count").click
|
|
expect(page).to have_css(".voting-voters__list")
|
|
expect(page).to have_css(".voting-voters__avatar", count: 1)
|
|
end
|
|
|
|
it "shows empty state when no votes" do
|
|
DiscourseTopicVoting::Vote.where(topic: voting_topic1).destroy_all
|
|
voting_topic1.update_vote_count
|
|
|
|
sign_in(admin)
|
|
visit("/t/#{voting_topic1.slug}/#{voting_topic1.id}")
|
|
|
|
find(".title-voting .voting-wrapper__count").click
|
|
expect(page).to have_css(".voting-voters__empty")
|
|
end
|
|
|
|
it "uses the topic vote count for overflow text while only loading preview rows" do
|
|
stub_const(DiscourseTopicVoting, "VOTER_PREVIEW_LIMIT", 10) do
|
|
Fabricate
|
|
.times(11, :user)
|
|
.each { |user| DiscourseTopicVoting::Vote.create!(user: user, topic: voting_topic1) }
|
|
voting_topic1.update_vote_count
|
|
|
|
sign_in(admin)
|
|
visit("/t/#{voting_topic1.slug}/#{voting_topic1.id}")
|
|
|
|
find(".title-voting .voting-wrapper__count").click
|
|
expect(page).to have_css(".voting-voters__avatar", count: 10)
|
|
expect(page).to have_css(".voting-voters__overflow", text: "and 2 more...")
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when viewing navigation tooltips" do
|
|
before { DiscourseTopicVoting::CategorySetting.create!(category: voting_category) }
|
|
|
|
it "shows custom tooltips in voting categories" do
|
|
category_page.visit(voting_category)
|
|
|
|
hot_item = find("#navigation-bar .nav-item_hot")
|
|
votes_item = find("#navigation-bar .nav-item_votes")
|
|
|
|
expect(hot_item["title"]).to eq(I18n.t("js.topic_voting.hot_nav_help"))
|
|
expect(votes_item["title"]).to eq(I18n.t("js.filters.votes.help"))
|
|
end
|
|
|
|
it "shows default Hot tooltip in non-voting categories" do
|
|
category_page.visit(category1)
|
|
|
|
hot_item = find("#navigation-bar .nav-item_hot")
|
|
expect(hot_item["title"]).to eq(I18n.t("js.filters.hot.help"))
|
|
end
|
|
end
|
|
|
|
context "when vote limits are disabled" do
|
|
fab!(:voting_post) { Fabricate(:post, topic: voting_topic1) }
|
|
fab!(:voting_post2) { Fabricate(:post, topic: voting_topic2) }
|
|
|
|
before do
|
|
DiscourseTopicVoting::CategorySetting.create!(category: voting_category)
|
|
SiteSetting.topic_voting_enable_vote_limits = false
|
|
SiteSetting.topic_voting_tl4_vote_limit = 1
|
|
end
|
|
|
|
it "allows voting past the TL limit and hides limit UI" do
|
|
visit("/t/#{voting_topic1.slug}/#{voting_topic1.id}")
|
|
|
|
topic_page.vote
|
|
expect(topic_page.vote_count).to have_text("1")
|
|
expect(topic_page).to have_voted
|
|
|
|
visit("/t/#{voting_topic2.slug}/#{voting_topic2.id}")
|
|
|
|
topic_page.vote
|
|
expect(topic_page.vote_count).to have_text("1")
|
|
end
|
|
|
|
it "allows removing a vote" do
|
|
DiscourseTopicVoting::Vote.create!(user: admin, topic: voting_topic1)
|
|
voting_topic1.update_vote_count
|
|
|
|
visit("/t/#{voting_topic1.slug}/#{voting_topic1.id}")
|
|
expect(topic_page.vote_count).to have_text("1")
|
|
|
|
topic_page.remove_vote
|
|
expect(topic_page.vote_count).to have_text("0")
|
|
end
|
|
|
|
it "still offers the watch notification prompt after voting" do
|
|
visit("/t/#{voting_topic1.slug}/#{voting_topic1.id}")
|
|
|
|
topic_page.vote
|
|
expect(topic_page.vote_count).to have_text("1")
|
|
expect(topic_page).to have_watch_toggle_off
|
|
expect(page).to have_no_css(".see-votes")
|
|
|
|
topic_page.click_watch_toggle
|
|
expect(topic_page).to have_watch_toggle_on
|
|
expect(TopicUser.find_by(user: admin, topic: voting_topic1).notification_level).to eq(
|
|
TopicUser.notification_levels[:watching],
|
|
)
|
|
end
|
|
end
|
|
end
|