diff --git a/.circleci/config.yml b/.circleci/config.yml index aab1b298ab4..a0df9b774a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -891,7 +891,7 @@ commands: export FEATURE_FLAG_VALIDATION_CODE_REQUIRED=false export FEATURE_FLAG_MOVE_LOCK=false export FEATURE_FLAG_OKTA_DODID_INPUT=false - export FEATURE_FLAG_HEADQUARTERS_ROLE=false + export FEATURE_FLAG_HEADQUARTERS_ROLE=true export FEATURE_FLAG_GSR_ROLE=false export FEATURE_FLAG_SAFETY_MOVE=false export FEATURE_FLAG_MANAGE_SUPPORTING_DOCS=false @@ -936,7 +936,7 @@ commands: FEATURE_FLAG_VALIDATION_CODE_REQUIRED: 'false' FEATURE_FLAG_MOVE_LOCK: 'false' FEATURE_FLAG_OKTA_DODID_INPUT: 'false' - FEATURE_FLAG_HEADQUARTERS_ROLE: 'false' + FEATURE_FLAG_HEADQUARTERS_ROLE: 'true' FEATURE_FLAG_GSR_ROLE: 'false' FEATURE_FLAG_SAFETY_MOVE: 'false' FEATURE_FLAG_MANAGE_SUPPORTING_DOCS: 'false' diff --git a/playwright/tests/admin/officeUsers.spec.js b/playwright/tests/admin/officeUsers.spec.js index fd7646a2604..64fb83f27dd 100644 --- a/playwright/tests/admin/officeUsers.spec.js +++ b/playwright/tests/admin/officeUsers.spec.js @@ -88,10 +88,10 @@ test.describe('Office User Create Page', () => { // The autocomplete form results in multiple matching elements, so // pick the input element - await page.getByLabel('Transportation Office').nth(0).fill('JPPSO Testy McTest'); + await page.getByLabel('Transportation Office').nth(0).fill('PPPO Scott AFB - USAF'); // the autocomplete might return multiples because of concurrent // tests running that are adding offices - await page.getByRole('option', { name: 'JPPSO Testy McTest' }).first().click(); + await page.getByRole('option', { name: 'PPPO Scott AFB - USAF' }).first().click(); await page.getByRole('button', { name: 'Save' }).click(); await adminPage.waitForPage.adminPage(); @@ -110,6 +110,161 @@ test.describe('Office User Create Page', () => { await expect(page.locator('#telephone')).toHaveValue('222-555-1234'); await expect(page.locator('#active')).toHaveText('Yes'); }); + + test('has correct supervisor role permissions', async ({ page, adminPage }) => { + await adminPage.signInAsNewAdminUser(); + // we tested the side nav in the previous test, + // so let's work with the assumption that we were already redirected to this page: + expect(page.url()).toContain('/system/requested-office-users'); + await page.getByRole('menuitem', { name: 'Office Users', exact: true }).click(); + expect(page.url()).toContain('/system/office-users'); + + await page.getByRole('link', { name: 'Create' }).click(); + await expect(page.getByRole('heading', { name: 'Create Office Users', exact: true })).toBeVisible(); + + expect(page.url()).toContain('/system/office-users/create'); + + // we need to add the date to the email so that it is unique every time (only one record per email allowed in db) + const testEmail = `cy.admin_user.${Date.now()}@example.com`; + + // create an office user + const firstName = page.getByLabel('First name'); + await firstName.focus(); + await firstName.fill('Cypress'); + + const lastName = page.getByLabel('Last name'); + await lastName.focus(); + await lastName.fill('Test'); + + const email = page.getByLabel('Email'); + await email.focus(); + await email.fill(testEmail); + + const phone = page.getByLabel('Telephone'); + await phone.focus(); + await phone.fill('222-555-1234'); + + // Define constants for all roles checkboxes to be tested + const customerCheckbox = page.getByLabel('Customer', { exact: true }); + const contractingOfficerCheckbox = page.getByLabel('Contracting Officer', { exact: true }); + const servicesCounselorCheckbox = page.getByLabel('Services Counselor', { exact: true }); + const primeSimulatorCheckbox = page.getByLabel('Prime Simulator', { exact: true }); + const qualityAssuranceEvaluatorCheckbox = page.getByLabel('Quality Assurance Evaluator', { exact: true }); + const customerServiceRepersentativeCheckbox = page.getByLabel('Customer Service Representative', { exact: true }); + const governmentSurveillanceRepresentativeCheckbox = page.getByLabel('Government Surveillance Representative', { + exact: true, + }); + const headquartersCheckbox = page.getByLabel('Headquarters', { exact: true }); + const taskOrderingOfficerCheckbox = page.getByLabel('Task Ordering Officer', { exact: true }); + const taskInvoicingOfficerCheckbox = page.getByLabel('Task Invoicing Officer', { exact: true }); + + // Define constants for privileges + const supervisorCheckbox = page.getByLabel('Supervisor', { exact: true }); + + // Check roles that cannot have supervisor priveleges + await customerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(customerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await customerCheckbox.click(); + await contractingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(contractingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await contractingOfficerCheckbox.click(); + await primeSimulatorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(primeSimulatorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await primeSimulatorCheckbox.click(); + await qualityAssuranceEvaluatorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(qualityAssuranceEvaluatorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await qualityAssuranceEvaluatorCheckbox.click(); + await customerServiceRepersentativeCheckbox.click(); + await supervisorCheckbox.click(); + await expect(customerServiceRepersentativeCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await customerServiceRepersentativeCheckbox.click(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await supervisorCheckbox.click(); + await expect(governmentSurveillanceRepresentativeCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await headquartersCheckbox.click(); + await supervisorCheckbox.click(); + await expect(headquartersCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await headquartersCheckbox.click(); + + // Check roles that can have supervisor priveleges + await taskOrderingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(taskOrderingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await taskOrderingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await taskInvoicingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(taskInvoicingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await taskInvoicingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await servicesCounselorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(servicesCounselorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await servicesCounselorCheckbox.click(); + + // Check selecting roles after having supervisor selected for unallowed roles + await customerCheckbox.click(); + await expect(customerCheckbox).not.toBeChecked(); + await contractingOfficerCheckbox.click(); + await expect(contractingOfficerCheckbox).not.toBeChecked(); + await primeSimulatorCheckbox.click(); + await expect(primeSimulatorCheckbox).not.toBeChecked(); + await qualityAssuranceEvaluatorCheckbox.click(); + await expect(qualityAssuranceEvaluatorCheckbox).not.toBeChecked(); + await customerServiceRepersentativeCheckbox.click(); + await expect(customerServiceRepersentativeCheckbox).not.toBeChecked(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await expect(governmentSurveillanceRepresentativeCheckbox).not.toBeChecked(); + await headquartersCheckbox.click(); + await expect(headquartersCheckbox).not.toBeChecked(); + + // Check selecting roles after having supervisor selected for allowed roles + await taskOrderingOfficerCheckbox.click(); + await expect(taskOrderingOfficerCheckbox).toBeChecked(); + await taskOrderingOfficerCheckbox.click(); + await taskInvoicingOfficerCheckbox.click(); + await expect(taskInvoicingOfficerCheckbox).toBeChecked(); + await taskInvoicingOfficerCheckbox.click(); + await servicesCounselorCheckbox.click(); + await expect(servicesCounselorCheckbox).toBeChecked(); + + // Continue test to ensure form still submits with valid information + // The autocomplete form results in multiple matching elements, so + // pick the input element + await page.getByLabel('Transportation Office').nth(0).fill('PPPO Scott AFB - USAF'); + // the autocomplete might return multiples because of concurrent + // tests running that are adding offices + await page.getByRole('option', { name: 'PPPO Scott AFB - USAF' }).first().click(); + + await page.getByRole('button', { name: 'Save' }).click(); + await adminPage.waitForPage.adminPage(); + + // redirected to edit details page + const officeUserID = await page.locator('#id').inputValue(); + + await expect(page.getByRole('heading', { name: `Office Users #${officeUserID}` })).toBeVisible(); + + await expect(page.locator('#email')).toHaveValue(testEmail); + await expect(page.locator('#firstName')).toHaveValue('Cypress'); + await expect(page.locator('#lastName')).toHaveValue('Test'); + await expect(page.locator('#telephone')).toHaveValue('222-555-1234'); + await expect(page.locator('#active')).toHaveText('Yes'); + }); }); test.describe('Office Users Show Page', () => { @@ -228,6 +383,178 @@ test.describe('Office Users Edit Page', () => { await expect(page.locator(`tr:has(:text("${email}")) >> td.column-lastName`)).toHaveText('NewLast'); }); + test('has correct supervisor role permissions', async ({ page, adminPage }) => { + const officeUser = await adminPage.testHarness.buildOfficeUserWithTOOAndTIO(); + const email = officeUser.okta_email; + + await adminPage.signInAsNewAdminUser(); + + expect(page.url()).toContain('/system/requested-office-users'); + await page.getByRole('menuitem', { name: 'Office Users', exact: true }).click(); + expect(page.url()).toContain('/system/office-users'); + await searchForOfficeUser(page, email); + await page.getByText(email).click(); + await adminPage.waitForPage.adminPage(); + + await page.getByRole('link', { name: 'Edit' }).click(); + await adminPage.waitForPage.adminPage(); + + const disabledFields = ['id', 'email', 'userId', 'createdAt', 'updatedAt']; + for (const field of disabledFields) { + await expect(page.locator(`#${field}`)).toBeDisabled(); + } + + const firstName = page.getByLabel('First name'); + await firstName.focus(); + await firstName.clear(); + await firstName.fill('NewFirst'); + + const lastName = page.getByLabel('Last name'); + await lastName.focus(); + await lastName.clear(); + await lastName.fill('NewLast'); + + // The autocomplete form results in multiple matching elements, so + // pick the input element + await expect(page.getByLabel('Transportation Office').nth(0)).toBeEditable(); + + // Add a Transportation Office Assignment + await page.getByTestId('addTransportationOfficeButton').click(); + // n = 2 because of the disabled GBLOC input + await expect(page.getByLabel('Transportation Office').nth(2)).toBeEditable(); + await page.getByLabel('Transportation Office').nth(2).fill('AGFM'); + // the autocomplete might return multiples because of concurrent + // tests running that are adding offices + await page.getByRole('option', { name: 'JPPSO - North East (AGFM) - USAF' }).first().click(); + // Set as primary transportation office + await page.getByLabel('Primary Office').nth(1).click(); + await page.getByText('You cannot designate more than one primary transportation office.'); + await page.getByLabel('Primary Office').nth(1).click(); + + // set the user to the active status they did NOT have before + const activeStatus = await page.locator('div:has(label :text-is("Active")) >> input[name="active"]').inputValue(); + + const newStatus = (activeStatus !== 'true').toString(); + await page.locator('div:has(label :text-is("Active")) >> #active').click(); + await page.locator(`ul[aria-labelledby="active-label"] >> li[data-value="${newStatus}"]`).click(); + + // Define constants for all roles checkboxes to be tested + const customerCheckbox = page.getByLabel('Customer', { exact: true }); + const contractingOfficerCheckbox = page.getByLabel('Contracting Officer', { exact: true }); + const servicesCounselorCheckbox = page.getByLabel('Services Counselor', { exact: true }); + const primeSimulatorCheckbox = page.getByLabel('Prime Simulator', { exact: true }); + const qualityAssuranceEvaluatorCheckbox = page.getByLabel('Quality Assurance Evaluator', { exact: true }); + const customerServiceRepersentativeCheckbox = page.getByLabel('Customer Service Representative', { exact: true }); + const governmentSurveillanceRepresentativeCheckbox = page.getByLabel('Government Surveillance Representative', { + exact: true, + }); + const headquartersCheckbox = page.getByLabel('Headquarters', { exact: true }); + const taskOrderingOfficerCheckbox = page.getByLabel('Task Ordering Officer', { exact: true }); + const taskInvoicingOfficerCheckbox = page.getByLabel('Task Invoicing Officer', { exact: true }); + + // Define constants for privileges + const supervisorCheckbox = page.getByLabel('Supervisor', { exact: true }); + + // Disable existing roles for testing + await taskOrderingOfficerCheckbox.click(); + await taskInvoicingOfficerCheckbox.click(); + + // Check roles that cannot have supervisor priveleges + await customerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(customerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await customerCheckbox.click(); + await contractingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(contractingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await contractingOfficerCheckbox.click(); + await primeSimulatorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(primeSimulatorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await primeSimulatorCheckbox.click(); + await qualityAssuranceEvaluatorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(qualityAssuranceEvaluatorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await qualityAssuranceEvaluatorCheckbox.click(); + await customerServiceRepersentativeCheckbox.click(); + await supervisorCheckbox.click(); + await expect(customerServiceRepersentativeCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await customerServiceRepersentativeCheckbox.click(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await supervisorCheckbox.click(); + await expect(governmentSurveillanceRepresentativeCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await headquartersCheckbox.click(); + await supervisorCheckbox.click(); + await expect(headquartersCheckbox).toBeChecked(); + await expect(supervisorCheckbox).not.toBeChecked(); + await headquartersCheckbox.click(); + + // Check roles that can have supervisor priveleges + await taskOrderingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(taskOrderingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await taskOrderingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await taskInvoicingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await expect(taskInvoicingOfficerCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await taskInvoicingOfficerCheckbox.click(); + await supervisorCheckbox.click(); + await servicesCounselorCheckbox.click(); + await supervisorCheckbox.click(); + await expect(servicesCounselorCheckbox).toBeChecked(); + await expect(supervisorCheckbox).toBeChecked(); + await servicesCounselorCheckbox.click(); + + // Check selecting roles after having supervisor selected for unallowed roles + await customerCheckbox.click(); + await expect(customerCheckbox).not.toBeChecked(); + await contractingOfficerCheckbox.click(); + await expect(contractingOfficerCheckbox).not.toBeChecked(); + await primeSimulatorCheckbox.click(); + await expect(primeSimulatorCheckbox).not.toBeChecked(); + await qualityAssuranceEvaluatorCheckbox.click(); + await expect(qualityAssuranceEvaluatorCheckbox).not.toBeChecked(); + await customerServiceRepersentativeCheckbox.click(); + await expect(customerServiceRepersentativeCheckbox).not.toBeChecked(); + await governmentSurveillanceRepresentativeCheckbox.click(); + await expect(governmentSurveillanceRepresentativeCheckbox).not.toBeChecked(); + await headquartersCheckbox.click(); + await expect(headquartersCheckbox).not.toBeChecked(); + + // Check selecting roles after having supervisor selected for allowed roles + await taskOrderingOfficerCheckbox.click(); + await expect(taskOrderingOfficerCheckbox).toBeChecked(); + await taskOrderingOfficerCheckbox.click(); + await taskInvoicingOfficerCheckbox.click(); + await expect(taskInvoicingOfficerCheckbox).toBeChecked(); + await taskInvoicingOfficerCheckbox.click(); + await servicesCounselorCheckbox.click(); + await expect(servicesCounselorCheckbox).toBeChecked(); + + // Continue test to ensure form still submits with valid information + await page.getByRole('button', { name: 'Save' }).click(); + await adminPage.waitForPage.adminPage(); + + await searchForOfficeUser(page, email); + await expect(page.locator(`tr:has(:text("${email}")) >> td.column-active >> svg`)).toHaveAttribute( + 'data-testid', + newStatus, + ); + + await expect(page.locator(`tr:has(:text("${email}")) >> td.column-firstName`)).toHaveText('NewFirst'); + await expect(page.locator(`tr:has(:text("${email}")) >> td.column-lastName`)).toHaveText('NewLast'); + }); + test('prevents safety move priv selection with Customer role', async ({ page, adminPage }) => { const officeUser = await adminPage.testHarness.buildOfficeUserWithCustomer(); const email = officeUser.okta_email; diff --git a/src/components/Admin/ImportOfficeUserButton/validation.test.js b/src/components/Admin/ImportOfficeUserButton/validation.test.js index 379cb8a7be6..7639ea9876f 100644 --- a/src/components/Admin/ImportOfficeUserButton/validation.test.js +++ b/src/components/Admin/ImportOfficeUserButton/validation.test.js @@ -1,6 +1,7 @@ -import { checkRequiredFields, checkTelephone, parseRoles } from './validation'; +import { checkRequiredFields, checkTelephone, parseRoles, parsePrivileges } from './validation'; import { adminOfficeRoles, roleTypes } from 'constants/userRoles'; +import { elevatedPrivilegeTypes } from 'constants/userPrivileges'; describe('checkRequiredFields', () => { it('success: does nothing if all fields provided', () => { @@ -71,3 +72,36 @@ describe('parseRoles', () => { expect(parseInvalidRoles).toThrowError('Invalid roles provided for row.'); }); }); + +describe('parsePrivileges', () => { + const supervisorPrivilege = { privilegeType: 'supervisor', name: 'Supervisor' }; + const safetyPrivilege = { privilegeType: 'safety', name: 'Safety Moves' }; + + it('fail: throws an error if there are no privileges', () => { + function parseEmptyPrivileges() { + parsePrivileges(''); + } + expect(parseEmptyPrivileges).toThrowError('Processing Error: Unable to parse privileges for row.'); + }); + + it('success: parses one privilege into an array of len 1', () => { + const privileges = elevatedPrivilegeTypes.SUPERVISOR; + const privilegesArray = parsePrivileges(privileges); + expect(privilegesArray).toHaveLength(1); + expect(privilegesArray).toContainEqual(supervisorPrivilege); + }); + + it('success: parses multiple privileges into an array', () => { + const privileges = `${elevatedPrivilegeTypes.SUPERVISOR}, ${elevatedPrivilegeTypes.SAFETY}`; + const privilegesArray = parsePrivileges(privileges); + expect(privilegesArray).toHaveLength(2); + expect(privilegesArray).toEqual(expect.arrayContaining([supervisorPrivilege, safetyPrivilege])); + }); + + it('fail: throws an error if there is an invalid privilege', () => { + function parseInvalidPrivileges() { + parsePrivileges('test_privilege'); + } + expect(parseInvalidPrivileges).toThrowError('Invalid privileges provided for row.'); + }); +}); diff --git a/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx b/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx index 1cc554ed70d..d2fdac32c02 100644 --- a/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx +++ b/src/scenes/SystemAdmin/shared/RolesPrivilegesCheckboxes.jsx @@ -50,6 +50,36 @@ const RolesPrivilegesCheckboxInput = (props) => { input.splice(index, 1); } } + if (input.includes(roleTypes.PRIME_SIMULATOR)) { + index = input.indexOf(roleTypes.PRIME_SIMULATOR); + if (index !== -1) { + input.splice(index, 1); + } + } + if (input.includes(roleTypes.QAE)) { + index = input.indexOf(roleTypes.QAE); + if (index !== -1) { + input.splice(index, 1); + } + } + if (input.includes(roleTypes.CUSTOMER_SERVICE_REPRESENTATIVE)) { + index = input.indexOf(roleTypes.CUSTOMER_SERVICE_REPRESENTATIVE); + if (index !== -1) { + input.splice(index, 1); + } + } + if (input.includes(roleTypes.GSR)) { + index = input.indexOf(roleTypes.GSR); + if (index !== -1) { + input.splice(index, 1); + } + } + if (input.includes(roleTypes.HQ)) { + index = input.indexOf(roleTypes.HQ); + if (index !== -1) { + input.splice(index, 1); + } + } } if (privilegesSelected.includes(elevatedPrivilegeTypes.SAFETY)) { @@ -110,8 +140,16 @@ const RolesPrivilegesCheckboxInput = (props) => { }; const parsePrivilegesCheckboxInput = (input) => { - var index; - if (rolesSelected.includes(roleTypes.CUSTOMER) || rolesSelected.includes(roleTypes.CONTRACTING_OFFICER)) { + let index; + if ( + rolesSelected.includes(roleTypes.CUSTOMER) || + rolesSelected.includes(roleTypes.CONTRACTING_OFFICER) || + rolesSelected.includes(roleTypes.PRIME_SIMULATOR) || + rolesSelected.includes(roleTypes.QAE) || + rolesSelected.includes(roleTypes.CUSTOMER_SERVICE_REPRESENTATIVE) || + rolesSelected.includes(roleTypes.GSR) || + rolesSelected.includes(roleTypes.HQ) + ) { if (input.includes(elevatedPrivilegeTypes.SUPERVISOR)) { index = input.indexOf(elevatedPrivilegeTypes.SUPERVISOR); if (index !== -1) { @@ -140,7 +178,6 @@ const RolesPrivilegesCheckboxInput = (props) => { return privilegesArray; }, []); }; - // filter the privileges to exclude the Safety Moves checkbox if the admin user is NOT a super admin const filteredPrivileges = officeUserPrivileges.filter((privilege) => { if (privilege.privilegeType === elevatedPrivilegeTypes.SAFETY && !adminUser?.super) { @@ -168,7 +205,8 @@ const RolesPrivilegesCheckboxInput = (props) => { optionValue="privilegeType" /> - Privileges cannot be selected with Customer or Contracting Officer roles. + The Supervisor privilege can only be selected for the following roles: Task Ordering Officer, Task Invoicing + Officer, Services Counselor. The Safety Moves privilege can only be selected for the following roles: Task Ordering Officer, Task Invoicing