discourse/plugins/discourse-reactions/spec/system/reactions_post_spec.rb
Régis Hanol 2c9ca260ce
FIX: Prevent reactions on posts in archived topics (#39038)
When a post in an archived topic has no existing likes, the backend
omits the like entry from `actions_summary` (no count, no `can_act`, no
`acted`), making `likeAction` null on the frontend.

Core's like button handles this correctly (renders as disabled), but the
reactions plugin — which replaces core's like button — had guards that
checked `likeAction && !(canToggle || can_undo)`. When `likeAction` was
null, the guard was bypassed, allowing clicks that triggered a 403 from
the backend with a misleading "too long ago" error message.

Posts with existing likes were unaffected since `likeAction` existed
with `canToggle: false`, and the guard worked as intended.

This commit:

- Flips the `toggleFromButton` guard from `likeAction && ...` to
`!likeAction || ...` so null likeAction blocks the interaction
- Reorders the `toggle()` condition to check `likeAction?.canToggle`
first, preventing reactions via the emoji picker
- Adds an early return in `pointerOver` when `!likeAction?.canToggle` to
prevent the reaction picker from opening on hover
- Adds optional chaining in the picker's `reactionInfo` getter to fix a
TypeError crash on `post.likeAction.canToggle` when null
- Uses the server error message in `_extractErrors` when available
instead of a hardcoded misleading string, and updates the fallback
translation to a generic message

https://meta.discourse.org/t/399681
2026-04-01 16:38:30 +02:00

111 lines
3.8 KiB
Ruby

# frozen_string_literal: true
describe "Reactions | Post reactions" do
fab!(:current_user, :user)
fab!(:topic)
fab!(:post_1) { Fabricate(:post, topic:) }
fab!(:post_2) { Fabricate(:post, topic:) }
let(:reactions_button) do
PageObjects::Components::PostReactionsButton.new("#post_#{post_2.post_number}")
end
let(:reactions_list) do
PageObjects::Components::PostReactionsList.new("#post_#{post_2.post_number}")
end
before do
SiteSetting.discourse_reactions_enabled = true
sign_in(current_user)
end
context "when topic is archived" do
fab!(:unliked_post) { Fabricate(:post, topic:) }
before { topic.update!(archived: true) }
it "does not allow reacting to a post with no likes" do
visit unliked_post.url
selector = "#post_#{unliked_post.post_number}"
expect(page).to have_no_css("#{selector} .discourse-reactions-actions.can-toggle-reaction")
find("#{selector} .discourse-reactions-reaction-button").click
expect(page).to have_no_css(".dialog-body")
expect(DiscourseReactions::ReactionUser.where(user: current_user).count).to eq(0)
end
end
context "when user has reacted but like_count is 0 and undo window passed" do
fab!(:reaction) { Fabricate(:reaction, post: post_2) }
fab!(:reaction_user) { Fabricate(:reaction_user, reaction:, user: current_user, post: post_2) }
fab!(:post_action) do
Fabricate(
:post_action,
user: current_user,
post: post_2,
post_action_type_id: PostActionType.types[:like],
created_at: 1.day.ago,
)
end
before do
SiteSetting.post_undo_action_window_mins = 10
post_2.update_column(:like_count, 0)
end
it "displays the user's reaction" do
visit post_2.url
expect(reactions_list).to have_reaction(reaction.reaction_value)
end
end
it "can do a basic post reaction with a default reaction" do
visit post_2.url
reactions_button.hover_like_button(post_2.id)
expect(reactions_button).to have_expanded_reactions_picker(post_2.id)
reactions_button.pick_reaction("laughing")
expect(reactions_list).to have_reaction("laughing")
end
it "does not show emoji_deny_list emojis for post reactions" do
SiteSetting.emoji_deny_list = "middle_finger"
visit post_2.url
reactions_button.hover_like_button(post_2.id)
expect(reactions_button).to have_expanded_reactions_picker(post_2.id)
expect(reactions_button).to have_no_emoji("middle_finger")
end
it "only shows enabled reaction emojis" do
SiteSetting.discourse_reactions_enabled_reactions = "clap|hugs"
visit post_2.url
reactions_button.hover_like_button(post_2.id)
expect(reactions_button).to have_expanded_reactions_picker(post_2.id)
expect(reactions_button).to have_no_emoji("open_mouth")
expect(reactions_button).to have_emoji("clap")
expect(reactions_button).to have_emoji("hugs")
end
context "when discourse_reactions_allow_any_emoji is enabled" do
before { SiteSetting.discourse_reactions_allow_any_emoji = true }
it "allows selecting any emoji for a post reaction" do
visit post_2.url
reactions_button.hover_like_button(post_2.id)
expect(reactions_button).to have_expanded_reactions_picker(post_2.id)
reactions_button.pick_any_reaction("yawning_face")
expect(reactions_list).to have_reaction("yawning_face")
end
it "does not allow selecting any emoji_deny_list emojis for post reactions" do
SiteSetting.emoji_deny_list = "middle_finger"
visit post_2.url
reactions_button.hover_like_button(post_2.id)
expect(reactions_button).to have_expanded_reactions_picker(post_2.id)
reactions_button.open_emoji_picker
reactions_button.filter_emoji_picker("middle_finger")
expect(reactions_button).to have_no_emoji_picker_emoji("middle_finger")
end
end
end