Skip to content

Commit

Permalink
Adding tests to Organization data sync upload file - Needed more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-bizz committed Nov 17, 2023
1 parent 05d2fd8 commit 6c46a67
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 146 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { render, waitFor } from '@testing-library/react';
import { render, waitFor, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { SnackbarProvider } from 'notistack';
import { ThemeProvider } from '@mui/material/styles';
import { GqlMockedProvider } from '../../../../../../__tests__/util/graphqlMocking';
import { IntegrationsContextProvider } from 'pages/accountLists/[accountListId]/settings/integrations/IntegrationsContext';
import TestRouter from '__tests__/util/TestRouter';
import theme from '../../../../../theme';
import { validateFile } from 'src/components/Shared/FileUploads/tntConnectDataSync';
import { OrganizationImportDataSyncModal } from './OrganizationImportDataSyncModal';
import {
OrganizationImportDataSyncModal,
validateFile,
} from './OrganizationImportDataSyncModal';

jest.mock('src/components/Shared/FileUploads/tntConnectDataSync');
jest.mock('next-auth/react');

const accountListId = 'account-list-1';
Expand Down Expand Up @@ -48,93 +49,217 @@ const Components = (children: React.ReactElement) => (

const handleClose = jest.fn();

const t = (text: string) => {
return text;
};

describe('OrganizationImportDataSyncModal', () => {
process.env.OAUTH_URL = 'https://auth.mpdx.org';
describe('ValidateFile()', () => {
it('File type is not correct', () => {
const file = new File(['contents'], 'image.png', {
type: 'image/png',
});
const response = validateFile({ file, t });

beforeEach(() => {
handleClose.mockClear();
(validateFile as jest.Mock).mockReturnValue({ success: true });
});
it('should render modal', async () => {
const { getByText, getByTestId } = render(
Components(
<GqlMockedProvider>
<OrganizationImportDataSyncModal
handleClose={handleClose}
organizationId={organizationId}
organizationName={organizationName}
accountListId={accountListId}
/>
</GqlMockedProvider>,
),
);

expect(getByText('Import TntConnect DataSync file')).toBeInTheDocument();

userEvent.click(getByText(/cancel/i));
expect(handleClose).toHaveBeenCalledTimes(1);
userEvent.click(getByTestId('CloseIcon'));
expect(handleClose).toHaveBeenCalledTimes(2);
});
expect(response).toEqual({
success: false,
message:
'Cannot upload file: file must be an .tntmpd or .tntdatasync file.',
});
});

it('File size is too big', () => {
const file = new File(['contents'], '.tntmpd', {
type: 'xml',
});
Object.defineProperty(file, 'size', { value: 200_000_000 });
const response = validateFile({ file, t });

it('should return error when no file present', async () => {
const mutationSpy = jest.fn();
const { getByText } = render(
Components(
<GqlMockedProvider onCall={mutationSpy}>
<OrganizationImportDataSyncModal
handleClose={handleClose}
organizationId={organizationId}
organizationName={organizationName}
accountListId={accountListId}
/>
</GqlMockedProvider>,
),
);
userEvent.click(getByText('Upload File'));

await waitFor(() =>
expect(mockEnqueue).toHaveBeenCalledWith(
'Please select a file to upload.',
{
variant: 'error',
},
),
);
expect(response).toEqual({
success: false,
message: 'Cannot upload file: file size cannot exceed 100MB',
});
});

it('File type is correct', () => {
const file = new File(['contents'], '.tntmpd', {
type: 'xml',
});
const response = validateFile({ file, t });

expect(response).toEqual({
success: true,
});
});
});

it('should inform user of the error when uploading file.', async () => {
(validateFile as jest.Mock).mockReturnValue({
success: false,
message: 'Invalid file',
describe('Render and upload file tests', () => {
process.env.OAUTH_URL = 'https://auth.mpdx.org';

beforeEach(() => {
handleClose.mockClear();
});
const mutationSpy = jest.fn();
const { getByTestId, getByText } = render(
Components(
<GqlMockedProvider onCall={mutationSpy}>
<OrganizationImportDataSyncModal
handleClose={handleClose}
organizationId={organizationId}
organizationName={organizationName}
accountListId={accountListId}
/>
</GqlMockedProvider>,
),
);

const file = new File(['contents'], 'image.png', {
type: 'image/png',
it('should render modal', async () => {
const { getByText, getByTestId } = render(
Components(
<GqlMockedProvider>
<OrganizationImportDataSyncModal
handleClose={handleClose}
organizationId={organizationId}
organizationName={organizationName}
accountListId={accountListId}
/>
</GqlMockedProvider>,
),
);

expect(getByText('Import TntConnect DataSync file')).toBeInTheDocument();

userEvent.click(getByText(/cancel/i));
expect(handleClose).toHaveBeenCalledTimes(1);
userEvent.click(getByTestId('CloseIcon'));
expect(handleClose).toHaveBeenCalledTimes(2);
});
userEvent.upload(getByTestId('importFileUploader'), file);

userEvent.click(getByText('Upload File'));
describe('Send Files to API', () => {
const fetch = jest
.fn()
.mockResolvedValue(Promise.resolve({ status: 201 }));
beforeEach(() => {
window.fetch = fetch;
});

it('should return error when file is too large', async () => {
const mutationSpy = jest.fn();
const { getByText, getByTestId } = render(
Components(
<GqlMockedProvider onCall={mutationSpy}>
<OrganizationImportDataSyncModal
handleClose={handleClose}
organizationId={organizationId}
organizationName={organizationName}
accountListId={accountListId}
/>
</GqlMockedProvider>,
),
);
const file = new File(['contents'], '.tntmpd', {
type: 'xml',
});
Object.defineProperty(file, 'size', {
value: 200_000_000,
configurable: true,
});
userEvent.upload(getByTestId('importFileUploader'), file);

await waitFor(() =>
expect(mockEnqueue).toHaveBeenCalledWith(
'Cannot upload file: file size cannot exceed 100MB',
{
variant: 'error',
},
),
);
expect(getByText('Upload File')).toBeDisabled();
});

it('should inform user of the error when uploading file.', async () => {
const mutationSpy = jest.fn();
const { getByTestId, getByText } = render(
Components(
<GqlMockedProvider onCall={mutationSpy}>
<OrganizationImportDataSyncModal
handleClose={handleClose}
organizationId={organizationId}
organizationName={organizationName}
accountListId={accountListId}
/>
</GqlMockedProvider>,
),
);

const file = new File(['contents'], 'image.png', {
type: 'image/png',
});
userEvent.upload(getByTestId('importFileUploader'), file);
await waitFor(() =>
expect(mockEnqueue).toHaveBeenCalledWith(
'Cannot upload file: file must be an .tntmpd or .tntdatasync file.',
{
variant: 'error',
},
),
);
expect(getByText('Upload File')).toBeDisabled();
});

it('should send formData and show successful banner', async () => {
const mutationSpy = jest.fn();
const { getByTestId, getByText } = render(
Components(
<GqlMockedProvider onCall={mutationSpy}>
<OrganizationImportDataSyncModal
handleClose={handleClose}
organizationId={organizationId}
organizationName={organizationName}
accountListId={accountListId}
/>
</GqlMockedProvider>,
),
);

await waitFor(() =>
expect(mockEnqueue).toHaveBeenCalledWith('Invalid file', {
variant: 'error',
}),
);
await waitFor(() => {
expect(getByText('Upload File')).toBeDisabled();
});

const testValue = [{ isTest: 'It is a test' }];
const str = JSON.stringify(testValue);
const blob = new Blob([str]);
const tntDataSync = new File([blob], '.tntmpd', {
type: 'xml',
});

await act(() => {
userEvent.upload(getByTestId('importFileUploader'), tntDataSync);
});
await waitFor(() => {
expect(getByText('Upload File')).not.toBeDisabled();
});
userEvent.click(getByText('Upload File'));
await waitFor(() => {
expect(window.fetch).toHaveBeenCalledWith(
'/api/uploads/tnt-data-sync',
expect.objectContaining({
method: 'POST',
body: expect.any(FormData),
}),
);
});

const formData = Array.from(
(window.fetch as jest.Mock<any, any>).mock.calls[0][1].body.entries(),
).reduce(
(acc, f) => ({
...(acc as Array<object>),
[(f as Array<string>)[0]]: (f as Array<string>)[1],
}),
{},
);

expect(formData).toEqual({
accountListId,
organizationId,
tntDataSync,
});
await waitFor(() =>
expect(mockEnqueue).toHaveBeenCalledWith(
`File successfully uploaded. The import to ${organizationName} will begin in the background.`,
{
variant: 'success',
},
),
);
});
});
});
// TODO: Need more tests with uploading correct file.
// Issue with node-fetch.
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSnackbar } from 'notistack';
import { TFunction } from 'i18next';
import { styled } from '@mui/material/styles';
import {
Box,
Expand All @@ -16,9 +17,33 @@ import {
} from 'src/components/common/Modal/ActionButtons/ActionButtons';
import theme from 'src/theme';
import Modal from 'src/components/common/Modal/Modal';
import { validateFile } from 'src/components/Shared/FileUploads/tntConnectDataSync';
import { getErrorMessage } from 'src/lib/getErrorFromCatch';

export const validateFile = ({
file,
t,
}: {
file: File;
t: TFunction;
}): { success: true } | { success: false; message: string } => {
if (!new RegExp(/.*\.tntmpd$|.*\.tntdatasync$/).test(file.name)) {
return {
success: false,
message: t(
'Cannot upload file: file must be an .tntmpd or .tntdatasync file.',
),
};
}
if (file.size > 100_000_000) {
return {
success: false,
message: t('Cannot upload file: file size cannot exceed 100MB'),
};
}

return { success: true };
};

interface OrganizationImportDataSyncModalProps {
handleClose: () => void;
organizationId: string;
Expand All @@ -40,6 +65,7 @@ export const OrganizationImportDataSyncModal: React.FC<
const { t } = useTranslation();
const { enqueueSnackbar } = useSnackbar();
const [isSubmitting, setIsSubmitting] = useState(false);
const [isValid, setIsValid] = useState(false);
const [importFile, setImportFile] = useState<File | null>(null);
const handleSubmit = async (event) => {
event.preventDefault();
Expand Down Expand Up @@ -77,7 +103,6 @@ export const OrganizationImportDataSyncModal: React.FC<
});
}
};

const handleFileChange: React.ChangeEventHandler<HTMLInputElement> = (
event,
) => {
Expand All @@ -88,7 +113,9 @@ export const OrganizationImportDataSyncModal: React.FC<
const validationResult = validateFile({ file, t });
if (!validationResult.success) throw new Error(validationResult.message);
setImportFile(file);
setIsValid(true);
} catch (err) {
setIsValid(false);
enqueueSnackbar(getErrorMessage(err), {
variant: 'error',
});
Expand Down Expand Up @@ -159,7 +186,7 @@ export const OrganizationImportDataSyncModal: React.FC<

<DialogActions>
<CancelButton onClick={handleClose} disabled={isSubmitting} />
<SubmitButton disabled={isSubmitting}>
<SubmitButton disabled={isSubmitting || !isValid}>
{t('Upload File')}
</SubmitButton>
</DialogActions>
Expand Down
Loading

0 comments on commit 6c46a67

Please sign in to comment.