box-ui-elements-mlh
Version:
592 lines (498 loc) • 22.9 kB
JavaScript
import React from 'react';
import { shallow } from 'enzyme';
import { FormattedMessage } from 'react-intl';
import messages from '../../common/messages';
import { ContentOpenWithComponent as ContentOpenWith } from '../ContentOpenWith';
import { BOX_EDIT_INTEGRATION_ID, BOX_EDIT_SFC_INTEGRATION_ID } from '../../../constants';
import BoxToolsInstallMessage from '../BoxToolsInstallMessage';
jest.mock('lodash/uniqueId', () => () => 'uniqueId');
const ADOBE_INTEGRATION_ID = '1234';
const BLACKLISTED_ERROR_MESSAGE_KEY = 'boxToolsBlacklistedError';
const BOX_TOOLS_INSTALL_ERROR_MESSAGE_KEY = 'boxToolsInstallErrorMessage';
describe('elements/content-open-with/ContentOpenWith', () => {
const fileId = '1234';
const token = '4321';
let wrapper;
let instance;
const getWrapper = props => shallow(<ContentOpenWith {...props} />);
beforeEach(() => {
wrapper = getWrapper({ fileId, token });
instance = wrapper.instance();
jest.spyOn(global.console, 'error').mockImplementation();
});
afterEach(() => {
jest.restoreAllMocks();
});
describe('componentDidMount()', () => {
test('should fetch Open With data', () => {
instance.fetchOpenWithData = jest.fn();
instance.componentDidMount();
expect(instance.fetchOpenWithData).toHaveBeenCalled();
});
});
describe('componentDidUpdate()', () => {
test('should reset loading state and get Open With data if the file ID has changed', () => {
instance.fetchOpenWithData = jest.fn();
instance.setState = jest.fn();
instance.componentDidUpdate({ fileId: '4321' });
expect(instance.fetchOpenWithData).toHaveBeenCalled();
expect(instance.setState).toHaveBeenCalledWith({ ...instance.initialState });
});
});
describe('isBoxEditIntegration()', () => {
test('should determine if the integration is a Box Edit integration', () => {
expect(instance.isBoxEditIntegration(ADOBE_INTEGRATION_ID)).toBe(false);
expect(instance.isBoxEditIntegration(BOX_EDIT_INTEGRATION_ID)).toBe(true);
expect(instance.isBoxEditIntegration(BOX_EDIT_SFC_INTEGRATION_ID)).toBe(true);
});
});
describe('isBoxEditSFCIntegration()', () => {
test('should determine if the integration is a Box Edit SFC integration', () => {
expect(instance.isBoxEditSFCIntegration(ADOBE_INTEGRATION_ID)).toBe(false);
expect(instance.isBoxEditSFCIntegration(BOX_EDIT_INTEGRATION_ID)).toBe(false);
expect(instance.isBoxEditSFCIntegration(BOX_EDIT_SFC_INTEGRATION_ID)).toBe(true);
});
});
describe('fetchOpenWithData()', () => {
const fileStub = jest.fn();
const openWithStub = jest.fn();
const api = {
getFileAPI: () => ({
getFile: fileStub,
}),
getOpenWithAPI: () => ({
getOpenWithIntegrations: openWithStub,
}),
};
test('should should fetch Open With integrations', () => {
instance = getWrapper({
fileId: '1234',
language: 'en-US',
}).instance();
instance.setState = jest.fn();
instance.api = api;
instance.fetchOpenWithData();
expect(openWithStub).toHaveBeenCalledWith('1234', expect.any(Function), expect.any(Function));
});
});
describe('fetchOpenWithSuccessHandler()', () => {
let mockIntegrations = [];
let boxEditIntegration = {};
const extension = 'pdf';
beforeEach(() => {
instance.setState = jest.fn();
mockIntegrations = [
{
isDisabled: false,
name: 'Adobe',
appIntegrationId: '2',
disabledReasons: [],
},
{
isDisabled: false,
name: 'Google',
appIntegrationId: '1',
disabledReasons: [],
},
];
boxEditIntegration = {
isDisabled: false,
name: 'Open',
appIntegrationId: BOX_EDIT_INTEGRATION_ID,
disabledReasons: [],
};
});
test('should set the state with the new integrations and disable loading', async () => {
instance.getIntegrationFileExtension = jest.fn();
await instance.fetchOpenWithSuccessHandler(mockIntegrations);
expect(instance.setState).toHaveBeenCalledWith({
integrations: mockIntegrations,
isLoading: false,
});
expect(instance.getIntegrationFileExtension).not.toBeCalled();
});
test('should set the disabled reason if we are unable to get the extension before setting state', async () => {
instance.getIntegrationFileExtension = jest.fn().mockRejectedValue(new Error('error'));
instance.isBoxEditAvailable = jest.fn();
instance.canOpenExtensionWithBoxEdit = jest.fn();
await instance.fetchOpenWithSuccessHandler([boxEditIntegration]);
expect(instance.setState).toBeCalled();
expect(instance.isBoxEditAvailable).not.toBeCalled();
expect(instance.canOpenExtensionWithBoxEdit).not.toBeCalled();
});
test('should get the file extension and check box edit for availability and openability before setting state', async () => {
const integrationWithExtension = {
...boxEditIntegration,
extension,
};
instance.getIntegrationFileExtension = jest.fn().mockResolvedValue({ extension });
instance.isBoxEditAvailable = jest.fn().mockResolvedValue(true);
instance.canOpenExtensionWithBoxEdit = jest.fn().mockResolvedValue(true);
await instance.fetchOpenWithSuccessHandler([...mockIntegrations, boxEditIntegration]);
expect(instance.setState).toBeCalledWith({
integrations: [...mockIntegrations, integrationWithExtension],
isLoading: false,
});
expect(instance.isBoxEditAvailable).toBeCalled();
expect(instance.canOpenExtensionWithBoxEdit).toBeCalled();
});
test('should set the disabled reason if Box Tools is not available before setting state', async () => {
instance.getIntegrationFileExtension = jest.fn().mockResolvedValue({ extension });
instance.isBoxEditAvailable = jest.fn().mockRejectedValue(new Error(BOX_TOOLS_INSTALL_ERROR_MESSAGE_KEY));
instance.canOpenExtensionWithBoxEdit = jest.fn().mockResolvedValue();
await instance.fetchOpenWithSuccessHandler([boxEditIntegration]);
expect(instance.setState).toBeCalledWith({
integrations: [
{
...boxEditIntegration,
isDisabled: true,
// eslint-disable-next-line
disabledReasons: [<BoxToolsInstallMessage />],
},
],
isLoading: false,
});
});
test('should set the disabled reason if the file type is black listed by box tools before setting state', async () => {
instance.getIntegrationFileExtension = jest.fn().mockResolvedValue({ extension });
instance.isBoxEditAvailable = jest.fn().mockResolvedValue();
instance.canOpenExtensionWithBoxEdit = jest
.fn()
.mockRejectedValue(new Error(BLACKLISTED_ERROR_MESSAGE_KEY));
await instance.fetchOpenWithSuccessHandler([boxEditIntegration]);
expect(instance.setState).toBeCalledWith({
integrations: [
{
...boxEditIntegration,
isDisabled: true,
// eslint-disable-next-line
disabledReasons: [<FormattedMessage {...messages.boxToolsBlacklistedError} />],
},
],
isLoading: false,
});
});
test('should set the default disabled reason if there was a failure for an unknown reason', async () => {
instance.getIntegrationFileExtension = jest.fn().mockRejectedValue(new Error('foo'));
await instance.fetchOpenWithSuccessHandler([boxEditIntegration]);
expect(instance.setState).toBeCalledWith({
integrations: [
{
...boxEditIntegration,
isDisabled: true,
// eslint-disable-next-line
disabledReasons: [<FormattedMessage {...messages.executeIntegrationOpenWithErrorHeader} />],
},
],
isLoading: false,
});
});
});
describe('getIntegrationFileExtension()', () => {
test('should get the file extension', () => {
const getFileExtensionStub = jest.fn();
instance.api = {
getFileAPI: () => ({ getFileExtension: getFileExtensionStub }),
};
instance.getIntegrationFileExtension();
expect(getFileExtensionStub).toBeCalled();
});
});
describe('isBoxEditAvailable()', () => {
test('should resolve with result of box edit', async () => {
let checkBoxEditAvailabilityStub = jest.fn().mockResolvedValueOnce();
instance.api = {
getBoxEditAPI: () => ({ checkBoxEditAvailability: checkBoxEditAvailabilityStub }),
};
const result = await instance.isBoxEditAvailable();
expect(result).toBe(undefined);
checkBoxEditAvailabilityStub = jest.fn().mockRejectedValue('Not Available!');
try {
await instance.isBoxEditAvailable();
} catch (error) {
expect(typeof error.message).toBe('string');
}
});
});
describe('canOpenExtensionWithBoxEdit()', () => {
test('should resolve with result of box edit', async () => {
let getAppForExtensionStub = jest.fn().mockResolvedValueOnce();
instance.api = {
getBoxEditAPI: () => ({ getAppForExtension: getAppForExtensionStub }),
};
const result = await instance.canOpenExtensionWithBoxEdit('pdf');
expect(result).toBe(undefined);
getAppForExtensionStub = jest.fn().mockRejectedValue('blacklisted!');
try {
await instance.canOpenExtensionWithBoxEdit('js');
} catch (error) {
expect(typeof error.message).toBe('string');
}
});
});
describe('fetchErrorHandler()', () => {
test('should set the error state', () => {
const mockError = new Error();
instance.setState = jest.fn();
instance.fetchErrorHandler(mockError);
expect(instance.setState).toHaveBeenCalledWith({
fetchError: mockError,
isLoading: false,
});
});
});
describe('onIntegrationClick()', () => {
let api;
let displayIntegration;
beforeEach(() => {
const executeStub = jest.fn();
api = {
getAppIntegrationsAPI: () => ({ execute: executeStub }),
};
displayIntegration = {
appIntegrationId: '1',
displayName: 'Adobe Sign',
};
instance.setState = jest.fn();
instance.api = api;
});
it('should open a new window, set state, unload, title, and kick off the integration execution', () => {
instance.window.open = jest.fn().mockReturnValue({
onunload: null,
document: {
title: null,
},
});
instance.onIntegrationClick(displayIntegration);
expect(instance.window.open).toBeCalled();
expect(typeof instance.integrationWindow.onunload).toEqual('function');
expect(instance.integrationWindow.document.title).toEqual(displayIntegration.displayName);
expect(instance.setState).toHaveBeenCalledWith({
shouldRenderLoadingIntegrationPortal: true,
shouldRenderErrorIntegrationPortal: false,
});
expect(api.getAppIntegrationsAPI().execute).toBeCalled();
});
it('should not perform any window management for a box edit integration', () => {
instance.isBoxEditIntegration = jest.fn().mockReturnValue(true);
instance.executeBoxEditErrorHandler = jest.fn();
instance.onIntegrationClick(displayIntegration);
expect(api.getAppIntegrationsAPI().execute).toBeCalled();
expect(instance.setState).not.toBeCalled();
expect(api.getAppIntegrationsAPI().execute).toBeCalledWith(
expect.any(String),
expect.any(String),
expect.any(Function),
instance.executeBoxEditErrorHandler,
);
});
});
describe('cleanupIntegrationWindow()', () => {
it('should clear portal related state', () => {
instance.setState = jest.fn();
instance.cleanupIntegrationWindow();
expect(instance.setState).toHaveBeenCalledWith({
shouldRenderLoadingIntegrationPortal: false,
shouldRenderErrorIntegrationPortal: false,
});
});
});
describe('executeIntegrationSuccessHandler()', () => {
const id = 3;
const executeData = {
method: 'GET',
url: 'foo.com/bar',
};
beforeEach(() => {
instance.isBoxEditIntegration = jest.fn();
instance.executeBoxEditSuccessHandler = jest.fn();
instance.executeOnlineIntegrationSuccessHandler = jest.fn();
instance.onExecute = jest.fn();
});
test('should invoke the box edit success handler if we executed a box edit integration', () => {
instance.isBoxEditIntegration.mockReturnValue(true);
instance.executeIntegrationSuccessHandler(id, executeData);
expect(instance.executeBoxEditSuccessHandler).toBeCalledWith(id, executeData);
});
test('should invoke the online success handler if we executed an online integration', () => {
instance.isBoxEditIntegration.mockReturnValue(false);
instance.executeIntegrationSuccessHandler(id, executeData);
expect(instance.executeOnlineIntegrationSuccessHandler).toBeCalledWith(executeData);
expect(instance.executeBoxEditSuccessHandler).not.toBeCalled();
});
test('should invoke the execute callback', () => {
instance.onExecute = jest.fn();
instance.executeIntegrationSuccessHandler(id, executeData);
expect(instance.onExecute).toBeCalledWith(id);
});
});
describe('executeOnlineIntegrationSuccessHandler()', () => {
test('should set the post data in state for a POST integration', () => {
const executeData = {
method: 'POST',
url: 'foo.com/bar',
};
instance.setState = jest.fn();
instance.executeOnlineIntegrationSuccessHandler(executeData);
expect(instance.setState).toBeCalledWith({
executePostData: executeData,
});
});
test('should null the integrationWindow', () => {
instance.onExecute = jest.fn();
const executeData = {
method: 'GET',
url: 'foo.com/bar',
};
instance.integrationWindow = {
location: null,
opener: 'url',
};
instance.executeOnlineIntegrationSuccessHandler(executeData);
expect(instance.integrationWindow).toEqual(null);
});
test('should throw an error in the default case', () => {
const executeData = {
method: 'CRYPTO',
url: 'foo.com/bar',
};
instance.executeIntegrationErrorHandler = jest.fn();
instance.executeOnlineIntegrationSuccessHandler(executeData);
expect(instance.executeIntegrationErrorHandler).toBeCalled();
});
});
describe('executeBoxEditSuccessHandler()', () => {
test('should use box edit to open the file', () => {
const openFileStub = jest.fn().mockResolvedValue('open');
const authCode = 'abcde';
instance.api = {
getBoxEditAPI: () => ({ openFile: openFileStub }),
};
instance.isBoxEditSFCIntegration = jest.fn().mockReturnValue(true);
const executeData = {
url: `www.box.com/execute?file_id=1&auth_code=${authCode}&other_param=foo`,
};
instance.executeBoxEditSuccessHandler('1234', executeData);
expect(openFileStub).toBeCalledWith(fileId, {
data: {
auth_code: authCode,
token,
token_scope: 'file',
},
});
});
test('should call the onError callback when Box Tools cannot open a file ', async () => {
const onError = jest.fn();
const error = { error: 'error' };
wrapper = getWrapper({ fileId, token, onError });
instance = wrapper.instance();
const openFileStub = jest.fn().mockRejectedValue(error);
const authCode = 'abcde';
instance.api = {
getBoxEditAPI: () => ({ openFile: openFileStub }),
};
instance.isBoxEditSFCIntegration = jest.fn().mockReturnValue(true);
const executeData = {
url: `www.box.com/execute?file_id=1&auth_code=${authCode}&other_param=foo`,
};
await instance.executeBoxEditSuccessHandler('1234', executeData);
expect(onError).toBeCalledWith(error, 'execute_integrations_error', { error });
});
});
describe('onExecuteFormSubmit()', () => {
test('should clear out the form state data', () => {
instance.setState = jest.fn();
instance.onExecuteFormSubmit();
expect(instance.setState).toBeCalledWith({ executePostData: null });
});
});
describe('onExecute()', () => {
test('should call the user provided callback and clear the portal loading state', () => {});
const propFunction = jest.fn();
const id = '1';
instance = getWrapper({ onExecute: propFunction }).instance();
instance.setState = jest.fn();
instance.onExecute(id);
expect(propFunction).toBeCalledWith(id);
expect(instance.setState).toBeCalledWith({
shouldRenderLoadingIntegrationPortal: false,
});
});
describe('executeIntegrationErrorHandler()', () => {
test('should call the user provided callback and set the portal state', () => {
const propFunction = jest.fn();
instance = getWrapper({ onError: propFunction }).instance();
const errorCode = 'foo';
instance.setState = jest.fn();
const error = new Error();
instance.executeIntegrationErrorHandler(error, errorCode);
expect(propFunction).toBeCalledWith(error, errorCode, expect.any(Object));
expect(instance.setState).toBeCalledWith({
shouldRenderLoadingIntegrationPortal: false,
shouldRenderErrorIntegrationPortal: true,
});
});
});
describe('getDisplayIntegration()', () => {
test('should return null iff there is not one integration', () => {
instance.setState({ integrations: null });
const result = instance.getDisplayIntegration();
expect(result).toEqual(null);
instance.setState({ integrations: ['Adobe', 'Google'] });
const multipleResult = instance.getDisplayIntegration();
expect(multipleResult).toEqual(null);
instance.setState({ integrations: [] });
const emptyResult = instance.getDisplayIntegration();
expect(emptyResult).toEqual(null);
});
test('should return the sole integration as the display integration', () => {
instance = getWrapper({ fileId }).instance();
instance.setState({ integrations: ['Adobe'] });
const result = instance.getDisplayIntegration();
expect(result).toEqual('Adobe');
});
});
describe('render()', () => {
test('should render the Open With button when loading', () => {
expect(wrapper).toMatchSnapshot();
});
test('should render the Open With button if there is one or fewer integrations', () => {
instance.setState({
integrations: ['Adobe'],
isLoading: false,
});
expect(wrapper).toMatchSnapshot();
});
test('should render the Open With dropdown if there is more than one integration', () => {
instance.setState({
integrations: ['Adobe', 'Google Suite'],
isLoading: false,
});
expect(wrapper).toMatchSnapshot();
});
test('should render the PortalContainer if the integration is loading', () => {
instance.setState({
integrations: ['Adobe', 'Google Suite'],
shouldRenderLoadingIntegrationPortal: true,
});
expect(wrapper).toMatchSnapshot();
});
test('should render the PortalContainer if the integration is errored', () => {
instance.setState({
integrations: ['Adobe', 'Google Suite'],
shouldRenderErrorIntegrationPortal: true,
});
expect(wrapper).toMatchSnapshot();
});
test('should render the ExecuteForm if we have data to post', () => {
instance.setState({
integrations: ['Adobe'],
executePostData: {
url: 'foo.com',
params: [{ foo: 'bar' }],
},
});
expect(wrapper).toMatchSnapshot();
});
});
});