From 8b70e9e1f302079fda484f4c26eeb11ecef15a88 Mon Sep 17 00:00:00 2001 From: Dwight Scott Date: Thu, 26 May 2022 10:57:08 -0400 Subject: [PATCH] added ability to assign moderator role via admin/super admin (#17759) * added ability to assign moderator role via admin/super admin * create Mod User in seeds * add Cypress specs * fix failing Cypress tests * fix failing specs by moving test Mod user to the end * nudge Travis * nudge Travis Co-authored-by: Arit Amana --- app/models/role.rb | 3 +- .../moderator/manage_activity_and_roles.rb | 53 ++++---- cypress/fixtures/users/moderatorUser.json | 5 + .../articleFlows/postModerationTools.spec.js | 113 ++++++++++++------ spec/models/role_spec.rb | 4 +- .../manage_activity_and_roles_spec.rb | 10 ++ spec/support/seeds/seeds_e2e.rb | 30 +++++ 7 files changed, 153 insertions(+), 65 deletions(-) create mode 100644 cypress/fixtures/users/moderatorUser.json diff --git a/app/models/role.rb b/app/models/role.rb index 6d3ec6eb5..cd97568bb 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -3,7 +3,9 @@ class Role < ApplicationRecord admin codeland_admin comment_suspended + creator mod_relations_admin + moderator podcast_admin restricted_liquid_tag single_resource_admin @@ -15,7 +17,6 @@ class Role < ApplicationRecord trusted warned workshop_pass - creator ].freeze has_and_belongs_to_many :users, join_table: :users_roles # rubocop:disable Rails/HasAndBelongsToMany diff --git a/app/services/moderator/manage_activity_and_roles.rb b/app/services/moderator/manage_activity_and_roles.rb index 8d8a2aa23..21baf3d20 100644 --- a/app/services/moderator/manage_activity_and_roles.rb +++ b/app/services/moderator/manage_activity_and_roles.rb @@ -56,44 +56,47 @@ module Moderator def handle_user_status(role, note) case role + when "Admin" + assign_elevated_role_to_user(user, :admin) + TagModerators::AddTrustedRole.call(user) + when "Comment Suspend" + comment_suspended when "Suspend" || "Spammer" user.add_role(:suspended) remove_privileges - when "Warn" - warned - when "Comment Suspend" - comment_suspended + when "Moderator" + assign_elevated_role_to_user(user, :moderator) + TagModerators::AddTrustedRole.call(user) when "Regular Member" regular_member - when "Trusted" - remove_negative_roles - TagModerators::AddTrustedRole.call(user) - when "Admin" - check_super_admin - remove_negative_roles - user.add_role(:admin) - TagModerators::AddTrustedRole.call(user) - when "Super Admin" - check_super_admin - remove_negative_roles - user.add_role(:super_admin) - TagModerators::AddTrustedRole.call(user) - when "Tech Admin" - check_super_admin - remove_negative_roles - user.add_role(:tech_admin) - # DataUpdateScripts falls under the admin namespace - # and hence requires a single_resource_admin role to view - # this technical admin resource - user.add_role(:single_resource_admin, DataUpdateScript) when /^(Resource Admin: )/ check_super_admin remove_negative_roles user.add_role(:single_resource_admin, role.split("Resource Admin: ").last.safe_constantize) + when "Super Admin" + assign_elevated_role_to_user(user, :super_admin) + TagModerators::AddTrustedRole.call(user) + when "Tech Admin" + assign_elevated_role_to_user(user, :tech_admin) + # DataUpdateScripts falls under the admin namespace + # and hence requires a single_resource_admin role to view + # this technical admin resource + user.add_role(:single_resource_admin, DataUpdateScript) + when "Trusted" + remove_negative_roles + TagModerators::AddTrustedRole.call(user) + when "Warn" + warned end create_note(role, note) end + def assign_elevated_role_to_user(user, role) + check_super_admin + remove_negative_roles + user.add_role(role) + end + def check_super_admin raise I18n.t("services.moderator.manage_activity_and_roles.need_super") unless @admin.super_admin? end diff --git a/cypress/fixtures/users/moderatorUser.json b/cypress/fixtures/users/moderatorUser.json new file mode 100644 index 000000000..760bebaf3 --- /dev/null +++ b/cypress/fixtures/users/moderatorUser.json @@ -0,0 +1,5 @@ +{ + "username": "moderator_user", + "email": "moderator-user@forem.local", + "password": "password" +} diff --git a/cypress/integration/seededFlows/articleFlows/postModerationTools.spec.js b/cypress/integration/seededFlows/articleFlows/postModerationTools.spec.js index 371a66ddd..6a3006016 100644 --- a/cypress/integration/seededFlows/articleFlows/postModerationTools.spec.js +++ b/cypress/integration/seededFlows/articleFlows/postModerationTools.spec.js @@ -21,57 +21,96 @@ describe('Moderation Tools for Posts', () => { }); }); - it('should not alter tags from a post if a reason is not specified', () => { - cy.fixture('users/adminUser.json').as('adminUser'); - cy.get('@adminUser').then((user) => { - cy.loginAndVisit(user, '/admin_mcadmin/tag-test-article').then(() => { - cy.findByRole('button', { name: 'Moderation' }).click(); + context('as admin user', () => { + beforeEach(() => { + cy.fixture('users/adminUser.json').as('adminUser'); + }); - // Helper function for pipe command - const click = ($el) => $el.click(); + it('should not alter tags from a post if a reason is not specified', () => { + cy.get('@adminUser').then((user) => { + cy.loginAndVisit(user, '/admin_mcadmin/tag-test-article').then(() => { + cy.findByRole('button', { name: 'Moderation' }).click(); - cy.getIframeBody('[title="Moderation panel actions"]').within(() => { - // We use `pipe` here to retry the click, as the animation of the mod tools opening can sometimes cause the button to not be ready yet - cy.findByRole('button', { name: 'Open adjust tags section' }) - .as('adjustTagsButton') - .pipe(click) - .should('have.attr', 'aria-expanded', 'true'); + // Helper function for pipe command + const click = ($el) => $el.click(); - cy.findByRole('button', { name: '#tag1 Remove tag' }).click(); - cy.findByRole('button', { name: 'Submit' }).click(); + cy.getIframeBody('[title="Moderation panel actions"]').within(() => { + // We use `pipe` here to retry the click, as the animation of the mod tools opening can sometimes cause the button to not be ready yet + cy.findByRole('button', { name: 'Open adjust tags section' }) + .as('adjustTagsButton') + .pipe(click) + .should('have.attr', 'aria-expanded', 'true'); + + cy.findByRole('button', { name: '#tag1 Remove tag' }).click(); + cy.findByRole('button', { name: 'Submit' }).click(); + }); + + cy.findByTestId('snackbar').should('not.exist'); }); + }); + }); - cy.findByTestId('snackbar').should('not.exist'); + it('should show Feature Post button on an unfeatured post for an admin user', () => { + cy.get('@adminUser').then((user) => { + cy.loginAndVisit(user, '/admin_mcadmin/unfeatured-article-slug').then( + () => { + cy.findByRole('button', { name: 'Moderation' }).click(); + + cy.getIframeBody('[title="Moderation panel actions"]').within( + () => { + cy.findByRole('button', { name: 'Feature Post' }).should( + 'exist', + ); + }, + ); + }, + ); + }); + }); + + it('should show Unfeature Post button on a featured post for an admin user', () => { + cy.get('@adminUser').then((user) => { + cy.loginAndVisit(user, '/admin_mcadmin/test-article-slug').then(() => { + cy.findByRole('button', { name: 'Moderation' }).click(); + + cy.getIframeBody('[title="Moderation panel actions"]').within(() => { + cy.findByRole('button', { name: 'Unfeature Post' }).should('exist'); + }); + }); }); }); }); - it('should show Feature Post button on an unfeatured post for an admin user', () => { - cy.fixture('users/adminUser.json').as('adminUser'); - cy.get('@adminUser').then((user) => { - cy.loginAndVisit(user, '/admin_mcadmin/unfeatured-article-slug').then( - () => { - cy.findByRole('button', { name: 'Moderation' }).click(); - - cy.getIframeBody('[title="Moderation panel actions"]').within(() => { - cy.findByRole('button', { name: 'Feature Post' }).should('exist'); - }); - }, - ); + context('as moderator user', () => { + beforeEach(() => { + cy.fixture('users/moderatorUser.json').as('moderatorUser'); }); - }); - it('should show Unfeature Post button on a featured post for an admin user', () => { - cy.fixture('users/adminUser.json').as('adminUser'); - cy.get('@adminUser').then((user) => { - cy.loginAndVisit(user, '/admin_mcadmin/test-article-slug').then(() => { - cy.findByRole('button', { name: 'Moderation' }).click(); - - cy.getIframeBody('[title="Moderation panel actions"]').within(() => { - cy.findByRole('button', { name: 'Unfeature Post' }).should('exist'); + it('should load moderation tools on a post for a moderator user', () => { + cy.get('@moderatorUser').then((user) => { + cy.loginAndVisit(user, '/admin_mcadmin/test-article-slug').then(() => { + cy.findByRole('button', { name: 'Moderation' }).should('exist'); }); }); }); + + it('should not show Feature Post button on a post for a moderator user', () => { + cy.get('@moderatorUser').then((user) => { + cy.loginAndVisit(user, '/admin_mcadmin/unfeatured-article-slug').then( + () => { + cy.findByRole('button', { name: 'Moderation' }).click(); + + cy.getIframeBody('[title="Moderation panel actions"]').within( + () => { + cy.findByRole('button', { name: 'Feature Post' }).should( + 'not.exist', + ); + }, + ); + }, + ); + }); + }); }); context('as trusted user', () => { diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb index 5e7285a7e..cd0349bee 100644 --- a/spec/models/role_spec.rb +++ b/spec/models/role_spec.rb @@ -10,9 +10,9 @@ RSpec.describe Role, type: :model do expected_roles = %w[ admin codeland_admin comment_suspended mod_relations_admin podcast_admin restricted_liquid_tag single_resource_admin super_admin support_admin suspended tag_moderator tech_admin - trusted warned workshop_pass creator + trusted warned workshop_pass creator moderator ] - expect(described_class::ROLES).to eq(expected_roles) + expect(described_class::ROLES).to match_array(expected_roles) end end end diff --git a/spec/services/moderator/manage_activity_and_roles_spec.rb b/spec/services/moderator/manage_activity_and_roles_spec.rb index e12f5daea..6f5aea8ef 100644 --- a/spec/services/moderator/manage_activity_and_roles_spec.rb +++ b/spec/services/moderator/manage_activity_and_roles_spec.rb @@ -119,5 +119,15 @@ RSpec.describe Moderator::ManageActivityAndRoles, type: :service do ) end.to raise_error(StandardError) end + + it "updates user to moderator" do + expect do + described_class.handle_user_roles( + admin: admin, + user: user, + user_params: { note_for_current_role: "Upgrading to moderator", user_status: "Moderator" }, + ) + end.to raise_error(StandardError) + end end end diff --git a/spec/support/seeds/seeds_e2e.rb b/spec/support/seeds/seeds_e2e.rb index f395b35b8..0247623e9 100644 --- a/spec/support/seeds/seeds_e2e.rb +++ b/spec/support/seeds/seeds_e2e.rb @@ -34,6 +34,7 @@ work_attr = ProfileField.find_by(label: "Work").attribute_name education_attr = ProfileField.find_by(label: "Education").attribute_name ############################################################################## +# admin-user needs to be the first user, to maintain specs validity seeder.create_if_doesnt_exist(User, "email", "admin@forem.local") do user = User.create!( name: "Admin McAdmin", @@ -71,6 +72,7 @@ admin_user = User.find_by(email: "admin@forem.local") ############################################################################## +# trusted-user-1 needs to be the second user, to maintain specs validity seeder.create_if_doesnt_exist(User, "email", "trusted-user-1@forem.local") do user = User.create!( name: "Trusted User 1 \\:/", @@ -99,6 +101,7 @@ trusted_user = User.find_by(email: "trusted-user-1@forem.local") ############################################################################## +# punctuated-name-user needs to remain the 3rd user created, for tests' sake seeder.create_if_doesnt_exist(User, "email", "punctuated-name-user@forem.local") do user = User.create!( name: "User \"The test breaker\" A'postrophe \\:/", @@ -175,6 +178,33 @@ end ############################################################################## +seeder.create_if_doesnt_exist(User, "email", "moderator-user@forem.local") do + user = User.create!( + name: "Moderator User", + email: "moderator-user@forem.local", + username: "moderator_user", + profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")), + confirmed_at: Time.current, + registered_at: Time.current, + password: "password", + password_confirmation: "password", + saw_onboarding: true, + checked_code_of_conduct: true, + checked_terms_and_conditions: true, + ) + user.notification_setting.update( + email_comment_notifications: false, + email_follower_notifications: false, + ) + + user.profile.update(website_url: Faker::Internet.url) + + user.add_role(:moderator) + user.add_role(:trusted) +end + +############################################################################## + seeder.create_if_doesnt_exist(Organization, "slug", "bachmanity") do organization = Organization.create!( name: "Bachmanity",