UNPKG

box-ui-elements-mlh

Version:
1,241 lines (1,076 loc) 45 kB
import React from 'react'; import noop from 'lodash/noop'; import { shallow } from 'enzyme'; import * as TokenService from '../../../utils/TokenService'; import PreviewMask from '../PreviewMask'; import SidebarUtils from '../../content-sidebar/SidebarUtils'; import { ContentPreviewComponent as ContentPreview } from '../ContentPreview'; import { PREVIEW_FIELDS_TO_FETCH } from '../../../utils/fields'; jest.mock('../../common/Internationalize', () => 'mock-internationalize'); describe('elements/content-preview/ContentPreview', () => { const getWrapper = (props = {}) => shallow(<ContentPreview logger={{ onReadyMetric: jest.fn(), onPreviewMetric: jest.fn() }} {...props} />); const PERFORMANCE_TIME = 100; let props; let file; beforeEach(() => { global.Box = {}; global.Box.Preview = function Preview() { this.updateFileCache = jest.fn(); this.show = jest.fn(); this.updateToken = jest.fn(); this.addListener = jest.fn(); }; global.performance = { now: jest.fn().mockReturnValue(PERFORMANCE_TIME), }; }); afterEach(() => { delete global.Box; }); describe('constructor()', () => { let onReadyMetric; beforeEach(() => { const wrapper = getWrapper(); ({ onReadyMetric } = wrapper.instance().props.logger); }); test('should emit when js loaded', () => { expect(onReadyMetric).toHaveBeenCalledWith({ endMarkName: expect.any(String), }); }); }); describe('componentDidUpdate()', () => { test('should not reload preview if component updates but we should not load preview', async () => { file = { id: '123' }; props = { hasSidebar: true, token: 'token', fileId: file.id, }; const wrapper = getWrapper(props); wrapper.setState({ file }); const instance = wrapper.instance(); instance.shouldLoadPreview = jest.fn().mockReturnValue(false); instance.loadPreview = jest.fn(); wrapper.setProps({ hasSidebar: false, }); expect(instance.loadPreview).toHaveBeenCalledTimes(0); }); test('should destroy preview before attempting to load it', () => { file = { id: '123' }; const wrapper = getWrapper(); const instance = wrapper.instance(); instance.destroyPreview = jest.fn(); instance.shouldLoadPreview = jest.fn().mockReturnValue(true); instance.loadPreview = jest.fn(); wrapper.setState({ file }); expect(instance.destroyPreview).toHaveBeenCalledWith(false); expect(instance.loadPreview).toHaveBeenCalledTimes(1); }); test('should destroy preview and reset selectedVersion state on new fileId', () => { file = { id: '123' }; const wrapper = getWrapper(); const instance = wrapper.instance(); instance.destroyPreview = jest.fn(); instance.fetchFile = jest.fn(); wrapper.setProps({ fileId: '456' }); expect(instance.destroyPreview).toHaveBeenCalledWith(); expect(wrapper.state('selectedVersion')).toBe(undefined); expect(instance.fetchFile).toHaveBeenCalledWith('456'); }); }); describe('shouldLoadPreview()', () => { let wrapper; let instance; beforeEach(() => { wrapper = getWrapper(props); instance = wrapper.instance(); file = { id: '123', file_version: { id: '1' } }; wrapper.setState({ file, }); instance.preview = new global.Box.Preview(); }); test('should return true if file version ID has changed', () => { const oldFile = { id: '123', file_version: { id: '1234' } }; expect(instance.shouldLoadPreview({ file: oldFile })).toBe(true); }); test('should return true if file object has newly been populated', () => { wrapper.setState({ file: { id: '123' } }); expect(instance.shouldLoadPreview({ file: undefined })).toBeTruthy(); }); test('should return false if file has not changed', () => { expect(instance.shouldLoadPreview({ file })).toBe(false); }); test('should return true if the currently-selected version ID has changed', () => { expect(instance.shouldLoadPreview({ selectedVersion: { id: '12345' } })).toBe(true); }); test('should return true if the selected version is missing and the previous selection was an old version', () => { wrapper.setState({ selectedVersion: { id: undefined } }); expect(instance.shouldLoadPreview({ selectedVersion: { id: '12345' } })).toBe(true); }); test('should return false if the selected version is missing but the previous selection was the current version', () => { wrapper.setState({ selectedVersion: { id: undefined } }); expect(instance.shouldLoadPreview({ selectedVersion: { id: '1' } })).toBe(false); }); }); describe('canDownload()', () => { let wrapper; let instance; beforeEach(() => { file = { id: '123', permissions: { can_download: true, }, is_download_available: true, }; }); test('should return true when all conditions are met', () => { wrapper = getWrapper(props); instance = wrapper.instance(); wrapper.setState({ file }); expect(instance.canDownload()).toBeTruthy(); }); test('should return false if canDownload is false', () => { props.canDownload = false; wrapper = getWrapper(props); instance = wrapper.instance(); wrapper.setState({ file }); expect(instance.canDownload()).toBeFalsy(); }); test('should return false if can_download is false', () => { props.canDownload = true; file.permissions.can_download = false; wrapper = getWrapper(props); instance = wrapper.instance(); wrapper.setState({ file }); expect(instance.canDownload()).toBeFalsy(); }); test('should return false if is_download_available is false', () => { props.canDownload = true; file.is_download_available = false; wrapper = getWrapper(props); instance = wrapper.instance(); wrapper.setState({ file }); expect(instance.canDownload()).toBeFalsy(); }); }); describe('handleCanPrint()', () => { beforeEach(() => { file = { id: '123', permissions: { can_download: true, }, is_download_available: true, }; }); test.each([ [true, true], [false, false], ])('should set canPrint to %s when ability to print is %s', (expected, value) => { const wrapper = getWrapper(); const instance = wrapper.instance(); const canPrintMock = jest.fn().mockReturnValue(value); wrapper.setState({ file }); instance.preview = { canPrint: canPrintMock, }; instance.handleCanPrint(); expect(canPrintMock).toBeCalled(); expect(wrapper.state('canPrint')).toEqual(expected); }); it('should show print icon if printCheck is not available', () => { const wrapper = getWrapper(); const instance = wrapper.instance(); instance.preview = {}; instance.destroyPreview = jest.fn(); wrapper.setState({ file }); instance.handleCanPrint(); expect(wrapper.state('canPrint')).toEqual(true); }); }); describe('loadPreview()', () => { beforeEach(() => { // Fresh global preview object global.Box = {}; global.Box.Preview = function Preview() { this.addListener = jest.fn(); this.updateFileCache = jest.fn(); this.show = jest.fn(); this.removeAllListeners = jest.fn(); this.destroy = jest.fn(); }; file = { id: '123' }; props = { onMetric: jest.fn(), token: 'token', fileId: file.id, }; }); test('should bind onPreviewError prop to preview "preview_error" event', async () => { const wrapper = getWrapper({ ...props, onError: jest.fn() }); wrapper.setState({ file }); const instance = wrapper.instance(); instance.onPreviewError = jest.fn(); await instance.loadPreview(); expect(instance.preview.addListener).toHaveBeenCalledWith('preview_error', instance.onPreviewError); }); test('should bind onPreviewMetric prop to preview "preview_metric" event', async () => { const wrapper = getWrapper(props); wrapper.setState({ file }); const instance = wrapper.instance(); instance.onPreviewMetric = jest.fn(); await instance.loadPreview(); expect(instance.preview.addListener).toHaveBeenCalledWith('preview_metric', instance.onPreviewMetric); }); test('should bind onPreviewLoad method to preview "load" event', async () => { const wrapper = getWrapper(props); wrapper.setState({ file }); const instance = wrapper.instance(); await instance.loadPreview(); expect(instance.preview.addListener).toHaveBeenCalledWith('load', instance.onPreviewLoad); }); test('should call preview show with correct params', async () => { const wrapper = getWrapper(props); wrapper.setState({ file }); const instance = wrapper.instance(); await instance.loadPreview(); expect(instance.preview.show).toHaveBeenCalledWith( file.id, expect.any(Function), expect.objectContaining({ container: expect.stringContaining('.bcpr-content'), header: 'none', showDownload: false, showLoading: false, showProgress: false, skipServerUpdate: true, useHotkeys: false, }), ); }); test('should call preview show with file version params if provided', async () => { const wrapper = getWrapper(props); wrapper.setState({ file: { ...file, file_version: { id: '67890' } }, selectedVersion: { id: '12345', }, }); const instance = wrapper.instance(); await instance.loadPreview(); expect(instance.preview.show).toHaveBeenCalledWith( file.id, expect.any(Function), expect.objectContaining({ container: expect.stringContaining('.bcpr-content'), fileOptions: { [file.id]: { fileVersionId: '12345', currentFileVersionId: '67890', }, }, header: 'none', showDownload: false, showLoading: false, showProgress: false, skipServerUpdate: true, useHotkeys: false, }), ); }); test('should call preview show with activeAnnotationId if provided', async () => { const wrapper = getWrapper({ ...props, annotatorState: { activeAnnotationId: '123' } }); wrapper.setState({ file }); const instance = wrapper.instance(); await instance.loadPreview(); expect(instance.preview.show).toHaveBeenCalledWith( file.id, expect.any(Function), expect.objectContaining({ fileOptions: { [file.id]: { annotations: { activeId: '123', }, }, }, }), ); }); test('should call preview show with startAt params if provided', async () => { const wrapper = getWrapper(props); wrapper.setState({ file, startAt: { unit: 'pages', value: 3, }, }); const instance = wrapper.instance(); await instance.loadPreview(); expect(instance.preview.show).toHaveBeenCalledWith( file.id, expect.any(Function), expect.objectContaining({ container: expect.stringContaining('.bcpr-content'), header: 'none', showDownload: false, skipServerUpdate: true, useHotkeys: false, fileOptions: { [file.id]: { startAt: { unit: 'pages', value: 3, }, }, }, }), ); }); test('should use boxAnnotations instance if provided', async () => { const boxAnnotations = jest.fn(); const wrapper = getWrapper({ ...props, boxAnnotations }); wrapper.setState({ file }); const instance = wrapper.instance(); await instance.loadPreview(); expect(instance.preview.show).toHaveBeenCalledWith( file.id, expect.any(Function), expect.objectContaining({ boxAnnotations, }), ); }); test.each` called | showAnnotationsControls ${true} | ${true} ${false} | ${false} `( 'should call onAnnotationCreate $called if showAnnotationsControls is $showAnnotationsControls', async ({ called, showAnnotationsControls }) => { const onAnnotator = jest.fn(); const wrapper = getWrapper({ ...props, showAnnotationsControls, onAnnotator }); wrapper.setState({ file }); const instance = wrapper.instance(); await instance.loadPreview(); if (called) { expect(instance.preview.addListener).toHaveBeenCalledWith('annotator_create', onAnnotator); } else { expect(instance.preview.addListener).not.toHaveBeenCalledWith('annotator_create', onAnnotator); } }, ); }); describe('fetchFile()', () => { let getFileStub; let instance; beforeEach(() => { file = { id: '123' }; props = { token: 'token', fileId: file.id, contentSidebarProps: { hasSkills: true, }, }; const wrapper = getWrapper(props); instance = wrapper.instance(); getFileStub = jest.fn(); instance.api = { getFileAPI: () => ({ getFile: getFileStub, }), }; }); test('should fetch the file with provided success and error callbacks', () => { const success = jest.fn(); const error = jest.fn(); SidebarUtils.canHaveSidebar = jest.fn().mockReturnValueOnce(true); instance.fetchFile(file.id, success, error, { forceFetch: false, refreshCache: true, }); expect(getFileStub).toBeCalledWith(file.id, success, error, { forceFetch: false, refreshCache: true, fields: PREVIEW_FIELDS_TO_FETCH, }); }); test('should fetch the file with default success and error callback', () => { instance.fetchFileSuccessCallback = jest.fn(); instance.fetchFileErrorCallback = jest.fn(); SidebarUtils.canHaveSidebar = jest.fn().mockReturnValueOnce(true); instance.fetchFile(file.id); expect(getFileStub).toBeCalledWith( file.id, instance.fetchFileSuccessCallback, instance.fetchFileErrorCallback, { fields: PREVIEW_FIELDS_TO_FETCH, }, ); }); test('should fetch the file without sidebar fields', () => { instance.fetchFileSuccessCallback = jest.fn(); instance.fetchFileErrorCallback = jest.fn(); SidebarUtils.canHaveSidebar = jest.fn().mockReturnValueOnce(false); instance.fetchFile(file.id); expect(getFileStub).toBeCalledWith( file.id, instance.fetchFileSuccessCallback, instance.fetchFileErrorCallback, { fields: PREVIEW_FIELDS_TO_FETCH, }, ); }); test('should short circuit if there is no fileId', () => { instance.fetchFile(null); expect(getFileStub).not.toBeCalled(); }); }); describe('fetchFileSuccessCallback()', () => { let instance; beforeEach(() => { const wrapper = getWrapper(props); instance = wrapper.instance(); }); test('should set state to the new file', () => { instance.fetchFileSuccessCallback(file); expect(instance.state.file).toEqual(file); expect(instance.state.error).toBeUndefined(); expect(instance.state.isReloadNotificationVisible).toEqual(false); }); test('should set the state to new file if watermarked', () => { const newFile = { ...file }; newFile.watermark_info = { is_watermarked: true }; instance.setState({ file }); instance.fetchFileSuccessCallback(newFile); expect(instance.state.file).toEqual(newFile); expect(instance.state.error).toBeUndefined(); expect(instance.state.isReloadNotificationVisible).toEqual(false); }); test('should not set new file in state if sha1 matches', () => { const newFile = { ...file }; newFile.file_version = { sha1: 'sha' }; file.file_version = { sha1: 'sha' }; instance.setState({ file, }); instance.fetchFileSuccessCallback(newFile); expect(instance.state.file).toEqual(file); }); test('should not set new file in state but show notification if sha1 changes', () => { const newFile = { ...file }; newFile.file_version = { sha1: 'sha1' }; file.file_version = { sha1: 'sha2' }; instance.setState({ file, isFileError: true, isReloadNotificationVisible: true, }); instance.fetchFileSuccessCallback(newFile); expect(instance.stagedFile).toEqual(newFile); expect(instance.state.file).toEqual(file); expect(instance.state.error).toBeUndefined(); expect(instance.state.isReloadNotificationVisible).toBeTruthy(); }); }); describe('fetchFileErrorCallback()', () => { let instance; let error; let onError; beforeEach(() => { onError = jest.fn(); const wrapper = getWrapper({ ...props, onError, }); instance = wrapper.instance(); instance.fetchFile = jest.fn(); error = new Error('foo'); }); test('should set the error state from the error object', () => { instance.fetchFileErrorCallback(error, 'code'); expect(instance.state.error).toEqual({ code: 'code', message: 'foo' }); expect(instance.fetchFile).not.toBeCalled(); expect(instance.file).toBeUndefined(); expect(onError).toHaveBeenCalled(); }); test('should use the code from response if it exists', () => { instance.fetchFileErrorCallback({ code: 'specialCode', message: 'specialMessage' }, 'code'); expect(instance.state.error).toEqual({ code: 'specialCode', message: 'specialMessage' }); expect(instance.fetchFile).not.toBeCalled(); expect(instance.file).toBeUndefined(); expect(onError).toHaveBeenCalled(); }); }); describe('getTotalFileFetchTime()', () => { let instance; const startTime = 1.23; const endTime = 5.46; beforeEach(() => { props = { token: 'token', fileId: file.id, }; const wrapper = getWrapper(props); instance = wrapper.instance(); instance.fetchFileStartTime = startTime; instance.fetchFileEndTime = endTime; }); test('should return the default if no start time', () => { instance.fetchFileStartTime = null; const totalMetrics = instance.getTotalFileFetchTime(); expect(totalMetrics).toEqual(0); }); test('should return the default if no end time', () => { instance.fetchFileEndTime = null; const totalMetrics = instance.getTotalFileFetchTime(); expect(totalMetrics).toEqual(0); }); test('should return the total fetching time', () => { const total = instance.getTotalFileFetchTime(); expect(total).toEqual(4); }); }); describe('addFetchFileTimeToPreviewMetrics()', () => { let instance; const metrics = { conversion: 0, rendering: 100, total: 100, }; const FETCHING_TIME = 200; beforeEach(() => { props = { token: 'token', fileId: file.id, }; const wrapper = getWrapper(props); instance = wrapper.instance(); instance.getTotalFileFetchTime = jest.fn().mockReturnValue(FETCHING_TIME); }); test('should add the total file fetching time to rendering if the file was converted', () => { const totalMetrics = instance.addFetchFileTimeToPreviewMetrics(metrics); const { conversion, rendering } = metrics; const totalRendering = rendering + FETCHING_TIME; expect(instance.getTotalFileFetchTime).toBeCalled(); expect(totalMetrics).toEqual({ conversion, rendering: totalRendering, total: conversion + totalRendering, preload: undefined, }); }); test('should add the total file fetching time to conversion if the file was not converted', () => { const CONVERSION_TIME = 50; const totalMetrics = instance.addFetchFileTimeToPreviewMetrics({ ...metrics, conversion: CONVERSION_TIME, }); const { rendering } = metrics; const totalConversion = CONVERSION_TIME + FETCHING_TIME; expect(instance.getTotalFileFetchTime).toBeCalled(); expect(totalMetrics).toEqual({ conversion: totalConversion, rendering, total: rendering + totalConversion, preload: undefined, }); }); test('should add the total file fetching time to preload if it exists', () => { const PRELOAD_TIME = 20; const totalMetrics = instance.addFetchFileTimeToPreviewMetrics({ ...metrics, preload: PRELOAD_TIME, }); const { conversion, rendering } = metrics; const totalRendering = rendering + FETCHING_TIME; expect(instance.getTotalFileFetchTime).toBeCalled(); expect(totalMetrics).toEqual({ conversion, rendering: totalRendering, total: conversion + totalRendering, preload: PRELOAD_TIME + FETCHING_TIME, }); }); }); describe('onPreviewLoad()', () => { let instance; const data = { foo: 'bar', metrics: { time: { conversion: 5, rendering: 50, total: 150, }, }, }; const totalTimeMetrics = { conversion: 100, rendering: 50, total: 150, }; beforeEach(() => { props = { collection: [{}, {}], fileId: file.id, onLoad: jest.fn(), token: 'token', }; const wrapper = getWrapper(props); instance = wrapper.instance(); instance.preview = {}; instance.focusPreview = jest.fn(); instance.prefetch = jest.fn(); instance.getFileIndex = jest.fn().mockReturnValue(0); instance.addFetchFileTimeToPreviewMetrics = jest.fn().mockReturnValue(totalTimeMetrics); }); test('should modify the timing metrics to add in the total file fetching time', () => { instance.onPreviewLoad(data); expect(instance.addFetchFileTimeToPreviewMetrics).toBeCalledWith(data.metrics.time); expect(props.onLoad).toBeCalledWith({ ...data, metrics: { time: totalTimeMetrics, }, }); }); test('should call prefetch if filesToPrefetch is not empty', () => { instance.onPreviewLoad(data); expect(instance.prefetch).toBeCalled(); }); test('should set isLoading to false', () => { instance.onPreviewLoad(data); expect(instance.state.isLoading).toBe(false); }); }); describe('onPreviewMetric()', () => { let wrapper; let instance; let onPreviewMetric; const data = { foo: 'bar', file_info_time: 0, convert_time: 0, download_response_time: 20, full_document_load_time: 20, value: 40, }; const FETCHING_TIME = 20; beforeEach(() => { props = { token: 'token', fileId: '123', }; wrapper = getWrapper(props); instance = wrapper.instance(); instance.getTotalFileFetchTime = jest.fn().mockReturnValue(FETCHING_TIME); ({ onPreviewMetric } = instance.props.logger); }); test('should add in the total file fetching time to load events', () => { data.event_name = 'load'; instance.onPreviewMetric(data); expect(onPreviewMetric).toBeCalledWith({ ...data, file_info_time: FETCHING_TIME, value: data.value + FETCHING_TIME, }); }); test('should not emit a load time related metric if invalid load time is present', () => { data.event_name = 'load'; data.value = 0; instance.getTotalFileFetchTime = jest.fn().mockReturnValue(0); instance.onPreviewMetric(data); expect(onPreviewMetric).not.toBeCalled(); }); }); describe('render()', () => { test('should render PreviewMask', () => { const wrapper = getWrapper(props); expect(wrapper.find(PreviewMask).exists()).toBe(true); }); test('should render PreviewMask with the current file extension if available', () => { const fileId = '123'; const wrapper = getWrapper({ fileId }); wrapper.setState({ file: { extension: 'pdf', id: fileId } }); expect(wrapper.find(PreviewMask).prop('extension')).toBe('pdf'); }); test('should render PreviewMask with no extension if the file id recently changed', () => { const fileId = '123'; const wrapper = getWrapper({ fileId }); wrapper.setState({ file: { extension: 'pdf', id: fileId } }); wrapper.setProps({ fileId: '456' }); // New file id means the internal file state is stale expect(wrapper.find(PreviewMask).prop('extension')).toBe(''); }); test('should render nothing if there is no fileId', () => { const wrapper = getWrapper({ fileId: null, }); expect(wrapper.getElement()).toBe(null); }); }); describe('loadFileFromStage()', () => { test('should set new file in state if it exists', () => { const wrapper = getWrapper(props); const instance = wrapper.instance(); instance.setState({ isReloadNotificationVisible: true, isFileError: true, }); file = { id: '123' }; instance.stagedFile = file; instance.loadFileFromStage(); expect(instance.state.file).toEqual(file); expect(instance.stagedFile).toBeUndefined(); expect(instance.state.isReloadNotificationVisible).toBeFalsy(); expect(instance.state.error).toBeUndefined(); }); }); describe('closeReloadNotification()', () => { test('should set new file in state if it exists', () => { const wrapper = getWrapper(props); const instance = wrapper.instance(); instance.setState({ isReloadNotificationVisible: true, }); instance.closeReloadNotification(); expect(instance.state.isReloadNotificationVisible).toBeFalsy(); }); }); describe('prefetch()', () => { test('should prefetch files', async () => { props.token = jest.fn(); const wrapper = getWrapper(props); const instance = wrapper.instance(); const options = { refreshCache: false, }; instance.fetchFile = jest.fn(); TokenService.default.cacheTokens = jest.fn().mockReturnValueOnce(Promise.resolve()); await instance.prefetch(['1', '2', '3']); expect(TokenService.default.cacheTokens).toHaveBeenCalledWith(['file_1', 'file_2', 'file_3'], props.token); expect(instance.fetchFile).toHaveBeenCalledTimes(3); expect(instance.fetchFile).toHaveBeenNthCalledWith(1, '1', noop, noop, options); expect(instance.fetchFile).toHaveBeenNthCalledWith(2, '2', noop, noop, options); expect(instance.fetchFile).toHaveBeenNthCalledWith(3, '3', noop, noop, options); }); }); describe('updatePreviewToken()', () => { let instance; const token = 'token'; beforeEach(() => { const wrapper = getWrapper({ token, fileId: 'foo', }); instance = wrapper.instance(); }); test('should update the preview token and not reload', () => { instance.preview = new global.Box.Preview(); instance.updatePreviewToken(); expect(instance.preview.updateToken).toBeCalledWith(token, false); }); }); describe('componentDidUpdate()', () => { let wrapper; let instance; const token = 'token'; beforeEach(() => { wrapper = getWrapper({ token, fileId: 'foo', }); instance = wrapper.instance(); instance.fetchFile = jest.fn(); instance.destroyPreview = jest.fn(); instance.shouldLoadPreview = jest.fn(); instance.updatePreviewToken = jest.fn(); instance.loadPreview = jest.fn(); }); test('should destroy preview and load the file if fileId changed', () => { wrapper.setProps({ fileId: 'bar', }); expect(instance.destroyPreview).toBeCalledTimes(1); expect(instance.fetchFile).toBeCalledTimes(1); }); test('should update the loading state if fileId changes', () => { wrapper.setState({ isLoading: false }); // Simulate existing preview wrapper.setProps({ fileId: 'bar' }); expect(wrapper.state('isLoading')).toBe(true); }); test("should load preview if fileId hasn't changed and shouldLoadPreview returns true", () => { instance.shouldLoadPreview = jest.fn().mockReturnValue(true); wrapper.setProps({ foo: 'bar', }); expect(instance.loadPreview).toBeCalledTimes(1); }); test("should update the loading state if fileId hasn't changed and shouldLoadPreview returns true", () => { instance.shouldLoadPreview = jest.fn().mockReturnValue(true); wrapper.setState({ isLoading: false }); // Simulate existing preview wrapper.setProps({ fileId: 'bar' }); expect(wrapper.state('isLoading')).toBe(true); }); test('should update the preview with the new token if it changes', () => { wrapper.setProps({ token: 'bar', }); expect(instance.updatePreviewToken).toBeCalledTimes(1); }); }); describe('getDerivedStateFromProps()', () => { let wrapper; const token = 'token'; const initialFileId = 'foo'; const newFileId = 'bar'; const currentFileId = 'currentFileId'; const prevFileIdProp = 'prevFileIdProp'; beforeEach(() => { wrapper = getWrapper({ token, fileId: initialFileId, }); expect(wrapper.state(currentFileId)).toBe(initialFileId); }); test('should update the currentFileId in state if the fileId prop changes', () => { wrapper.setProps({ fileId: newFileId, }); expect(wrapper.state(currentFileId)).toBe(newFileId); }); test('should not update the currentFileId in state if the fileId prop stays the same', () => { wrapper.setState({ currentFileId: newFileId, }); expect(wrapper.state(currentFileId)).toBe(newFileId); wrapper.setProps({ fileId: initialFileId, foo: 'baz', }); expect(wrapper.state(currentFileId)).toBe(newFileId); }); test('should update preview if navigation occurs then browser back clicked', () => { // navigation wrapper.setState({ currentFileId: newFileId, }); expect(wrapper.state(prevFileIdProp)).toBe(initialFileId); expect(wrapper.state(currentFileId)).toBe(newFileId); // URL update wrapper.setProps({ fileId: newFileId, foo: 'baz', }); expect(wrapper.state(currentFileId)).toBe(newFileId); expect(wrapper.state(prevFileIdProp)).toBe(newFileId); // browser back wrapper.setProps({ fileId: initialFileId, foo: 'baz', }); expect(wrapper.state(prevFileIdProp)).toBe(initialFileId); expect(wrapper.state(currentFileId)).toBe(initialFileId); }); test('should have the correct state when navigation and props update', () => { // navigation wrapper.setState({ currentFileId: newFileId, }); expect(wrapper.state(prevFileIdProp)).toBe(initialFileId); expect(wrapper.state(currentFileId)).toBe(newFileId); // URL update wrapper.setProps({ fileId: newFileId, }); expect(wrapper.state(currentFileId)).toBe(newFileId); expect(wrapper.state(prevFileIdProp)).toBe(newFileId); // browser back wrapper.setProps({ fileId: initialFileId, }); expect(wrapper.state(prevFileIdProp)).toBe(initialFileId); expect(wrapper.state(currentFileId)).toBe(initialFileId); // browser forward wrapper.setProps({ fileId: newFileId, }); expect(wrapper.state(prevFileIdProp)).toBe(newFileId); expect(wrapper.state(currentFileId)).toBe(newFileId); // browser back wrapper.setProps({ fileId: initialFileId, }); expect(wrapper.state(prevFileIdProp)).toBe(initialFileId); expect(wrapper.state(currentFileId)).toBe(initialFileId); // navigation wrapper.setState({ currentFileId: newFileId, }); expect(wrapper.state(prevFileIdProp)).toBe(initialFileId); expect(wrapper.state(currentFileId)).toBe(newFileId); // URL update wrapper.setProps({ fileId: newFileId, }); expect(wrapper.state(currentFileId)).toBe(newFileId); expect(wrapper.state(prevFileIdProp)).toBe(newFileId); }); }); describe('canAnnotate()', () => { let wrapper; let instance; beforeEach(() => { file = { id: '123', permissions: { can_annotate: true, }, }; }); test('should return true if showAnnotations prop is true and there are annotations edit permissions', () => { wrapper = getWrapper({ ...props, showAnnotations: true }); instance = wrapper.instance(); wrapper.setState({ file }); expect(instance.canAnnotate()).toBeTruthy(); }); test('should return false if showAnnotations prop is false (default is false)', () => { wrapper = getWrapper(props); instance = wrapper.instance(); wrapper.setState({ file }); expect(instance.canAnnotate()).toBeFalsy(); }); test('should return false if can_annotate permission is false', () => { wrapper = getWrapper({ ...props, showAnnotations: true }); wrapper = getWrapper(props); instance = wrapper.instance(); file.permissions.can_annotate = false; wrapper.setState({ file }); expect(instance.canAnnotate()).toBeFalsy(); }); }); describe('canViewAnnotations()', () => { let wrapper; let instance; beforeEach(() => { props.showAnnotations = true; file = { id: '123', permissions: { can_annotate: true, can_view_annotations_all: false, can_view_annotations_self: false, }, }; }); test('should return true if showAnnotations prop is true and has can_annotate permission', () => { wrapper = getWrapper(props); instance = wrapper.instance(); instance.canAnnotate = jest.fn().mockReturnValue(true); wrapper.setState({ file }); expect(instance.canViewAnnotations()).toBeTruthy(); }); test('should return true if showAnnotations prop is true and has can view all annotations', () => { file.permissions = { can_annotate: false, can_view_annotations_all: true, can_view_annotations_self: false, }; wrapper = getWrapper(props); instance = wrapper.instance(); wrapper.setState({ file }); expect(instance.canViewAnnotations()).toBeTruthy(); }); test('should return true if showAnnotations prop is true and has can view self annotations', () => { file.permissions = { can_annotate: false, can_view_annotations_all: false, can_view_annotations_self: true, }; wrapper = getWrapper(props); instance = wrapper.instance(); wrapper.setState({ file }); expect(instance.canViewAnnotations()).toBeTruthy(); }); test('should return false if showAnnotations prop is false', () => { props.showAnnotations = false; wrapper = getWrapper(props); instance = wrapper.instance(); wrapper.setState({ file }); expect(instance.canViewAnnotations()).toBeFalsy(); }); test('should return false if there are no view or edit permissions', () => { wrapper = getWrapper(props); props.showAnnotations = true; file.permissions = { can_annotate: false, can_view_annotations_all: false, can_view_annotations_self: false, }; instance = wrapper.instance(); wrapper.setState({ file }); expect(instance.canViewAnnotations()).toBeFalsy(); }); }); describe('componentWillUnmount()', () => { let wrapper; let instance; beforeEach(() => { wrapper = getWrapper(props, { disableLifecycleMethods: true, }); instance = wrapper.instance(); instance.api = { destroy: jest.fn(), }; instance.destroyPreview = jest.fn(); }); test('shoud destroy the API and preview', () => { instance.componentWillUnmount(); expect(instance.api.destroy).toHaveBeenCalledWith(false); expect(instance.destroyPreview).toHaveBeenCalled(); }); }); describe('handleAnnotationSelect', () => { test.each` annotationFileVersionId | selectedVersionId | locationType | setStateCount ${'123'} | ${'124'} | ${'page'} | ${1} ${'124'} | ${'124'} | ${'page'} | ${0} ${'123'} | ${'124'} | ${''} | ${0} ${undefined} | ${'124'} | ${'page'} | ${0} `( 'should call onVersionChange $onVersionChangeCount times and setState $setStateCount times', ({ annotationFileVersionId, selectedVersionId, locationType, setStateCount }) => { const annotation = { id: '123', file_version: { id: annotationFileVersionId, }, target: { location: { type: locationType, }, }, }; const emit = jest.fn(); const wrapper = getWrapper(); const instance = wrapper.instance(); jest.spyOn(instance, 'getViewer').mockReturnValue({ emit }); wrapper.setState({ selectedVersion: { id: selectedVersionId } }); instance.setState = jest.fn(); instance.handleAnnotationSelect(annotation); expect(instance.setState).toHaveBeenCalledTimes(setStateCount); expect(emit).toBeCalledWith('scrolltoannotation', { id: annotation.id, target: annotation.target }); }, ); }); });