From 3e14dbdd3a792302dbe144bd25ec5e527f94944e Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Mon, 22 Sep 2025 07:55:18 -0700 Subject: [PATCH 01/12] added manage user tests --- .../tests/cypress/component/ManageUsers.cy.js | 265 ++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 frontend/tests/cypress/component/ManageUsers.cy.js diff --git a/frontend/tests/cypress/component/ManageUsers.cy.js b/frontend/tests/cypress/component/ManageUsers.cy.js new file mode 100644 index 000000000..176d4a230 --- /dev/null +++ b/frontend/tests/cypress/component/ManageUsers.cy.js @@ -0,0 +1,265 @@ +import ManageUsers from '@/components/accountMgmt/ManageUsers.vue'; +import vuetify from '@/plugins/vuetify'; +import { ApiRoutes, PATHS } from '@/utils/constants'; + +const organizationId = '1234'; +const userId = '123'; + +function mountWithPinia(initialState = {}, dataOverride = {}) { + cy.setupPinia({ initialState, stubActions: false }).then((pinia) => { + const pushStub = cy.stub(); + cy.mount(ManageUsers, { + global: { + plugins: [pinia, vuetify], + mocks: { + $router: { + push: pushStub, + }, + }, + }, + ...dataOverride, + }); + cy.wrap(pushStub).as('routerPush'); + }); +} + +function mockApiResponses(overrides = {}) { + const defaultResponses = { + getFacilities: { + method: 'GET', + url: `${ApiRoutes.ORGANIZATION}/${organizationId}/facilities`, + response: [], + }, + getOrg: { + method: 'GET', + url: `${ApiRoutes.ORGANIZATION}/${organizationId}`, + response: [], + }, + getContacts: { + method: 'GET', + url: `${ApiRoutes.CONTACTS}/organization/${organizationId}`, + response: [], + }, + }; + + Object.keys(defaultResponses).forEach((key) => { + const { method, url, response } = { ...defaultResponses[key], ...overrides[key] }; + cy.intercept(method, url, { statusCode: 200, body: response }).as(key); + }); +} + +describe('', () => { + beforeEach(() => { + window.localStorage.setItem('jwtToken', 'mocked-jwt-token'); + mockApiResponses(); + }); + + afterEach(() => { + window.localStorage.removeItem('jwtToken'); + }); + + context('Component Rendering Tests', () => { + it('should display `Manage Users` component text', () => { + mountWithPinia({ organization: { organizationId } }); + cy.contains('h1', 'Manage Users'); + }); + + it('should render empty table for no contacts', () => { + mountWithPinia({ organization: { organizationId } }); + cy.contains('No data available'); + }); + + it('should display organization name and account number', () => { + const organizationName = 'Test Organization'; + const organizationAccountNumber = '12345'; + mountWithPinia({ + organization: { + organizationId, + organizationName, + organizationAccountNumber, + }, + }); + cy.contains('p', organizationName); + cy.contains('p', organizationAccountNumber); + }); + }); + + context('User Table Tests', () => { + it('should render portal user table with contact info', () => { + const user = { + contactid: '1', + role: { roleNumber: 123, roleName: 'Portal User' }, + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@test.com', + isPortalUser: true, + telephone: '250-999-9999', + }; + + mockApiResponses({ + getContacts: { response: [user] }, + }); + + mountWithPinia({ + organization: { organizationId, loadedModel: { primaryContactId: '2' } }, + auth: { userInfo: { contactId: userId } }, + }); + + cy.wait('@getContacts'); + cy.get('td').contains('div', 'First Name').next('div').should('have.text', 'John'); + cy.get('td').contains('div', 'Last Name').next('div').should('have.text', 'Doe'); + cy.get('td').contains('div', 'Phone Number').next('div').should('have.text', '250-999-9999'); + cy.get('td').contains('div', 'Email').next('div').should('have.text', 'john.doe@test.com'); + cy.get('td').contains('div', 'Access Type').next('div').contains('Portal User'); + }); + + it('should render `No Role Assigned`', () => { + const user = { + contactid: '1', + role: { roleNumber: 123 }, + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@test.com', + isPortalUser: true, + telephone: '250-999-9999', + }; + + mockApiResponses({ + getContacts: { response: [user] }, + }); + + mountWithPinia({ + organization: { organizationId, loadedModel: { primaryContactId: '2' } }, + auth: { userInfo: { contactId: userId } }, + }); + + cy.wait('@getContacts'); + cy.get('td').contains('div', 'Access Type').next('div').contains('No Role Assigned'); + }); + + it('should render user table with multiple users', () => { + const users = [ + { + contactid: '1', + role: { roleNumber: 123 }, + firstName: 'John', + lastName: 'Doe', + isPortalUser: true, + telephone: '250-999-9999', + }, + { + contactid: '2', + role: { roleNumber: 1243 }, + firstName: 'Jane', + lastName: 'Smith', + isPortalUser: false, + telephone: '250-123-4567', + }, + { + contactid: '3', + role: { roleNumber: 1235 }, + firstName: 'Anna', + lastName: 'Brown', + isPortalUser: false, + telephone: '250-000-0000', + }, + ]; + + mockApiResponses({ + getContacts: { response: users }, + }); + + mountWithPinia({ + organization: { organizationId, loadedModel: { primaryContactId: '2' } }, + auth: { userInfo: { contactId: userId } }, + }); + + cy.wait('@getContacts'); + cy.get('tbody').find('tr').should('have.length', 3); + }); + }); + + context('User Interaction Tests', () => { + it('should render `Add User` button and open add user dialog on click', () => { + mountWithPinia({ organization: { organizationId } }); + cy.contains('button', 'Add User').click(); + cy.contains('p', 'What type of user are you adding?'); + }); + + it('should render `Remove` button and confirm removal', () => { + const user = { + contactid: '1', + role: { roleNumber: 123 }, + firstName: 'John', + lastName: 'Doe', + isPortalUser: true, + telephone: '250-999-9999', + }; + + mockApiResponses({ + getContacts: { response: [user] }, + }); + + mountWithPinia({ + organization: { organizationId, loadedModel: { primaryContactId: '2' } }, + auth: { userInfo: { contactId: userId } }, + }); + + cy.wait('@getContacts'); + cy.contains('button', 'Remove').click(); + cy.contains('Are you sure you want to remove John Doe'); + }); + + it('should not display `Remove` button for current user', () => { + mockApiResponses({ + getContacts: { response: [{ contactId: '2', firstName: 'John' }] }, + }); + + mountWithPinia({ + organization: { organizationId }, + auth: { userInfo: { contactId: '2' } }, + }); + + cy.wait('@getContacts'); + cy.contains('button', 'Remove').should('not.exist'); + }); + + it('should navigate on clicking Back button', () => { + mountWithPinia({ organization: { organizationId } }); + cy.contains('button', 'Back').click(); + cy.get('@routerPush').should('have.been.calledWith', PATHS.ROOT.HOME); + }); + }); + + context('Access Type Tests', () => { + it('should show `Access Type` as `Contact Only`', () => { + mockApiResponses({ + getContacts: { response: [{ contactId: '2', firstName: 'John', isPortalUser: false }] }, + }); + + mountWithPinia({ + organization: { organizationId }, + auth: { userInfo: { contactId: '2' } }, + }); + + cy.wait('@getContacts'); + cy.get('td').contains('div', 'Access Type').next('div').contains('Contact Only'); + }); + + it('should render as `Primary Contact`', () => { + mockApiResponses({ + getContacts: { response: [{ contactId: '2', firstName: 'John', isPortalUser: false }] }, + }); + + mountWithPinia({ + organization: { + organizationId, + loadedModel: { primaryContactId: '2' }, + }, + }); + + cy.wait('@getContacts'); + cy.contains('.v-chip', 'Primary Contact'); + }); + }); +}); From ac5d091955d2605f89ac0e077f4be8cfd3ebd5b0 Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Mon, 22 Sep 2025 08:06:58 -0700 Subject: [PATCH 02/12] fixed github sonar issues --- frontend/tests/cypress/component/ManageUsers.cy.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/tests/cypress/component/ManageUsers.cy.js b/frontend/tests/cypress/component/ManageUsers.cy.js index 176d4a230..fd71c2996 100644 --- a/frontend/tests/cypress/component/ManageUsers.cy.js +++ b/frontend/tests/cypress/component/ManageUsers.cy.js @@ -42,20 +42,23 @@ function mockApiResponses(overrides = {}) { }, }; - Object.keys(defaultResponses).forEach((key) => { - const { method, url, response } = { ...defaultResponses[key], ...overrides[key] }; + for (const key of Object.keys(defaultResponses)) { + const { method, url, response } = { + ...defaultResponses[key], + ...overrides[key], + }; cy.intercept(method, url, { statusCode: 200, body: response }).as(key); - }); + } } describe('', () => { beforeEach(() => { - window.localStorage.setItem('jwtToken', 'mocked-jwt-token'); + globalThis.localStorage.setItem('jwtToken', 'mocked-jwt-token'); mockApiResponses(); }); afterEach(() => { - window.localStorage.removeItem('jwtToken'); + globalThis.localStorage.removeItem('jwtToken'); }); context('Component Rendering Tests', () => { From d8c635aa88d12ddf2463d389fe34b7b6d4283791 Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Mon, 22 Sep 2025 16:44:02 -0700 Subject: [PATCH 03/12] fixed failing tests from main changes --- .../tests/cypress/component/ManageUsers.cy.js | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/frontend/tests/cypress/component/ManageUsers.cy.js b/frontend/tests/cypress/component/ManageUsers.cy.js index fd71c2996..da369be61 100644 --- a/frontend/tests/cypress/component/ManageUsers.cy.js +++ b/frontend/tests/cypress/component/ManageUsers.cy.js @@ -1,6 +1,8 @@ import ManageUsers from '@/components/accountMgmt/ManageUsers.vue'; import vuetify from '@/plugins/vuetify'; +import { useAuthStore } from '@/store/auth'; import { ApiRoutes, PATHS } from '@/utils/constants'; +import { PERMISSIONS } from '@/utils/constants/permissions.js'; const organizationId = '1234'; const userId = '123'; @@ -184,7 +186,20 @@ describe('', () => { context('User Interaction Tests', () => { it('should render `Add User` button and open add user dialog on click', () => { - mountWithPinia({ organization: { organizationId } }); + mountWithPinia({ + organization: { organizationId }, + auth: { + isAuthenticated: true, + userInfo: { + serverTime: new Date(), + }, + }, + }); + cy.then(() => { + const authStore = useAuthStore(); + authStore.permissions = [PERMISSIONS.ADD_USERS]; + }); + cy.contains('button', 'Add User').click(); cy.contains('p', 'What type of user are you adding?'); }); @@ -205,7 +220,12 @@ describe('', () => { mountWithPinia({ organization: { organizationId, loadedModel: { primaryContactId: '2' } }, - auth: { userInfo: { contactId: userId } }, + auth: { isAuthenticated: true, userInfo: { contactId: userId } }, + }); + + cy.then(() => { + const authStore = useAuthStore(); + authStore.permissions = [PERMISSIONS.DELETE_USERS]; }); cy.wait('@getContacts'); From 34ef13d18faac8b57b548840bee1e38ecf638fbc Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Tue, 23 Sep 2025 08:00:13 -0700 Subject: [PATCH 04/12] added add user dialog tests --- .../cypress/component/AddUserDialog.cy.js | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 frontend/tests/cypress/component/AddUserDialog.cy.js diff --git a/frontend/tests/cypress/component/AddUserDialog.cy.js b/frontend/tests/cypress/component/AddUserDialog.cy.js new file mode 100644 index 000000000..12055747d --- /dev/null +++ b/frontend/tests/cypress/component/AddUserDialog.cy.js @@ -0,0 +1,117 @@ +import AddUserDialog from '@/components/accountMgmt/AddUserDialog.vue'; +import vuetify from '@/plugins/vuetify'; +import { ApiRoutes } from '@/utils/constants.js'; + +function mountWithPinia(initialState = {}, dataOverride = {}) { + cy.setupPinia({ initialState, stubActions: false }).then((pinia) => { + const pushStub = cy.stub(); + cy.mount(AddUserDialog, { + global: { + plugins: [pinia, vuetify], + }, + on: { + 'close-add-dialog': cy.spy().as('yourEventSpy'), + }, + data() { + return { + ...dataOverride, + }; + }, + }); + cy.wrap(pushStub).as('routerPush'); + }); +} + +describe('', () => { + beforeEach(() => { + cy.viewport(1020, 1000); + }); + + context('Step One', () => { + it('should render step one', () => { + mountWithPinia({}, { dialog: true }); + cy.contains('p', 'What type of user are you adding?'); + }); + + it('should render portal user options', () => { + mountWithPinia({}, { dialog: true }); + cy.contains('p', 'What level of portal access should this user have?'); + }); + + it('should not render admin options for read only portal user', () => { + mountWithPinia({}, { dialog: true }); + cy.contains('p', 'Which facilities should this user have access to?').should('not.be.visible'); + }); + + it('should render cancel button', () => { + mountWithPinia({}, { dialog: true }); + cy.contains('button', 'Cancel'); + }); + + it('should not render back button', () => { + mountWithPinia({}, { dialog: true }); + cy.contains('button', 'Back').should('not.exist'); + }); + }); + + context('Step Two', () => { + it('should render next button and navigate to step two', () => { + mountWithPinia({}, { dialog: true }); + cy.contains('button', 'Next').click(); + cy.contains('p', 'What type of user are you adding?').should('not.be.visible'); + }); + + it('should disable `Add` button when inputs are blank', () => { + const userFields = { + firstName: '', + lastName: '', + email: '', + telephone: '', + bceid: '', + }; + mountWithPinia({}, { dialog: true, userFields }); + cy.contains('button', 'Next').click(); + cy.contains('button', 'Add').should('have.css', 'pointer-events', 'none'); + }); + + it('should disable `Add` button when some are blank', () => { + const userFields = { + firstName: 'John', + lastName: '', + email: '', + telephone: '', + bceid: '1001', + }; + mountWithPinia({}, { dialog: true, userFields }); + cy.contains('button', 'Next').click(); + cy.contains('button', 'Add').should('have.css', 'pointer-events', 'none'); + }); + + it('should call addUser method when `Add` button is clicked', () => { + cy.intercept('POST', `${ApiRoutes.CONTACTS}`, { + statusCode: 200, + }).as('addUserRequest'); + + const userFields = { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@test.com', + telephone: '250-999-9999', + bceid: '1001', + }; + + mountWithPinia({}, { dialog: true, userFields }); + + cy.spy(AddUserDialog.methods, 'addUser').as('addUserSpy'); + + cy.contains('button', 'Next').click(); + cy.contains('button', 'Add').click(); + + cy.get('@addUserSpy').should('have.been.calledOnce'); + cy.wait('@addUserRequest').its('request').should('have.property', 'method', 'POST'); + cy.contains('p', 'User Added Successfully'); + cy.contains('button', 'Return to Manage Users').click(); + cy.contains('p', 'User Added Successfully').should('not.be.visible'); + }); + }); +}); From 4c1cefcd3a62022eadf7d81e3f5f6773878882b6 Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Wed, 24 Sep 2025 15:17:51 -0700 Subject: [PATCH 05/12] move manage user related tests to separate folder --- .../{ => accountMgmt}/AddUserDialog.cy.js | 3 --- .../accountMgmt/EditUserDialog.cy.js | 25 +++++++++++++++++++ .../{ => accountMgmt}/ManageUsers.cy.js | 0 .../accountMgmt/RemoveUserDialog.cy.js | 0 4 files changed, 25 insertions(+), 3 deletions(-) rename frontend/tests/cypress/component/{ => accountMgmt}/AddUserDialog.cy.js (97%) create mode 100644 frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js rename frontend/tests/cypress/component/{ => accountMgmt}/ManageUsers.cy.js (100%) create mode 100644 frontend/tests/cypress/component/accountMgmt/RemoveUserDialog.cy.js diff --git a/frontend/tests/cypress/component/AddUserDialog.cy.js b/frontend/tests/cypress/component/accountMgmt/AddUserDialog.cy.js similarity index 97% rename from frontend/tests/cypress/component/AddUserDialog.cy.js rename to frontend/tests/cypress/component/accountMgmt/AddUserDialog.cy.js index 12055747d..ac6bfdd63 100644 --- a/frontend/tests/cypress/component/AddUserDialog.cy.js +++ b/frontend/tests/cypress/component/accountMgmt/AddUserDialog.cy.js @@ -9,9 +9,6 @@ function mountWithPinia(initialState = {}, dataOverride = {}) { global: { plugins: [pinia, vuetify], }, - on: { - 'close-add-dialog': cy.spy().as('yourEventSpy'), - }, data() { return { ...dataOverride, diff --git a/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js new file mode 100644 index 000000000..02778ac45 --- /dev/null +++ b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js @@ -0,0 +1,25 @@ +import EditUserDialog from '@/components/accountMgmt/EditUserDialog.vue'; +import vuetify from '@/plugins/vuetify'; + +function mountWithPinia(initialState = {}, dataOverride = {}) { + cy.setupPinia({ initialState, stubActions: false }).then((pinia) => { + const pushStub = cy.stub(); + cy.mount(EditUserDialog, { + global: { + plugins: [pinia, vuetify], + }, + data() { + return { + ...dataOverride, + }; + }, + }); + cy.wrap(pushStub).as('routerPush'); + }); +} + +describe('', () => { + it('mounts', () => { + mountWithPinia(); + }); +}); diff --git a/frontend/tests/cypress/component/ManageUsers.cy.js b/frontend/tests/cypress/component/accountMgmt/ManageUsers.cy.js similarity index 100% rename from frontend/tests/cypress/component/ManageUsers.cy.js rename to frontend/tests/cypress/component/accountMgmt/ManageUsers.cy.js diff --git a/frontend/tests/cypress/component/accountMgmt/RemoveUserDialog.cy.js b/frontend/tests/cypress/component/accountMgmt/RemoveUserDialog.cy.js new file mode 100644 index 000000000..e69de29bb From 5a1eeb72cded29bbc86f78fd114eb118df7fbd88 Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Thu, 25 Sep 2025 11:09:18 -0700 Subject: [PATCH 06/12] added prop passing --- .../accountMgmt/EditUserDialog.cy.js | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js index 02778ac45..c3ce5e2d2 100644 --- a/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js +++ b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js @@ -1,9 +1,21 @@ import EditUserDialog from '@/components/accountMgmt/EditUserDialog.vue'; import vuetify from '@/plugins/vuetify'; +const mockUser = { + contactId: 123, + firstName: 'Test', + lastName: 'User', + bceid: 'testuser', + email: 'test@example.com', + telephone: '123-456-7890', + role: { roleNumber: 1 }, + facilities: [], +}; + function mountWithPinia(initialState = {}, dataOverride = {}) { cy.setupPinia({ initialState, stubActions: false }).then((pinia) => { const pushStub = cy.stub(); + cy.mount(EditUserDialog, { global: { plugins: [pinia, vuetify], @@ -13,13 +25,24 @@ function mountWithPinia(initialState = {}, dataOverride = {}) { ...dataOverride, }; }, + props: { + show: true, + user: mockUser, + }, + }).then(({ component }) => { + component.loadEditUserData(); }); + cy.wrap(pushStub).as('routerPush'); }); } describe('', () => { + beforeEach(() => { + cy.viewport(800, 1000); + }); + it('mounts', () => { - mountWithPinia(); + mountWithPinia({}, { dialogOpen: true }); }); }); From 205c849200c2efd5f2f768bda76812825351428d Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Fri, 26 Sep 2025 08:05:58 -0700 Subject: [PATCH 07/12] added remove and edit user tests --- .../accountMgmt/EditUserDialog.cy.js | 148 +++++++++++++++++- .../accountMgmt/RemoveUserDialog.cy.js | 70 +++++++++ 2 files changed, 211 insertions(+), 7 deletions(-) diff --git a/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js index c3ce5e2d2..ca12cdd97 100644 --- a/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js +++ b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js @@ -1,6 +1,10 @@ import EditUserDialog from '@/components/accountMgmt/EditUserDialog.vue'; import vuetify from '@/plugins/vuetify'; +import { useAuthStore } from '@/store/auth'; +import { ApiRoutes, ROLES } from '@/utils/constants.js'; +import { PERMISSIONS } from '@/utils/constants/permissions.js'; +const organizationId = '1234'; const mockUser = { contactId: 123, firstName: 'Test', @@ -11,10 +15,36 @@ const mockUser = { role: { roleNumber: 1 }, facilities: [], }; +const adminUser = { + contactId: 123, + firstName: 'Test', + lastName: 'User', + bceid: 'testuser', + email: 'test@example.com', + telephone: '123-456-7890', + role: { roleNumber: ROLES.FAC_ADMIN_ADVANCED }, + facilities: [], +}; +const facilityResponse = [ + { + facilityName: 'Test-Facility1', + facilityId: '777', + }, + { + facilityName: 'Test-Facility2', + facilityId: '888', + }, + { + facilityName: 'Test-Facility3', + facilityId: '999', + }, +]; -function mountWithPinia(initialState = {}, dataOverride = {}) { +function mountWithPinia(initialState = {}, user) { cy.setupPinia({ initialState, stubActions: false }).then((pinia) => { const pushStub = cy.stub(); + const onContactUpdated = cy.spy().as('contactUpdatedSpy'); + const onCloseEditDialog = cy.spy().as('closeEditDialogSpy'); cy.mount(EditUserDialog, { global: { @@ -22,12 +52,15 @@ function mountWithPinia(initialState = {}, dataOverride = {}) { }, data() { return { - ...dataOverride, + dialogOpen: true, }; }, + attrs: { + 'onContact-updated': onContactUpdated, + 'onClose-edit-dialog': onCloseEditDialog, + }, props: { - show: true, - user: mockUser, + user: user ?? mockUser, }, }).then(({ component }) => { component.loadEditUserData(); @@ -39,10 +72,111 @@ function mountWithPinia(initialState = {}, dataOverride = {}) { describe('', () => { beforeEach(() => { - cy.viewport(800, 1000); + globalThis.localStorage.setItem('jwtToken', 'mocked-jwt-token'); + cy.viewport(1200, 1500); + }); + + afterEach(() => { + globalThis.localStorage.removeItem('jwtToken'); + }); + + it('should mount the dialog and display inputs for user without edit permissions', () => { + mountWithPinia({}, mockUser); + cy.contains('h3', 'Edit User'); + + cy.get('form input').should('have.length', 5); + + cy.get('form input').eq(0).prev('label').should('have.text', 'First Name'); + cy.get('form input').eq(1).prev('label').should('have.text', 'Last Name'); + cy.get('form input').eq(2).prev('label').should('have.text', 'Business BCeID'); + cy.get('form input').eq(3).prev('label').should('have.text', 'Phone Number'); + cy.get('form input').eq(4).prev('label').should('have.text', 'Email Address'); + }); + + it('should load the users prior data', () => { + mountWithPinia({}, mockUser); + cy.get('form input').eq(0).should('have.value', mockUser.firstName); + cy.get('form input').eq(1).should('have.value', mockUser.lastName); + cy.get('form input').eq(2).should('have.value', mockUser.bceid).shou; + cy.get('form input').eq(3).should('have.value', mockUser.telephone); + cy.get('form input').eq(4).should('have.value', mockUser.email); + }); + + it('should disable bceid input', () => { + mountWithPinia({}, mockUser); + cy.get('form input').eq(2).should('have.value', mockUser.bceid).should('have.css', 'pointer-events', 'none'); + }); + + it('should render user role input for users with edit permission', () => { + mountWithPinia({ + auth: { + isAuthenticated: true, + userInfo: { + serverTime: new Date(), + }, + }, + }); + + cy.then(() => { + const authStore = useAuthStore(); + authStore.permissions = [PERMISSIONS.EDIT_USERS]; + }); + + cy.contains('label', 'User Role'); + cy.get('form input').eq(5).should('have.value', mockUser.role.roleNumber); + }); + + it('should render facilities options', () => { + cy.intercept('GET', `${ApiRoutes.ORGANIZATION}/${organizationId}/facilities`, { + statusCode: 200, + body: facilityResponse, + }); + + mountWithPinia( + { + auth: { + isAuthenticated: true, + userInfo: { + serverTime: new Date(), + }, + }, + organization: { + organizationId, + }, + }, + adminUser, + ); + + cy.then(() => { + const authStore = useAuthStore(); + authStore.permissions = [PERMISSIONS.EDIT_USERS]; + }); + + cy.contains('label', 'Facilities'); + // TODO: assert size of results + }); + + it('should update user on clicking `Update` button', () => { + cy.intercept('PATCH', `${ApiRoutes.CONTACTS}/${mockUser.contactId}`, { + statusCode: 200, + body: {}, + }); + + mountWithPinia({}); + + cy.contains('button', 'Update').click(); + cy.get('@contactUpdatedSpy').should('have.been.calledOnce'); + }); + + it('should disable `Update` button when inputs are empty', () => { + mountWithPinia({}, { firstName: 'John' }); + cy.contains('button', 'Update').should('have.css', 'pointer-events', 'none'); }); - it('mounts', () => { - mountWithPinia({}, { dialogOpen: true }); + it('should close dialog', () => { + mountWithPinia({}, mockUser); + cy.contains('button', 'Cancel').click(); + cy.contains('Edit User').should('not.be.visible'); + cy.get('@closeEditDialogSpy').should('have.been.calledOnce'); }); }); diff --git a/frontend/tests/cypress/component/accountMgmt/RemoveUserDialog.cy.js b/frontend/tests/cypress/component/accountMgmt/RemoveUserDialog.cy.js index e69de29bb..7a1f7e5a7 100644 --- a/frontend/tests/cypress/component/accountMgmt/RemoveUserDialog.cy.js +++ b/frontend/tests/cypress/component/accountMgmt/RemoveUserDialog.cy.js @@ -0,0 +1,70 @@ +import RemoveUserDialog from '@/components/accountMgmt/RemoveUserDialog.vue'; +import vuetify from '@/plugins/vuetify'; +import { ApiRoutes } from '@/utils/constants.js'; + +const mockUser = { + contactId: 123, + firstName: 'Test', + lastName: 'User', + bceid: 'testuser', + email: 'test@example.com', + telephone: '123-456-7890', + role: { roleNumber: 1 }, + facilities: [], +}; + +function mountWithPinia(initialState = {}, user) { + cy.setupPinia({ initialState, stubActions: false }).then((pinia) => { + const pushStub = cy.stub(); + const onRemoveDialog = cy.spy().as('closeRemoveDialogSpy'); + const onContactDeactivated = cy.spy().as('contactDeactivated'); + + cy.mount(RemoveUserDialog, { + global: { + plugins: [pinia, vuetify], + }, + data() { + return { + dialogOpen: true, + }; + }, + attrs: { + 'onClose-remove-dialog': onRemoveDialog, + 'onContact-deactivated': onContactDeactivated, + }, + props: { + user: mockUser, + }, + }); + cy.wrap(pushStub).as('routerPush'); + }); +} + +describe('', () => { + beforeEach(() => { + cy.viewport(1200, 1200); + }); + + it('should render remove user dialog', () => { + mountWithPinia(); + cy.contains('Remove User'); + cy.contains(`Are you sure you want to remove ${mockUser.firstName} ${mockUser.lastName}? You can't undo this.`); + }); + + it('should cancel removal of user', () => { + mountWithPinia(); + cy.contains('button', 'Cancel').click(); + cy.get('@closeRemoveDialogSpy').should('have.been.calledOnce'); + }); + + it('should delete user', () => { + cy.intercept('DELETE', `${ApiRoutes.CONTACTS}/${mockUser.contactId}`, { + statusCode: 204, + }); + + mountWithPinia(); + cy.contains('button', 'Yes, remove the user').click(); + cy.get('@contactDeactivated').should('have.been.calledOnce'); + cy.get('@closeRemoveDialogSpy').should('have.been.calledOnce'); + }); +}); From dfb53bab8c4d3bc3f7ed483c10fa74b95039dfee Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Fri, 26 Sep 2025 08:16:32 -0700 Subject: [PATCH 08/12] finished manage user tests --- .../component/accountMgmt/ManageUsers.cy.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/frontend/tests/cypress/component/accountMgmt/ManageUsers.cy.js b/frontend/tests/cypress/component/accountMgmt/ManageUsers.cy.js index da369be61..8ff7d167d 100644 --- a/frontend/tests/cypress/component/accountMgmt/ManageUsers.cy.js +++ b/frontend/tests/cypress/component/accountMgmt/ManageUsers.cy.js @@ -247,6 +247,35 @@ describe('', () => { cy.contains('button', 'Remove').should('not.exist'); }); + it('should render edit button', () => { + const user = { + contactid: '1', + role: { roleNumber: 123 }, + firstName: 'John', + lastName: 'Doe', + isPortalUser: true, + telephone: '250-999-9999', + }; + + mockApiResponses({ + getContacts: { response: [user] }, + }); + + mountWithPinia({ + organization: { organizationId }, + auth: { isAuthenticated: true, userInfo: { contactId: userId } }, + }); + + cy.then(() => { + const authStore = useAuthStore(); + authStore.permissions = [PERMISSIONS.EDIT_USERS]; + }); + + cy.contains('button', 'Edit').click(); + cy.contains('Edit User'); + cy.get('form input').eq(0).should('have.value', user.firstName); + }); + it('should navigate on clicking Back button', () => { mountWithPinia({ organization: { organizationId } }); cy.contains('button', 'Back').click(); From b42c9fa76806e847236d64f4ad4757a4ab35eed9 Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Fri, 26 Sep 2025 08:41:04 -0700 Subject: [PATCH 09/12] fix sonar issues --- .../accountMgmt/EditUserDialog.cy.js | 36 +++++++++---------- .../accountMgmt/RemoveUserDialog.cy.js | 2 +- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js index ca12cdd97..4a71e1bf4 100644 --- a/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js +++ b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js @@ -40,7 +40,7 @@ const facilityResponse = [ }, ]; -function mountWithPinia(initialState = {}, user) { +function mountWithPinia(user, initialState = {}) { cy.setupPinia({ initialState, stubActions: false }).then((pinia) => { const pushStub = cy.stub(); const onContactUpdated = cy.spy().as('contactUpdatedSpy'); @@ -81,7 +81,7 @@ describe('', () => { }); it('should mount the dialog and display inputs for user without edit permissions', () => { - mountWithPinia({}, mockUser); + mountWithPinia(mockUser); cy.contains('h3', 'Edit User'); cy.get('form input').should('have.length', 5); @@ -94,7 +94,7 @@ describe('', () => { }); it('should load the users prior data', () => { - mountWithPinia({}, mockUser); + mountWithPinia(mockUser); cy.get('form input').eq(0).should('have.value', mockUser.firstName); cy.get('form input').eq(1).should('have.value', mockUser.lastName); cy.get('form input').eq(2).should('have.value', mockUser.bceid).shou; @@ -103,12 +103,12 @@ describe('', () => { }); it('should disable bceid input', () => { - mountWithPinia({}, mockUser); + mountWithPinia(mockUser); cy.get('form input').eq(2).should('have.value', mockUser.bceid).should('have.css', 'pointer-events', 'none'); }); it('should render user role input for users with edit permission', () => { - mountWithPinia({ + mountWithPinia(null, { auth: { isAuthenticated: true, userInfo: { @@ -132,20 +132,17 @@ describe('', () => { body: facilityResponse, }); - mountWithPinia( - { - auth: { - isAuthenticated: true, - userInfo: { - serverTime: new Date(), - }, - }, - organization: { - organizationId, + mountWithPinia(adminUser, { + auth: { + isAuthenticated: true, + userInfo: { + serverTime: new Date(), }, }, - adminUser, - ); + organization: { + organizationId, + }, + }); cy.then(() => { const authStore = useAuthStore(); @@ -153,7 +150,6 @@ describe('', () => { }); cy.contains('label', 'Facilities'); - // TODO: assert size of results }); it('should update user on clicking `Update` button', () => { @@ -162,7 +158,7 @@ describe('', () => { body: {}, }); - mountWithPinia({}); + mountWithPinia(); cy.contains('button', 'Update').click(); cy.get('@contactUpdatedSpy').should('have.been.calledOnce'); @@ -174,7 +170,7 @@ describe('', () => { }); it('should close dialog', () => { - mountWithPinia({}, mockUser); + mountWithPinia(mockUser); cy.contains('button', 'Cancel').click(); cy.contains('Edit User').should('not.be.visible'); cy.get('@closeEditDialogSpy').should('have.been.calledOnce'); diff --git a/frontend/tests/cypress/component/accountMgmt/RemoveUserDialog.cy.js b/frontend/tests/cypress/component/accountMgmt/RemoveUserDialog.cy.js index 7a1f7e5a7..b65af1e9a 100644 --- a/frontend/tests/cypress/component/accountMgmt/RemoveUserDialog.cy.js +++ b/frontend/tests/cypress/component/accountMgmt/RemoveUserDialog.cy.js @@ -13,7 +13,7 @@ const mockUser = { facilities: [], }; -function mountWithPinia(initialState = {}, user) { +function mountWithPinia(initialState = {}) { cy.setupPinia({ initialState, stubActions: false }).then((pinia) => { const pushStub = cy.stub(); const onRemoveDialog = cy.spy().as('closeRemoveDialogSpy'); From 94f104a4e4b9cf97a8b0ee92d27138cd410d740c Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Fri, 26 Sep 2025 08:47:58 -0700 Subject: [PATCH 10/12] remove redundant code --- .../cypress/component/accountMgmt/EditUserDialog.cy.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js index 4a71e1bf4..0d99dcc51 100644 --- a/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js +++ b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js @@ -81,7 +81,7 @@ describe('', () => { }); it('should mount the dialog and display inputs for user without edit permissions', () => { - mountWithPinia(mockUser); + mountWithPinia(); cy.contains('h3', 'Edit User'); cy.get('form input').should('have.length', 5); @@ -94,7 +94,7 @@ describe('', () => { }); it('should load the users prior data', () => { - mountWithPinia(mockUser); + mountWithPinia(); cy.get('form input').eq(0).should('have.value', mockUser.firstName); cy.get('form input').eq(1).should('have.value', mockUser.lastName); cy.get('form input').eq(2).should('have.value', mockUser.bceid).shou; @@ -103,7 +103,7 @@ describe('', () => { }); it('should disable bceid input', () => { - mountWithPinia(mockUser); + mountWithPinia(); cy.get('form input').eq(2).should('have.value', mockUser.bceid).should('have.css', 'pointer-events', 'none'); }); @@ -170,7 +170,7 @@ describe('', () => { }); it('should close dialog', () => { - mountWithPinia(mockUser); + mountWithPinia(); cy.contains('button', 'Cancel').click(); cy.contains('Edit User').should('not.be.visible'); cy.get('@closeEditDialogSpy').should('have.been.calledOnce'); From 143c6fe5a5ab203766abb4b68a7d775c7db374fd Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Mon, 29 Sep 2025 13:32:26 -0700 Subject: [PATCH 11/12] pr requested changes --- .../component/accountMgmt/ManageUsers.cy.js | 114 ++++++------------ 1 file changed, 40 insertions(+), 74 deletions(-) diff --git a/frontend/tests/cypress/component/accountMgmt/ManageUsers.cy.js b/frontend/tests/cypress/component/accountMgmt/ManageUsers.cy.js index 8ff7d167d..d98efc92b 100644 --- a/frontend/tests/cypress/component/accountMgmt/ManageUsers.cy.js +++ b/frontend/tests/cypress/component/accountMgmt/ManageUsers.cy.js @@ -7,6 +7,35 @@ import { PERMISSIONS } from '@/utils/constants/permissions.js'; const organizationId = '1234'; const userId = '123'; +const portalUser = { + contactid: '1', + role: { roleNumber: 123, roleName: 'Portal User' }, + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@test.com', + isPortalUser: true, + telephone: '250-999-9999', +}; + +const noRoleUser = { + contactid: '2', + role: { roleNumber: 123 }, + firstName: 'Jane', + lastName: 'Smith', + email: 'Jane.Smith@test.com', + isPortalUser: true, + telephone: '250-123-4567', +}; + +const mockUser = { + contactid: '3', + role: { roleNumber: 123 }, + firstName: 'Anna', + lastName: 'Brown', + isPortalUser: true, + telephone: '250-000-0000', +}; + function mountWithPinia(initialState = {}, dataOverride = {}) { cy.setupPinia({ initialState, stubActions: false }).then((pinia) => { const pushStub = cy.stub(); @@ -91,18 +120,8 @@ describe('', () => { context('User Table Tests', () => { it('should render portal user table with contact info', () => { - const user = { - contactid: '1', - role: { roleNumber: 123, roleName: 'Portal User' }, - firstName: 'John', - lastName: 'Doe', - email: 'john.doe@test.com', - isPortalUser: true, - telephone: '250-999-9999', - }; - mockApiResponses({ - getContacts: { response: [user] }, + getContacts: { response: [portalUser] }, }); mountWithPinia({ @@ -111,26 +130,16 @@ describe('', () => { }); cy.wait('@getContacts'); - cy.get('td').contains('div', 'First Name').next('div').should('have.text', 'John'); - cy.get('td').contains('div', 'Last Name').next('div').should('have.text', 'Doe'); - cy.get('td').contains('div', 'Phone Number').next('div').should('have.text', '250-999-9999'); - cy.get('td').contains('div', 'Email').next('div').should('have.text', 'john.doe@test.com'); + cy.get('td').contains('div', 'First Name').next('div').should('have.text', portalUser.firstName); + cy.get('td').contains('div', 'Last Name').next('div').should('have.text', portalUser.lastName); + cy.get('td').contains('div', 'Phone Number').next('div').should('have.text', portalUser.telephone); + cy.get('td').contains('div', 'Email').next('div').should('have.text', portalUser.email); cy.get('td').contains('div', 'Access Type').next('div').contains('Portal User'); }); it('should render `No Role Assigned`', () => { - const user = { - contactid: '1', - role: { roleNumber: 123 }, - firstName: 'John', - lastName: 'Doe', - email: 'john.doe@test.com', - isPortalUser: true, - telephone: '250-999-9999', - }; - mockApiResponses({ - getContacts: { response: [user] }, + getContacts: { response: [noRoleUser] }, }); mountWithPinia({ @@ -143,32 +152,7 @@ describe('', () => { }); it('should render user table with multiple users', () => { - const users = [ - { - contactid: '1', - role: { roleNumber: 123 }, - firstName: 'John', - lastName: 'Doe', - isPortalUser: true, - telephone: '250-999-9999', - }, - { - contactid: '2', - role: { roleNumber: 1243 }, - firstName: 'Jane', - lastName: 'Smith', - isPortalUser: false, - telephone: '250-123-4567', - }, - { - contactid: '3', - role: { roleNumber: 1235 }, - firstName: 'Anna', - lastName: 'Brown', - isPortalUser: false, - telephone: '250-000-0000', - }, - ]; + const users = [portalUser, noRoleUser, mockUser]; mockApiResponses({ getContacts: { response: users }, @@ -205,17 +189,8 @@ describe('', () => { }); it('should render `Remove` button and confirm removal', () => { - const user = { - contactid: '1', - role: { roleNumber: 123 }, - firstName: 'John', - lastName: 'Doe', - isPortalUser: true, - telephone: '250-999-9999', - }; - mockApiResponses({ - getContacts: { response: [user] }, + getContacts: { response: [mockUser] }, }); mountWithPinia({ @@ -230,7 +205,7 @@ describe('', () => { cy.wait('@getContacts'); cy.contains('button', 'Remove').click(); - cy.contains('Are you sure you want to remove John Doe'); + cy.contains(`Are you sure you want to remove ${mockUser.firstName} ${mockUser.lastName}`); }); it('should not display `Remove` button for current user', () => { @@ -248,17 +223,8 @@ describe('', () => { }); it('should render edit button', () => { - const user = { - contactid: '1', - role: { roleNumber: 123 }, - firstName: 'John', - lastName: 'Doe', - isPortalUser: true, - telephone: '250-999-9999', - }; - mockApiResponses({ - getContacts: { response: [user] }, + getContacts: { response: [mockUser] }, }); mountWithPinia({ @@ -273,7 +239,7 @@ describe('', () => { cy.contains('button', 'Edit').click(); cy.contains('Edit User'); - cy.get('form input').eq(0).should('have.value', user.firstName); + cy.get('form input').eq(0).should('have.value', mockUser.firstName); }); it('should navigate on clicking Back button', () => { From d4911b72ce3cc3558cbfd7b41e585c636e036709 Mon Sep 17 00:00:00 2001 From: saibannur-cgi Date: Mon, 29 Sep 2025 14:09:08 -0700 Subject: [PATCH 12/12] fixed typo --- .../tests/cypress/component/accountMgmt/EditUserDialog.cy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js index 0d99dcc51..b79b7ddd9 100644 --- a/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js +++ b/frontend/tests/cypress/component/accountMgmt/EditUserDialog.cy.js @@ -97,7 +97,7 @@ describe('', () => { mountWithPinia(); cy.get('form input').eq(0).should('have.value', mockUser.firstName); cy.get('form input').eq(1).should('have.value', mockUser.lastName); - cy.get('form input').eq(2).should('have.value', mockUser.bceid).shou; + cy.get('form input').eq(2).should('have.value', mockUser.bceid); cy.get('form input').eq(3).should('have.value', mockUser.telephone); cy.get('form input').eq(4).should('have.value', mockUser.email); });