Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,34 @@ describe('RequestPage', () => {
});
});

it('shows loading indicator when context loading is true and no request data', () => {
mockUseAdditionalSalaryRequest.mockReturnValue({
...defaultMockContextValue,
loading: true,
requestData: null,
} as unknown as ReturnType<typeof useAdditionalSalaryRequest>);

const { getByTestId, queryByRole } = render(<TestWrapper />);

expect(getByTestId('Loading')).toBeInTheDocument();
expect(
queryByRole('button', { name: /continue/i }),
).not.toBeInTheDocument();
});

it('shows loading indicator when creating a new request', async () => {
mockUseAdditionalSalaryRequest.mockReturnValue({
...defaultMockContextValue,
requestData: { latestAdditionalSalaryRequest: null },
} as unknown as ReturnType<typeof useAdditionalSalaryRequest>);

const { getByRole, findByTestId } = render(<TestWrapper />);

userEvent.click(getByRole('button', { name: /continue/i }));

expect(await findByTestId('Loading')).toBeInTheDocument();
});

it('hides back href on About this Form step in New mode', () => {
mockUseAdditionalSalaryRequest.mockReturnValue({
...defaultMockContextValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
setIsNewAsr,
} = useAdditionalSalaryRequest();

const [createRequest] = useCreateAdditionalSalaryRequestMutation();
const [createRequest, { loading: creatingRequest }] =
useCreateAdditionalSalaryRequestMutation();

const { values, submitForm, validateForm, submitCount, isValid, errors } =
useFormikContext<CompleteFormValues>();
Expand Down Expand Up @@ -100,8 +101,8 @@
t,
);

if (loading && !requestData) {
return <Loading loading={loading} />;
if (creatingRequest || (loading && !requestData)) {

Check warning on line 104 in src/components/Reports/AdditionalSalaryRequest/RequestPage/RequestPage.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

❌ New issue: Complex Conditional

MainContent:React.FC has 1 complex conditionals with 2 branches, threshold = 2. A complex conditional is an expression inside a branch (e.g. if, for, while) which consists of multiple, logical operators such as AND/OR. The more logical operators in an expression, the more severe the code smell.
return <Loading loading />;
}

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { ThemeProvider } from '@mui/material/styles';
import { render } from '@testing-library/react';
import { render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { SnackbarProvider } from 'notistack';
import TestRouter from '__tests__/util/TestRouter';
import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
Expand All @@ -18,10 +19,16 @@ import {
singleNoMhaNoException,
} from '../Shared/HcmData/mockData';
import { MinisterHousingAllowanceReport } from './MinisterHousingAllowance';
import { MinistryHousingAllowanceRequestsQuery } from './MinisterHousingAllowance.generated';
import {
CreateHousingAllowanceRequestMutation,
MinistryHousingAllowanceRequestsQuery,
} from './MinisterHousingAllowance.generated';
import { MinisterHousingAllowanceProvider } from './Shared/Context/MinisterHousingAllowanceContext';
import { mockMHARequest } from './mockData';

const mutationSpy = jest.fn();
const mockPush = jest.fn();

interface TestComponentProps {
hcmMock: HcmDataQuery['hcm'];
mhaRequestsMock: MinistryHousingAllowanceRequestsQuery['ministryHousingAllowanceRequests']['nodes'];
Expand All @@ -32,11 +39,12 @@ const TestComponent: React.FC<TestComponentProps> = ({
mhaRequestsMock,
}) => (
<ThemeProvider theme={theme}>
<TestRouter>
<TestRouter router={{ push: mockPush }}>
<SnackbarProvider>
<GqlMockedProvider<{
HcmData: HcmDataQuery;
MinistryHousingAllowanceRequests: MinistryHousingAllowanceRequestsQuery;
CreateHousingAllowanceRequest: CreateHousingAllowanceRequestMutation;
}>
mocks={{
HcmData: {
Expand All @@ -47,7 +55,15 @@ const TestComponent: React.FC<TestComponentProps> = ({
nodes: mhaRequestsMock,
},
},
CreateHousingAllowanceRequest: {
createMinistryHousingAllowanceRequest: {
ministryHousingAllowanceRequest: {
id: 'new-mha-id',
},
},
},
}}
onCall={mutationSpy}
>
<MinisterHousingAllowanceProvider>
<MinisterHousingAllowanceReport />
Expand Down Expand Up @@ -240,4 +256,59 @@ describe('MinisterHousingAllowanceReport', () => {

expect(await findByText('Current MHA Request')).toBeInTheDocument();
});

it('does not render Current Request section when eligible user has no requests', async () => {
const { queryByText, findByText } = render(
<TestComponent hcmMock={singleMhaNoException} mhaRequestsMock={[]} />,
);

expect(await findByText('Your MHA')).toBeInTheDocument();
expect(queryByText('Current MHA Request')).not.toBeInTheDocument();
});

it('shows loading and calls router.push when creating a new MHA request', async () => {
const { findByText, findByTestId } = render(
<TestComponent
hcmMock={singleMhaNoException}
mhaRequestsMock={[
{ ...mockMHARequest, status: MhaStatusEnum.BoardApproved },
]}
/>,
);

const requestButton = await findByText('Request New MHA');
userEvent.click(requestButton);

expect(await findByTestId('Loading')).toBeInTheDocument();

await waitFor(() => {
expect(mutationSpy).toHaveGraphqlOperation(
'CreateHousingAllowanceRequest',
);
});

await waitFor(() => {
expect(mockPush).toHaveBeenCalledWith(
'/accountLists/account-list-1/reports/housingAllowance/new-mha-id?mode=new',
);
});
});

it('shows success snackbar when MHA request is created', async () => {
const { findByText } = render(
<TestComponent
hcmMock={singleMhaNoException}
mhaRequestsMock={[
{ ...mockMHARequest, status: MhaStatusEnum.BoardApproved },
]}
/>,
);

const requestButton = await findByText('Request New MHA');
userEvent.click(requestButton);

expect(
await findByText('Successfully created MHA Request.'),
).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from 'react';
import { useRouter } from 'next/router';

Check notice on line 1 in src/components/Reports/MinisterHousingAllowance/MinisterHousingAllowance.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

✅ No longer an issue: Complex Method

MinisterHousingAllowanceReport (top-level context) is no longer above the threshold for cyclomatic complexity. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
import React, { useState } from 'react';
import { Box, Button, Container, Stack, Typography } from '@mui/material';
import { DateTime } from 'luxon';
import { useSnackbar } from 'notistack';
import { useTranslation } from 'react-i18next';
import Loading from 'src/components/Loading/Loading';
import { Notification } from 'src/components/Notification/Notification';
import { MhaStatusEnum } from 'src/graphql/types.generated';
import { useAccountListId } from 'src/hooks/useAccountListId';
Expand All @@ -29,103 +31,104 @@
const { t } = useTranslation();
const { enqueueSnackbar } = useSnackbar();
const accountListId = useAccountListId();
const router = useRouter();

const {
data,
error: requestsError,
loading,
} = useMinistryHousingAllowanceRequestsQuery();
const requests = data?.ministryHousingAllowanceRequests.nodes ?? [];

const {
isMarried,
preferredName,
spousePreferredName,
userEligibleForMHA,
spouseEligibleForMHA,
userHcmData,
spouseHcmData,
} = useMinisterHousingAllowance();

const personNumber = userHcmData?.staffInfo?.personNumber ?? '';
const spousePersonNumber = spouseHcmData?.staffInfo?.personNumber ?? '';
const lastName = userHcmData?.staffInfo?.lastName ?? '';
const spouseLastName = spouseHcmData?.staffInfo?.lastName ?? '';

const names = isMarried
? `${preferredName} ${lastName} and ${spousePreferredName} ${spouseLastName}`
: `${preferredName} ${lastName}`;
const personNumbers = isMarried
? `${personNumber} and ${spousePersonNumber}`
: personNumber;

const [createMHA] = useCreateHousingAllowanceRequestMutation();
const [creatingRequest, setCreatingRequest] = useState(false);
Comment thread
canac marked this conversation as resolved.

const onCreateMHARequest = async () => {
setCreatingRequest(true);
await createMHA({
variables: {
requestAttributes: {
phoneNumber: userHcmData?.staffInfo.primaryPhoneNumber,
emailAddress: userHcmData?.staffInfo.emailAddress,
},
},
onCompleted: ({ createMinistryHousingAllowanceRequest: newRequest }) => {
enqueueSnackbar(
t("Successfully created MHA Request. You'll be redirected shortly."),
{
variant: 'success',
},
);
enqueueSnackbar(t('Successfully created MHA Request.'), {
variant: 'success',
});
const mhaRequestId = newRequest?.ministryHousingAllowanceRequest.id;
const requestLink = getRequestUrl(accountListId, mhaRequestId, 'new');

// Wait 1 second before redirecting
setTimeout(() => {
window.location.href = requestLink;
}, 1000);
router.push(requestLink);
},
onError: (err) => {
setCreatingRequest(false);
enqueueSnackbar(
t('Error while creating MHA Request - {{error}}', {
error: err.message,
}),
{
variant: 'error',
},
);
},
});
};

const currentRequest = requests[0] || {};
const currentRequest = requests[0] ?? undefined;
// It default to true when no availableDate as the request is likely still being processed
const isCurrentRequestPending =
currentRequest.status === MhaStatusEnum.BoardApproved &&
currentRequest.requestAttributes?.availableDate
? DateTime.fromISO(currentRequest.requestAttributes.availableDate) >
currentRequest?.status === MhaStatusEnum.BoardApproved &&
currentRequest?.requestAttributes?.availableDate
? DateTime.fromISO(currentRequest?.requestAttributes?.availableDate) >
DateTime.now()
: true;

const previousApprovedRequest = requests
.slice(1)
?.find(
(request) =>
request.status === MhaStatusEnum.BoardApproved &&
isCurrentRequestPending,
);

const hasNoRequests = !requests.length;

const bothEligible = userEligibleForMHA && spouseEligibleForMHA;
const eitherPersonEligible = userEligibleForMHA || spouseEligibleForMHA;

const showIneligibleDisplay =
!userEligibleForMHA || (isMarried && !bothEligible);
const showNewRequestButton =
eitherPersonEligible && (!isCurrentRequestPending || hasNoRequests);
const showCurrentRequest = eitherPersonEligible && currentRequest;
const showPreviousRequests = eitherPersonEligible && previousApprovedRequest;

if (creatingRequest) {
return <Loading loading />;
}

Check warning on line 131 in src/components/Reports/MinisterHousingAllowance/MinisterHousingAllowance.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

❌ New issue: Complex Method

MinisterHousingAllowanceReport has a cyclomatic complexity of 43, threshold = 10. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
return (
<PanelLayout
panelType={PanelTypeEnum.Empty}
Expand Down
Loading