-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #116 from bcgov/sso-team-1002
feat: realm request ui
- Loading branch information
Showing
22 changed files
with
4,215 additions
and
399 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
name: Run unit tests | ||
|
||
on: push | ||
|
||
jobs: | ||
unit-test: | ||
runs-on: ubuntu-20.04 | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-node@v4 | ||
with: | ||
node-version: 18 | ||
- run: | | ||
cd app | ||
yarn | ||
yarn test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,4 +39,5 @@ TF_GH_REPO= | |
TF_MODULE_GH_REF= | ||
GH_ACCESS_TOKEN= | ||
GH_API_TOKEN= | ||
DATABASE_URL= | ||
IDIR_REQUESTOR_USER_GUID= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
import React from 'react'; | ||
import { render, screen, within, waitFor } from '@testing-library/react'; | ||
import App from 'pages/_app'; | ||
import CustomRealmDashboard from 'pages/custom-realm-dashboard'; | ||
import { updateRealmProfile } from 'services/realm'; | ||
import { CustomRealmFormData } from 'types/realm-profile'; | ||
import Router from 'next/router'; | ||
|
||
jest.mock('services/realm', () => { | ||
return { | ||
deleteRealmRequest: jest.fn((realmInfo: CustomRealmFormData) => Promise.resolve([true, null])), | ||
updateRealmProfile: jest.fn((id: number, status: string) => Promise.resolve([true, null])), | ||
}; | ||
}); | ||
|
||
jest.mock('next/router', () => ({ | ||
useRouter() { | ||
return { | ||
route: '/', | ||
pathname: '', | ||
query: '', | ||
asPath: '', | ||
push: jest.fn(() => Promise.resolve(true)), | ||
events: { | ||
on: jest.fn(), | ||
off: jest.fn(), | ||
}, | ||
beforePopState: jest.fn(() => null), | ||
prefetch: jest.fn(() => null), | ||
}; | ||
}, | ||
})); | ||
|
||
// Mock authentication | ||
const mockSession = { | ||
expires: new Date(Date.now() + 2 * 86400).toISOString(), | ||
user: { username: 'admin' }, | ||
}; | ||
jest.mock('next-auth/react', () => { | ||
const originalModule = jest.requireActual('next-auth/react'); | ||
return { | ||
__esModule: true, | ||
...originalModule, | ||
useSession: jest.fn(() => { | ||
return { data: mockSession, status: 'authenticated' }; // return type is [] in v3 but changed to {} in v4 | ||
}), | ||
}; | ||
}); | ||
|
||
jest.mock('next-auth/next', () => { | ||
return { | ||
__esModule: true, | ||
getServerSession: jest.fn(() => { | ||
return { data: mockSession, status: 'authenticated' }; | ||
}), | ||
}; | ||
}); | ||
|
||
const defaultData: CustomRealmFormData[] = [ | ||
{ | ||
id: 1, | ||
realm: 'realm 1', | ||
purpose: 'purpose', | ||
primaryEndUsers: ['livingInBC', 'businessInBC', 'govEmployees', 'details'], | ||
environments: ['dev', 'test', 'prod'], | ||
preferredAdminLoginMethod: 'idir', | ||
productOwnerEmail: '[email protected]', | ||
productOwnerIdirUserId: 'me', | ||
technicalContactEmail: '[email protected]', | ||
technicalContactIdirUserId: '[email protected]', | ||
secondTechnicalContactIdirUserId: 'dmsd', | ||
secondTechnicalContactEmail: '[email protected]', | ||
status: 'pending', | ||
approved: null, | ||
}, | ||
{ | ||
id: 2, | ||
realm: 'realm 2', | ||
purpose: 'purpose', | ||
primaryEndUsers: ['livingInBC', 'businessInBC', 'govEmployees', 'details'], | ||
environments: ['dev', 'test', 'prod'], | ||
preferredAdminLoginMethod: 'idir', | ||
productOwnerEmail: '[email protected]', | ||
productOwnerIdirUserId: 'me', | ||
technicalContactEmail: '[email protected]', | ||
technicalContactIdirUserId: '[email protected]', | ||
secondTechnicalContactIdirUserId: 'dmsd', | ||
secondTechnicalContactEmail: '[email protected]', | ||
status: 'pending', | ||
approved: null, | ||
}, | ||
]; | ||
|
||
jest.mock('../pages/api/realms', () => { | ||
return { | ||
__esModule: true, | ||
getAllRealms: jest.fn(() => Promise.resolve([defaultData, null])), | ||
authOptions: {}, | ||
}; | ||
}); | ||
|
||
jest.mock('../pages/api/auth/[...nextauth]', () => { | ||
return { | ||
__esModule: true, | ||
authOptions: {}, | ||
}; | ||
}); | ||
|
||
describe('Table', () => { | ||
it('Loads in table data from serverside props', () => { | ||
render(<CustomRealmDashboard defaultRealmRequests={defaultData} />); | ||
const table = screen.getByTestId('custom-realm-table'); | ||
expect(within(table).getByText('realm 1')); | ||
expect(within(table).getByText('realm 2')); | ||
}); | ||
}); | ||
|
||
describe('Status update', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('Prompts modal for request approval', async () => { | ||
render( | ||
<App | ||
Component={CustomRealmDashboard} | ||
pageProps={{ session: {}, defaultRealmRequests: defaultData }} | ||
router={Router as any} | ||
/>, | ||
); | ||
|
||
screen.getByText('Access Request').click(); | ||
await waitFor(() => screen.getByText('Approve Custom Realm', { selector: 'button' }).click()); | ||
await waitFor(() => screen.getByText('Are you sure you want to approve request 1?')); | ||
}); | ||
|
||
it('Prompts modal for request declination', async () => { | ||
render( | ||
<App | ||
Component={CustomRealmDashboard} | ||
pageProps={{ session: {}, defaultRealmRequests: defaultData }} | ||
router={Router as any} | ||
/>, | ||
); | ||
screen.getByText('Access Request').click(); | ||
await waitFor(() => screen.getByText('Decline Custom Realm', { selector: 'button' }).click()); | ||
await waitFor(() => screen.getByText('Are you sure you want to decline request 1?')); | ||
}); | ||
|
||
it('Fires expected api request when approving', async () => { | ||
render( | ||
<App | ||
Component={CustomRealmDashboard} | ||
pageProps={{ session: {}, defaultRealmRequests: defaultData }} | ||
router={Router as any} | ||
/>, | ||
); | ||
screen.getByText('Access Request').click(); | ||
await waitFor(() => screen.getByText('Approve Custom Realm', { selector: 'button' }).click()); | ||
await waitFor(() => screen.getByText('Are you sure you want to approve request 1?')); | ||
screen.getByText('Confirm', { selector: 'button' }).click(); | ||
|
||
const payload = (updateRealmProfile as jest.Mock).mock.calls[0][1]; | ||
expect(payload.approved).toBeTruthy(); | ||
}); | ||
|
||
it('Fires expected api request when declining', async () => { | ||
render( | ||
<App | ||
Component={CustomRealmDashboard} | ||
pageProps={{ session: {}, defaultRealmRequests: defaultData }} | ||
router={Router as any} | ||
/>, | ||
); | ||
screen.getByText('Access Request').click(); | ||
await waitFor(() => screen.getByText('Decline Custom Realm', { selector: 'button' }).click()); | ||
await waitFor(() => screen.getByText('Are you sure you want to decline request 1?')); | ||
screen.getByText('Confirm', { selector: 'button' }).click(); | ||
|
||
const payload = (updateRealmProfile as jest.Mock).mock.calls[0][1]; | ||
expect(payload.approved).toBeFalsy(); | ||
}); | ||
|
||
it('Updates status in table only when successfully approved', async () => { | ||
render( | ||
<App | ||
Component={CustomRealmDashboard} | ||
pageProps={{ session: {}, defaultRealmRequests: defaultData }} | ||
router={Router as any} | ||
/>, | ||
); | ||
(updateRealmProfile as jest.MockedFunction<any>).mockImplementationOnce(() => | ||
Promise.resolve([null, { message: 'failure' }]), | ||
); | ||
screen.getByText('Access Request').click(); | ||
await waitFor(() => screen.getByText('Approve Custom Realm', { selector: 'button' }).click()); | ||
await waitFor(() => screen.getByText('Are you sure you want to approve request 1?')); | ||
screen.getByText('Confirm', { selector: 'button' }).click(); | ||
|
||
// Still pending | ||
const firstRow = screen.getByTestId('custom-realm-row-1'); | ||
within(firstRow).getByText('pending'); | ||
|
||
// Successful request | ||
(updateRealmProfile as jest.MockedFunction<any>).mockImplementationOnce(() => Promise.resolve([true, null])); | ||
screen.getByText('Approve Custom Realm', { selector: 'button' }).click(); | ||
screen.getByText('Confirm', { selector: 'button' }).click(); | ||
await waitFor(() => within(firstRow).getByText('Approved')); | ||
}); | ||
|
||
it('Updates status in table only when successfully declined', async () => { | ||
render( | ||
<App | ||
Component={CustomRealmDashboard} | ||
pageProps={{ session: {}, defaultRealmRequests: defaultData }} | ||
router={Router as any} | ||
/>, | ||
); | ||
(updateRealmProfile as jest.MockedFunction<any>).mockImplementationOnce(() => | ||
Promise.resolve([null, { message: 'failure' }]), | ||
); | ||
screen.getByText('Access Request').click(); | ||
await waitFor(() => screen.getByText('Decline Custom Realm', { selector: 'button' }).click()); | ||
await waitFor(() => screen.getByText('Are you sure you want to decline request 1?')); | ||
screen.getByText('Confirm', { selector: 'button' }).click(); | ||
|
||
// Still pending | ||
const firstRow = screen.getByTestId('custom-realm-row-1'); | ||
within(firstRow).getByText('pending'); | ||
|
||
// Successful request | ||
(updateRealmProfile as jest.MockedFunction<any>).mockImplementationOnce(() => Promise.resolve([true, null])); | ||
screen.getByText('Decline Custom Realm', { selector: 'button' }).click(); | ||
screen.getByText('Confirm', { selector: 'button' }).click(); | ||
await waitFor(() => within(firstRow).getByText('Declined')); | ||
}); | ||
}); |
Oops, something went wrong.