huridocs/uwazi

View on GitHub
app/react/Viewer/components/specs/OCRStatus.spec.tsx

Summary

Maintainability
A
0 mins
Test Coverage
/**
 * @jest-environment jest-environment-jsdom
 */
import React from 'react';
import { fireEvent, RenderResult, screen } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import Immutable from 'immutable';
import { FileType } from 'shared/types/fileType';
import { renderConnectedContainer, defaultState } from 'app/utils/test/renderConnected';
import { socket } from 'app/socket';
import { Provider } from 'react-redux';
import { OCRStatus } from '../OCRStatus';
import * as ocrActions from '../../actions/ocrActions';
import * as documentActions from '../../actions/documentActions';

describe('OCRStatus', () => {
  let file: FileType;
  let store: any;
  let renderResult: RenderResult;
  let configuredStore: any;

  const mockSocketOn: any = {};

  jest.spyOn(socket, 'on').mockImplementation((event: string, callback: any) => {
    mockSocketOn[event] = callback;
  });
  jest.spyOn(socket, 'off').mockImplementation((event: string, callback: any) => {
    mockSocketOn[event] = callback;
  });

  jest.spyOn(documentActions, 'reloadDocument').mockReturnValue(async () => Promise.resolve());

  jest.spyOn(ocrActions, 'postToOcr').mockResolvedValue();
  jest.spyOn(ocrActions, 'getOcrStatus').mockImplementation(async filename =>
    Promise.resolve({
      status: filename,
      lastUpdated: 1000,
    })
  );

  beforeEach(() => {
    jest.clearAllMocks();
    file = { _id: 'file_id', filename: 'noOCR', entity: 'entitySharedId' };
    store = { ...defaultState };
  });

  const render = (ocrServiceEnabled: boolean, pdf: FileType) => {
    const reduxStore = {
      ...store,
      settings: {
        collection: Immutable.fromJS({ ocrServiceEnabled }),
      },
    };
    ({ renderResult, store: configuredStore } = renderConnectedContainer(
      <OCRStatus file={pdf} />,
      () => reduxStore
    ));
  };

  it('should not try to get the status if the feature is not toggled on', async () => {
    render(false, file);
    expect(ocrActions.getOcrStatus).not.toHaveBeenCalled();
  });

  describe('rendering', () => {
    it('should first render with a loading OCR message', async () => {
      render(true, file);
      expect(await screen.findByText('Loading')).not.toBeNull();
    });
  });

  describe('status', () => {
    it('should render according to the pdf OCR status', async () => {
      file = { filename: 'withOCR' };
      render(true, file);
      expect(await screen.findByText('OCR')).not.toBeNull();
    });

    it('should render the date with the last update', async () => {
      file = { filename: 'inQueue' };
      render(true, file);
      expect(
        await screen.findByText(`Last updated: ${new Date(1000).toLocaleString('en')}`)
      ).not.toBeNull();
    });

    it('should have localization for the last update date format', async () => {
      file = { filename: 'inQueue' };
      store.locale = 'es';
      render(true, file);
      expect(
        await screen.findByText(`Last updated: ${new Date(1000).toLocaleString('es')}`)
      ).not.toBeNull();
    });

    it('should render a button if the file has no OCR', async () => {
      render(true, file);
      expect((await screen.findByRole('button')).textContent).toBe('OCR PDF');
    });

    describe('adding to ocr queue', () => {
      it('should trigger the OCR service when clicking the button', async () => {
        render(true, file);
        const ocrButton: Element = await screen.findByRole('button');
        fireEvent.click(ocrButton);
        expect(ocrActions.postToOcr).toHaveBeenCalledWith(file.filename);
      });

      it('should change to show the file is in the queue inmediatly', async () => {
        render(true, file);
        const ocrButton: Element = await screen.findByRole('button');
        fireEvent.click(ocrButton);
        expect(await screen.findByText('In OCR queue')).not.toBeNull();
      });
    });

    describe('when language is not supported', () => {
      beforeEach(() => {
        jest.spyOn(ocrActions, 'getOcrStatus').mockImplementationOnce(async _filename =>
          Promise.resolve({
            status: 'unsupported_language',
            lastUpdated: undefined,
          })
        );
      });

      it('should show a language not supported message', async () => {
        render(true, { ...file, language: 'other' });
        expect(await screen.findByText('Unsupported OCR language')).not.toBeNull();
      });

      it('should re-request the status if the file changes', async () => {
        render(true, { ...file, language: 'other' });
        expect(await screen.findByText('Unsupported OCR language')).not.toBeNull();
        jest.spyOn(ocrActions, 'getOcrStatus').mockImplementationOnce(async _filename =>
          Promise.resolve({
            status: 'noOCR',
            lastUpdated: undefined,
          })
        );
        renderResult.rerender(
          <Provider store={configuredStore}>
            <OCRStatus file={{ ...file, language: 'eng' }} />
          </Provider>
        );
        expect(await screen.findByText('OCR PDF')).not.toBeNull();
      });
    });
  });

  describe('sockets', () => {
    const renderAndSubmit = async () => {
      render(true, file);
      const ocrButton: Element = await screen.findByRole('button');
      fireEvent.click(ocrButton);

      await new Promise<void>(resolve => {
        setTimeout(() => {
          resolve();
        }, 0);
      });
    };

    it('should listen for the ocr service on submit', async () => {
      await renderAndSubmit();
      expect(socket.on).toHaveBeenCalled();
    });

    it('should change to display that the ocr is done when the service reports ready, and call to replace the file', async () => {
      await renderAndSubmit();
      await act(() => {
        mockSocketOn['ocr:ready']('file_id');
      });
      expect(await screen.findByText('OCR completed')).not.toBeNull();
      expect(documentActions.reloadDocument).toHaveBeenCalledWith('entitySharedId');
    });

    it('should change to display that the ocr has failed when the service reports error', async () => {
      await renderAndSubmit();
      await act(() => {
        mockSocketOn['ocr:error']('file_id');
      });
      expect(await screen.findByText('Could not be processed')).not.toBeNull();
    });

    it('should listen to the ocr service if the document is in the ocr queue when the component loads', async () => {
      render(true, { ...file, filename: 'inQueue' });
      expect(await screen.findByText('In OCR queue')).not.toBeNull();
      expect(socket.on).toHaveBeenCalled();
    });

    it('should ignore service reports for other files', async () => {
      await renderAndSubmit();
      await act(() => {
        mockSocketOn['ocr:ready']('another_file_id');
      });
      expect(await screen.findByText('In OCR queue')).not.toBeNull();
      expect(documentActions.reloadDocument).not.toHaveBeenCalled();
    });

    it('should unsubscribe from socket event when unmounting the component', async () => {
      render(true, file);
      expect((await screen.findByRole('button')).textContent).toBe('OCR PDF');
      renderResult.unmount();
      expect(socket.off).toHaveBeenCalled();
    });
  });
});