mirror of
https://github.com/discourse/discourse.git
synced 2026-08-05 18:57:20 -05:00
FEATURE: Allow admins to see users' upcoming change stats (#36768)
Allows admins and moderators to see users' upcoming changes and the reason the user has that change enabled, or not enabled. It is added to the existing page at `/admin/users/:id/:username` It could either be because the change is enabled for Everyone, or only for specific groups: We give a shortcut link to each group so the admin can inspect it.
This commit is contained in:
@@ -1025,6 +1025,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.user-upcoming-changes-table {
|
||||
margin-top: var(--space-4);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.paginated-topics-list {
|
||||
.user-content {
|
||||
width: 100%;
|
||||
|
||||
@@ -1963,6 +1963,10 @@ class User < ActiveRecord::Base
|
||||
UpcomingChanges.enabled_for_user?(upcoming_change, self)
|
||||
end
|
||||
|
||||
def upcoming_change_stats(acting_guardian)
|
||||
UpcomingChanges.stats_for_user(user: self, acting_guardian: acting_guardian)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def badge_grant
|
||||
|
||||
@@ -37,7 +37,8 @@ class AdminDetailedUserSerializer < AdminUserSerializer
|
||||
:api_key_count,
|
||||
:external_ids,
|
||||
:similar_users_count,
|
||||
:latest_export
|
||||
:latest_export,
|
||||
:upcoming_changes_stats
|
||||
|
||||
has_one :approved_by, serializer: BasicUserSerializer, embed: :objects
|
||||
has_one :suspended_by, serializer: BasicUserSerializer, embed: :objects
|
||||
@@ -183,4 +184,12 @@ class AdminDetailedUserSerializer < AdminUserSerializer
|
||||
|
||||
UserExportSerializer.new(export, scope:).as_json if export
|
||||
end
|
||||
|
||||
def upcoming_changes_stats
|
||||
object.upcoming_change_stats(scope)
|
||||
end
|
||||
|
||||
def include_upcoming_changes_stats?
|
||||
SiteSetting.enable_upcoming_changes && scope.is_staff?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1482,7 +1482,17 @@ en:
|
||||
private_message: "Message"
|
||||
private_messages: "Messages"
|
||||
upcoming_changes:
|
||||
title: "Upcoming change enabled"
|
||||
title: "Upcoming changes"
|
||||
for_user:
|
||||
upcoming_change: "Upcoming change"
|
||||
enabled: "Enabled?"
|
||||
why: "Why?"
|
||||
why_reasons:
|
||||
not_in_specific_groups: "The user is not in the specific groups required for this change"
|
||||
in_specific_groups: "The user is in the specific groups required for this change"
|
||||
enabled_for_everyone: "This change is enabled for all members"
|
||||
enabled_for_no_one: "This change is disabled for all members"
|
||||
|
||||
user_notifications:
|
||||
filters:
|
||||
filter_by: "Filter By"
|
||||
@@ -1826,7 +1836,8 @@ en:
|
||||
tags: "Tags"
|
||||
interface: "Interface"
|
||||
apps: "Apps"
|
||||
navigation_menu: "Navigation Menu"
|
||||
navigation_menu: "Navigation menu"
|
||||
upcoming_changes: "Upcoming changes"
|
||||
|
||||
change_password:
|
||||
success: "(email sent)"
|
||||
@@ -8144,6 +8155,11 @@ en:
|
||||
approve_bulk_success: "Success! All selected users have been approved and notified."
|
||||
time_read: "Read Time"
|
||||
post_edits_count: "Post Edits"
|
||||
upcoming_changes:
|
||||
title: "User upcoming changes"
|
||||
description: "To modify groups or to enable or disable upcoming changes, please visit the <a href='%{basePath}/admin/config/upcoming-changes'>upcoming changes config page</a>."
|
||||
filter_placeholder: "Filter upcoming changes by name or description"
|
||||
filter_no_results: "No upcoming changes found"
|
||||
exports:
|
||||
title: User exports
|
||||
download:
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { array } from "@ember/helper";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import AdminFilterControls from "discourse/admin/components/admin-filter-controls";
|
||||
import { UPCOMING_CHANGES_USER_ENABLED_REASONS } from "discourse/lib/constants";
|
||||
import { bind } from "discourse/lib/decorators";
|
||||
import getUrl from "discourse/lib/get-url";
|
||||
import { escapeExpression } from "discourse/lib/utilities";
|
||||
import { and, eq } from "discourse/truth-helpers";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
export default class AdminUserUpcomingChanges extends Component {
|
||||
@bind
|
||||
reasonKey(reason) {
|
||||
return `user.upcoming_changes.why_reasons.${reason}`;
|
||||
}
|
||||
|
||||
@bind
|
||||
getGroupLinks(groups) {
|
||||
return (
|
||||
"(" +
|
||||
groups
|
||||
.map((group) => {
|
||||
return `<a href="${getUrl(`/groups/${encodeURIComponent(group)}`)}">${escapeExpression(group)}</a>`;
|
||||
})
|
||||
.join(", ") +
|
||||
")"
|
||||
);
|
||||
}
|
||||
|
||||
<template>
|
||||
<AdminFilterControls
|
||||
@array={{@user.upcoming_changes_stats}}
|
||||
@searchableProps={{array "humanized_name" "description"}}
|
||||
@inputPlaceholder={{i18n
|
||||
"admin.user.upcoming_changes.filter_placeholder"
|
||||
}}
|
||||
@noResultsMessage={{i18n "admin.user.upcoming_changes.filter_no_results"}}
|
||||
>
|
||||
<:content as |filteredChanges|>
|
||||
<table class="d-table user-upcoming-changes-table">
|
||||
<thead class="d-table__header">
|
||||
<th>{{i18n "user.upcoming_changes.for_user.upcoming_change"}}</th>
|
||||
<th>{{i18n "user.upcoming_changes.for_user.enabled"}}</th>
|
||||
<th>{{i18n "user.upcoming_changes.for_user.why"}}</th>
|
||||
</thead>
|
||||
<tbody class="d-table__body">
|
||||
{{#each filteredChanges as |upcomingChange|}}
|
||||
<tr
|
||||
class="d-table__row"
|
||||
data-upcoming-change-name={{upcomingChange.name}}
|
||||
>
|
||||
<td class="d-table__cell --overview">
|
||||
<div class="d-table__overview-name">
|
||||
{{upcomingChange.humanized_name}}
|
||||
</div>
|
||||
{{#if upcomingChange.description}}
|
||||
<div class="d-table__overview-about">
|
||||
{{upcomingChange.description}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class="d-table__cell">
|
||||
<div class="d-admin-row__mobile-label">
|
||||
{{i18n "user.upcoming_changes.for_user.enabled"}}
|
||||
</div>
|
||||
|
||||
<span class="upcoming-change-enabled-status">
|
||||
{{if
|
||||
upcomingChange.enabled
|
||||
(i18n "yes_value")
|
||||
(i18n "no_value")
|
||||
}}
|
||||
</span>
|
||||
</td>
|
||||
<td class="d-table__cell">
|
||||
<div class="d-admin-row__mobile-label">
|
||||
{{i18n "user.upcoming_changes.for_user.why"}}
|
||||
</div>
|
||||
|
||||
<span class="upcoming-change-reason">
|
||||
{{i18n
|
||||
(this.reasonKey upcomingChange.reason)
|
||||
username=@user.username
|
||||
}}
|
||||
</span>
|
||||
{{#if
|
||||
(and
|
||||
(eq
|
||||
upcomingChange.reason
|
||||
UPCOMING_CHANGES_USER_ENABLED_REASONS.in_specific_groups
|
||||
)
|
||||
upcomingChange.specific_groups.length
|
||||
)
|
||||
}}
|
||||
<span class="upcoming-change-groups">
|
||||
{{htmlSafe
|
||||
(this.getGroupLinks upcomingChange.specific_groups)
|
||||
}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</:content>
|
||||
</AdminFilterControls>
|
||||
</template>
|
||||
}
|
||||
@@ -22,6 +22,7 @@ export default class AdminUserIndexController extends Controller {
|
||||
@service router;
|
||||
@service dialog;
|
||||
@service adminTools;
|
||||
@service siteSettings;
|
||||
@service modal;
|
||||
|
||||
originalPrimaryGroupId = null;
|
||||
|
||||
@@ -3,11 +3,13 @@ import { LinkTo } from "@ember/routing";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import AdminEditableField from "discourse/admin/components/admin-editable-field";
|
||||
import AdminUserExportsTable from "discourse/admin/components/admin-user-exports-table";
|
||||
import AdminUserUpcomingChanges from "discourse/admin/components/admin-user-upcoming-changes";
|
||||
import IpLookup from "discourse/admin/components/ip-lookup";
|
||||
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import avatar from "discourse/helpers/avatar";
|
||||
import basePath from "discourse/helpers/base-path";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
import formatDate from "discourse/helpers/format-date";
|
||||
import formatDuration from "discourse/helpers/format-duration";
|
||||
@@ -931,6 +933,22 @@ export default <template>
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
{{#if
|
||||
(and
|
||||
@controller.currentUser.staff
|
||||
@controller.model.upcoming_changes_stats
|
||||
@controller.siteSettings.enable_upcoming_changes
|
||||
)
|
||||
}}
|
||||
<section class="details">
|
||||
<h1>{{i18n "admin.user.upcoming_changes.title"}}</h1>
|
||||
<p>{{htmlSafe
|
||||
(i18n "admin.user.upcoming_changes.description" basePath=basePath)
|
||||
}}</p>
|
||||
<AdminUserUpcomingChanges @user={{@controller.model}} />
|
||||
</section>
|
||||
{{/if}}
|
||||
|
||||
{{#if @controller.currentUser.admin}}
|
||||
<AdminUserExportsTable @model={{@controller.model}} />
|
||||
{{/if}}
|
||||
|
||||
@@ -115,6 +115,13 @@ export const INVITE_DESCRIPTION_MAX_LENGTH = 100;
|
||||
|
||||
export const USER_OPTION_COMPOSITION_MODES = { markdown: 0, rich: 1 };
|
||||
|
||||
export const UPCOMING_CHANGES_USER_ENABLED_REASONS = {
|
||||
enabled_for_everyone: "enabled_for_everyone",
|
||||
enabled_for_no_one: "enabled_for_no_one",
|
||||
in_specific_groups: "in_specific_groups",
|
||||
not_in_specific_groups: "not_in_specific_groups",
|
||||
};
|
||||
|
||||
export const INTERFACE_COLOR_MODES = {
|
||||
AUTO: 1,
|
||||
LIGHT: 2,
|
||||
|
||||
@@ -97,6 +97,8 @@ task "javascript:update_constants" => :environment do
|
||||
|
||||
export const USER_OPTION_COMPOSITION_MODES = #{UserOption.composition_mode_types.to_json};
|
||||
|
||||
export const UPCOMING_CHANGES_USER_ENABLED_REASONS = #{UpcomingChanges.user_enabled_reasons.to_json};
|
||||
|
||||
export const INTERFACE_COLOR_MODES = {
|
||||
AUTO: #{UserOption::AUTO_MODE},
|
||||
LIGHT: #{UserOption::LIGHT_MODE},
|
||||
|
||||
+51
-1
@@ -1,9 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module UpcomingChanges
|
||||
def self.user_enabled_reasons
|
||||
@user_enabled_reasons ||=
|
||||
::Enum.new(
|
||||
enabled_for_everyone: :enabled_for_everyone,
|
||||
enabled_for_no_one: :enabled_for_no_one,
|
||||
in_specific_groups: :in_specific_groups,
|
||||
not_in_specific_groups: :not_in_specific_groups,
|
||||
)
|
||||
end
|
||||
|
||||
def self.statuses
|
||||
@statuses ||=
|
||||
Enum.new(experimental: 0, alpha: 100, beta: 200, stable: 300, permanent: 500, never: 9999)
|
||||
::Enum.new(experimental: 0, alpha: 100, beta: 200, stable: 300, permanent: 500, never: 9999)
|
||||
end
|
||||
|
||||
def self.image_exists?(change_setting_name)
|
||||
@@ -84,4 +94,44 @@ module UpcomingChanges
|
||||
|
||||
setting_enabled
|
||||
end
|
||||
|
||||
def self.stats_for_user(user:, acting_guardian:)
|
||||
guardian_visible_group_ids = Group.visible_groups(acting_guardian.user).pluck(:id)
|
||||
user_belonging_to_group_ids = user.belonging_to_group_ids
|
||||
|
||||
SiteSetting.upcoming_change_site_settings.map do |upcoming_change|
|
||||
enabled = user.upcoming_change_enabled?(upcoming_change)
|
||||
has_groups = UpcomingChanges.has_groups?(upcoming_change)
|
||||
|
||||
specific_groups = []
|
||||
reason =
|
||||
if has_groups
|
||||
visible_group_ids =
|
||||
UpcomingChanges.group_ids_for(upcoming_change) & guardian_visible_group_ids &
|
||||
user_belonging_to_group_ids
|
||||
|
||||
specific_groups = Group.where(id: visible_group_ids).pluck(:name)
|
||||
if enabled
|
||||
UpcomingChanges.user_enabled_reasons[:in_specific_groups]
|
||||
else
|
||||
UpcomingChanges.user_enabled_reasons[:not_in_specific_groups]
|
||||
end
|
||||
else
|
||||
if enabled
|
||||
UpcomingChanges.user_enabled_reasons[:enabled_for_everyone]
|
||||
else
|
||||
UpcomingChanges.user_enabled_reasons[:enabled_for_no_one]
|
||||
end
|
||||
end
|
||||
|
||||
{
|
||||
name: upcoming_change,
|
||||
humanized_name: SiteSetting.humanized_name(upcoming_change),
|
||||
description: SiteSetting.description(upcoming_change),
|
||||
enabled: enabled,
|
||||
specific_groups: specific_groups,
|
||||
reason: reason,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3786,4 +3786,168 @@ RSpec.describe User do
|
||||
expect(user.effective_locale).to eq(SiteSetting.default_locale)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".upcoming_change_stats" do
|
||||
fab!(:target_user, :user)
|
||||
fab!(:guardian_user, :user)
|
||||
fab!(:admin_user, :admin)
|
||||
fab!(:group1) { Fabricate(:group, name: "test_group_1") }
|
||||
fab!(:group2) { Fabricate(:group, name: "test_group_2") }
|
||||
fab!(:hidden_group) do
|
||||
Fabricate(:group, name: "hidden_group", visibility_level: Group.visibility_levels[:owners])
|
||||
end
|
||||
|
||||
let(:guardian) { Guardian.new(guardian_user) }
|
||||
let(:admin_guardian) { Guardian.new(admin_user) }
|
||||
|
||||
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 all users" do
|
||||
before { SiteSetting.enable_upload_debug_mode = true }
|
||||
|
||||
it "returns enabled as true and reason as enabled_for_everyone" do
|
||||
stats = target_user.upcoming_change_stats(guardian)
|
||||
change_stat = stats.find { |s| s[:name] == :enable_upload_debug_mode }
|
||||
|
||||
expect(change_stat[:enabled]).to be(true)
|
||||
expect(change_stat[:reason]).to eq(
|
||||
UpcomingChanges.user_enabled_reasons[:enabled_for_everyone],
|
||||
)
|
||||
expect(change_stat[:specific_groups]).to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
context "when the change is enabled for no users" do
|
||||
before { SiteSetting.enable_upload_debug_mode = false }
|
||||
|
||||
it "returns enabled as false and reason as enabled_for_no_one" do
|
||||
stats = target_user.upcoming_change_stats(guardian)
|
||||
change_stat = stats.find { |s| s[:name] == :enable_upload_debug_mode }
|
||||
|
||||
expect(change_stat[:enabled]).to be(false)
|
||||
expect(change_stat[:reason]).to eq(
|
||||
UpcomingChanges.user_enabled_reasons[:enabled_for_no_one],
|
||||
)
|
||||
expect(change_stat[:specific_groups]).to eq([])
|
||||
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 target user belongs to one of those groups" do
|
||||
before { group1.add(target_user) }
|
||||
|
||||
it "returns enabled as true and reason as in_specific_groups" do
|
||||
stats = target_user.upcoming_change_stats(admin_guardian)
|
||||
change_stat = stats.find { |s| s[:name] == :enable_upload_debug_mode }
|
||||
|
||||
expect(change_stat[:enabled]).to be(true)
|
||||
expect(change_stat[:reason]).to eq(
|
||||
UpcomingChanges.user_enabled_reasons[:in_specific_groups],
|
||||
)
|
||||
expect(change_stat[:specific_groups]).to contain_exactly("test_group_1")
|
||||
end
|
||||
end
|
||||
|
||||
context "when the target user belongs to multiple groups" do
|
||||
before do
|
||||
group1.add(target_user)
|
||||
group2.add(target_user)
|
||||
end
|
||||
|
||||
it "returns enabled as true with all groups in specific_groups" do
|
||||
stats = target_user.upcoming_change_stats(admin_guardian)
|
||||
change_stat = stats.find { |s| s[:name] == :enable_upload_debug_mode }
|
||||
|
||||
expect(change_stat[:enabled]).to be(true)
|
||||
expect(change_stat[:reason]).to eq(
|
||||
UpcomingChanges.user_enabled_reasons[:in_specific_groups],
|
||||
)
|
||||
expect(change_stat[:specific_groups]).to contain_exactly("test_group_1", "test_group_2")
|
||||
end
|
||||
end
|
||||
|
||||
context "when the target user does NOT belong to any of those groups" do
|
||||
it "returns enabled as false and reason as not_in_specific_groups" do
|
||||
stats = target_user.upcoming_change_stats(admin_guardian)
|
||||
change_stat = stats.find { |s| s[:name] == :enable_upload_debug_mode }
|
||||
|
||||
expect(change_stat[:enabled]).to be(false)
|
||||
expect(change_stat[:reason]).to eq(
|
||||
UpcomingChanges.user_enabled_reasons[:not_in_specific_groups],
|
||||
)
|
||||
expect(change_stat[:specific_groups]).to eq([])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when guardian user visibility is restricted" do
|
||||
before do
|
||||
SiteSetting.enable_upload_debug_mode = true
|
||||
group1.add(target_user)
|
||||
hidden_group.add(target_user)
|
||||
Fabricate(
|
||||
:site_setting_group,
|
||||
name: "enable_upload_debug_mode",
|
||||
group_ids: "#{group1.id}|#{hidden_group.id}",
|
||||
)
|
||||
end
|
||||
|
||||
it "only shows groups the guardian user is allowed to see in specific_groups" do
|
||||
stats = target_user.upcoming_change_stats(guardian)
|
||||
change_stat = stats.find { |s| s[:name] == :enable_upload_debug_mode }
|
||||
|
||||
expect(change_stat[:enabled]).to be(true)
|
||||
expect(change_stat[:reason]).to eq(
|
||||
UpcomingChanges.user_enabled_reasons[:in_specific_groups],
|
||||
)
|
||||
expect(change_stat[:specific_groups]).to contain_exactly("test_group_1")
|
||||
expect(change_stat[:specific_groups]).not_to include("hidden_group")
|
||||
end
|
||||
|
||||
it "shows all groups when guardian is admin" do
|
||||
stats = target_user.upcoming_change_stats(admin_guardian)
|
||||
change_stat = stats.find { |s| s[:name] == :enable_upload_debug_mode }
|
||||
|
||||
expect(change_stat[:enabled]).to be(true)
|
||||
expect(change_stat[:specific_groups]).to contain_exactly("test_group_1", "hidden_group")
|
||||
end
|
||||
end
|
||||
|
||||
describe "metadata fields" do
|
||||
before { SiteSetting.enable_upload_debug_mode = true }
|
||||
|
||||
it "includes correct name, humanized_name, and description" do
|
||||
stats = target_user.upcoming_change_stats(guardian)
|
||||
change_stat = stats.find { |s| s[:name] == :enable_upload_debug_mode }
|
||||
|
||||
expect(change_stat[:name]).to eq(:enable_upload_debug_mode)
|
||||
expect(change_stat[:humanized_name]).to eq(
|
||||
SiteSetting.humanized_name(:enable_upload_debug_mode),
|
||||
)
|
||||
expect(change_stat[:description]).to eq(SiteSetting.description(:enable_upload_debug_mode))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -61,6 +61,118 @@ describe "Admin User Page", type: :system do
|
||||
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
|
||||
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
|
||||
|
||||
@@ -55,6 +55,59 @@ module PageObjects
|
||||
def similar_users_warning
|
||||
find(".penalty-similar-users .alert-warning")["innerHTML"]
|
||||
end
|
||||
|
||||
class UpcomingChangeRow < PageObjects::Components::Base
|
||||
attr_reader :element
|
||||
|
||||
def initialize(element)
|
||||
@element = element
|
||||
end
|
||||
|
||||
def enabled?
|
||||
expect(element.find(".upcoming-change-enabled-status")).to have_content(
|
||||
I18n.t("js.yes_value"),
|
||||
)
|
||||
end
|
||||
|
||||
def disabled?
|
||||
expect(element.find(".upcoming-change-enabled-status")).to have_content(
|
||||
I18n.t("js.no_value"),
|
||||
)
|
||||
end
|
||||
|
||||
def has_reason?(reason_key)
|
||||
expected_text = I18n.t("js.user.upcoming_changes.why_reasons.#{reason_key}")
|
||||
expect(element.find(".upcoming-change-reason")).to have_content(expected_text)
|
||||
end
|
||||
|
||||
def specific_groups
|
||||
within element.find(".upcoming-change-groups") do
|
||||
all("a").map(&:text).sort
|
||||
end
|
||||
end
|
||||
|
||||
def has_specific_groups?(group_names)
|
||||
specific_groups == group_names.sort
|
||||
end
|
||||
|
||||
def has_no_specific_groups?
|
||||
expect(element).to have_no_css(".upcoming-change-groups")
|
||||
end
|
||||
end
|
||||
|
||||
def has_upcoming_change?(change_name)
|
||||
has_css?(
|
||||
".user-upcoming-changes-table .d-table__row[data-upcoming-change-name='#{change_name}']",
|
||||
)
|
||||
end
|
||||
|
||||
def upcoming_change(change_name)
|
||||
row =
|
||||
find(
|
||||
".user-upcoming-changes-table .d-table__row[data-upcoming-change-name='#{change_name}']",
|
||||
)
|
||||
UpcomingChangeRow.new(row)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user