Merge pull request #15348 from overleaf/ab-decoupling-refactor-members-table

[web] Modular members management table refactoring

GitOrigin-RevId: 9a3a00a32970e78e5b43b3a68621a627c490c728
This commit is contained in:
Alexandre Bourdin
2023-10-30 09:04:43 +00:00
committed by Copybot
parent c71301ac9a
commit 665f5e37c4
28 changed files with 1221 additions and 555 deletions
@@ -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',
@@ -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(
<GroupMembersProvider>
<GroupMembers />
</GroupMembersProvider>
)
})
}
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(
<GroupMembersProvider>
<GroupMembers />
</GroupMembersProvider>
)
})
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')
})
})
@@ -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',
@@ -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')
})
})
})
@@ -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(
<GroupMembersProvider>
<ManagedUserRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<ManagedUserRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<ManagedUserRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<ManagedUserRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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')
})
})
})
@@ -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<Record<string, unknown>>) {
@@ -20,7 +20,7 @@ function Wrapper({ children }: PropsWithChildren<Record<string, unknown>>) {
function mountDropDownComponent(user: User, subscriptionId: string) {
cy.mount(
<Wrapper>
<ManagedUserDropdownButton
<DropdownButton
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
@@ -218,6 +218,7 @@ describe('ManagedUserDropdownButton', function () {
cy.findByTestId('no-actions-available').should('exist')
})
})
describe('sending SSO invite reminder', function () {
const user = {
_id: 'some-user',
@@ -1,7 +1,7 @@
import ManagedUserStatus from '../../../../../../frontend/js/features/group-management/components/managed-users/managed-user-status'
import ManagedUserStatus from '@/features/group-management/components/members-table/managed-user-status'
import { User } from '../../../../../../types/group-management/user'
describe('ManagedUserStatus', function () {
describe('MemberStatus', function () {
describe('with a pending invite', function () {
const user: User = {
_id: 'some-user',
@@ -0,0 +1,685 @@
import sinon from 'sinon'
import MemberRow from '@/features/group-management/components/members-table/member-row'
import { GroupMembersProvider } from '@/features/group-management/context/group-members-context'
import { User } from '../../../../../../types/group-management/user'
describe('MemberRow', function () {
const subscriptionId = '123abc'
describe('default view', function () {
beforeEach(function () {
cy.window().then(win => {
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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(
<GroupMembersProvider>
<MemberRow
user={user}
openOffboardingModalForUser={sinon.stub()}
groupId={subscriptionId}
setManagedUserAlert={sinon.stub()}
/>
</GroupMembersProvider>
)
})
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')
})
})
})
})
@@ -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(
<GroupMembersProvider>
<ManagedUsersList groupId={groupId} />
<MembersList groupId={groupId} />
</GroupMembersProvider>
)
}
describe('ManagedUsersList', function () {
describe('MembersList', function () {
describe('with users', function () {
const users = [
{
@@ -98,7 +98,7 @@ describe('ManagedUsersList', function () {
})
cy.mount(
<GroupMembersProvider>
<ManagedUsersList groupId={groupId} />
<MembersList groupId={groupId} />
</GroupMembersProvider>
)
})
@@ -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 () {
@@ -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',