From c7317cb5773de88d23b85e575fafad8bd80f1d1a Mon Sep 17 00:00:00 2001 From: Samay Sofo Date: Wed, 29 Jan 2025 22:51:20 +0000 Subject: [PATCH 1/8] Added document upload status alert messages --- .../DocumentViewer/DocumentViewer.jsx | 99 ++++++++++- .../DocumentViewer/DocumentViewer.test.jsx | 168 +++++++++++++++++- .../DocumentViewerFileManager.jsx | 2 + .../MoveDocumentWrapper.jsx | 6 +- src/pages/Office/Orders/Orders.jsx | 4 +- .../ServicesCounselingMoveDocumentWrapper.jsx | 6 +- .../ServicesCounselingOrders.jsx | 4 +- .../SupportingDocuments.jsx | 8 +- src/shared/constants.js | 14 ++ 9 files changed, 300 insertions(+), 11 deletions(-) diff --git a/src/components/DocumentViewer/DocumentViewer.jsx b/src/components/DocumentViewer/DocumentViewer.jsx index ceb30cda9c5..703844f34e8 100644 --- a/src/components/DocumentViewer/DocumentViewer.jsx +++ b/src/components/DocumentViewer/DocumentViewer.jsx @@ -16,6 +16,8 @@ import { bulkDownloadPaymentRequest, updateUpload } from 'services/ghcApi'; import { formatDate } from 'shared/dates'; import { filenameFromPath } from 'utils/formatters'; import AsyncPacketDownloadLink from 'shared/AsyncPacketDownloadLink/AsyncPacketDownloadLink'; +import { UPLOAD_DOC_STATUS, UPLOAD_SCAN_STATUS, UPLOAD_DOC_STATUS_DISPLAY_MESSAGE } from 'shared/constants'; +import Alert from 'shared/Alert'; /** * TODO @@ -23,13 +25,15 @@ import AsyncPacketDownloadLink from 'shared/AsyncPacketDownloadLink/AsyncPacketD * - implement rotate left/right */ -const DocumentViewer = ({ files, allowDownload, paymentRequestId }) => { +const DocumentViewer = ({ files, allowDownload, paymentRequestId, isFileUploading }) => { const [selectedFileIndex, selectFile] = useState(0); const [disableSaveButton, setDisableSaveButton] = useState(false); const [menuIsOpen, setMenuOpen] = useState(false); const [showContentError, setShowContentError] = useState(false); const sortedFiles = files.sort((a, b) => moment(b.createdAt) - moment(a.createdAt)); const selectedFile = sortedFiles[parseInt(selectedFileIndex, 10)]; + const [isJustUploadedFile, setIsJustUploadedFile] = useState(false); + const [fileStatus, setFileStatus] = useState(null); const [rotationValue, setRotationValue] = useState(selectedFile?.rotation || 0); @@ -37,6 +41,15 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId }) => { const queryClient = useQueryClient(); + useEffect(() => { + if (isFileUploading) { + setIsJustUploadedFile(true); + setFileStatus(UPLOAD_DOC_STATUS.UPLOADING); + } else { + setIsJustUploadedFile(false); + } + }, [isFileUploading]); + const { mutate: mutateUploads } = useMutation(updateUpload, { onSuccess: async (data, variables) => { if (mountedRef.current) { @@ -75,12 +88,90 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId }) => { useEffect(() => { setShowContentError(false); setRotationValue(selectedFile?.rotation || 0); - }, [selectedFile]); + const handleFileProcessing = async (status) => { + switch (status) { + case UPLOAD_SCAN_STATUS.PROCESSING: + setFileStatus(UPLOAD_DOC_STATUS.SCANNING); + break; + case UPLOAD_SCAN_STATUS.CLEAN: + setFileStatus(UPLOAD_DOC_STATUS.ESTABLISHING); + break; + case UPLOAD_SCAN_STATUS.INFECTED: + setFileStatus(UPLOAD_DOC_STATUS.INFECTED); + break; + default: + throw new Error(`unrecognized file status : ${status}`); + } + }; + if (!isFileUploading && isJustUploadedFile) { + setFileStatus(UPLOAD_DOC_STATUS.UPLOADING); + } + + let sse; + if (selectedFile) { + sse = new EventSource(`/internal/uploads/${selectedFile.id}/status`, { withCredentials: true }); + sse.onmessage = (event) => { + handleFileProcessing(event.data); + if ( + event.data === UPLOAD_SCAN_STATUS.CLEAN || + event.data === UPLOAD_SCAN_STATUS.INFECTED || + event.data === 'Connection closed' + ) { + sse.close(); + } + }; + sse.onerror = () => { + sse.close(); + setFileStatus(null); + }; + } + + return () => { + sse?.close(); + }; + }, [selectedFile, isFileUploading, isJustUploadedFile]); + useEffect(() => { + if (fileStatus === UPLOAD_DOC_STATUS.ESTABLISHING) { + new Promise((resolve) => { + setTimeout(resolve, 2000); + }).then(() => setFileStatus(UPLOAD_DOC_STATUS.LOADED)); + } + }, [fileStatus]); const fileType = useRef(selectedFile?.contentType); - if (!selectedFile) { - return

File Not Found

; + const getStatusMessage = (currentFileStatus, currentSelectedFile) => { + switch (currentFileStatus) { + case UPLOAD_DOC_STATUS.UPLOADING: + return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.UPLOADING; + case UPLOAD_DOC_STATUS.SCANNING: + return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.SCANNING; + case UPLOAD_DOC_STATUS.ESTABLISHING: + return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW; + default: + if (!currentSelectedFile) { + return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.FILE_NOT_FOUND; + } + return null; + } + }; + + const alertMessage = getStatusMessage(fileStatus, selectedFile); + if (alertMessage) { + return ( + + {alertMessage} + + ); + } + + if (fileStatus === UPLOAD_SCAN_STATUS.INFECTED) { + return ( + + Our antivirus software flagged this file as a security risk. Contact the service member. Ask them to upload a + photo of the original document instead. + + ); } const openMenu = () => { diff --git a/src/components/DocumentViewer/DocumentViewer.test.jsx b/src/components/DocumentViewer/DocumentViewer.test.jsx index b5a211cd951..b1aaf460e85 100644 --- a/src/components/DocumentViewer/DocumentViewer.test.jsx +++ b/src/components/DocumentViewer/DocumentViewer.test.jsx @@ -1,5 +1,5 @@ /* eslint-disable react/jsx-props-no-spreading */ -import React from 'react'; +import React, { act } from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; @@ -10,6 +10,7 @@ import sampleJPG from './sample.jpg'; import samplePNG from './sample2.png'; import sampleGIF from './sample3.gif'; +import { UPLOAD_DOC_STATUS, UPLOAD_SCAN_STATUS, UPLOAD_DOC_STATUS_DISPLAY_MESSAGE } from 'shared/constants'; import { bulkDownloadPaymentRequest } from 'services/ghcApi'; const toggleMenuClass = () => { @@ -110,6 +111,28 @@ jest.mock('./Content/Content', () => ({ }, })); +// Mock EventSource +class MockEventSource { + constructor(url, config) { + this.url = url; + this.config = config; + this.onmessage = null; + this.onerror = null; + } + + sendMessage(data) { + if (this.onmessage) { + this.onmessage({ data }); + } + } + + triggerError() { + if (this.onerror) { + this.onerror(); + } + } +} + describe('DocumentViewer component', () => { it('initial state is closed menu and first file selected', async () => { render( @@ -269,3 +292,146 @@ describe('DocumentViewer component', () => { }); }); }); + +// describe('File upload status', () => { +// const setup = async (fileStatus, isFileUploading = false) => { +// await act(async () => { +// render(); +// }); +// act(() => { +// switch (fileStatus) { +// case UPLOAD_SCAN_STATUS.PROCESSING: +// DocumentViewer.setFileStatus(UPLOAD_DOC_STATUS.SCANNING); +// break; +// case UPLOAD_SCAN_STATUS.CLEAN: +// DocumentViewer.setFileStatus(UPLOAD_DOC_STATUS.ESTABLISHING); +// break; +// case UPLOAD_SCAN_STATUS.INFECTED: +// DocumentViewer.setFileStatus(UPLOAD_DOC_STATUS.INFECTED); +// break; +// default: +// break; +// } +// }); +// }; + +// it('renders SCANNING status', () => { +// setup(UPLOAD_SCAN_STATUS.PROCESSING); +// expect(screen.getByText('Scanning')).toBeInTheDocument(); +// }); + +// it('renders ESTABLISHING status', () => { +// setup(UPLOAD_SCAN_STATUS.CLEAN); +// expect(screen.getByText('Establishing Document for View')).toBeInTheDocument(); +// }); + +// it('renders INFECTED status', () => { +// setup(UPLOAD_SCAN_STATUS.INFECTED); +// expect(screen.getByText('Ask for a new file')).toBeInTheDocument(); +// }); +// }); + +// describe('DocumentViewer component', () => { +// const files = [ +// { +// id: '1', +// createdAt: '2022-01-01T00:00:00Z', +// contentType: 'application/pdf', +// filename: 'file1.pdf', +// url: samplePDF, +// }, +// ]; + +// beforeEach(() => { +// global.EventSource = MockEventSource; +// }); + +// const renderComponent = (fileStatus) => { +// render( +// +// +// , +// ); +// }; + +// it('displays Uploading alert when fileStatus is UPLOADING', () => { +// renderComponent(UPLOAD_DOC_STATUS.UPLOADING); +// expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.UPLOADING)).toBeInTheDocument(); +// }); + +// it('displays Scanning alert when fileStatus is SCANNING', () => { +// renderComponent(UPLOAD_DOC_STATUS.SCANNING); +// expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.SCANNING)).toBeInTheDocument(); +// }); + +// it('displays Establishing Document for View alert when fileStatus is ESTABLISHING', () => { +// renderComponent(UPLOAD_DOC_STATUS.ESTABLISHING); +// expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW)).toBeInTheDocument(); +// }); + +// it('displays File Not Found alert when selectedFile is null', () => { +// render(); +// expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.FILE_NOT_FOUND)).toBeInTheDocument(); +// }); + +// it('displays an error alert when fileStatus is INFECTED', () => { +// renderComponent(UPLOAD_SCAN_STATUS.INFECTED); +// expect( +// screen.getByText( +// 'Our antivirus software flagged this file as a security risk. Contact the service member. Ask them to upload a photo of the original document instead.', +// ), +// ).toBeInTheDocument(); +// }); +// }); + +describe('DocumentViewer component', () => { + const files = [ + { + id: '1', + createdAt: '2022-01-01T00:00:00Z', + contentType: 'application/pdf', + filename: 'file1.pdf', + url: samplePDF, + }, + ]; + beforeEach(() => { + global.EventSource = MockEventSource; + }); + + const renderComponent = () => { + render(); + }; + + test('handles file processing status', async () => { + renderComponent(UPLOAD_DOC_STATUS.UPLOADING); + + const eventSourceInstance = new MockEventSource(`/internal/uploads/${files[0].id}/status`, { + withCredentials: true, + }); + + // Simulate different statuses + await act(async () => { + eventSourceInstance.sendMessage(UPLOAD_SCAN_STATUS.PROCESSING); + }); + expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.SCANNING)).toBeInTheDocument(); + + await act(async () => { + eventSourceInstance.sendMessage(UPLOAD_SCAN_STATUS.CLEAN); + }); + expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW)).toBeInTheDocument(); + + await act(async () => { + eventSourceInstance.sendMessage(UPLOAD_SCAN_STATUS.INFECTED); + }); + expect( + screen.getByText( + 'Our antivirus software flagged this file as a security risk. Contact the service member. Ask them to upload a photo of the original document instead.', + ), + ).toBeInTheDocument(); + }); + + it('displays File Not Found alert when no selectedFile', () => { + render(); + expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.FILE_NOT_FOUND)).toBeInTheDocument(); + }); +}); diff --git a/src/components/DocumentViewerFileManager/DocumentViewerFileManager.jsx b/src/components/DocumentViewerFileManager/DocumentViewerFileManager.jsx index 7e765b93882..dd4789d8413 100644 --- a/src/components/DocumentViewerFileManager/DocumentViewerFileManager.jsx +++ b/src/components/DocumentViewerFileManager/DocumentViewerFileManager.jsx @@ -29,6 +29,7 @@ const DocumentViewerFileManager = ({ documentType, updateAmendedDocument, fileUploadRequired, + onAddFile, }) => { const queryClient = useQueryClient(); const filePondEl = useRef(); @@ -246,6 +247,7 @@ const DocumentViewerFileManager = ({ ref={filePondEl} createUpload={handleUpload} onChange={handleChange} + onAddFile={onAddFile} labelIdle={'Drag files here or click to upload'} /> PDF, JPG, or PNG only. Maximum file size 25MB. Each page must be clear and legible diff --git a/src/pages/Office/MoveDocumentWrapper/MoveDocumentWrapper.jsx b/src/pages/Office/MoveDocumentWrapper/MoveDocumentWrapper.jsx index f7d97fde5a9..186a1da3c4a 100644 --- a/src/pages/Office/MoveDocumentWrapper/MoveDocumentWrapper.jsx +++ b/src/pages/Office/MoveDocumentWrapper/MoveDocumentWrapper.jsx @@ -20,6 +20,7 @@ const MoveDocumentWrapper = () => { // this is to update the id when it is created to store amendedUpload data. const [amendedDocumentId, setAmendedDocumentId] = useState(amendedOrderDocumentId); const { amendedUpload } = useAmendedDocumentQueries(amendedDocumentId); + const [isFileUploading, setFileUploading] = useState(false); const updateAmendedDocument = (newId) => { setAmendedDocumentId(newId); @@ -63,7 +64,7 @@ const MoveDocumentWrapper = () => {
{documentsForViewer && (
- +
)} {showOrders ? ( @@ -72,6 +73,9 @@ const MoveDocumentWrapper = () => { files={documentsByTypes} amendedDocumentId={amendedDocumentId} updateAmendedDocument={updateAmendedDocument} + onAddFile={() => { + setFileUploading(true); + }} /> ) : ( diff --git a/src/pages/Office/Orders/Orders.jsx b/src/pages/Office/Orders/Orders.jsx index 1bf21c4fc50..aaba9623601 100644 --- a/src/pages/Office/Orders/Orders.jsx +++ b/src/pages/Office/Orders/Orders.jsx @@ -33,7 +33,7 @@ const ordersTypeDropdownOptions = dropdownInputOptions(ORDERS_TYPE_OPTIONS); const ordersTypeDetailsDropdownOptions = dropdownInputOptions(ORDERS_TYPE_DETAILS_OPTIONS); const payGradeDropdownOptions = dropdownInputOptions(ORDERS_PAY_GRADE_OPTIONS); -const Orders = ({ files, amendedDocumentId, updateAmendedDocument }) => { +const Orders = ({ files, amendedDocumentId, updateAmendedDocument, onAddFile }) => { const navigate = useNavigate(); const { moveCode } = useParams(); const [tacValidationState, tacValidationDispatch] = useReducer(tacReducer, null, initialTacState); @@ -375,6 +375,7 @@ const Orders = ({ files, amendedDocumentId, updateAmendedDocument }) => { documentId={documentId} files={ordersDocuments} documentType={MOVE_DOCUMENT_TYPE.ORDERS} + onAddFile={onAddFile} /> { files={amendedDocuments} documentType={MOVE_DOCUMENT_TYPE.AMENDMENTS} updateAmendedDocument={updateAmendedDocument} + onAddFile={onAddFile} />
diff --git a/src/pages/Office/ServicesCounselingMoveDocumentWrapper/ServicesCounselingMoveDocumentWrapper.jsx b/src/pages/Office/ServicesCounselingMoveDocumentWrapper/ServicesCounselingMoveDocumentWrapper.jsx index f3c50c20e39..60c9661dc26 100644 --- a/src/pages/Office/ServicesCounselingMoveDocumentWrapper/ServicesCounselingMoveDocumentWrapper.jsx +++ b/src/pages/Office/ServicesCounselingMoveDocumentWrapper/ServicesCounselingMoveDocumentWrapper.jsx @@ -20,6 +20,7 @@ const ServicesCounselingMoveDocumentWrapper = () => { // this is to update the id when it is created to store amendedUpload data. const [amendedDocumentId, setAmendedDocumentId] = useState(amendedOrderDocumentId); const { amendedUpload } = useAmendedDocumentQueries(amendedDocumentId); + const [isFileUploading, setFileUploading] = useState(false); const updateAmendedDocument = (newId) => { setAmendedDocumentId(newId); @@ -64,7 +65,7 @@ const ServicesCounselingMoveDocumentWrapper = () => {
{documentsForViewer && (
- +
)} {showOrders ? ( @@ -73,6 +74,9 @@ const ServicesCounselingMoveDocumentWrapper = () => { files={documentsByTypes} amendedDocumentId={amendedDocumentId} updateAmendedDocument={updateAmendedDocument} + onAddFile={() => { + setFileUploading(true); + }} /> ) : ( diff --git a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx index 5a3d37c59e0..b16324b0d1d 100644 --- a/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx +++ b/src/pages/Office/ServicesCounselingOrders/ServicesCounselingOrders.jsx @@ -37,7 +37,7 @@ const deptIndicatorDropdownOptions = dropdownInputOptions(DEPARTMENT_INDICATOR_O const ordersTypeDetailsDropdownOptions = dropdownInputOptions(ORDERS_TYPE_DETAILS_OPTIONS); const payGradeDropdownOptions = dropdownInputOptions(ORDERS_PAY_GRADE_OPTIONS); -const ServicesCounselingOrders = ({ files, amendedDocumentId, updateAmendedDocument }) => { +const ServicesCounselingOrders = ({ files, amendedDocumentId, updateAmendedDocument, onAddFile }) => { const navigate = useNavigate(); const queryClient = useQueryClient(); const { moveCode } = useParams(); @@ -371,6 +371,7 @@ const ServicesCounselingOrders = ({ files, amendedDocumentId, updateAmendedDocum documentId={orderDocumentId} files={ordersDocuments} documentType={MOVE_DOCUMENT_TYPE.ORDERS} + onAddFile={onAddFile} />
diff --git a/src/pages/Office/SupportingDocuments/SupportingDocuments.jsx b/src/pages/Office/SupportingDocuments/SupportingDocuments.jsx index aeae84fd136..a226732aaa7 100644 --- a/src/pages/Office/SupportingDocuments/SupportingDocuments.jsx +++ b/src/pages/Office/SupportingDocuments/SupportingDocuments.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import moment from 'moment'; import classNames from 'classnames'; @@ -10,6 +10,7 @@ import { permissionTypes } from 'constants/permissions'; import { MOVE_DOCUMENT_TYPE } from 'shared/constants'; const SupportingDocuments = ({ move, uploads }) => { + const [isFileUploading, setFileUploading] = useState(false); const filteredAndSortedUploads = Object.values(uploads || {}) ?.filter((file) => { return !file.deletedAt; @@ -23,7 +24,7 @@ const SupportingDocuments = ({ move, uploads }) => { filteredAndSortedUploads?.length <= 0 ? (

No supporting documents have been uploaded.

) : ( - + )}
@@ -36,6 +37,9 @@ const SupportingDocuments = ({ move, uploads }) => { documentId={move.additionalDocuments?.id} files={filteredAndSortedUploads} documentType={MOVE_DOCUMENT_TYPE.SUPPORTING} + onAddFile={() => { + setFileUploading(true); + }} /> diff --git a/src/shared/constants.js b/src/shared/constants.js index 56b7601c585..a354a2583f0 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -69,6 +69,20 @@ export const UPLOAD_SCAN_STATUS = { PROCESSING: 'PROCESSING', }; +export const UPLOAD_DOC_STATUS = { + UPLOADING: 'UPLOADING', + SCANNING: 'SCANNING', + ESTABLISHING: 'ESTABLISHING', + LOADED: 'LOADED', +}; + +export const UPLOAD_DOC_STATUS_DISPLAY_MESSAGE = { + FILE_NOT_FOUND: 'File Not Found', + UPLOADING: 'Uploading', + SCANNING: 'Scanning', + ESTABLISHING_DOCUMENT_FOR_VIEW: 'Establishing Document for View', +}; + export const CONUS_STATUS = { CONUS: 'CONUS', OCONUS: 'OCONUS', From 3445eef2859376bb406f62ebb617e5581c34c079 Mon Sep 17 00:00:00 2001 From: Samay Sofo Date: Wed, 5 Feb 2025 11:08:02 +0000 Subject: [PATCH 2/8] fixed breaking unit tests --- .../DocumentViewer/DocumentViewer.jsx | 14 +- .../DocumentViewer/DocumentViewer.test.jsx | 359 +++++++++++------- .../ReviewDocuments/ReviewDocuments.test.jsx | 6 + .../PaymentRequestReview.test.jsx | 6 + .../SupportingDocuments.test.jsx | 5 + 5 files changed, 248 insertions(+), 142 deletions(-) diff --git a/src/components/DocumentViewer/DocumentViewer.jsx b/src/components/DocumentViewer/DocumentViewer.jsx index 703844f34e8..d4be15f0d87 100644 --- a/src/components/DocumentViewer/DocumentViewer.jsx +++ b/src/components/DocumentViewer/DocumentViewer.jsx @@ -101,7 +101,7 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId, isFileUploadin setFileStatus(UPLOAD_DOC_STATUS.INFECTED); break; default: - throw new Error(`unrecognized file status : ${status}`); + throw new Error(`unrecognized file status`); } }; if (!isFileUploading && isJustUploadedFile) { @@ -110,7 +110,7 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId, isFileUploadin let sse; if (selectedFile) { - sse = new EventSource(`/internal/uploads/${selectedFile.id}/status`, { withCredentials: true }); + sse = new EventSource(`/ghc/v1/uploads/${selectedFile.id}/status`, { withCredentials: true }); sse.onmessage = (event) => { handleFileProcessing(event.data); if ( @@ -159,8 +159,8 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId, isFileUploadin const alertMessage = getStatusMessage(fileStatus, selectedFile); if (alertMessage) { return ( - - {alertMessage} + + {alertMessage} ); } @@ -168,8 +168,10 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId, isFileUploadin if (fileStatus === UPLOAD_SCAN_STATUS.INFECTED) { return ( - Our antivirus software flagged this file as a security risk. Contact the service member. Ask them to upload a - photo of the original document instead. + + Our antivirus software flagged this file as a security risk. Contact the service member. Ask them to upload a + photo of the original document instead. + ); } diff --git a/src/components/DocumentViewer/DocumentViewer.test.jsx b/src/components/DocumentViewer/DocumentViewer.test.jsx index b1aaf460e85..eedcbc49bea 100644 --- a/src/components/DocumentViewer/DocumentViewer.test.jsx +++ b/src/components/DocumentViewer/DocumentViewer.test.jsx @@ -1,5 +1,5 @@ /* eslint-disable react/jsx-props-no-spreading */ -import React, { act } from 'react'; +import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; @@ -10,7 +10,6 @@ import sampleJPG from './sample.jpg'; import samplePNG from './sample2.png'; import sampleGIF from './sample3.gif'; -import { UPLOAD_DOC_STATUS, UPLOAD_SCAN_STATUS, UPLOAD_DOC_STATUS_DISPLAY_MESSAGE } from 'shared/constants'; import { bulkDownloadPaymentRequest } from 'services/ghcApi'; const toggleMenuClass = () => { @@ -20,6 +19,16 @@ const toggleMenuClass = () => { } }; +global.EventSource = jest.fn().mockImplementation(() => ({ + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + close: jest.fn(), +})); + +beforeEach(() => { + jest.clearAllMocks(); +}); + const mockFiles = [ { id: 1, @@ -111,28 +120,6 @@ jest.mock('./Content/Content', () => ({ }, })); -// Mock EventSource -class MockEventSource { - constructor(url, config) { - this.url = url; - this.config = config; - this.onmessage = null; - this.onerror = null; - } - - sendMessage(data) { - if (this.onmessage) { - this.onmessage({ data }); - } - } - - triggerError() { - if (this.onerror) { - this.onerror(); - } - } -} - describe('DocumentViewer component', () => { it('initial state is closed menu and first file selected', async () => { render( @@ -293,145 +280,245 @@ describe('DocumentViewer component', () => { }); }); -// describe('File upload status', () => { -// const setup = async (fileStatus, isFileUploading = false) => { -// await act(async () => { -// render(); -// }); -// act(() => { -// switch (fileStatus) { -// case UPLOAD_SCAN_STATUS.PROCESSING: -// DocumentViewer.setFileStatus(UPLOAD_DOC_STATUS.SCANNING); -// break; -// case UPLOAD_SCAN_STATUS.CLEAN: -// DocumentViewer.setFileStatus(UPLOAD_DOC_STATUS.ESTABLISHING); -// break; -// case UPLOAD_SCAN_STATUS.INFECTED: -// DocumentViewer.setFileStatus(UPLOAD_DOC_STATUS.INFECTED); -// break; -// default: -// break; +// describe('Document viewer file upload status', () => { +// let originalEventSource; +// let mockEventSource; + +// const createMockEventSource = () => ({ +// onmessage: null, +// onerror: null, +// close: jest.fn(), +// simulateMessage(eventData) { +// if (this.onmessage) { +// this.onmessage({ data: eventData }); // } -// }); -// }; +// }, +// simulateError() { +// if (this.onerror) { +// this.onerror(); +// } +// }, +// }); -// it('renders SCANNING status', () => { -// setup(UPLOAD_SCAN_STATUS.PROCESSING); -// expect(screen.getByText('Scanning')).toBeInTheDocument(); +// let setFileStatusCallback; + +// beforeEach(() => { +// jest.spyOn(React, 'useState').mockImplementation((init) => { +// if (init === null) { +// const [state, setState] = React.useState(init); +// setFileStatusCallback = setState; +// return [state, setState]; +// } +// return React.useState(init); +// }); // }); -// it('renders ESTABLISHING status', () => { -// setup(UPLOAD_SCAN_STATUS.CLEAN); -// expect(screen.getByText('Establishing Document for View')).toBeInTheDocument(); +// beforeEach(() => { +// originalEventSource = global.EventSource; +// mockEventSource = createMockEventSource(); +// global.EventSource = jest.fn().mockImplementation(() => mockEventSource); // }); -// it('renders INFECTED status', () => { -// setup(UPLOAD_SCAN_STATUS.INFECTED); -// expect(screen.getByText('Ask for a new file')).toBeInTheDocument(); +// afterEach(() => { +// global.EventSource = originalEventSource; // }); -// }); -// describe('DocumentViewer component', () => { -// const files = [ -// { -// id: '1', -// createdAt: '2022-01-01T00:00:00Z', -// contentType: 'application/pdf', -// filename: 'file1.pdf', -// url: samplePDF, -// }, -// ]; +// const renderDocumentViewer = (files, isFileUploading = false) => { +// renderWithProviders(); +// return mockEventSource; +// }; -// beforeEach(() => { -// global.EventSource = MockEventSource; +// const testFileStatusMock = { +// id: '1', +// filename: 'test.pdf', +// contentType: 'application/pdf', +// url: samplePDF, +// createdAt: '2021-06-15T15:09:26.979879Z', +// status: undefined, +// }; + +// it('displays uploading status when isFileUploading is true', async () => { +// const files = [ +// { +// id: '1', +// filename: 'test.pdf', +// contentType: 'application/pdf', +// url: samplePDF, +// createdAt: '2023-05-20T12:00:00Z', +// }, +// ]; + +// const { container } = renderDocumentViewer({ files, isFileUploading: true }); + +// await waitFor(() => { +// // Look for the uploading message anywhere in the document +// const uploadingMessage = screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.UPLOADING); +// expect(uploadingMessage).toBeInTheDocument(); + +// // If you want to check if it's inside an Alert component, you can check for the class +// const alert = container.querySelector('.usa-alert'); +// expect(alert).toBeInTheDocument(); +// expect(alert).toContainElement(uploadingMessage); +// }); // }); -// const renderComponent = (fileStatus) => { -// render( -// -// -// , -// ); -// }; +// it('displays scanning status correctly', async () => { +// const eventSource = renderDocumentViewer([{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.PROCESSING }]); +// act(() => { +// eventSource.simulateMessage(UPLOAD_SCAN_STATUS.PROCESSING); +// }); +// await waitFor(() => { +// expect(screen.getByText('Scanning')).toBeInTheDocument(); +// }); +// }); -// it('displays Uploading alert when fileStatus is UPLOADING', () => { -// renderComponent(UPLOAD_DOC_STATUS.UPLOADING); -// expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.UPLOADING)).toBeInTheDocument(); +// it('displays establishing document status when file is clean', async () => { +// renderDocumentViewer({ files: [testFileStatusMock] }); + +// act(() => { +// setFileStatusCallback(UPLOAD_SCAN_STATUS.ESTABLISHING); +// }); + +// await waitFor(() => { +// // Use a more flexible text matching +// const statusElement = screen.getByText((content, element) => { +// return element.textContent.includes(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW); +// }); +// expect(statusElement).toBeInTheDocument(); +// }); // }); -// it('displays Scanning alert when fileStatus is SCANNING', () => { -// renderComponent(UPLOAD_DOC_STATUS.SCANNING); -// expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.SCANNING)).toBeInTheDocument(); +// it('displays establishing document for view status correctly', async () => { +// const eventSource = renderDocumentViewer([{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.CLEAN }]); +// act(() => { +// // eventSource.simulateMessage(UPLOAD_SCAN_STATUS.CLEAN); +// }); +// await waitFor(() => { +// expect(screen.getByText('Establishing document for view')).toBeInTheDocument(); +// }); // }); -// it('displays Establishing Document for View alert when fileStatus is ESTABLISHING', () => { -// renderComponent(UPLOAD_DOC_STATUS.ESTABLISHING); -// expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW)).toBeInTheDocument(); +// it('shows error for infected file', async () => { +// const eventSource = renderDocumentViewer([{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.INFECTED }]); +// act(() => { +// // eventSource.simulateMessage(UPLOAD_SCAN_STATUS.INFECTED); +// }); +// await waitFor(() => { +// expect(screen.getByText('Ask for a new file')).toBeInTheDocument(); +// }); // }); -// it('displays File Not Found alert when selectedFile is null', () => { -// render(); -// expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.FILE_NOT_FOUND)).toBeInTheDocument(); +// it('displays uploading status correctly', async () => { +// renderDocumentViewer(testFileStatusMock, true); +// await waitFor(() => { +// expect(screen.getByText('Uploading')).toBeInTheDocument(); +// }); // }); -// it('displays an error alert when fileStatus is INFECTED', () => { -// renderComponent(UPLOAD_SCAN_STATUS.INFECTED); -// expect( -// screen.getByText( -// 'Our antivirus software flagged this file as a security risk. Contact the service member. Ask them to upload a photo of the original document instead.', -// ), -// ).toBeInTheDocument(); +// it('displays file not found status correctly', async () => { +// renderDocumentViewer([]); +// await waitFor(() => { +// expect(screen.getByText(/File not found/i)).toBeInTheDocument(); +// }); // }); // }); -describe('DocumentViewer component', () => { - const files = [ - { - id: '1', - createdAt: '2022-01-01T00:00:00Z', - contentType: 'application/pdf', - filename: 'file1.pdf', - url: samplePDF, - }, - ]; - beforeEach(() => { - global.EventSource = MockEventSource; - }); +// describe('Document viewer file upload status', () => { +// let originalEventSource; +// let mockEventSource; + +// const createMockEventSource = () => ({ +// onmessage: null, +// onerror: null, +// close: jest.fn(), +// simulateMessage(eventData) { +// if (this.onmessage) { +// this.onmessage({ data: eventData }); +// } +// }, +// simulateError() { +// if (this.onerror) { +// this.onerror(); +// } +// }, +// }); - const renderComponent = () => { - render(); - }; +// beforeEach(() => { +// originalEventSource = global.EventSource; +// mockEventSource = createMockEventSource(); +// global.EventSource = jest.fn().mockImplementation(() => mockEventSource); +// }); - test('handles file processing status', async () => { - renderComponent(UPLOAD_DOC_STATUS.UPLOADING); +// afterEach(() => { +// global.EventSource = originalEventSource; +// }); - const eventSourceInstance = new MockEventSource(`/internal/uploads/${files[0].id}/status`, { - withCredentials: true, - }); +// const renderDocumentViewer = (files, isFileUploading = false) => { +// renderWithProviders(); +// return mockEventSource; +// }; - // Simulate different statuses - await act(async () => { - eventSourceInstance.sendMessage(UPLOAD_SCAN_STATUS.PROCESSING); - }); - expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.SCANNING)).toBeInTheDocument(); +// const testFileStatusMock = { +// id: '1', +// filename: 'Test File 1.pdf', +// contentType: 'application/pdf', +// url: samplePDF, +// createdAt: '2021-06-15T15:09:26.979879Z', +// status: undefined, +// }; - await act(async () => { - eventSourceInstance.sendMessage(UPLOAD_SCAN_STATUS.CLEAN); - }); - expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW)).toBeInTheDocument(); +// const testCases = [ +// { +// name: 'Uploading displays when file is in the upload status', +// files: [testFileStatusMock], +// isFileUploading: true, +// simulateStatus: UPLOAD_SCAN_STATUS.UPLOADING, +// expectedText: 'Uploading', +// }, +// { +// name: 'Scanning displays scanning status correctly', +// files: [{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.PROCESSING }], +// simulateStatus: UPLOAD_SCAN_STATUS.PROCESSING, +// expectedText: 'Scanning', +// }, +// { +// name: 'Establishing document for view displays establishing status correctly', +// files: [{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.CLEAN }], +// simulateStatus: UPLOAD_SCAN_STATUS.CLEAN, +// expectedText: 'Establishing document for view', +// }, +// { +// name: 'shows error for infected file', +// files: [{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.INFECTED }], +// simulateStatus: UPLOAD_SCAN_STATUS.INFECTED, +// expectedText: 'Ask for a new file', +// }, +// ]; - await act(async () => { - eventSourceInstance.sendMessage(UPLOAD_SCAN_STATUS.INFECTED); - }); - expect( - screen.getByText( - 'Our antivirus software flagged this file as a security risk. Contact the service member. Ask them to upload a photo of the original document instead.', - ), - ).toBeInTheDocument(); - }); +// testCases.forEach(({ name, files, isFileUploading, simulateStatus, expectedText }) => { +// it(name, async () => { +// const eventSource = renderDocumentViewer(files, isFileUploading); +// act(() => { +// eventSource.simulateMessage(simulateStatus); +// }); +// await waitFor(() => { +// expect(screen.getByText(expectedText)).toBeInTheDocument(); +// // expect(screen.getByTestId('documentStatusMessage')).toHaveTextContent(expectedText); +// }); +// }); +// }); - it('displays File Not Found alert when no selectedFile', () => { - render(); - expect(screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.FILE_NOT_FOUND)).toBeInTheDocument(); - }); -}); +// it('displays uploading status correctly', async () => { +// renderDocumentViewer(testFileStatusMock, true); +// await waitFor(() => { +// expect(screen.getByText('Uploading')).toBeInTheDocument(); +// }); +// }); + +// it('displays file not found status correctly', async () => { +// renderDocumentViewer([]); +// await waitFor(() => { +// expect(screen.getByText(/File not found/i)).toBeInTheDocument(); +// }); +// }); +// }); diff --git a/src/pages/Office/PPM/ReviewDocuments/ReviewDocuments.test.jsx b/src/pages/Office/PPM/ReviewDocuments/ReviewDocuments.test.jsx index ec2f277d650..9685d68dc01 100644 --- a/src/pages/Office/PPM/ReviewDocuments/ReviewDocuments.test.jsx +++ b/src/pages/Office/PPM/ReviewDocuments/ReviewDocuments.test.jsx @@ -34,6 +34,12 @@ jest.mock('react-router-dom', () => ({ useNavigate: () => mockNavigate, })); +global.EventSource = jest.fn().mockImplementation(() => ({ + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + close: jest.fn(), +})); + const mockPatchWeightTicket = jest.fn(); const mockPatchProGear = jest.fn(); const mockPatchExpense = jest.fn(); diff --git a/src/pages/Office/PaymentRequestReview/PaymentRequestReview.test.jsx b/src/pages/Office/PaymentRequestReview/PaymentRequestReview.test.jsx index f95bd113559..f97ad6da589 100644 --- a/src/pages/Office/PaymentRequestReview/PaymentRequestReview.test.jsx +++ b/src/pages/Office/PaymentRequestReview/PaymentRequestReview.test.jsx @@ -16,6 +16,12 @@ jest.mock('react-router-dom', () => ({ useNavigate: () => jest.fn(), })); +global.EventSource = jest.fn().mockImplementation(() => ({ + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + close: jest.fn(), +})); + const mockPDFUpload = { contentType: 'application/pdf', createdAt: '2020-09-17T16:00:48.099137Z', diff --git a/src/pages/Office/SupportingDocuments/SupportingDocuments.test.jsx b/src/pages/Office/SupportingDocuments/SupportingDocuments.test.jsx index 3e466e8fabc..81f91f7fc1a 100644 --- a/src/pages/Office/SupportingDocuments/SupportingDocuments.test.jsx +++ b/src/pages/Office/SupportingDocuments/SupportingDocuments.test.jsx @@ -12,6 +12,11 @@ beforeEach(() => { jest.clearAllMocks(); }); +global.EventSource = jest.fn().mockImplementation(() => ({ + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + close: jest.fn(), +})); // prevents react-fileviewer from throwing errors without mocking relevant DOM elements jest.mock('components/DocumentViewer/Content/Content', () => { const MockContent = () =>
Content
; From 065f60821f8364d5c3ec11008807452c2fd86850 Mon Sep 17 00:00:00 2001 From: Samay Sofo Date: Wed, 5 Feb 2025 22:40:45 +0000 Subject: [PATCH 3/8] Added tests for all file upload statuses and code cleanup --- .../DocumentViewer/DocumentViewer.test.jsx | 432 +++++------------- 1 file changed, 126 insertions(+), 306 deletions(-) diff --git a/src/components/DocumentViewer/DocumentViewer.test.jsx b/src/components/DocumentViewer/DocumentViewer.test.jsx index eedcbc49bea..27db5ff3240 100644 --- a/src/components/DocumentViewer/DocumentViewer.test.jsx +++ b/src/components/DocumentViewer/DocumentViewer.test.jsx @@ -1,8 +1,7 @@ /* eslint-disable react/jsx-props-no-spreading */ import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; import DocumentViewer from './DocumentViewer'; import samplePDF from './sample.pdf'; @@ -11,6 +10,8 @@ import samplePNG from './sample2.png'; import sampleGIF from './sample3.gif'; import { bulkDownloadPaymentRequest } from 'services/ghcApi'; +import { UPLOAD_DOC_STATUS, UPLOAD_SCAN_STATUS, UPLOAD_DOC_STATUS_DISPLAY_MESSAGE } from 'shared/constants'; +import { renderWithProviders } from 'testUtils'; const toggleMenuClass = () => { const container = document.querySelector('[data-testid="menuButtonContainer"]'); @@ -18,6 +19,13 @@ const toggleMenuClass = () => { container.className = container.className === 'closed' ? 'open' : 'closed'; } }; +// Mocking necessary functions/module +const mockMutateUploads = jest.fn(); + +jest.mock('@tanstack/react-query', () => ({ + ...jest.requireActual('@tanstack/react-query'), + useMutation: () => ({ mutate: mockMutateUploads }), +})); global.EventSource = jest.fn().mockImplementation(() => ({ addEventListener: jest.fn(), @@ -122,11 +130,7 @@ jest.mock('./Content/Content', () => ({ describe('DocumentViewer component', () => { it('initial state is closed menu and first file selected', async () => { - render( - - - , - ); + renderWithProviders(); const selectedFileTitle = await screen.getAllByTestId('documentTitle')[0]; expect(selectedFileTitle.textContent).toEqual('Test File 4.gif - Added on 16 Jun 2021'); @@ -136,23 +140,14 @@ describe('DocumentViewer component', () => { }); it('renders the file creation date with the correctly sorted props', async () => { - render( - - - , - ); - + renderWithProviders(); const files = screen.getAllByRole('listitem'); expect(files[0].textContent).toContain('Test File 4.gif - Added on 2021-06-16T15:09:26.979879Z'); }); it('renders the title bar with the correct props', async () => { - render( - - - , - ); + renderWithProviders(); const title = await screen.getAllByTestId('documentTitle')[0]; @@ -160,11 +155,7 @@ describe('DocumentViewer component', () => { }); it('handles the open menu button', async () => { - render( - - - , - ); + renderWithProviders(); const openMenuButton = await screen.findByTestId('menuButton'); @@ -175,11 +166,7 @@ describe('DocumentViewer component', () => { }); it('handles the close menu button', async () => { - render( - - - , - ); + renderWithProviders(); // defaults to closed so we need to open it first. const openMenuButton = await screen.findByTestId('menuButton'); @@ -195,12 +182,8 @@ describe('DocumentViewer component', () => { }); it('shows error if file type is unsupported', async () => { - render( - - - , + renderWithProviders( + , ); expect(screen.getByText('id: undefined')).toBeInTheDocument(); @@ -210,38 +193,22 @@ describe('DocumentViewer component', () => { const errorMessageText = 'If your document does not display, please refresh your browser.'; const downloadLinkText = 'Download file'; it('no error message normally', async () => { - render( - - - , - ); + renderWithProviders(); expect(screen.queryByText(errorMessageText)).toBeNull(); }); it('download link normally', async () => { - render( - - - , - ); + renderWithProviders(); expect(screen.getByText(downloadLinkText)).toBeVisible(); }); it('show message on content error', async () => { - render( - - - , - ); + renderWithProviders(); expect(screen.getByText(errorMessageText)).toBeVisible(); }); it('download link on content error', async () => { - render( - - - , - ); + renderWithProviders(); expect(screen.getByText(downloadLinkText)).toBeVisible(); }); }); @@ -257,16 +224,14 @@ describe('DocumentViewer component', () => { data: null, }; - render( - - - , + renderWithProviders( + , ); bulkDownloadPaymentRequest.mockImplementation(() => Promise.resolve(mockResponse)); @@ -280,245 +245,100 @@ describe('DocumentViewer component', () => { }); }); -// describe('Document viewer file upload status', () => { -// let originalEventSource; -// let mockEventSource; - -// const createMockEventSource = () => ({ -// onmessage: null, -// onerror: null, -// close: jest.fn(), -// simulateMessage(eventData) { -// if (this.onmessage) { -// this.onmessage({ data: eventData }); -// } -// }, -// simulateError() { -// if (this.onerror) { -// this.onerror(); -// } -// }, -// }); - -// let setFileStatusCallback; - -// beforeEach(() => { -// jest.spyOn(React, 'useState').mockImplementation((init) => { -// if (init === null) { -// const [state, setState] = React.useState(init); -// setFileStatusCallback = setState; -// return [state, setState]; -// } -// return React.useState(init); -// }); -// }); - -// beforeEach(() => { -// originalEventSource = global.EventSource; -// mockEventSource = createMockEventSource(); -// global.EventSource = jest.fn().mockImplementation(() => mockEventSource); -// }); - -// afterEach(() => { -// global.EventSource = originalEventSource; -// }); - -// const renderDocumentViewer = (files, isFileUploading = false) => { -// renderWithProviders(); -// return mockEventSource; -// }; - -// const testFileStatusMock = { -// id: '1', -// filename: 'test.pdf', -// contentType: 'application/pdf', -// url: samplePDF, -// createdAt: '2021-06-15T15:09:26.979879Z', -// status: undefined, -// }; - -// it('displays uploading status when isFileUploading is true', async () => { -// const files = [ -// { -// id: '1', -// filename: 'test.pdf', -// contentType: 'application/pdf', -// url: samplePDF, -// createdAt: '2023-05-20T12:00:00Z', -// }, -// ]; - -// const { container } = renderDocumentViewer({ files, isFileUploading: true }); - -// await waitFor(() => { -// // Look for the uploading message anywhere in the document -// const uploadingMessage = screen.getByText(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.UPLOADING); -// expect(uploadingMessage).toBeInTheDocument(); - -// // If you want to check if it's inside an Alert component, you can check for the class -// const alert = container.querySelector('.usa-alert'); -// expect(alert).toBeInTheDocument(); -// expect(alert).toContainElement(uploadingMessage); -// }); -// }); - -// it('displays scanning status correctly', async () => { -// const eventSource = renderDocumentViewer([{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.PROCESSING }]); -// act(() => { -// eventSource.simulateMessage(UPLOAD_SCAN_STATUS.PROCESSING); -// }); -// await waitFor(() => { -// expect(screen.getByText('Scanning')).toBeInTheDocument(); -// }); -// }); - -// it('displays establishing document status when file is clean', async () => { -// renderDocumentViewer({ files: [testFileStatusMock] }); - -// act(() => { -// setFileStatusCallback(UPLOAD_SCAN_STATUS.ESTABLISHING); -// }); - -// await waitFor(() => { -// // Use a more flexible text matching -// const statusElement = screen.getByText((content, element) => { -// return element.textContent.includes(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW); -// }); -// expect(statusElement).toBeInTheDocument(); -// }); -// }); - -// it('displays establishing document for view status correctly', async () => { -// const eventSource = renderDocumentViewer([{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.CLEAN }]); -// act(() => { -// // eventSource.simulateMessage(UPLOAD_SCAN_STATUS.CLEAN); -// }); -// await waitFor(() => { -// expect(screen.getByText('Establishing document for view')).toBeInTheDocument(); -// }); -// }); - -// it('shows error for infected file', async () => { -// const eventSource = renderDocumentViewer([{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.INFECTED }]); -// act(() => { -// // eventSource.simulateMessage(UPLOAD_SCAN_STATUS.INFECTED); -// }); -// await waitFor(() => { -// expect(screen.getByText('Ask for a new file')).toBeInTheDocument(); -// }); -// }); - -// it('displays uploading status correctly', async () => { -// renderDocumentViewer(testFileStatusMock, true); -// await waitFor(() => { -// expect(screen.getByText('Uploading')).toBeInTheDocument(); -// }); -// }); - -// it('displays file not found status correctly', async () => { -// renderDocumentViewer([]); -// await waitFor(() => { -// expect(screen.getByText(/File not found/i)).toBeInTheDocument(); -// }); -// }); -// }); - -// describe('Document viewer file upload status', () => { -// let originalEventSource; -// let mockEventSource; - -// const createMockEventSource = () => ({ -// onmessage: null, -// onerror: null, -// close: jest.fn(), -// simulateMessage(eventData) { -// if (this.onmessage) { -// this.onmessage({ data: eventData }); -// } -// }, -// simulateError() { -// if (this.onerror) { -// this.onerror(); -// } -// }, -// }); - -// beforeEach(() => { -// originalEventSource = global.EventSource; -// mockEventSource = createMockEventSource(); -// global.EventSource = jest.fn().mockImplementation(() => mockEventSource); -// }); - -// afterEach(() => { -// global.EventSource = originalEventSource; -// }); - -// const renderDocumentViewer = (files, isFileUploading = false) => { -// renderWithProviders(); -// return mockEventSource; -// }; - -// const testFileStatusMock = { -// id: '1', -// filename: 'Test File 1.pdf', -// contentType: 'application/pdf', -// url: samplePDF, -// createdAt: '2021-06-15T15:09:26.979879Z', -// status: undefined, -// }; - -// const testCases = [ -// { -// name: 'Uploading displays when file is in the upload status', -// files: [testFileStatusMock], -// isFileUploading: true, -// simulateStatus: UPLOAD_SCAN_STATUS.UPLOADING, -// expectedText: 'Uploading', -// }, -// { -// name: 'Scanning displays scanning status correctly', -// files: [{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.PROCESSING }], -// simulateStatus: UPLOAD_SCAN_STATUS.PROCESSING, -// expectedText: 'Scanning', -// }, -// { -// name: 'Establishing document for view displays establishing status correctly', -// files: [{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.CLEAN }], -// simulateStatus: UPLOAD_SCAN_STATUS.CLEAN, -// expectedText: 'Establishing document for view', -// }, -// { -// name: 'shows error for infected file', -// files: [{ ...testFileStatusMock, status: UPLOAD_SCAN_STATUS.INFECTED }], -// simulateStatus: UPLOAD_SCAN_STATUS.INFECTED, -// expectedText: 'Ask for a new file', -// }, -// ]; - -// testCases.forEach(({ name, files, isFileUploading, simulateStatus, expectedText }) => { -// it(name, async () => { -// const eventSource = renderDocumentViewer(files, isFileUploading); -// act(() => { -// eventSource.simulateMessage(simulateStatus); -// }); -// await waitFor(() => { -// expect(screen.getByText(expectedText)).toBeInTheDocument(); -// // expect(screen.getByTestId('documentStatusMessage')).toHaveTextContent(expectedText); -// }); -// }); -// }); - -// it('displays uploading status correctly', async () => { -// renderDocumentViewer(testFileStatusMock, true); -// await waitFor(() => { -// expect(screen.getByText('Uploading')).toBeInTheDocument(); -// }); -// }); - -// it('displays file not found status correctly', async () => { -// renderDocumentViewer([]); -// await waitFor(() => { -// expect(screen.getByText(/File not found/i)).toBeInTheDocument(); -// }); -// }); -// }); +describe('Test documentViewer file upload statuses', () => { + // Trigger status change helper function + const triggerStatusChange = (status, fileId, onStatusChange) => { + // Mocking EventSource + const mockEventSource = jest.fn(); + + global.EventSource = mockEventSource; + + // Create a mock EventSource instance and trigger the onmessage event + const eventSourceMock = { + onmessage: () => { + const event = { data: status }; + onStatusChange(event.data); // Pass status to the callback + }, + close: jest.fn(), + }; + + mockEventSource.mockImplementationOnce(() => eventSourceMock); + + // Trigger the status change (this would simulate the file status update event) + const sse = new EventSource(`/ghc/v1/uploads/${fileId}/status`, { withCredentials: true }); + sse.onmessage({ data: status }); + }; + + it('displays UPLOADING status when file is uploading', async () => { + renderWithProviders(); + // Trigger UPLOADING status change + triggerStatusChange(UPLOAD_DOC_STATUS.UPLOADING, mockFiles[0].id, async () => { + // Wait for the component to update and check that the status is reflected + await waitFor(() => { + expect(screen.getByTestId('documentStatusMessage')).toHaveTextContent( + UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.UPLOADING, + ); + }); + }); + }); + + it('displays SCANNING status when file is scanning', async () => { + renderWithProviders( + , + ); + + // Trigger SCANNING status change + triggerStatusChange(UPLOAD_SCAN_STATUS.PROCESSING, mockFiles[0].id, async () => { + // Wait for the component to update and check that the status is reflected + await waitFor(() => { + expect(screen.getByTestId('documentStatusMessage')).toHaveTextContent( + UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.SCANNING, + ); + }); + }); + }); + + it('displays ESTABLISHING status when file is establishing', async () => { + renderWithProviders( + , + ); + + // Trigger ESTABLISHING status change + triggerStatusChange('CLEAN', mockFiles[0].id, async () => { + // Wait for the component to update and check that the status is reflected + await waitFor(() => { + const docStatus = screen.getByTestId('documentStatusMessage'); + expect(docStatus).toHaveTextContent(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW); + }); + }); + }); + + it('displays FILE_NOT_FOUND status when no file is found', async () => { + const emptyFileList = []; + renderWithProviders( + , + ); + + // Trigger FILE_NOT_FOUND status change (via props) + triggerStatusChange('FILE_NOT_FOUND', '', async () => { + // Wait for the component to update and check that the status is reflected + await waitFor(() => { + const fileNotFoundMessage = screen.getByTestId('documentStatusMessage'); + expect(fileNotFoundMessage).toHaveTextContent(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.FILE_NOT_FOUND); + }); + }); + }); + + it('displays INFECTED status when file is infected', async () => { + renderWithProviders( + , + ); + // Trigger INFECTED status change + triggerStatusChange(UPLOAD_SCAN_STATUS.INFECTED, mockFiles[0].id, async () => { + // Wait for the component to update and check that the status is reflected + await waitFor(() => { + expect(screen.getByText(/Our antivirus software flagged this file as a security risk/i)).toBeInTheDocument(); + }); + }); + }); +}); From 8af2ed33585e0c6c37a801476369eb7695fff198 Mon Sep 17 00:00:00 2001 From: Samay Sofo Date: Thu, 6 Feb 2025 20:01:38 +0000 Subject: [PATCH 4/8] code cleanup --- .../DocumentViewer/DocumentViewer.jsx | 20 +++++--------- .../DocumentViewer/DocumentViewer.test.jsx | 26 +++++++++++++------ src/shared/constants.js | 2 ++ 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/components/DocumentViewer/DocumentViewer.jsx b/src/components/DocumentViewer/DocumentViewer.jsx index d4be15f0d87..cd87efe9894 100644 --- a/src/components/DocumentViewer/DocumentViewer.jsx +++ b/src/components/DocumentViewer/DocumentViewer.jsx @@ -148,6 +148,8 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId, isFileUploadin return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.SCANNING; case UPLOAD_DOC_STATUS.ESTABLISHING: return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW; + case UPLOAD_DOC_STATUS.INFECTED: + return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.INFECTED_FILE_MESSAGE; default: if (!currentSelectedFile) { return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.FILE_NOT_FOUND; @@ -157,21 +159,13 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId, isFileUploadin }; const alertMessage = getStatusMessage(fileStatus, selectedFile); + const alertType = fileStatus && fileStatus === UPLOAD_SCAN_STATUS.INFECTED ? 'error' : 'info'; + const alertHeading = + fileStatus && fileStatus === UPLOAD_SCAN_STATUS.INFECTED ? 'Ask for a new file' : 'Document Status'; if (alertMessage) { return ( - - {alertMessage} - - ); - } - - if (fileStatus === UPLOAD_SCAN_STATUS.INFECTED) { - return ( - - - Our antivirus software flagged this file as a security risk. Contact the service member. Ask them to upload a - photo of the original document instead. - + + {alertMessage} ); } diff --git a/src/components/DocumentViewer/DocumentViewer.test.jsx b/src/components/DocumentViewer/DocumentViewer.test.jsx index 27db5ff3240..9a4a7a222c4 100644 --- a/src/components/DocumentViewer/DocumentViewer.test.jsx +++ b/src/components/DocumentViewer/DocumentViewer.test.jsx @@ -246,6 +246,7 @@ describe('DocumentViewer component', () => { }); describe('Test documentViewer file upload statuses', () => { + const documentStatus = 'Document Status'; // Trigger status change helper function const triggerStatusChange = (status, fileId, onStatusChange) => { // Mocking EventSource @@ -275,7 +276,8 @@ describe('Test documentViewer file upload statuses', () => { triggerStatusChange(UPLOAD_DOC_STATUS.UPLOADING, mockFiles[0].id, async () => { // Wait for the component to update and check that the status is reflected await waitFor(() => { - expect(screen.getByTestId('documentStatusMessage')).toHaveTextContent( + expect(screen.getByTestId('documentAlertHeading')).toHaveTextContent(documentStatus); + expect(screen.getByTestId('documentAlertMessage')).toHaveTextContent( UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.UPLOADING, ); }); @@ -291,7 +293,8 @@ describe('Test documentViewer file upload statuses', () => { triggerStatusChange(UPLOAD_SCAN_STATUS.PROCESSING, mockFiles[0].id, async () => { // Wait for the component to update and check that the status is reflected await waitFor(() => { - expect(screen.getByTestId('documentStatusMessage')).toHaveTextContent( + expect(screen.getByTestId('documentAlertHeading')).toHaveTextContent(documentStatus); + expect(screen.getByTestId('documentAlertMessage')).toHaveTextContent( UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.SCANNING, ); }); @@ -304,11 +307,13 @@ describe('Test documentViewer file upload statuses', () => { ); // Trigger ESTABLISHING status change - triggerStatusChange('CLEAN', mockFiles[0].id, async () => { + triggerStatusChange(UPLOAD_SCAN_STATUS.CLEAN, mockFiles[0].id, async () => { // Wait for the component to update and check that the status is reflected await waitFor(() => { - const docStatus = screen.getByTestId('documentStatusMessage'); - expect(docStatus).toHaveTextContent(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW); + expect(screen.getByTestId('documentAlertHeading')).toHaveTextContent(documentStatus); + expect(screen.getByTestId('documentAlertMessage')).toHaveTextContent( + UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW, + ); }); }); }); @@ -323,8 +328,10 @@ describe('Test documentViewer file upload statuses', () => { triggerStatusChange('FILE_NOT_FOUND', '', async () => { // Wait for the component to update and check that the status is reflected await waitFor(() => { - const fileNotFoundMessage = screen.getByTestId('documentStatusMessage'); - expect(fileNotFoundMessage).toHaveTextContent(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.FILE_NOT_FOUND); + expect(screen.getByTestId('documentAlertHeading')).toHaveTextContent(documentStatus); + expect(screen.getByTestId('documentAlertMessage')).toHaveTextContent( + UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.FILE_NOT_FOUND, + ); }); }); }); @@ -337,7 +344,10 @@ describe('Test documentViewer file upload statuses', () => { triggerStatusChange(UPLOAD_SCAN_STATUS.INFECTED, mockFiles[0].id, async () => { // Wait for the component to update and check that the status is reflected await waitFor(() => { - expect(screen.getByText(/Our antivirus software flagged this file as a security risk/i)).toBeInTheDocument(); + expect(screen.getByTestId('documentAlertHeading')).toHaveTextContent('Ask for a new file'); + expect(screen.getByTestId('documentAlertMessage')).toHaveTextContent( + UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.INFECTED_FILE_MESSAGE, + ); }); }); }); diff --git a/src/shared/constants.js b/src/shared/constants.js index a354a2583f0..b6676bf0011 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -81,6 +81,8 @@ export const UPLOAD_DOC_STATUS_DISPLAY_MESSAGE = { UPLOADING: 'Uploading', SCANNING: 'Scanning', ESTABLISHING_DOCUMENT_FOR_VIEW: 'Establishing Document for View', + INFECTED_FILE_MESSAGE: + 'Our antivirus software flagged this file as a security risk. Contact the service member. Ask them to upload a photo of the original document instead.', }; export const CONUS_STATUS = { From 6ee9676e839b0abc3cbacd32dafc1de3c7ad6f03 Mon Sep 17 00:00:00 2001 From: Samay Sofo Date: Fri, 7 Feb 2025 13:02:35 +0000 Subject: [PATCH 5/8] fixed alert message verbiage --- src/components/DocumentViewer/DocumentViewer.jsx | 2 +- src/components/DocumentViewer/DocumentViewer.test.jsx | 2 +- src/shared/constants.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/DocumentViewer/DocumentViewer.jsx b/src/components/DocumentViewer/DocumentViewer.jsx index cd87efe9894..98ff92ae3c8 100644 --- a/src/components/DocumentViewer/DocumentViewer.jsx +++ b/src/components/DocumentViewer/DocumentViewer.jsx @@ -147,7 +147,7 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId, isFileUploadin case UPLOAD_DOC_STATUS.SCANNING: return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.SCANNING; case UPLOAD_DOC_STATUS.ESTABLISHING: - return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW; + return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEWING; case UPLOAD_DOC_STATUS.INFECTED: return UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.INFECTED_FILE_MESSAGE; default: diff --git a/src/components/DocumentViewer/DocumentViewer.test.jsx b/src/components/DocumentViewer/DocumentViewer.test.jsx index 9a4a7a222c4..f6d8757f7fb 100644 --- a/src/components/DocumentViewer/DocumentViewer.test.jsx +++ b/src/components/DocumentViewer/DocumentViewer.test.jsx @@ -312,7 +312,7 @@ describe('Test documentViewer file upload statuses', () => { await waitFor(() => { expect(screen.getByTestId('documentAlertHeading')).toHaveTextContent(documentStatus); expect(screen.getByTestId('documentAlertMessage')).toHaveTextContent( - UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEW, + UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEWING, ); }); }); diff --git a/src/shared/constants.js b/src/shared/constants.js index b6676bf0011..bdc43c7d035 100644 --- a/src/shared/constants.js +++ b/src/shared/constants.js @@ -80,7 +80,7 @@ export const UPLOAD_DOC_STATUS_DISPLAY_MESSAGE = { FILE_NOT_FOUND: 'File Not Found', UPLOADING: 'Uploading', SCANNING: 'Scanning', - ESTABLISHING_DOCUMENT_FOR_VIEW: 'Establishing Document for View', + ESTABLISHING_DOCUMENT_FOR_VIEWING: 'Establishing document for viewing', INFECTED_FILE_MESSAGE: 'Our antivirus software flagged this file as a security risk. Contact the service member. Ask them to upload a photo of the original document instead.', }; From 9592f1021d8d57f72056ae30c4cee2562628f1c6 Mon Sep 17 00:00:00 2001 From: Samay Sofo Date: Tue, 11 Feb 2025 17:12:20 +0000 Subject: [PATCH 6/8] Fixed bug with switching docs. --- src/components/DocumentViewer/DocumentViewer.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/DocumentViewer/DocumentViewer.jsx b/src/components/DocumentViewer/DocumentViewer.jsx index 98ff92ae3c8..d8f6ebdf84e 100644 --- a/src/components/DocumentViewer/DocumentViewer.jsx +++ b/src/components/DocumentViewer/DocumentViewer.jsx @@ -179,6 +179,7 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId, isFileUploadin const handleSelectFile = (index) => { selectFile(index); + setFileStatus(UPLOAD_DOC_STATUS.ESTABLISHING); closeMenu(); }; From effdab8e226edcbf7a1406f66a8e8451bbb3ef60 Mon Sep 17 00:00:00 2001 From: Samay Sofo Date: Wed, 12 Feb 2025 15:16:52 +0000 Subject: [PATCH 7/8] code refactoring --- src/components/DocumentViewer/DocumentViewer.jsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/components/DocumentViewer/DocumentViewer.jsx b/src/components/DocumentViewer/DocumentViewer.jsx index d8f6ebdf84e..c28661850bf 100644 --- a/src/components/DocumentViewer/DocumentViewer.jsx +++ b/src/components/DocumentViewer/DocumentViewer.jsx @@ -133,9 +133,9 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId, isFileUploadin }, [selectedFile, isFileUploading, isJustUploadedFile]); useEffect(() => { if (fileStatus === UPLOAD_DOC_STATUS.ESTABLISHING) { - new Promise((resolve) => { - setTimeout(resolve, 2000); - }).then(() => setFileStatus(UPLOAD_DOC_STATUS.LOADED)); + setTimeout(() => { + setFileStatus(UPLOAD_DOC_STATUS.LOADED); + }, 2000); } }, [fileStatus]); const fileType = useRef(selectedFile?.contentType); @@ -159,9 +159,8 @@ const DocumentViewer = ({ files, allowDownload, paymentRequestId, isFileUploadin }; const alertMessage = getStatusMessage(fileStatus, selectedFile); - const alertType = fileStatus && fileStatus === UPLOAD_SCAN_STATUS.INFECTED ? 'error' : 'info'; - const alertHeading = - fileStatus && fileStatus === UPLOAD_SCAN_STATUS.INFECTED ? 'Ask for a new file' : 'Document Status'; + const alertType = fileStatus === UPLOAD_SCAN_STATUS.INFECTED ? 'error' : 'info'; + const alertHeading = fileStatus === UPLOAD_SCAN_STATUS.INFECTED ? 'Ask for a new file' : 'Document Status'; if (alertMessage) { return ( From b8cab8573b6c0b0c6e31e6d5c7e69c622f6b8a14 Mon Sep 17 00:00:00 2001 From: Samay Sofo Date: Wed, 12 Feb 2025 21:21:26 +0000 Subject: [PATCH 8/8] updated docviewer unit tests --- .../DocumentViewer/DocumentViewer.test.jsx | 164 +++++++----------- 1 file changed, 65 insertions(+), 99 deletions(-) diff --git a/src/components/DocumentViewer/DocumentViewer.test.jsx b/src/components/DocumentViewer/DocumentViewer.test.jsx index f6d8757f7fb..9de2f71a640 100644 --- a/src/components/DocumentViewer/DocumentViewer.test.jsx +++ b/src/components/DocumentViewer/DocumentViewer.test.jsx @@ -1,6 +1,6 @@ /* eslint-disable react/jsx-props-no-spreading */ import React from 'react'; -import { screen, waitFor } from '@testing-library/react'; +import { screen, waitFor, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import DocumentViewer from './DocumentViewer'; @@ -10,7 +10,7 @@ import samplePNG from './sample2.png'; import sampleGIF from './sample3.gif'; import { bulkDownloadPaymentRequest } from 'services/ghcApi'; -import { UPLOAD_DOC_STATUS, UPLOAD_SCAN_STATUS, UPLOAD_DOC_STATUS_DISPLAY_MESSAGE } from 'shared/constants'; +import { UPLOAD_SCAN_STATUS, UPLOAD_DOC_STATUS_DISPLAY_MESSAGE } from 'shared/constants'; import { renderWithProviders } from 'testUtils'; const toggleMenuClass = () => { @@ -27,12 +27,6 @@ jest.mock('@tanstack/react-query', () => ({ useMutation: () => ({ mutate: mockMutateUploads }), })); -global.EventSource = jest.fn().mockImplementation(() => ({ - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - close: jest.fn(), -})); - beforeEach(() => { jest.clearAllMocks(); }); @@ -245,110 +239,82 @@ describe('DocumentViewer component', () => { }); }); -describe('Test documentViewer file upload statuses', () => { - const documentStatus = 'Document Status'; - // Trigger status change helper function - const triggerStatusChange = (status, fileId, onStatusChange) => { - // Mocking EventSource - const mockEventSource = jest.fn(); - - global.EventSource = mockEventSource; - - // Create a mock EventSource instance and trigger the onmessage event - const eventSourceMock = { - onmessage: () => { - const event = { data: status }; - onStatusChange(event.data); // Pass status to the callback - }, - close: jest.fn(), - }; - - mockEventSource.mockImplementationOnce(() => eventSourceMock); - - // Trigger the status change (this would simulate the file status update event) - const sse = new EventSource(`/ghc/v1/uploads/${fileId}/status`, { withCredentials: true }); - sse.onmessage({ data: status }); - }; +// Mock the EventSource +class MockEventSource { + constructor(url) { + this.url = url; + this.onmessage = null; + } - it('displays UPLOADING status when file is uploading', async () => { - renderWithProviders(); - // Trigger UPLOADING status change - triggerStatusChange(UPLOAD_DOC_STATUS.UPLOADING, mockFiles[0].id, async () => { - // Wait for the component to update and check that the status is reflected - await waitFor(() => { - expect(screen.getByTestId('documentAlertHeading')).toHaveTextContent(documentStatus); - expect(screen.getByTestId('documentAlertMessage')).toHaveTextContent( - UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.UPLOADING, - ); - }); - }); + close() { + this.isClosed = true; + } +} +global.EventSource = MockEventSource; +// Helper function for finding the file status text +const findByTextContent = (text) => { + return screen.getByText((content, node) => { + const hasText = (element) => element.textContent.includes(text); + const nodeHasText = hasText(node); + const childrenDontHaveText = Array.from(node.children).every((child) => !hasText(child)); + return nodeHasText && childrenDontHaveText; }); +}; - it('displays SCANNING status when file is scanning', async () => { - renderWithProviders( - , - ); +describe('Test DocumentViewer File Upload Statuses', () => { + let eventSource; + const renderDocumentViewer = (props) => { + return renderWithProviders(); + }; - // Trigger SCANNING status change - triggerStatusChange(UPLOAD_SCAN_STATUS.PROCESSING, mockFiles[0].id, async () => { - // Wait for the component to update and check that the status is reflected - await waitFor(() => { - expect(screen.getByTestId('documentAlertHeading')).toHaveTextContent(documentStatus); - expect(screen.getByTestId('documentAlertMessage')).toHaveTextContent( - UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.SCANNING, - ); - }); - }); + beforeEach(() => { + eventSource = new MockEventSource(''); + jest.spyOn(global, 'EventSource').mockImplementation(() => eventSource); }); - it('displays ESTABLISHING status when file is establishing', async () => { - renderWithProviders( - , - ); + afterEach(() => { + jest.restoreAllMocks(); + }); - // Trigger ESTABLISHING status change - triggerStatusChange(UPLOAD_SCAN_STATUS.CLEAN, mockFiles[0].id, async () => { - // Wait for the component to update and check that the status is reflected - await waitFor(() => { - expect(screen.getByTestId('documentAlertHeading')).toHaveTextContent(documentStatus); - expect(screen.getByTestId('documentAlertMessage')).toHaveTextContent( - UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEWING, - ); - }); - }); + it('displays Uploading status', () => { + renderDocumentViewer({ files: mockFiles, isFileUploading: true }); + expect(findByTextContent(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.UPLOADING)).toBeInTheDocument(); }); - it('displays FILE_NOT_FOUND status when no file is found', async () => { - const emptyFileList = []; - renderWithProviders( - , - ); + it('displays Scanning status', async () => { + renderDocumentViewer({ files: mockFiles }); + await act(async () => { + eventSource.onmessage({ data: UPLOAD_SCAN_STATUS.PROCESSING }); + }); + await waitFor(() => { + expect(findByTextContent(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.SCANNING)).toBeInTheDocument(); + }); + }); - // Trigger FILE_NOT_FOUND status change (via props) - triggerStatusChange('FILE_NOT_FOUND', '', async () => { - // Wait for the component to update and check that the status is reflected - await waitFor(() => { - expect(screen.getByTestId('documentAlertHeading')).toHaveTextContent(documentStatus); - expect(screen.getByTestId('documentAlertMessage')).toHaveTextContent( - UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.FILE_NOT_FOUND, - ); - }); + it('displays Establishing document for viewing status', async () => { + renderDocumentViewer({ files: mockFiles }); + await act(async () => { + eventSource.onmessage({ data: UPLOAD_SCAN_STATUS.CLEAN }); + }); + await waitFor(() => { + expect( + findByTextContent(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.ESTABLISHING_DOCUMENT_FOR_VIEWING), + ).toBeInTheDocument(); }); }); - it('displays INFECTED status when file is infected', async () => { - renderWithProviders( - , - ); - // Trigger INFECTED status change - triggerStatusChange(UPLOAD_SCAN_STATUS.INFECTED, mockFiles[0].id, async () => { - // Wait for the component to update and check that the status is reflected - await waitFor(() => { - expect(screen.getByTestId('documentAlertHeading')).toHaveTextContent('Ask for a new file'); - expect(screen.getByTestId('documentAlertMessage')).toHaveTextContent( - UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.INFECTED_FILE_MESSAGE, - ); - }); + it('displays infected file message', async () => { + renderDocumentViewer({ files: mockFiles }); + await act(async () => { + eventSource.onmessage({ data: UPLOAD_SCAN_STATUS.INFECTED }); + }); + await waitFor(() => { + expect(findByTextContent(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.INFECTED_FILE_MESSAGE)).toBeInTheDocument(); }); }); + + it('displays File Not Found message when no file is selected', () => { + renderDocumentViewer({ files: [] }); + expect(findByTextContent(UPLOAD_DOC_STATUS_DISPLAY_MESSAGE.FILE_NOT_FOUND)).toBeInTheDocument(); + }); });