Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: realm request ui #116

Merged
merged 18 commits into from
Nov 15, 2023
16 changes: 16 additions & 0 deletions .github/workflows/test.yml
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
1 change: 1 addition & 0 deletions app/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ TF_GH_REPO=
TF_MODULE_GH_REF=
GH_ACCESS_TOKEN=
GH_API_TOKEN=
DATABASE_URL=
IDIR_REQUESTOR_USER_GUID=
237 changes: 237 additions & 0 deletions app/__tests__/custom-realm-dashboard.test.tsx
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: 'a@b.com',
productOwnerIdirUserId: 'me',
technicalContactEmail: 'b@c.com',
technicalContactIdirUserId: 'd@e.com',
secondTechnicalContactIdirUserId: 'dmsd',
secondTechnicalContactEmail: 'dksadlks@fkjlsdj.com',
status: 'pending',
approved: null,
},
{
id: 2,
realm: 'realm 2',
purpose: 'purpose',
primaryEndUsers: ['livingInBC', 'businessInBC', 'govEmployees', 'details'],
environments: ['dev', 'test', 'prod'],
preferredAdminLoginMethod: 'idir',
productOwnerEmail: 'a@b.com',
productOwnerIdirUserId: 'me',
technicalContactEmail: 'b@c.com',
technicalContactIdirUserId: 'd@e.com',
secondTechnicalContactIdirUserId: 'dmsd',
secondTechnicalContactEmail: 'dksadlks@fkjlsdj.com',
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'));
});
});
Loading
Loading