2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-03-04 01:15:08 +08:00
discourse/spec/system/admin_user_spec.rb
Régis Hanol 4acd9c6753
FIX: Custom groups save button not clearing on admin user page (#37625)
The approve/reject buttons for custom group changes on the admin user
page would remain visible after saving, even though the changes were
persisted successfully. Removing a group also failed to update the UI.

Three issues in the reactivity chain caused this:

1. The `@filter` computed macros (classic Ember) on the AdminUser model
did not react to `@trackedArray` mutations. Replaced `customGroups` and
`automaticGroups` with native getters so they participate in Glimmer
autotracking.

2. The controller's `groupAdded()` and `groupRemoved()` wrappers did not
return their promise chains, so `await` in `saveCustomGroups()` resolved
immediately before the API calls completed and before the groups array
was updated.

3. The controller's `customGroupIds`, `customGroupsDirty`, and
`automaticGroups` used `@discourseComputed` which could not track
changes from native getters on the model. Converted these to native
getters and made `customGroupIdsBuffer` `@tracked`.

Additionally, `saveCustomGroups()` now awaits each operation
sequentially and calls `resetCustomGroups()` on completion to sync the
buffer with the updated group list, clearing the dirty state.

Note: only the properties involved in the groups reactivity chain were
converted to modern Glimmer patterns. The rest of the controller and
model still use classic Ember (`@discourseComputed`, `this.set`, etc.)
and can be modernized separately.

Ref - https://meta.discourse.org/t/395210
2026-02-10 19:49:04 +01:00

378 lines
13 KiB
Ruby

# frozen_string_literal: true
describe "Admin User Page", type: :system do
fab!(:current_user, :admin)
let(:admin_users_page) { PageObjects::Pages::AdminUsers.new }
let(:admin_user_page) { PageObjects::Pages::AdminUser.new }
let(:suspend_user_modal) { PageObjects::Modals::PenalizeUser.new("suspend") }
let(:silence_user_modal) { PageObjects::Modals::PenalizeUser.new("silence") }
before { sign_in(current_user) }
context "when visiting an admin's page" do
fab!(:admin)
before { admin_user_page.visit(admin) }
it "doesn't display the suspend or silence buttons" do
expect(admin_user_page).to have_no_suspend_button
expect(admin_user_page).to have_no_silence_button
end
end
context "when visiting a moderator's page" do
fab!(:moderator)
before { admin_user_page.visit(moderator) }
it "doesn't display the suspend or silence buttons" do
expect(admin_user_page).to have_no_suspend_button
expect(admin_user_page).to have_no_silence_button
end
end
context "when visiting a regular user's page" do
fab!(:user) { Fabricate(:user, ip_address: "93.123.44.90") }
fab!(:similar_user) { Fabricate(:user, ip_address: user.ip_address) }
fab!(:another_mod) { Fabricate(:moderator, ip_address: user.ip_address) }
fab!(:another_admin) { Fabricate(:admin, ip_address: user.ip_address) }
before { admin_user_page.visit(user) }
it "can list accounts with identical IPs" do
find(".ip-lookup-trigger").click
expect(page).to have_content("#{I18n.t("js.ip_lookup.other_accounts")}\n3")
table = page.find(".other-accounts table")
expect(table).to have_content(similar_user.username)
expect(table).to have_content(another_mod.username)
expect(table).to have_content(another_admin.username)
end
it "displays the suspend and silence buttons" do
expect(admin_user_page).to have_suspend_button
expect(admin_user_page).to have_silence_button
end
it "displays username in the title" do
expect(page).to have_css(".display-row.username")
expect(page.title).to eq("#{user.username} - Users - Admin - Discourse")
end
describe "the upcoming changes section" do
fab!(:group1) { Fabricate(:group, name: "test_group_1") }
fab!(:group2) { Fabricate(:group, name: "test_group_2") }
before do
SiteSetting.enable_upcoming_changes = true
mock_upcoming_change_metadata(
{
enable_upload_debug_mode: {
impact: "feature,all_members",
status: :beta,
impact_type: "feature",
impact_role: "all_members",
},
},
)
end
context "when the change is enabled for everyone" do
before { SiteSetting.enable_upload_debug_mode = true }
it "displays the upcoming change with enabled status and correct reason" do
admin_user_page.visit(user)
expect(admin_user_page).to have_upcoming_change("enable_upload_debug_mode")
expect(admin_user_page.upcoming_change("enable_upload_debug_mode")).to be_enabled
expect(admin_user_page.upcoming_change("enable_upload_debug_mode")).to have_reason(
"enabled_for_everyone",
)
expect(
admin_user_page.upcoming_change("enable_upload_debug_mode"),
).to have_no_specific_groups
end
end
context "when the change is disabled for everyone" do
before { SiteSetting.enable_upload_debug_mode = false }
it "displays the upcoming change with disabled status and correct reason" do
admin_user_page.visit(user)
expect(admin_user_page).to have_upcoming_change("enable_upload_debug_mode")
expect(admin_user_page.upcoming_change("enable_upload_debug_mode")).to be_disabled
expect(admin_user_page.upcoming_change("enable_upload_debug_mode")).to have_reason(
"enabled_for_no_one",
)
expect(
admin_user_page.upcoming_change("enable_upload_debug_mode"),
).to have_no_specific_groups
end
end
context "when the change is enabled for specific groups" do
before do
SiteSetting.enable_upload_debug_mode = true
Fabricate(
:site_setting_group,
name: "enable_upload_debug_mode",
group_ids: "#{group1.id}|#{group2.id}",
)
end
context "when the user belongs to one of those groups" do
before { group1.add(user) }
it "displays the upcoming change with enabled status, correct reason, and specific groups" do
admin_user_page.visit(user)
expect(admin_user_page).to have_upcoming_change("enable_upload_debug_mode")
expect(admin_user_page.upcoming_change("enable_upload_debug_mode")).to be_enabled
expect(admin_user_page.upcoming_change("enable_upload_debug_mode")).to have_reason(
"in_specific_groups",
)
expect(
admin_user_page.upcoming_change("enable_upload_debug_mode"),
).to have_specific_groups(["test_group_1"])
end
end
context "when the user belongs to multiple groups" do
before do
group1.add(user)
group2.add(user)
end
it "displays the upcoming change with all groups" do
admin_user_page.visit(user)
expect(admin_user_page).to have_upcoming_change("enable_upload_debug_mode")
expect(admin_user_page.upcoming_change("enable_upload_debug_mode")).to be_enabled
expect(admin_user_page.upcoming_change("enable_upload_debug_mode")).to have_reason(
"in_specific_groups",
)
expect(
admin_user_page.upcoming_change("enable_upload_debug_mode"),
).to have_specific_groups(%w[test_group_1 test_group_2])
end
end
context "when the user does not belong to any of those groups" do
it "displays the upcoming change with disabled status, correct reason, and no specific groups" do
admin_user_page.visit(user)
expect(admin_user_page).to have_upcoming_change("enable_upload_debug_mode")
expect(admin_user_page.upcoming_change("enable_upload_debug_mode")).to be_disabled
expect(admin_user_page.upcoming_change("enable_upload_debug_mode")).to have_reason(
"not_in_specific_groups",
)
expect(
admin_user_page.upcoming_change("enable_upload_debug_mode"),
).to have_no_specific_groups
end
end
end
it "does not show conceptual upcoming changes" do
mock_upcoming_change_metadata(
{
enable_upload_debug_mode: {
impact: "feature,all_members",
status: :beta,
impact_type: "feature",
impact_role: "all_members",
},
about_page_extra_groups_show_description: {
impact: "feature,all_members",
status: :conceptual,
impact_type: "feature",
impact_role: "all_members",
},
},
)
admin_user_page.visit(user)
expect(admin_user_page).to have_upcoming_change("enable_upload_debug_mode")
expect(admin_user_page).to have_no_upcoming_change(
"about_page_extra_groups_show_description",
)
end
end
describe "the suspend user modal" do
it "displays the list of users who share the same IP but are not mods or admins" do
admin_user_page.click_suspend_button
expect(suspend_user_modal.similar_users).to contain_exactly(similar_user.username)
expect(admin_user_page.similar_users_warning).to include(
I18n.t("admin_js.admin.user.other_matches", count: 1, username: user.username),
)
end
it "suspends and unsuspends the user" do
admin_user_page.click_suspend_button
suspend_user_modal.fill_in_suspend_reason("spamming")
suspend_user_modal.set_future_date("tomorrow")
suspend_user_modal.perform
expect(suspend_user_modal).to be_closed
expect(page).to have_css(".suspension-info")
admin_user_page.click_unsuspend_button
expect(page).not_to have_css(".suspension-info")
end
it "displays error when used is already suspended" do
admin_user_page.click_suspend_button
suspend_user_modal.fill_in_suspend_reason("spamming")
suspend_user_modal.set_future_date("tomorrow")
user.update!(suspended_till: 1.day.from_now)
StaffActionLogger.new(current_user).log_user_suspend(user, "spamming")
suspend_user_modal.perform
expect(suspend_user_modal).to have_error_message(
"User was already suspended by #{current_user.username} just now.",
)
expect(suspend_user_modal).to be_open
end
end
describe "the silence user modal" do
it "displays the list of users who share the same IP but are not mods or admins" do
admin_user_page.click_silence_button
expect(silence_user_modal.similar_users).to contain_exactly(similar_user.username)
expect(admin_user_page.similar_users_warning).to include(
I18n.t("admin_js.admin.user.other_matches", count: 1, username: user.username),
)
end
it "silence and unsilence the user" do
admin_user_page.click_silence_button
silence_user_modal.fill_in_silence_reason("spamming")
silence_user_modal.set_future_date("tomorrow")
silence_user_modal.perform
expect(silence_user_modal).to be_closed
expect(page).to have_css(".silence-info")
admin_user_page.click_unsilence_button
expect(page).not_to have_css(".silence-info")
end
end
end
describe "custom groups" do
fab!(:user)
fab!(:group)
it "saves and displays the added group" do
admin_user_page.visit(user)
group_chooser = admin_user_page.custom_groups_chooser
group_chooser.expand
group_chooser.select_row_by_value(group.id)
expect(admin_user_page).to have_custom_groups_save_button
admin_user_page.save_custom_groups
expect(admin_user_page).to have_no_custom_groups_save_button
expect(admin_user_page).to have_custom_group(group.name)
expect(GroupUser.exists?(user:, group:)).to eq(true)
end
it "saves and removes the group from the list" do
group.add(user)
admin_user_page.visit(user)
expect(admin_user_page).to have_custom_group(group.name)
group_chooser = admin_user_page.custom_groups_chooser
group_chooser.expand
group_chooser.unselect_by_name(group.name)
expect(admin_user_page).to have_custom_groups_save_button
admin_user_page.save_custom_groups
expect(admin_user_page).to have_no_custom_groups_save_button
expect(admin_user_page).to have_no_custom_group(group.name)
expect(GroupUser.exists?(user:, group:)).to eq(false)
end
end
context "when logged in as a moderator" do
fab!(:current_user, :moderator)
context "when visiting a regular user's page" do
fab!(:user)
context "when moderators_change_trust_levels setting is enabled" do
before { SiteSetting.moderators_change_trust_levels = true }
it "the dropdown to change trust level is enabled" do
admin_user_page.visit(user)
expect(admin_user_page).to have_change_trust_level_dropdown_enabled
end
end
context "when moderators_change_trust_levels setting is disabled" do
before { SiteSetting.moderators_change_trust_levels = false }
it "the dropdown to change trust level is disabled" do
admin_user_page.visit(user)
expect(admin_user_page).to have_change_trust_level_dropdown_disabled
end
end
end
end
context "when navigating to a user's page from the list" do
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
it "displays the groups correctly" do
admin_users_page.visit
# navigate to the user page
admin_users_page.user_row(user.id).username.click
# ensure the automatic groups are displayed
page.find(".admin-user__automatic-groups").has_text?("trust_level")
end
describe "with user action logs" do
let(:staff_action_logs_page) { PageObjects::Pages::AdminStaffActionLogs.new }
fab!(:user_a, :user)
fab!(:user_a_silenced) do
Fabricate(:user_history, action: UserHistory.actions[:silence_user], target_user: user_a)
end
fab!(:user_b, :user)
fab!(:user_b_silenced) do
Fabricate(:user_history, action: UserHistory.actions[:silence_user], target_user: user_b)
end
# Relates to
# meta.discourse.org/t/-/387508
it "refreshes the filter when navigating thought the action logs button" do
admin_users_page.visit
admin_users_page.user_row(user_a.id).username.click
admin_user_page.click_action_logs_button
expect(staff_action_logs_page).to have_log_row(user_a_silenced)
expect(staff_action_logs_page).to have_no_log_row(user_b_silenced)
page.go_back # navigate back to user page
page.go_back # navigate back to user list
admin_users_page.user_row(user_b.id).username.click
admin_user_page.click_action_logs_button
expect(staff_action_logs_page).to have_log_row(user_b_silenced)
expect(staff_action_logs_page).to have_no_log_row(user_a_silenced)
end
end
end
end