diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 06ec2dc65f..640bafb455 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -1074,6 +1074,7 @@ "source": "", "spell_check": "", "sso": "", + "sso_active": "", "sso_config_prop_help_certificate": "", "sso_config_prop_help_first_name": "", "sso_config_prop_help_last_name": "", @@ -1092,9 +1093,8 @@ "sso_is_enabled_explanation_2": "", "sso_link_error": "", "sso_link_invite_has_been_sent_to_email": "", - "sso_linked": "", "sso_logs": "", - "sso_unlinked": "", + "sso_not_active": "", "start_a_free_trial": "", "start_by_adding_your_email": "", "start_free_trial": "", diff --git a/services/web/frontend/js/features/group-management/components/group-members-list.tsx b/services/web/frontend/js/features/group-management/components/group-members-list.tsx deleted file mode 100644 index 0e8c8d95aa..0000000000 --- a/services/web/frontend/js/features/group-management/components/group-members-list.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { useCallback } from 'react' -import { Col, Row } from 'react-bootstrap' -import { useTranslation } from 'react-i18next' -import Tooltip from '../../../shared/components/tooltip' -import { useGroupMembersContext } from '../context/group-members-context' -import GroupMemberRow from './group-member-row' - -export default function GroupMembersList() { - const { t } = useTranslation() - const { - selectedUsers, - users, - selectUser, - unselectUser, - selectAllUsers, - unselectAllUsers, - } = useGroupMembersContext() - - const handleSelectAllClick = useCallback( - (e: React.ChangeEvent) => { - if (e.target.checked) { - selectAllUsers() - } else { - unselectAllUsers() - } - }, - [selectAllUsers, unselectAllUsers] - ) - - return ( - - ) -} diff --git a/services/web/frontend/js/features/group-management/components/group-members.tsx b/services/web/frontend/js/features/group-management/components/group-members.tsx index fb624b31a0..168f6b238c 100644 --- a/services/web/frontend/js/features/group-management/components/group-members.tsx +++ b/services/web/frontend/js/features/group-management/components/group-members.tsx @@ -6,8 +6,7 @@ import useWaitForI18n from '../../../shared/hooks/use-wait-for-i18n' import getMeta from '../../../utils/meta' import { useGroupMembersContext } from '../context/group-members-context' import ErrorAlert from './error-alert' -import ManagedUsersList from './managed-users/managed-users-list' -import GroupMembersList from './group-members-list' +import MembersList from './members-table/members-list' export default function GroupMembers() { const { isReady } = useWaitForI18n() @@ -28,7 +27,6 @@ export default function GroupMembers() { const groupId: string = getMeta('ol-groupId') const groupName: string = getMeta('ol-groupName') const groupSize: number = getMeta('ol-groupSize') - const managedUsersActive: any = getMeta('ol-managedUsersActive') const handleEmailsChange = useCallback( e => { @@ -91,11 +89,7 @@ export default function GroupMembers() {
- {managedUsersActive ? ( - - ) : ( - - )} +

{users.length < groupSize && ( diff --git a/services/web/frontend/js/features/group-management/components/managers-table.tsx b/services/web/frontend/js/features/group-management/components/managers-table.tsx index 6faebc985d..313ada11a8 100644 --- a/services/web/frontend/js/features/group-management/components/managers-table.tsx +++ b/services/web/frontend/js/features/group-management/components/managers-table.tsx @@ -1,17 +1,13 @@ import { useCallback, useState } from 'react' import { Button, Col, Form, FormControl, Row } from 'react-bootstrap' import { useTranslation } from 'react-i18next' -import { - deleteJSON, - FetchError, - postJSON, -} from '../../../infrastructure/fetch-json' +import { deleteJSON, FetchError, postJSON } from '@/infrastructure/fetch-json' import MaterialIcon from '../../../shared/components/material-icon' import Tooltip from '../../../shared/components/tooltip' import getMeta from '../../../utils/meta' import { parseEmails } from '../utils/emails' import ErrorAlert, { APIError } from './error-alert' -import GroupMemberRow from './group-member-row' +import UserRow from './user-row' import useUserSelection from '../hooks/use-user-selection' import { User } from '../../../../../types/group-management/user' import { debugConsole } from '@/utils/debugging' @@ -216,7 +212,7 @@ export function ManagersTable({ )} {users.map(user => ( - > } -export default function ManagedUserDropdownButton({ +export default function DropdownButton({ user, openOffboardingModalForUser, groupId, diff --git a/services/web/frontend/js/features/group-management/components/managed-users/managed-users-list-alert.tsx b/services/web/frontend/js/features/group-management/components/members-table/list-alert.tsx similarity index 99% rename from services/web/frontend/js/features/group-management/components/managed-users/managed-users-list-alert.tsx rename to services/web/frontend/js/features/group-management/components/members-table/list-alert.tsx index 86937dd842..cce436476b 100644 --- a/services/web/frontend/js/features/group-management/components/managed-users/managed-users-list-alert.tsx +++ b/services/web/frontend/js/features/group-management/components/members-table/list-alert.tsx @@ -9,7 +9,7 @@ type ManagedUsersListAlertProps = { onDismiss: () => void } -export default function ManagedUsersListAlert({ +export default function ListAlert({ variant, invitedUserEmail, onDismiss, diff --git a/services/web/frontend/js/features/group-management/components/managed-users/managed-user-status.tsx b/services/web/frontend/js/features/group-management/components/members-table/managed-user-status.tsx similarity index 94% rename from services/web/frontend/js/features/group-management/components/managed-users/managed-user-status.tsx rename to services/web/frontend/js/features/group-management/components/members-table/managed-user-status.tsx index ea46b60952..39b6220f53 100644 --- a/services/web/frontend/js/features/group-management/components/managed-users/managed-user-status.tsx +++ b/services/web/frontend/js/features/group-management/components/members-table/managed-user-status.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next' import { User } from '../../../../../../types/group-management/user' -import MaterialIcon from '../../../../shared/components/material-icon' +import MaterialIcon from '@/shared/components/material-icon' type ManagedUserStatusProps = { user: User diff --git a/services/web/frontend/js/features/group-management/components/managed-users/managed-user-row.tsx b/services/web/frontend/js/features/group-management/components/members-table/member-row.tsx similarity index 83% rename from services/web/frontend/js/features/group-management/components/managed-users/managed-user-row.tsx rename to services/web/frontend/js/features/group-management/components/members-table/member-row.tsx index d9fcbcc7aa..343511ed80 100644 --- a/services/web/frontend/js/features/group-management/components/managed-users/managed-user-row.tsx +++ b/services/web/frontend/js/features/group-management/components/members-table/member-row.tsx @@ -7,8 +7,8 @@ import Tooltip from '../../../../shared/components/tooltip' import type { ManagedUserAlert } from '../../utils/types' import ManagedUserStatus from './managed-user-status' import SSOStatus from './sso-status' -import ManagedUserDropdownButton from './managed-user-dropdown-button' -import ManagedUsersSelectUserCheckbox from './managed-users-select-user-checkbox' +import DropdownButton from './dropdown-button' +import SelectUserCheckbox from './select-user-checkbox' import getMeta from '@/utils/meta' type ManagedUserRowProps = { @@ -18,13 +18,14 @@ type ManagedUserRowProps = { setManagedUserAlert: Dispatch> } -export default function ManagedUserRow({ +export default function MemberRow({ user, openOffboardingModalForUser, setManagedUserAlert, groupId, }: ManagedUserRowProps) { const { t } = useTranslation() + const managedUsersActive: any = getMeta('ol-managedUsersActive') const groupSSOActive = getMeta('ol-groupSSOActive') return ( @@ -32,8 +33,8 @@ export default function ManagedUserRow({ key={`user-${user.email}`} className={`managed-user-row ${user.invite ? 'text-muted' : ''}`} > - - + + {user.email} {user.invite ? ( @@ -83,13 +84,15 @@ export default function ManagedUserRow({ )} - -
- -
- + {managedUsersActive && ( + +
+ +
+ + )} - ( undefined @@ -23,32 +24,37 @@ export default function ManagedUsersList({ groupId }: ManagedUsersListProps) { const [managedUserAlert, setManagedUserAlert] = useState(undefined) const { users } = useGroupMembersContext() + const managedUsersActive: any = getMeta('ol-managedUsersActive') const groupSSOActive = getMeta('ol-groupSSOActive') return (
- {managedUserAlert && ( - setManagedUserAlert(undefined)} /> )} -
    +
    • - - )} - + {managedUsersActive && ( + + )} @@ -88,7 +96,7 @@ export default function ManagedUsersList({ groupId }: ManagedUsersListProps) { )} {users.map((user: any) => ( - +
      + + {t('email')} @@ -73,9 +79,11 @@ export default function ManagedUsersList({ groupId }: ManagedUsersListProps) { {t('security')} - {t('managed')} - + {t('managed')} +
      diff --git a/services/web/frontend/js/features/group-management/components/managed-users/managed-users-select-user-checkbox.tsx b/services/web/frontend/js/features/group-management/components/members-table/select-user-checkbox.tsx similarity index 86% rename from services/web/frontend/js/features/group-management/components/managed-users/managed-users-select-user-checkbox.tsx rename to services/web/frontend/js/features/group-management/components/members-table/select-user-checkbox.tsx index d92d4989f3..2940586772 100644 --- a/services/web/frontend/js/features/group-management/components/managed-users/managed-users-select-user-checkbox.tsx +++ b/services/web/frontend/js/features/group-management/components/members-table/select-user-checkbox.tsx @@ -2,17 +2,15 @@ import { useTranslation } from 'react-i18next' import type { User } from '../../../../../../types/group-management/user' import { useGroupMembersContext } from '../../context/group-members-context' import { useCallback } from 'react' -import getMeta from '@/utils/meta' type ManagedUsersSelectUserCheckboxProps = { user: User } -export default function ManagedUsersSelectUserCheckbox({ +export default function SelectUserCheckbox({ user, }: ManagedUsersSelectUserCheckboxProps) { const { t } = useTranslation() - const groupSSOActive = getMeta('ol-groupSSOActive') const { users, selectedUsers, selectUser, unselectUser } = useGroupMembersContext() @@ -40,11 +38,7 @@ export default function ManagedUsersSelectUserCheckbox({ const selected = selectedUsers.includes(user) return ( - + {/* the next check will hide the `checkbox` but still show the `td` */} {user.enrollment?.managedBy ? null : ( <> diff --git a/services/web/frontend/js/features/group-management/components/managed-users/sso-status.tsx b/services/web/frontend/js/features/group-management/components/members-table/sso-status.tsx similarity index 85% rename from services/web/frontend/js/features/group-management/components/managed-users/sso-status.tsx rename to services/web/frontend/js/features/group-management/components/members-table/sso-status.tsx index f1165a9308..ad3fbcd7e5 100644 --- a/services/web/frontend/js/features/group-management/components/managed-users/sso-status.tsx +++ b/services/web/frontend/js/features/group-management/components/members-table/sso-status.tsx @@ -19,13 +19,13 @@ export default function SSOStatus({ user }: SSOStatusProps) { ) const acceptedSSO = ( - +   {t('sso')} ) const notAcceptedSSO = ( - +   {t('sso')} ) diff --git a/services/web/frontend/js/features/group-management/components/group-member-row.tsx b/services/web/frontend/js/features/group-management/components/user-row.tsx similarity index 98% rename from services/web/frontend/js/features/group-management/components/group-member-row.tsx rename to services/web/frontend/js/features/group-management/components/user-row.tsx index 844f96f3f4..56c649e6bc 100644 --- a/services/web/frontend/js/features/group-management/components/group-member-row.tsx +++ b/services/web/frontend/js/features/group-management/components/user-row.tsx @@ -11,7 +11,7 @@ type GroupMemberRowProps = { selected: boolean } -export default function GroupMemberRow({ +export default function UserRow({ user, selectUser, unselectUser, diff --git a/services/web/frontend/js/features/group-management/context/group-members-context.tsx b/services/web/frontend/js/features/group-management/context/group-members-context.tsx index 069ea629bc..2c443b7f41 100644 --- a/services/web/frontend/js/features/group-management/context/group-members-context.tsx +++ b/services/web/frontend/js/features/group-management/context/group-members-context.tsx @@ -7,13 +7,9 @@ import { useState, } from 'react' import { User } from '../../../../../types/group-management/user' -import { - deleteJSON, - FetchError, - postJSON, -} from '../../../infrastructure/fetch-json' -import { mapSeries } from '../../../infrastructure/promise' -import getMeta from '../../../utils/meta' +import { deleteJSON, FetchError, postJSON } from '@/infrastructure/fetch-json' +import { mapSeries } from '@/infrastructure/promise' +import getMeta from '@/utils/meta' import { APIError } from '../components/error-alert' import useUserSelection from '../hooks/use-user-selection' import { parseEmails } from '../utils/emails' diff --git a/services/web/frontend/stylesheets/components/group-members.less b/services/web/frontend/stylesheets/components/group-members.less index 8664f731ce..4693267f3a 100644 --- a/services/web/frontend/stylesheets/components/group-members.less +++ b/services/web/frontend/stylesheets/components/group-members.less @@ -103,75 +103,39 @@ } } - .cell-checkbox { - width: 5%; - } - .cell-checkbox-with-sso-col { - width: 2.5%; - } - - .cell-email { - width: 45%; - } - .cell-email-with-sso-col { - width: 37%; - } - - .cell-name { - width: 15%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .cell-last-active { - width: 15%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .cell-security { - width: 10%; - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .cell-managed { - width: 15%; - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .cell-dropdown { - width: 5%; - min-width: 25px; - } - @media (min-width: @screen-xs) { .cell-checkbox { width: 5%; - } - - .cell-checkbox-with-sso-col { - width: 2.5%; + .managed-users-active.group-sso-active & { + width: 3%; + } } .cell-email { - width: 34%; - } - .cell-email-with-sso-col { - width: 29%; + width: 50%; + .managed-users-active & { + width: 35%; + } + .group-sso-active & { + width: 37%; + } + .managed-users-active.group-sso-active & { + width: 29%; + } } .cell-name { width: 20%; + .managed-users-active.group-sso-active & { + width: 18%; + } } .cell-last-active { width: 20%; + .managed-users-active.group-sso-active & { + width: 16%; + } } .cell-security { @@ -191,20 +155,28 @@ @media (min-width: @screen-lg) { .cell-checkbox { width: 5%; + .managed-users-active.group-sso-active & { + width: 3%; + } } - .cell-checkbox-with-sso-col { - width: 2.5%; - } - .cell-email { - width: 43%; - } - .cell-email-with-sso-col { - width: 37%; + width: 55%; + .managed-users-active & { + width: 42%; + } + .group-sso-active & { + width: 45%; + } + .managed-users-active.group-sso-active & { + width: 36%; + } } .cell-name { width: 20%; + .managed-users-active.group-sso-active & { + width: 18%; + } } .cell-last-active { @@ -216,7 +188,7 @@ } .cell-managed { - width: 12%; + width: 13%; } .cell-dropdown { diff --git a/services/web/locales/en.json b/services/web/locales/en.json index c4bcebedc7..eec996e607 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -1644,6 +1644,7 @@ "spread_the_word_and_fill_bar": "Spread the word and fill this bar up", "sso": "SSO", "sso_account_already_linked": "Account already linked to another __appName__ user", + "sso_active": "SSO active", "sso_config_prop_help_certificate": "Base64 encoded certificate without whitespace", "sso_config_prop_help_first_name": "Property in SAML assertion to use for first name", "sso_config_prop_help_last_name": "Property in SAML assertion to use for last name", @@ -1664,10 +1665,9 @@ "sso_is_enabled_explanation_2": "If there are any problems with the configuration, only you (as the group administrator) will be able to disable SSO.", "sso_link_error": "Error linking account", "sso_link_invite_has_been_sent_to_email": "An SSO invite reminder has been sent to <0>__email__", - "sso_linked": "SSO linked", "sso_logs": "SSO Logs", + "sso_not_active": "SSO not active", "sso_not_linked": "You have not linked your account to __provider__. Please log in to your account another way and link your __provider__ account via your account settings.", - "sso_unlinked": "SSO unlinked", "sso_user_denied_access": "Cannot log in because __appName__ was not granted access to your __provider__ account. Please try again.", "standard": "Standard", "start_a_free_trial": "Start a free trial", diff --git a/services/web/test/frontend/features/group-management/components/group-managers.spec.tsx b/services/web/test/frontend/features/group-management/components/group-managers.spec.tsx index 94040b3c7f..99d06c05d1 100644 --- a/services/web/test/frontend/features/group-management/components/group-managers.spec.tsx +++ b/services/web/test/frontend/features/group-management/components/group-managers.spec.tsx @@ -1,4 +1,4 @@ -import GroupManagers from '../../../../../frontend/js/features/group-management/components/group-managers' +import GroupManagers from '@/features/group-management/components/group-managers' const JOHN_DOE = { _id: 'abc123def456', diff --git a/services/web/test/frontend/features/group-management/components/group-members.spec.tsx b/services/web/test/frontend/features/group-management/components/group-members.spec.tsx index 189f92c144..dd72bd04b8 100644 --- a/services/web/test/frontend/features/group-management/components/group-members.spec.tsx +++ b/services/web/test/frontend/features/group-management/components/group-members.spec.tsx @@ -1,22 +1,6 @@ -import GroupMembers from '../../../../../frontend/js/features/group-management/components/group-members' -import { GroupMembersProvider } from '../../../../../frontend/js/features/group-management/context/group-members-context' +import GroupMembers from '@/features/group-management/components/group-members' +import { GroupMembersProvider } from '@/features/group-management/context/group-members-context' -const JOHN_DOE = { - _id: 'abc123def456', - first_name: 'John', - last_name: 'Doe', - email: 'john.doe@test.com', - last_active_at: new Date('2023-01-15'), - invite: true, -} -const BOBBY_LAPOINTE = { - _id: 'bcd234efa567', - first_name: 'Bobby', - last_name: 'Lapointe', - email: 'bobby.lapointe@test.com', - last_active_at: new Date('2023-01-02'), - invite: false, -} const GROUP_ID = '777fff777fff' const PATHS = { addMember: `/manage/groups/${GROUP_ID}/invites`, @@ -25,139 +9,436 @@ const PATHS = { exportMembers: `/manage/groups/${GROUP_ID}/members/export`, } -describe('group members, without managed users', function () { - beforeEach(function () { - cy.window().then(win => { - win.metaAttributesCache.set('ol-groupId', GROUP_ID) - win.metaAttributesCache.set('ol-groupName', 'My Awesome Team') - win.metaAttributesCache.set('ol-groupSize', 10) - win.metaAttributesCache.set('ol-users', [JOHN_DOE, BOBBY_LAPOINTE]) - }) - +describe('GroupMembers', function () { + function mountGroupMembersProvider() { cy.mount( ) - }) + } - it('renders the group members page', function () { - cy.get('h1').contains('My Awesome Team') - cy.get('small').contains('You have added 2 of 10 available members') + describe('with Managed Users and Group SSO disabled', function () { + const JOHN_DOE = { + _id: 'abc123def456', + first_name: 'John', + last_name: 'Doe', + email: 'john.doe@test.com', + last_active_at: new Date('2023-01-15'), + invite: true, + } + const BOBBY_LAPOINTE = { + _id: 'bcd234efa567', + first_name: 'Bobby', + last_name: 'Lapointe', + email: 'bobby.lapointe@test.com', + last_active_at: new Date('2023-01-02'), + invite: false, + } - cy.get('ul').within(() => { - cy.get('li:nth-child(2)').within(() => { - cy.contains('john.doe@test.com') - cy.contains('John Doe') - cy.contains('15th Jan 2023') - cy.get(`[aria-label="Invite not yet accepted"]`) + beforeEach(function () { + cy.window().then(win => { + win.metaAttributesCache.set('ol-groupId', GROUP_ID) + win.metaAttributesCache.set('ol-groupName', 'My Awesome Team') + win.metaAttributesCache.set('ol-groupSize', 10) + win.metaAttributesCache.set('ol-users', [JOHN_DOE, BOBBY_LAPOINTE]) }) - cy.get('li:nth-child(3)').within(() => { - cy.contains('bobby.lapointe@test.com') - cy.contains('Bobby Lapointe') - cy.contains('2nd Jan 2023') - cy.get(`[aria-label="Accepted invite"]`) + cy.mount( + + + + ) + }) + + it('renders the group members page', function () { + cy.get('h1').contains('My Awesome Team') + cy.get('small').contains('You have added 2 of 10 available members') + + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.contains('john.doe@test.com') + cy.contains('John Doe') + cy.contains('15th Jan 2023') + cy.get('.badge-new-comment').contains('Pending invite') + }) + + cy.get('tr:nth-child(2)').within(() => { + cy.contains('bobby.lapointe@test.com') + cy.contains('Bobby Lapointe') + cy.contains('2nd Jan 2023') + cy.get('.badge-new-comment').should('not.exist') + }) }) }) + + it('sends an invite', function () { + cy.intercept('POST', PATHS.addMember, { + statusCode: 201, + body: { + user: { + email: 'someone.else@test.com', + invite: true, + }, + }, + }) + + cy.get('.form-control').type('someone.else@test.com') + cy.get('.add-more-members-form button').click() + + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(3)').within(() => { + cy.contains('someone.else@test.com') + cy.contains('N/A') + cy.get('.badge-new-comment').contains('Pending invite') + }) + }) + }) + + it('tries to send an invite and displays the error', function () { + cy.intercept('POST', PATHS.addMember, { + statusCode: 500, + body: { + error: { + message: 'User already added', + }, + }, + }) + + cy.get('.form-control').type('someone.else@test.com') + cy.get('.add-more-members-form button').click() + cy.get('.alert').contains('Error: User already added') + }) + + it('checks the select all checkbox', function () { + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.get('.select-item').should('not.be.checked') + }) + cy.get('tr:nth-child(2)').within(() => { + cy.get('.select-item').should('not.be.checked') + }) + }) + + cy.get('.select-all').click() + + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.get('.select-item').should('be.checked') + }) + cy.get('tr:nth-child(2)').within(() => { + cy.get('.select-item').should('be.checked') + }) + }) + }) + + it('remove a member', function () { + cy.intercept('DELETE', `${PATHS.removeMember}/abc123def456`, { + statusCode: 200, + }) + + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.get('.select-item').check() + }) + }) + + cy.get('button').contains('Remove from group').click() + + cy.get('small').contains('You have added 1 of 10 available members') + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.contains('bobby.lapointe@test.com') + cy.contains('Bobby Lapointe') + cy.contains('2nd Jan 2023') + cy.contains('Pending invite').should('not.exist') + }) + }) + }) + + it('tries to remove a user and displays the error', function () { + cy.intercept('DELETE', `${PATHS.removeMember}/abc123def456`, { + statusCode: 500, + }) + + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.get('.select-item').check() + }) + }) + cy.get('button').contains('Remove from group').click() + + cy.get('.alert').contains('Sorry, something went wrong') + }) }) - it('sends an invite', function () { - cy.intercept('POST', PATHS.addMember, { - statusCode: 201, - body: { - user: { - email: 'someone.else@test.com', - invite: true, + describe('with Managed Users enabled', function () { + const JOHN_DOE = { + _id: 'abc123def456', + first_name: 'John', + last_name: 'Doe', + email: 'john.doe@test.com', + last_active_at: new Date('2023-01-15'), + invite: true, + } + const BOBBY_LAPOINTE = { + _id: 'bcd234efa567', + first_name: 'Bobby', + last_name: 'Lapointe', + email: 'bobby.lapointe@test.com', + last_active_at: new Date('2023-01-02'), + invite: false, + } + const CLAIRE_JENNINGS = { + _id: 'defabc231453', + first_name: 'Claire', + last_name: 'Jennings', + email: 'claire.jennings@test.com', + last_active_at: new Date('2023-01-03'), + invite: false, + enrollment: { + managedBy: GROUP_ID, + enrolledAt: new Date('2023-01-03'), + sso: { + providerId: '123', + externalId: '123', }, }, + } + + beforeEach(function () { + cy.window().then(win => { + win.metaAttributesCache = new Map() + win.metaAttributesCache.set('ol-users', [ + JOHN_DOE, + BOBBY_LAPOINTE, + CLAIRE_JENNINGS, + ]) + win.metaAttributesCache.set('ol-groupId', GROUP_ID) + win.metaAttributesCache.set('ol-groupName', 'My Awesome Team') + win.metaAttributesCache.set('ol-groupSize', 10) + win.metaAttributesCache.set('ol-managedUsersActive', true) + }) + mountGroupMembersProvider() }) - cy.get('.form-control').type('someone.else@test.com') - cy.get('button').click() + it('renders the group members page', function () { + cy.get('h1').contains('My Awesome Team') + cy.get('small').contains('You have added 3 of 10 available members') - cy.get('ul').within(() => { - cy.get('li:nth-child(4)').within(() => { - cy.contains('someone.else@test.com') - cy.contains('N/A') - cy.get(`[aria-label="Invite not yet accepted"]`) + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.contains('john.doe@test.com') + cy.contains('John Doe') + cy.contains('15th Jan 2023') + cy.get('.sr-only').contains('Pending invite') + cy.get('.badge-new-comment').contains('Pending invite') + cy.get(`.security-state-invite-pending`).should('exist') + + cy.get('.badge-new-comment').contains('Pending invite') + cy.get(`.security-state-invite-pending`).should('exist') + }) + + cy.get('tr:nth-child(2)').within(() => { + cy.contains('bobby.lapointe@test.com') + cy.contains('Bobby Lapointe') + cy.contains('2nd Jan 2023') + cy.get('.badge-new-comment').should('not.exist') + cy.get('.sr-only').contains('Not managed') + }) + + cy.get('tr:nth-child(3)').within(() => { + cy.contains('claire.jennings@test.com') + cy.contains('Claire Jennings') + cy.contains('3rd Jan 2023') + cy.get('.badge-new-comment').should('not.exist') + cy.get('.sr-only').contains('Managed') + }) }) }) + + it('sends an invite', function () { + cy.intercept('POST', PATHS.addMember, { + statusCode: 201, + body: { + user: { + email: 'someone.else@test.com', + invite: true, + }, + }, + }) + + cy.get('.form-control').type('someone.else@test.com') + cy.get('.add-more-members-form button').click() + + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(4)').within(() => { + cy.contains('someone.else@test.com') + cy.contains('N/A') + cy.get('.sr-only').contains('Pending invite') + cy.get('.badge-new-comment').contains('Pending invite') + cy.get(`.security-state-invite-pending`).should('exist') + }) + }) + }) + + it('tries to send an invite and displays the error', function () { + cy.intercept('POST', PATHS.addMember, { + statusCode: 500, + body: { + error: { + message: 'User already added', + }, + }, + }) + + cy.get('.form-control').type('someone.else@test.com') + cy.get('.add-more-members-form button').click() + cy.get('.alert').contains('Error: User already added') + }) + + it('checks the select all checkbox', function () { + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.get('.select-item').should('not.be.checked') + }) + cy.get('tr:nth-child(2)').within(() => { + cy.get('.select-item').should('not.be.checked') + }) + }) + + cy.get('.select-all').click() + + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.get('.select-item').should('be.checked') + }) + cy.get('tr:nth-child(2)').within(() => { + cy.get('.select-item').should('be.checked') + }) + }) + + cy.get('button').contains('Remove from group').click() + }) + + it('remove a member', function () { + cy.intercept('DELETE', `${PATHS.removeMember}/abc123def456`, { + statusCode: 200, + }) + + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.get('.select-item').check() + }) + }) + + cy.get('button').contains('Remove from group').click() + + cy.get('small').contains('You have added 2 of 10 available members') + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.contains('bobby.lapointe@test.com') + cy.contains('Bobby Lapointe') + cy.contains('2nd Jan 2023') + }) + }) + }) + + it('cannot remove a managed member', function () { + cy.intercept('DELETE', `${PATHS.removeMember}/abc123def456`, { + statusCode: 200, + }) + + cy.get('ul.managed-users-list table > tbody').within(() => { + // no checkbox should be shown for 'Claire Jennings', a managed user + cy.get('tr:nth-child(3)').within(() => { + cy.get('.select-item').should('not.exist') + }) + }) + }) + + it('tries to remove a user and displays the error', function () { + cy.intercept('DELETE', `${PATHS.removeMember}/abc123def456`, { + statusCode: 500, + }) + + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(1)').within(() => { + cy.get('.select-item').check() + }) + }) + cy.get('.page-header').within(() => { + cy.get('button').contains('Remove from group').click() + }) + + cy.get('.alert').contains('Sorry, something went wrong') + }) }) - it('tries to send an invite and displays the error', function () { - cy.intercept('POST', PATHS.addMember, { - statusCode: 500, - body: { - error: { - message: 'User already added', + describe('with Group SSO enabled', function () { + const JOHN_DOE = { + _id: 'abc123def456', + first_name: 'John', + last_name: 'Doe', + email: 'john.doe@test.com', + last_active_at: new Date('2023-01-15'), + invite: true, + } + const BOBBY_LAPOINTE = { + _id: 'bcd234efa567', + first_name: 'Bobby', + last_name: 'Lapointe', + email: 'bobby.lapointe@test.com', + last_active_at: new Date('2023-01-02'), + invite: false, + } + const CLAIRE_JENNINGS = { + _id: 'defabc231453', + first_name: 'Claire', + last_name: 'Jennings', + email: 'claire.jennings@test.com', + last_active_at: new Date('2023-01-03'), + invite: false, + enrollment: { + managedBy: GROUP_ID, + enrolledAt: new Date('2023-01-03'), + sso: { + providerId: '123', + externalId: '123', }, }, + } + + beforeEach(function () { + cy.window().then(win => { + win.metaAttributesCache = new Map() + win.metaAttributesCache.set('ol-users', [ + JOHN_DOE, + BOBBY_LAPOINTE, + CLAIRE_JENNINGS, + ]) + win.metaAttributesCache.set('ol-groupId', GROUP_ID) + win.metaAttributesCache.set('ol-groupName', 'My Awesome Team') + win.metaAttributesCache.set('ol-groupSize', 10) + win.metaAttributesCache.set('ol-managedUsersActive', false) + win.metaAttributesCache.set('ol-groupSSOActive', true) + }) + + mountGroupMembersProvider() }) - cy.get('.form-control').type('someone.else@test.com') - cy.get('button').click() - cy.get('.alert').contains('Error: User already added') - }) + it('should display the Security column', function () { + cy.get('ul.managed-users-list table > tbody').within(() => { + cy.get('tr:nth-child(2)').within(() => { + cy.contains('bobby.lapointe@test.com') + cy.get('.sr-only').contains('SSO not active') + }) - it('checks the select all checkbox', function () { - cy.get('ul').within(() => { - cy.get('li:nth-child(2)').within(() => { - cy.get('.select-item').should('not.be.checked') - }) - cy.get('li:nth-child(3)').within(() => { - cy.get('.select-item').should('not.be.checked') + cy.get('tr:nth-child(3)').within(() => { + cy.contains('claire.jennings@test.com') + cy.get('.sr-only').contains('SSO active') + }) }) }) - - cy.get('.select-all').click() - - cy.get('ul').within(() => { - cy.get('li:nth-child(2)').within(() => { - cy.get('.select-item').should('be.checked') - }) - cy.get('li:nth-child(3)').within(() => { - cy.get('.select-item').should('be.checked') - }) - }) - }) - - it('remove a member', function () { - cy.intercept('DELETE', `${PATHS.removeMember}/abc123def456`, { - statusCode: 200, - }) - - cy.get('ul').within(() => { - cy.get('li:nth-child(2)').within(() => { - cy.get('.select-item').check() - }) - }) - - cy.get('button').contains('Remove from group').click() - - cy.get('small').contains('You have added 1 of 10 available members') - cy.get('ul').within(() => { - cy.get('li:nth-child(2)').within(() => { - cy.contains('bobby.lapointe@test.com') - cy.contains('Bobby Lapointe') - cy.contains('2nd Jan 2023') - cy.get(`[aria-label="Accepted invite"]`) - }) - }) - }) - - it('tries to remove a user and displays the error', function () { - cy.intercept('DELETE', `${PATHS.removeMember}/abc123def456`, { - statusCode: 500, - }) - - cy.get('ul').within(() => { - cy.get('li:nth-child(2)').within(() => { - cy.get('.select-item').check() - }) - }) - cy.get('button').contains('Remove from group').click() - - cy.get('.alert').contains('Sorry, something went wrong') }) }) diff --git a/services/web/test/frontend/features/group-management/components/institution-managers.spec.tsx b/services/web/test/frontend/features/group-management/components/institution-managers.spec.tsx index ab443f58bf..89d46e8db0 100644 --- a/services/web/test/frontend/features/group-management/components/institution-managers.spec.tsx +++ b/services/web/test/frontend/features/group-management/components/institution-managers.spec.tsx @@ -1,4 +1,4 @@ -import InstitutionManagers from '../../../../../frontend/js/features/group-management/components/institution-managers' +import InstitutionManagers from '@/features/group-management/components/institution-managers' const JOHN_DOE = { _id: 'abc123def456', diff --git a/services/web/test/frontend/features/group-management/components/managed-users/managed-group-members.spec.tsx b/services/web/test/frontend/features/group-management/components/managed-group-members.spec.tsx similarity index 93% rename from services/web/test/frontend/features/group-management/components/managed-users/managed-group-members.spec.tsx rename to services/web/test/frontend/features/group-management/components/managed-group-members.spec.tsx index fba765f949..11a9f86cd9 100644 --- a/services/web/test/frontend/features/group-management/components/managed-users/managed-group-members.spec.tsx +++ b/services/web/test/frontend/features/group-management/components/managed-group-members.spec.tsx @@ -1,5 +1,5 @@ -import GroupMembers from '../../../../../../frontend/js/features/group-management/components/group-members' -import { GroupMembersProvider } from '../../../../../../frontend/js/features/group-management/context/group-members-context' +import GroupMembers from '@/features/group-management/components/group-members' +import { GroupMembersProvider } from '@/features/group-management/context/group-members-context' const GROUP_ID = '777fff777fff' const JOHN_DOE = { @@ -241,16 +241,17 @@ describe('Group members when group SSO is enabled', function () { cy.get('ul.managed-users-list table > tbody').within(() => { cy.get('tr:nth-child(2)').within(() => { cy.contains('bobby.lapointe@test.com') - cy.get('.sr-only').contains('SSO unlinked').should('not.exist') + cy.get('.sr-only').contains('SSO not active').should('not.exist') }) cy.get('tr:nth-child(3)').within(() => { cy.contains('claire.jennings@test.com') - cy.get('.sr-only').contains('SSO linked').should('not.exist') + cy.get('.sr-only').contains('SSO active').should('not.exist') }) }) }) - it('should display SSO Column when group sso is not enabled', function () { + + it('should display SSO Column when Group SSO is enabled', function () { cy.window().then(win => { win.metaAttributesCache.set('ol-groupSSOActive', true) }) @@ -258,12 +259,12 @@ describe('Group members when group SSO is enabled', function () { cy.get('ul.managed-users-list table > tbody').within(() => { cy.get('tr:nth-child(2)').within(() => { cy.contains('bobby.lapointe@test.com') - cy.get('.sr-only').contains('SSO unlinked') + cy.get('.sr-only').contains('SSO not active') }) cy.get('tr:nth-child(3)').within(() => { cy.contains('claire.jennings@test.com') - cy.get('.sr-only').contains('SSO linked') + cy.get('.sr-only').contains('SSO active') }) }) }) diff --git a/services/web/test/frontend/features/group-management/components/managed-users/managed-user-row.spec.tsx b/services/web/test/frontend/features/group-management/components/managed-users/managed-user-row.spec.tsx deleted file mode 100644 index 606a90378e..0000000000 --- a/services/web/test/frontend/features/group-management/components/managed-users/managed-user-row.spec.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import sinon from 'sinon' -import ManagedUserRow from '../../../../../../frontend/js/features/group-management/components/managed-users/managed-user-row' -import { GroupMembersProvider } from '../../../../../../frontend/js/features/group-management/context/group-members-context' -import { User } from '../../../../../../types/group-management/user' - -describe('ManagedUserRow', function () { - const subscriptionId = '123abc' - - describe('with an ordinary user', function () { - let user: User - - beforeEach(function () { - user = { - _id: 'some-user', - email: 'some.user@example.com', - first_name: 'Some', - last_name: 'User', - invite: false, - last_active_at: new Date('2070-11-21T03:00:00'), - enrollment: undefined, - isEntityAdmin: undefined, - } - cy.window().then(win => { - win.metaAttributesCache.set('ol-users', [user]) - }) - - cy.mount( - - - - ) - }) - - it('renders the row', function () { - cy.get('tr').should('exist') - // Checkbox - cy.get('.select-item').should('not.be.checked') - // Email - cy.get('tr').contains(user.email) - // Name - cy.get('tr').contains(user.first_name) - cy.get('tr').contains(user.last_name) - // Last active date - cy.get('tr').contains('21st Nov 2070') - // Managed status - cy.get('tr').contains('Managed') - // Dropdown button - cy.get('#managed-user-dropdown-some\\.user\\@example\\.com').should( - 'exist' - ) - }) - }) - - describe('with a pending invite', function () { - let user: User - - beforeEach(function () { - user = { - _id: 'some-user', - email: 'some.user@example.com', - first_name: 'Some', - last_name: 'User', - invite: true, - last_active_at: new Date('2070-11-21T03:00:00'), - enrollment: undefined, - isEntityAdmin: undefined, - } - cy.window().then(win => { - win.metaAttributesCache.set('ol-users', [user]) - }) - - cy.mount( - - - - ) - }) - - it('should render a "Pending invite" badge', function () { - cy.get('.badge-new-comment').contains('Pending invite') - }) - }) - - describe('with a group admin', function () { - let user: User - - beforeEach(function () { - user = { - _id: 'some-user', - email: 'some.user@example.com', - first_name: 'Some', - last_name: 'User', - invite: false, - last_active_at: new Date('2070-11-21T03:00:00'), - enrollment: undefined, - isEntityAdmin: true, - } - cy.window().then(win => { - win.metaAttributesCache.set('ol-users', [user]) - }) - - cy.mount( - - - - ) - }) - - it('should render a "Group admin" symbol', function () { - cy.get('[aria-label="Group admin"].fa-user-circle-o').should('exist') - }) - }) - - describe('selecting and unselecting user row', function () { - let user: User - - beforeEach(function () { - user = { - _id: 'some-user', - email: 'some.user@example.com', - first_name: 'Some', - last_name: 'User', - invite: false, - last_active_at: new Date('2070-11-21T03:00:00'), - enrollment: undefined, - isEntityAdmin: undefined, - } - cy.window().then(win => { - win.metaAttributesCache.set('ol-users', [user]) - }) - - cy.mount( - - - - ) - }) - - it('should select and unselect the user', function () { - cy.get('.select-item').should('not.be.checked') - cy.get('.select-item').click() - cy.get('.select-item').should('be.checked') - cy.get('.select-item').click() - cy.get('.select-item').should('not.be.checked') - }) - }) -}) diff --git a/services/web/test/frontend/features/group-management/components/managed-users/managed-user-dropdown-button.spec.tsx b/services/web/test/frontend/features/group-management/components/members-table/dropdown-button.spec.tsx similarity index 96% rename from services/web/test/frontend/features/group-management/components/managed-users/managed-user-dropdown-button.spec.tsx rename to services/web/test/frontend/features/group-management/components/members-table/dropdown-button.spec.tsx index 9d917ce932..564ebd4a5e 100644 --- a/services/web/test/frontend/features/group-management/components/managed-users/managed-user-dropdown-button.spec.tsx +++ b/services/web/test/frontend/features/group-management/components/members-table/dropdown-button.spec.tsx @@ -1,7 +1,7 @@ import type { PropsWithChildren } from 'react' import sinon from 'sinon' -import ManagedUserDropdownButton from '../../../../../../frontend/js/features/group-management/components/managed-users/managed-user-dropdown-button' -import { GroupMembersProvider } from '../../../../../../frontend/js/features/group-management/context/group-members-context' +import DropdownButton from '@/features/group-management/components/members-table/dropdown-button' +import { GroupMembersProvider } from '@/features/group-management/context/group-members-context' import { User } from '../../../../../../types/group-management/user' function Wrapper({ children }: PropsWithChildren>) { @@ -20,7 +20,7 @@ function Wrapper({ children }: PropsWithChildren>) { function mountDropDownComponent(user: User, subscriptionId: string) { cy.mount( - { + win.metaAttributesCache = new Map() + }) + }) + + describe('with an ordinary user', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('renders the row', function () { + cy.get('tr').should('exist') + // Checkbox + cy.get('.select-item').should('not.be.checked') + // Email + cy.get('tr').contains(user.email) + // Name + cy.get('tr').contains(user.first_name) + cy.get('tr').contains(user.last_name) + // Last active date + cy.get('tr').contains('21st Nov 2070') + // Dropdown button + cy.get('#managed-user-dropdown-some\\.user\\@example\\.com').should( + 'exist' + ) + + cy.get('tr').contains('SSO').should('not.exist') + cy.get('tr').contains('Managed').should('not.exist') + }) + }) + + describe('with a pending invite', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: true, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should render a "Pending invite" badge', function () { + cy.get('.badge-new-comment').contains('Pending invite') + }) + }) + + describe('with a group admin', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: true, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should render a "Group admin" symbol', function () { + cy.get('[aria-label="Group admin"].fa-user-circle-o').should('exist') + }) + }) + + describe('selecting and unselecting user row', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should select and unselect the user', function () { + cy.get('.select-item').should('not.be.checked') + cy.get('.select-item').click() + cy.get('.select-item').should('be.checked') + cy.get('.select-item').click() + cy.get('.select-item').should('not.be.checked') + }) + }) + }) + + describe('with Managed Users enabled', function () { + beforeEach(function () { + cy.window().then(win => { + win.metaAttributesCache = new Map() + win.metaAttributesCache.set('ol-managedUsersActive', true) + }) + }) + + describe('with an ordinary user', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('renders the row', function () { + cy.get('tr').should('exist') + // Checkbox + cy.get('.select-item').should('not.be.checked') + // Email + cy.get('tr').contains(user.email) + // Name + cy.get('tr').contains(user.first_name) + cy.get('tr').contains(user.last_name) + // Last active date + cy.get('tr').contains('21st Nov 2070') + // Managed status + cy.get('tr').contains('Managed') + // Dropdown button + cy.get('#managed-user-dropdown-some\\.user\\@example\\.com').should( + 'exist' + ) + }) + }) + + describe('with a pending invite', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: true, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should render a "Pending invite" badge', function () { + cy.get('.badge-new-comment').contains('Pending invite') + }) + }) + + describe('with a group admin', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: true, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should render a "Group admin" symbol', function () { + cy.get('[aria-label="Group admin"].fa-user-circle-o').should('exist') + }) + }) + + describe('selecting and unselecting user row', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should select and unselect the user', function () { + cy.get('.select-item').should('not.be.checked') + cy.get('.select-item').click() + cy.get('.select-item').should('be.checked') + cy.get('.select-item').click() + cy.get('.select-item').should('not.be.checked') + }) + }) + }) + + describe('with Group SSO enabled', function () { + beforeEach(function () { + cy.window().then(win => { + win.metaAttributesCache = new Map() + win.metaAttributesCache.set('ol-groupSSOActive', true) + }) + }) + + describe('with an ordinary user', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('renders the row', function () { + cy.get('tr').should('exist') + // Checkbox + cy.get('.select-item').should('not.be.checked') + // Email + cy.get('tr').contains(user.email) + // Name + cy.get('tr').contains(user.first_name) + cy.get('tr').contains(user.last_name) + // Last active date + cy.get('tr').contains('21st Nov 2070') + // SSO status + cy.get('tr').contains('SSO') + // Dropdown button + cy.get('#managed-user-dropdown-some\\.user\\@example\\.com').should( + 'exist' + ) + + cy.get('tr').contains('Managed').should('not.exist') + }) + }) + + describe('with a pending invite', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: true, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should render a "Pending invite" badge', function () { + cy.get('.badge-new-comment').contains('Pending invite') + }) + }) + + describe('with a group admin', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: true, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should render a "Group admin" symbol', function () { + cy.get('[aria-label="Group admin"].fa-user-circle-o').should('exist') + }) + }) + + describe('selecting and unselecting user row', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should select and unselect the user', function () { + cy.get('.select-item').should('not.be.checked') + cy.get('.select-item').click() + cy.get('.select-item').should('be.checked') + cy.get('.select-item').click() + cy.get('.select-item').should('not.be.checked') + }) + }) + }) + + describe('with Managed Users and Group SSO enabled', function () { + beforeEach(function () { + cy.window().then(win => { + win.metaAttributesCache = new Map() + win.metaAttributesCache.set('ol-managedUsersActive', true) + win.metaAttributesCache.set('ol-groupSSOActive', true) + }) + }) + + describe('with an ordinary user', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('renders the row', function () { + cy.get('tr').should('exist') + // Checkbox + cy.get('.select-item').should('not.be.checked') + // Email + cy.get('tr').contains(user.email) + // Name + cy.get('tr').contains(user.first_name) + cy.get('tr').contains(user.last_name) + // Last active date + cy.get('tr').contains('21st Nov 2070') + // Managed status + cy.get('tr').contains('Managed') + // SSO status + cy.get('tr').contains('SSO') + // Dropdown button + cy.get('#managed-user-dropdown-some\\.user\\@example\\.com').should( + 'exist' + ) + }) + }) + + describe('with a pending invite', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: true, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should render a "Pending invite" badge', function () { + cy.get('.badge-new-comment').contains('Pending invite') + }) + }) + + describe('with a group admin', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: true, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should render a "Group admin" symbol', function () { + cy.get('[aria-label="Group admin"].fa-user-circle-o').should('exist') + }) + }) + + describe('selecting and unselecting user row', function () { + let user: User + + beforeEach(function () { + user = { + _id: 'some-user', + email: 'some.user@example.com', + first_name: 'Some', + last_name: 'User', + invite: false, + last_active_at: new Date('2070-11-21T03:00:00'), + enrollment: undefined, + isEntityAdmin: undefined, + } + cy.window().then(win => { + win.metaAttributesCache.set('ol-users', [user]) + }) + + cy.mount( + + + + ) + }) + + it('should select and unselect the user', function () { + cy.get('.select-item').should('not.be.checked') + cy.get('.select-item').click() + cy.get('.select-item').should('be.checked') + cy.get('.select-item').click() + cy.get('.select-item').should('not.be.checked') + }) + }) + }) +}) diff --git a/services/web/test/frontend/features/group-management/components/managed-users/managed-users-list.spec.tsx b/services/web/test/frontend/features/group-management/components/members-table/members-list.spec.tsx similarity index 89% rename from services/web/test/frontend/features/group-management/components/managed-users/managed-users-list.spec.tsx rename to services/web/test/frontend/features/group-management/components/members-table/members-list.spec.tsx index af3918d72f..206d8ed204 100644 --- a/services/web/test/frontend/features/group-management/components/managed-users/managed-users-list.spec.tsx +++ b/services/web/test/frontend/features/group-management/components/members-table/members-list.spec.tsx @@ -1,17 +1,17 @@ -import ManagedUsersList from '../../../../../../frontend/js/features/group-management/components/managed-users/managed-users-list' -import { GroupMembersProvider } from '../../../../../../frontend/js/features/group-management/context/group-members-context' +import MembersList from '@/features/group-management/components/members-table/members-list' +import { GroupMembersProvider } from '@/features/group-management/context/group-members-context' const groupId = 'somegroup' function mountManagedUsersList() { cy.mount( - + ) } -describe('ManagedUsersList', function () { +describe('MembersList', function () { describe('with users', function () { const users = [ { @@ -98,7 +98,7 @@ describe('ManagedUsersList', function () { }) cy.mount( - + ) }) diff --git a/services/web/test/frontend/features/group-management/components/managed-users/offboard-managed-user-modal.spec.tsx b/services/web/test/frontend/features/group-management/components/members-table/offboard-managed-user-modal.spec.tsx similarity index 95% rename from services/web/test/frontend/features/group-management/components/managed-users/offboard-managed-user-modal.spec.tsx rename to services/web/test/frontend/features/group-management/components/members-table/offboard-managed-user-modal.spec.tsx index 2f350b0c6d..2db12d822c 100644 --- a/services/web/test/frontend/features/group-management/components/managed-users/offboard-managed-user-modal.spec.tsx +++ b/services/web/test/frontend/features/group-management/components/members-table/offboard-managed-user-modal.spec.tsx @@ -1,4 +1,4 @@ -import OffboardManagedUserModal from '../../../../../../frontend/js/features/group-management/components/managed-users/offboard-managed-user-modal' +import OffboardManagedUserModal from '@/features/group-management/components/members-table/offboard-managed-user-modal' import sinon from 'sinon' describe('OffboardManagedUserModal', function () { diff --git a/services/web/test/frontend/features/group-management/components/publisher-managers.spec.tsx b/services/web/test/frontend/features/group-management/components/publisher-managers.spec.tsx index 68f613e872..13b71b09e8 100644 --- a/services/web/test/frontend/features/group-management/components/publisher-managers.spec.tsx +++ b/services/web/test/frontend/features/group-management/components/publisher-managers.spec.tsx @@ -1,4 +1,4 @@ -import PublisherManagers from '../../../../../frontend/js/features/group-management/components/publisher-managers' +import PublisherManagers from '@/features/group-management/components/publisher-managers' const JOHN_DOE = { _id: 'abc123def456',