diff --git a/client/src/components/users/UserListItem.vue b/client/src/components/users/UserListItem.vue index 9b59805acde..c732d6f0cc7 100644 --- a/client/src/components/users/UserListItem.vue +++ b/client/src/components/users/UserListItem.vue @@ -232,8 +232,4 @@ async function onOptionsClick(event: Event): Promise { } } } - -.frozen { - filter: blur(1px); -} diff --git a/client/src/components/users/types.ts b/client/src/components/users/types.ts index 1d854350317..7e1e7291fdb 100644 --- a/client/src/components/users/types.ts +++ b/client/src/components/users/types.ts @@ -159,21 +159,11 @@ export class UserCollection { } case SortProperty.Profile: if (profile1 === profile2) { - // return user2.isRevoked() && !user1.isRevoked() ? -1 : 0; return this.statusDiff(user1, user2); } return diff; case SortProperty.Status: return ascending ? this.statusDiff(user1, user2) : this.statusDiff(user2, user1); - - // if (user2.isRevoked() === user1.isRevoked()) { - // return ascending ? diff : -diff; - // } - // if (ascending) { - // return user2.isRevoked() && !user1.isRevoked() ? -1 : 0; - // } else { - // return user1.isRevoked() && !user2.isRevoked() ? -1 : 0; - // } default: return 0; } diff --git a/client/tests/e2e/specs/user_details.spec.ts b/client/tests/e2e/specs/user_details.spec.ts index e5bcf0861d6..bfd73460ea9 100644 --- a/client/tests/e2e/specs/user_details.spec.ts +++ b/client/tests/e2e/specs/user_details.spec.ts @@ -27,8 +27,9 @@ msTest('User details modal', async ({ usersPage }) => { await expect(detailsItems.nth(0).locator('.details-item-name__title')).toHaveText('Name'); // cspell:disable-next-line await expect(detailsItems.nth(0).locator('.details-item-name__text')).toHaveText('Cernd'); - await expect(detailsItems.nth(1).locator('.details-item-name__title')).toHaveText('Joined'); - await expect(detailsItems.nth(1).locator('.details-item-name__text')).toHaveText(/^now|< 1 minute$/); + const timeItems = modal.locator('.ms-modal-content').locator('.time-list-item'); + await expect(timeItems.nth(0).locator('.time-list-item__title')).toHaveText('Joined'); + await expect(timeItems.nth(0).locator('.time-list-item__text')).toHaveText(/^now|< 1 minute$/); await expect(modal.locator('.workspace-list')).toBeVisible(); await expect(modal.locator('.workspace-empty')).toBeHidden(); await expect(modal.locator('.workspace-list').locator('.workspace-list-item').locator('.item-container__name')).toHaveText([ @@ -46,11 +47,12 @@ msTest('User details modal no common workspaces', async ({ usersPage }) => { await expect(detailsItems.nth(0).locator('.details-item-name__title')).toHaveText('Name'); // cspell:disable-next-line await expect(detailsItems.nth(0).locator('.details-item-name__text')).toHaveText('Arthas Menethil'); - await expect(detailsItems.nth(0).locator('.revoked-chip')).toBeVisible(); - await expect(detailsItems.nth(1).locator('.details-item-name__title')).toHaveText('Joined'); - await expect(detailsItems.nth(1).locator('.details-item-name__text')).toHaveText('Jul 3, 2002'); - await expect(detailsItems.nth(2).locator('.details-item-name__title')).toHaveText('Revoked since'); - await expect(detailsItems.nth(2).locator('.details-item-name__text')).toHaveText('Apr 7, 2022'); + await expect(detailsItems.nth(0).locator('.label-status')).toBeVisible(); + const timeItems = modal.locator('.ms-modal-content').locator('.time-list-item'); + await expect(timeItems.nth(0).locator('.time-list-item__title')).toHaveText('Joined'); + await expect(timeItems.nth(0).locator('.time-list-item__text')).toHaveText('Jul 3, 2002'); + await expect(timeItems.nth(1).locator('.time-list-item__title')).toHaveText('Revoked since'); + await expect(timeItems.nth(1).locator('.time-list-item__text')).toHaveText('Apr 7, 2022'); await expect(modal.locator('.workspace-list')).toBeHidden(); await expect(modal.locator('.workspace-empty')).toBeVisible(); await expect(modal.locator('.workspace-empty')).toHaveText('You have no workspaces in common with this user.'); diff --git a/client/tests/e2e/specs/users_list.spec.ts b/client/tests/e2e/specs/users_list.spec.ts index c304f2856e6..164a32206f2 100644 --- a/client/tests/e2e/specs/users_list.spec.ts +++ b/client/tests/e2e/specs/users_list.spec.ts @@ -10,6 +10,7 @@ const USERS = [ profile: 'Administrator', active: true, currentUser: true, + frozen: false, }, { // cspell:disable-next-line @@ -17,6 +18,7 @@ const USERS = [ email: 'jaheira@gmail.com', profile: 'Administrator', active: true, + frozen: false, }, { // cspell:disable-next-line @@ -24,6 +26,7 @@ const USERS = [ email: 'arthasmenethil@gmail.com', profile: 'Administrator', active: false, + frozen: false, }, { // cspell:disable-next-line @@ -31,12 +34,14 @@ const USERS = [ email: 'cernd@gmail.com', profile: 'Standard', active: true, + frozen: false, }, { name: 'Patches', email: 'patches@yahoo.fr', profile: 'Standard', active: true, + frozen: false, }, { // cspell:disable-next-line @@ -44,19 +49,22 @@ const USERS = [ email: 'val@gmail.com', profile: 'Standard', active: false, + frozen: false, }, { - // cspell:disable-next-line - name: 'Karl Hungus', - email: 'karlhungus@gmail.com', + name: 'Gaia', + email: 'gaia@gmail.com', profile: 'External', - active: true, + active: false, + frozen: false, }, { - name: 'Gaia', - email: 'gaia@gmail.com', + // cspell:disable-next-line + name: 'Karl Hungus', + email: 'karlhungus@gmail.com', profile: 'External', active: false, + frozen: true, }, ]; @@ -72,6 +80,13 @@ msTest('User list default state', async ({ usersPage }) => { await expect(usersPage.locator('#users-page-user-list').getByRole('listitem')).toHaveCount(USERS.length); }); +function getStatusForUser(user: any): string { + if (user.frozen) { + return 'Suspended'; + } + return user.active ? 'Active' : 'Revoked'; +} + msTest('Check user list items', async ({ usersPage }) => { const usersList = usersPage.locator('#users-page-user-list'); for (const [index, user] of USERS.entries()) { @@ -79,8 +94,8 @@ msTest('Check user list items', async ({ usersPage }) => { await expect(item.locator('.user-name').locator('.person-name')).toHaveText(user.name); await expect(item.locator('.user-profile')).toHaveText(user.profile); await expect(item.locator('.user-email')).toHaveText(user.email); - await expect(item.locator('.user-status')).toHaveText(user.active ? 'Active' : 'Revoked'); - if (!user.active) { + await expect(item.locator('.user-status')).toHaveText(getStatusForUser(user)); + if (!user.active && !user.frozen) { await expect(item).toHaveTheClass('revoked'); } } @@ -94,12 +109,12 @@ msTest('Check user grid items', async ({ usersPage }) => { const card = usersGrid.locator('.user-card-item').nth(index); await expect(card.locator('.user-card-info').locator('.user-card-info__name').locator('span').nth(0)).toHaveText(user.name); await expect(card.locator('.user-card-info').locator('.user-card-info__email')).toHaveText(user.email); - await expect(card.locator('.user-card-profile')).toHaveText(user.profile); - if (user.active) { - await expect(card.locator('.user-revoked')).toBeHidden(); + await expect(card.locator('.user-card-profile').locator('.label-profile')).toHaveText(user.profile); + if (user.frozen || !user.active) { + await expect(card.locator('.user-card-profile').locator('.label-status')).toBeVisible(); + await expect(card.locator('.user-card-profile').locator('.label-status')).toHaveText(getStatusForUser(user)); } else { - await expect(card.locator('.user-revoked')).toBeVisible(); - await expect(card).toHaveTheClass('revoked'); + await expect(card.locator('.user-card-profile').locator('.label-status')).toBeHidden(); } } }); @@ -243,7 +258,7 @@ msTest('Test users selection in list mode', async ({ usersPage }) => { await expect(checkbox).toHaveState('checked'); } } - const expectedSelected = USERS.filter((u) => u.active && u.name !== 'Gordon Freeman'); + const expectedSelected = USERS.filter((u) => (u.active || u.frozen) && u.name !== 'Gordon Freeman'); await expect(actionBar.locator('.counter')).toHaveText(`${expectedSelected.length} users selected`, { useInnerText: true }); await expect(headerCheckbox).toHaveState('checked'); // Unselect one @@ -324,8 +339,8 @@ msTest('User filter popover default state', async ({ usersPage }) => { await expect(popover.locator('#user-filter-list').getByRole('group')).toHaveCount(2); const statusGroup = popover.locator('#user-filter-list').getByRole('group').nth(0); await expect(statusGroup.locator('.list-group-title')).toHaveText('Status'); - await expect(statusGroup.getByRole('listitem')).toHaveCount(2); - await expect(statusGroup.getByRole('listitem')).toHaveText(['Active', 'Revoked']); + await expect(statusGroup.getByRole('listitem')).toHaveCount(3); + await expect(statusGroup.getByRole('listitem')).toHaveText(['Active', 'Revoked', 'Suspended']); for (const checkbox of await statusGroup.locator('ion-checkbox').all()) { await expect(checkbox).toHaveState('checked'); } @@ -357,17 +372,17 @@ msTest('Filter users list', async ({ usersPage }) => { // Also hides revoked await toggleFilter(usersPage, 'Revoked'); await expect(usersList.getByRole('listitem').locator('.user-name').locator('.person-name')).toHaveText( - USERS.filter((u) => u.profile !== 'Administrator' && u.active === true).map((u) => u.name), + USERS.filter((u) => u.profile !== 'Administrator' && (u.active === true || u.frozen)).map((u) => u.name), ); // Also hides external await toggleFilter(usersPage, 'External'); await expect(usersList.getByRole('listitem').locator('.user-name').locator('.person-name')).toHaveText( - USERS.filter((u) => u.profile !== 'Administrator' && u.profile !== 'External' && u.active === true).map((u) => u.name), + USERS.filter((u) => u.profile !== 'Administrator' && u.profile !== 'External' && (u.active === true || u.frozen)).map((u) => u.name), ); // Show admins again await toggleFilter(usersPage, 'Administrator'); await expect(usersList.getByRole('listitem').locator('.user-name').locator('.person-name')).toHaveText( - USERS.filter((u) => u.profile !== 'External' && u.active === true).map((u) => u.name), + USERS.filter((u) => u.profile !== 'External' && u.active === true && !u.frozen).map((u) => u.name), ); await expect(usersPage.locator('.no-match-result')).toBeHidden(); // Also hide active users @@ -438,26 +453,6 @@ msTest('Sort users list', async ({ usersPage }) => { } }).map((u) => u.name), ); - await sortBy(sortButton, 'Descending'); - await sortBy(sortButton, 'Status'); - await expect(sortButton).toHaveText('Status'); - const PROFILE_WEIGHTS = new Map([ - ['Administrator', 8], - ['Standard', 4], - ['External', 2], - ]); - await expect(usersList.getByRole('listitem').locator('.user-name').locator('.person-name')).toHaveText( - USERS.sort((u1, u2) => { - if (u1.currentUser) { - return -1; - } else if (u2.currentUser) { - return 1; - } - const u1Weight = (PROFILE_WEIGHTS.get(u1.profile) as number) + Number(u1.active) * 16; - const u2Weight = (PROFILE_WEIGHTS.get(u2.profile) as number) + Number(u2.active) * 16; - return u1.name.localeCompare(u2.name) - (u1Weight - u2Weight); - }).map((u) => u.name), - ); }); msTest('Invite new user', async ({ usersPage }) => { diff --git a/newsfragments/8694.feature.rst b/newsfragments/8694.feature.rst new file mode 100644 index 00000000000..59210da77ce --- /dev/null +++ b/newsfragments/8694.feature.rst @@ -0,0 +1 @@ +Display suspended users