Skip to content

Commit

Permalink
PRMDR-700 add feature flag check to upload LG workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
RachelHowellNHS authored Feb 28, 2024
1 parent 449839a commit 79770f8
Show file tree
Hide file tree
Showing 17 changed files with 239 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ const stubbedResponseMulti = {
};
describe('GP Workflow: Upload Lloyd George record when user is GP admin BSOL and patient has no record', () => {
const beforeEachConfiguration = () => {
cy.intercept('GET', '/FeatureFlags*', {
statusCode: 200,
body: {
uploadLloydGeorgeWorkflowEnabled: true,
uploadLambdaEnabled: true,
},
});
cy.login(Roles.GP_ADMIN);
cy.visit(searchPatientUrl);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { render, screen, waitFor } from '@testing-library/react';
import LgDownloadAllStage, { Props } from './LloydGeorgeDownloadAllStage';
import { buildLgSearchResult, buildPatientDetails } from '../../../helpers/test/testBuilders';
import {
buildConfig,
buildLgSearchResult,
buildPatientDetails,
} from '../../../helpers/test/testBuilders';
import axios from 'axios';
import { act } from 'react-dom/test-utils';
import userEvent from '@testing-library/user-event';
import usePatient from '../../../helpers/hooks/usePatient';
import { LinkProps } from 'react-router-dom';
import { routes } from '../../../types/generic/routes';
import useConfig from '../../../helpers/hooks/useConfig';
import { defaultFeatureFlags } from '../../../helpers/requests/getFeatureFlags';

jest.mock('../../../helpers/hooks/useConfig');
const mockedUseNavigate = jest.fn();
const mockedAxios = axios as jest.Mocked<typeof axios>;
const mockedUsePatient = usePatient as jest.Mock;
Expand All @@ -30,12 +32,13 @@ jest.mock('moment', () => {
jest.mock('axios');
jest.mock('../../../helpers/hooks/useBaseAPIHeaders');
jest.mock('../../../helpers/hooks/usePatient');
jest.mock('../../../helpers/hooks/useConfig');

describe('LloydGeorgeDownloadAllStage', () => {
beforeEach(() => {
process.env.REACT_APP_ENVIRONMENT = 'jest';
mockedUsePatient.mockReturnValue(mockPatient);
mockUseConfig.mockReturnValue({ featureFlags: defaultFeatureFlags, mockLocal: {} });
mockUseConfig.mockReturnValue(buildConfig());
});
afterEach(() => {
jest.clearAllMocks();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,32 @@ import useRole from '../../../helpers/hooks/useRole';
import { REPOSITORY_ROLE } from '../../../types/generic/authRole';
import { routes } from '../../../types/generic/routes';
import useIsBSOL from '../../../helpers/hooks/useIsBSOL';
import useConfig from '../../../helpers/hooks/useConfig';
import { buildConfig } from '../../../helpers/test/testBuilders';

const mockSetStage = jest.fn();
const mockNavigate = jest.fn();
jest.mock('../../../helpers/hooks/useIsBSOL');

jest.mock('../../../helpers/hooks/useRole');
jest.mock('../../../helpers/hooks/useConfig');
jest.mock('react-router-dom', () => ({
__esModule: true,
Link: (props: LinkProps) => <a {...props} role="link" />,
useNavigate: () => mockNavigate,
}));

jest.mock('react-router', () => ({
useNavigate: () => mockNavigate,
}));

jest.mock('../../../helpers/hooks/useRole');
const mockUseRole = useRole as jest.Mock;
const mockedIsBSOL = useIsBSOL as jest.Mock;
const mockIsBSOL = useIsBSOL as jest.Mock;
const mockUseConfig = useConfig as jest.Mock;
const mockSetStage = jest.fn();
const mockNavigate = jest.fn();

describe('LloydGeorgeRecordError', () => {
beforeEach(() => {
process.env.REACT_APP_ENVIRONMENT = 'jest';
mockUseRole.mockReturnValue(REPOSITORY_ROLE.GP_ADMIN);
mockUseConfig.mockReturnValue(buildConfig());
});

afterEach(() => {
Expand All @@ -48,6 +51,7 @@ describe('LloydGeorgeRecordError', () => {
).toBeInTheDocument();
expect(screen.getByText(/please download instead/i)).toBeInTheDocument();
});

it("renders an error when the document download status is 'Failed'", () => {
const timeoutStatus = DOWNLOAD_STAGE.FAILED;
render(
Expand All @@ -61,9 +65,10 @@ describe('LloydGeorgeRecordError', () => {
screen.getByText(/An error has occurred when creating the Lloyd George preview/i),
).toBeInTheDocument();
});

it("renders a message when the document download status is 'No records' and user is non BSOL", () => {
const timeoutStatus = DOWNLOAD_STAGE.NO_RECORDS;
mockedIsBSOL.mockReturnValue(false);
mockIsBSOL.mockReturnValue(false);

render(
<LloydGeorgeRecordError setStage={mockSetStage} downloadStage={timeoutStatus} />,
Expand All @@ -74,10 +79,20 @@ describe('LloydGeorgeRecordError', () => {
screen.queryByRole('button', { name: 'Upload patient record' }),
).not.toBeInTheDocument();
});
it("renders a message and upload button when the document download status is 'No records' and user is admin BSOL", () => {

it("renders a message and upload button when the document download status is 'No records', user is admin BSOL and upload flags are enabled", () => {
const noRecordsStatus = DOWNLOAD_STAGE.NO_RECORDS;
mockedIsBSOL.mockReturnValue(true);
mockIsBSOL.mockReturnValue(true);
mockUseRole.mockReturnValue(REPOSITORY_ROLE.GP_ADMIN);
mockUseConfig.mockReturnValue(
buildConfig(
{},
{
uploadLloydGeorgeWorkflowEnabled: true,
uploadLambdaEnabled: true,
},
),
);

render(
<LloydGeorgeRecordError setStage={mockSetStage} downloadStage={noRecordsStatus} />,
Expand All @@ -88,11 +103,28 @@ describe('LloydGeorgeRecordError', () => {
screen.getByRole('button', { name: 'Upload patient record' }),
).toBeInTheDocument();
});

it("renders a message but no upload button when the document download status is 'No records', user is admin BSOL and upload flags are not enabled", () => {
const noRecordsStatus = DOWNLOAD_STAGE.NO_RECORDS;

mockIsBSOL.mockReturnValue(true);
mockUseRole.mockReturnValue(REPOSITORY_ROLE.GP_ADMIN);

render(
<LloydGeorgeRecordError setStage={mockSetStage} downloadStage={noRecordsStatus} />,
);

expect(screen.getByText('No records available for this patient')).toBeInTheDocument();
expect(
screen.queryByRole('button', { name: 'Upload patient record' }),
).not.toBeInTheDocument();
});
});

describe('Navigation', () => {
it("renders a link that can navigate to the download all stage, when download status is 'Timeout'", () => {
const timeoutStatus = DOWNLOAD_STAGE.TIMEOUT;

render(
<LloydGeorgeRecordError setStage={mockSetStage} downloadStage={timeoutStatus} />,
);
Expand All @@ -110,6 +142,7 @@ describe('LloydGeorgeRecordError', () => {

it("navigates to the download all stage, when download status is 'Timeout' and the link is clicked: GP_ADMIN", () => {
const timeoutStatus = DOWNLOAD_STAGE.TIMEOUT;

render(
<LloydGeorgeRecordError setStage={mockSetStage} downloadStage={timeoutStatus} />,
);
Expand All @@ -131,9 +164,10 @@ describe('LloydGeorgeRecordError', () => {
expect(mockSetStage).toHaveBeenCalledWith(LG_RECORD_STAGE.DOWNLOAD_ALL);
});

it("navigates to unauthorised, when download status is 'Timeout' and the link is clicked: GP_CLINCIAL", () => {
it("navigates to unauthorised, when download status is 'Timeout' and the link is clicked: GP_CLINICAL", () => {
mockUseRole.mockReturnValue(REPOSITORY_ROLE.GP_CLINICAL);
const timeoutStatus = DOWNLOAD_STAGE.TIMEOUT;

render(
<LloydGeorgeRecordError setStage={mockSetStage} downloadStage={timeoutStatus} />,
);
Expand All @@ -154,17 +188,29 @@ describe('LloydGeorgeRecordError', () => {

expect(mockNavigate).toBeCalledWith(routes.UNAUTHORISED);
});
it("navigates to upload page, when the document download status is 'No records' and user is admin BSOL", () => {

it("navigates to upload page, when the document download status is 'No records', user is admin BSOL and upload flags are enabled", () => {
const noRecordsStatus = DOWNLOAD_STAGE.NO_RECORDS;
mockedIsBSOL.mockReturnValue(true);
mockIsBSOL.mockReturnValue(true);
mockUseRole.mockReturnValue(REPOSITORY_ROLE.GP_ADMIN);
mockUseConfig.mockReturnValue(
buildConfig(
{},
{
uploadLloydGeorgeWorkflowEnabled: true,
uploadLambdaEnabled: true,
},
),
);

render(
<LloydGeorgeRecordError setStage={mockSetStage} downloadStage={noRecordsStatus} />,
);

const uploadButton = screen.getByRole('button', { name: 'Upload patient record' });
expect(screen.getByText('No records available for this patient')).toBeInTheDocument();
expect(uploadButton).toBeInTheDocument();

act(() => {
uploadButton.click();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { REPOSITORY_ROLE } from '../../../types/generic/authRole';
import { routes } from '../../../types/generic/routes';
import useIsBSOL from '../../../helpers/hooks/useIsBSOL';
import { ButtonLink } from 'nhsuk-react-components';
import useConfig from '../../../helpers/hooks/useConfig';

type Props = {
downloadStage: DOWNLOAD_STAGE;
Expand All @@ -18,6 +19,7 @@ function LloydGeorgeRecordError({ downloadStage, setStage }: Props) {
const role = useRole();
const navigate = useNavigate();
const isBSOL = useIsBSOL();
const { featureFlags } = useConfig();

if (downloadStage === DOWNLOAD_STAGE.TIMEOUT) {
return (
Expand Down Expand Up @@ -48,21 +50,27 @@ function LloydGeorgeRecordError({ downloadStage, setStage }: Props) {
return (
<span>
<h3 data-testid="no-records-title">No records available for this patient </h3>
<p>
You can upload full or part of a patient record. You can upload supporting files
once the record is uploaded.
</p>
<div className="lloydgeorge_record-stage_header-content-no_record">
<ButtonLink
className="lloydgeorge_record-stage_header-content-no_record-upload"
data-testid="upload-patient-record-button"
onClick={() => {
navigate(routes.LLOYD_GEORGE_UPLOAD);
}}
>
Upload patient record
</ButtonLink>
</div>
{featureFlags.uploadLloydGeorgeWorkflowEnabled &&
featureFlags.uploadLambdaEnabled && (
<>
<p>
You can upload full or part of a patient record. You can upload
supporting files once the record is uploaded.
</p>

<div className="lloydgeorge_record-stage_header-content-no_record">
<ButtonLink
className="lloydgeorge_record-stage_header-content-no_record-upload"
data-testid="upload-patient-record-button"
onClick={() => {
navigate(routes.LLOYD_GEORGE_UPLOAD);
}}
>
Upload patient record
</ButtonLink>
</div>
</>
)}
</span>
);
} else if (downloadStage === DOWNLOAD_STAGE.NO_RECORDS) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
import { render, screen, waitFor } from '@testing-library/react';
import { buildLgSearchResult, buildPatientDetails } from '../../../helpers/test/testBuilders';
import {
buildConfig,
buildLgSearchResult,
buildPatientDetails,
} from '../../../helpers/test/testBuilders';
import userEvent from '@testing-library/user-event';
import LgRecordStage, { Props } from './LloydGeorgeRecordStage';
import { getFormattedDate } from '../../../helpers/utils/formatDate';
import { DOWNLOAD_STAGE } from '../../../types/generic/downloadStage';
import { useState } from 'react';
import formatFileSize from '../../../helpers/utils/formatFileSize';
import { act } from 'react-dom/test-utils';
import { LG_RECORD_STAGE } from '../../../types/blocks/lloydGeorgeStages';
import usePatient from '../../../helpers/hooks/usePatient';
import useRole from '../../../helpers/hooks/useRole';
import useIsBSOL from '../../../helpers/hooks/useIsBSOL';
import { REPOSITORY_ROLE } from '../../../types/generic/authRole';
import useConfig from '../../../helpers/hooks/useConfig';

const mockPdf = buildLgSearchResult();
const mockPatientDetails = buildPatientDetails();

jest.mock('../../../helpers/hooks/useRole');
jest.mock('../../../helpers/hooks/usePatient');
jest.mock('../../../helpers/hooks/useIsBSOL');
jest.mock('../../../helpers/hooks/useConfig');
const mockedUsePatient = usePatient as jest.Mock;
const mockNavigate = jest.fn();
const mockedUseRole = useRole as jest.Mock;
const mockedIsBSOL = useIsBSOL as jest.Mock;
const mockSetStage = jest.fn();
const mockUseConfig = useConfig as jest.Mock;

jest.mock('react-router', () => ({
useNavigate: () => mockNavigate,
Expand All @@ -33,6 +39,7 @@ describe('LloydGeorgeRecordStage', () => {
beforeEach(() => {
process.env.REACT_APP_ENVIRONMENT = 'jest';
mockedUsePatient.mockReturnValue(mockPatientDetails);
mockUseConfig.mockReturnValue(buildConfig());
});
afterEach(() => {
jest.clearAllMocks();
Expand Down
8 changes: 4 additions & 4 deletions app/src/helpers/hooks/useConfig.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { render, screen } from '@testing-library/react';
import useConfig from './useConfig';
import ConfigProvider, { GlobalConfig } from '../../providers/configProvider/ConfigProvider';
import { defaultFeatureFlags } from '../requests/getFeatureFlags';
import { defaultFeatureFlags } from '../../types/generic/featureFlags';

describe('useConfig', () => {
beforeEach(() => {
Expand All @@ -14,7 +14,7 @@ describe('useConfig', () => {

it('returns true when feature flag in context', () => {
const config: GlobalConfig = {
featureFlags: { ...defaultFeatureFlags, testFeature1: true },
featureFlags: { ...defaultFeatureFlags, uploadLloydGeorgeWorkflowEnabled: true },
mockLocal: {},
};
renderHook(config);
Expand All @@ -23,7 +23,7 @@ describe('useConfig', () => {

it('returns false when there is no feature flag in context', () => {
const config: GlobalConfig = {
featureFlags: { ...defaultFeatureFlags, testFeature1: false },
featureFlags: { ...defaultFeatureFlags, uploadLloydGeorgeWorkflowEnabled: false },
mockLocal: {},
};
renderHook(config);
Expand All @@ -33,7 +33,7 @@ describe('useConfig', () => {

const TestApp = () => {
const config = useConfig();
return <div>{`FLAG: ${!!config.featureFlags.testFeature1}`.normalize()}</div>;
return <div>{`FLAG: ${config.featureFlags.uploadLloydGeorgeWorkflowEnabled}`.normalize()}</div>;
};

const renderHook = (config?: GlobalConfig) => {
Expand Down
8 changes: 1 addition & 7 deletions app/src/helpers/requests/getFeatureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AuthHeaders } from '../../types/blocks/authHeaders';
import { endpoints } from '../../types/generic/endpoints';

import axios from 'axios';
import { FeatureFlags } from '../../types/generic/featureFlags';
import { defaultFeatureFlags, FeatureFlags } from '../../types/generic/featureFlags';

type Args = {
baseUrl: string;
Expand All @@ -13,12 +13,6 @@ type GetFeatureFlagsResponse = {
data: FeatureFlags;
};

export const defaultFeatureFlags = {
testFeature1: false,
testFeature2: false,
testFeature3: false,
};

const getFeatureFlags = async ({ baseUrl, baseHeaders }: Args) => {
const gatewayUrl = baseUrl + endpoints.FEATURE_FLAGS;
try {
Expand Down
Loading

0 comments on commit 79770f8

Please sign in to comment.