box-ui-elements-mlh
Version:
815 lines (699 loc) • 27.5 kB
JavaScript
import React from 'react';
import { shallow } from 'enzyme';
import messages from '../../common/messages';
import { ActivitySidebarComponent, activityFeedInlineError } from '../ActivitySidebar';
const { defaultErrorMaskSubHeaderMessage, currentUserErrorHeaderMessage } = messages;
jest.mock('lodash/debounce', () => jest.fn(i => i));
describe('elements/content-sidebar/ActivitySidebar', () => {
const feedAPI = {
createComment: jest.fn(),
createTaskNew: jest.fn(),
deleteAnnotation: jest.fn(),
deleteComment: jest.fn(),
deleteTaskNew: jest.fn(),
feedItems: jest.fn(),
updateAnnotation: jest.fn(),
updateTaskCollaborator: jest.fn(),
updateTaskNew: jest.fn(),
};
const usersAPI = {
get: jest.fn(),
getAvatarUrlWithAccessToken: jest.fn().mockResolvedValue('foo'),
getUser: jest.fn(),
};
const fileCollaboratorsAPI = {
getFileCollaborators: jest.fn(),
};
const api = {
getUsersAPI: () => usersAPI,
getFeedAPI: () => feedAPI,
getFileCollaboratorsAPI: () => fileCollaboratorsAPI,
};
const file = {
id: 'I_AM_A_FILE',
file_version: {
id: '123',
},
};
let currentUser = {
id: 'foo',
};
const collaborators = {
entries: [
{
id: '1',
name: 'foo',
item: {},
},
],
};
let onError = jest.fn();
const getWrapper = (props = {}) =>
shallow(
<ActivitySidebarComponent
api={api}
file={file}
logger={{ onReadyMetric: jest.fn() }}
onError={onError}
{...props}
/>,
);
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('componentDidMount()', () => {
let wrapper;
let instance;
currentUser = {
id: '123',
};
beforeEach(() => {
jest.spyOn(ActivitySidebarComponent.prototype, 'fetchFeedItems');
jest.spyOn(ActivitySidebarComponent.prototype, 'fetchCurrentUser');
wrapper = getWrapper({
currentUser,
});
instance = wrapper.instance();
});
afterEach(() => {
jest.restoreAllMocks();
});
test('should fetch the file and refresh the cache and fetch the current user', () => {
expect(instance.fetchFeedItems).toHaveBeenCalledWith(true);
expect(instance.fetchCurrentUser).toHaveBeenCalledWith(currentUser);
});
});
describe('render()', () => {
test('should render the activity feed sidebar', () => {
const wrapper = getWrapper();
expect(wrapper).toMatchSnapshot();
});
});
describe('createTask()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
});
test('should throw an error if there is not the current user in the state', () => {
expect(() => instance.createTask()).toThrow('Bad box user!');
});
test('should create the task and fetch the feed items', () => {
wrapper.setState({
currentUser,
});
const message = 'message';
const assignees = ['1', '2'];
const dueAt = 'test';
const taskType = 'GENERAL';
const completionRule = 'ALL_ASSIGNEES';
instance.fetchFeedItems = jest.fn();
instance.createTask(message, assignees, taskType, dueAt, completionRule);
expect(feedAPI.createTaskNew).toHaveBeenCalledWith(
file,
currentUser,
message,
assignees,
taskType,
dueAt,
completionRule,
expect.any(Function),
expect.any(Function),
);
expect(instance.fetchFeedItems).toHaveBeenCalled();
});
});
describe('deleteTask()', () => {
test('should call the deleteTask prop if it exists', () => {
const id = '1;';
const onTaskDelete = jest.fn();
const wrapper = getWrapper({ onTaskDelete });
const instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
instance.deleteTask({ id });
expect(feedAPI.deleteTaskNew).toHaveBeenCalled();
expect(instance.fetchFeedItems).toHaveBeenCalled();
});
});
describe('deleteComment()', () => {
let wrapper;
let instance;
beforeEach(() => {
const onCommentDelete = jest.fn();
wrapper = getWrapper({ onCommentDelete });
instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
});
test('should call the deleteComment prop if it exists', () => {
const id = '1';
const permissions = {
can_edit: false,
can_delete: true,
};
instance.deleteComment({ id, permissions });
expect(feedAPI.deleteComment).toHaveBeenCalled();
expect(instance.fetchFeedItems).toHaveBeenCalled();
});
});
describe('fetchCurrentUser()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.errorCallback = jest.fn();
});
test('should invoke setState() directly if user parameter is not missing', () => {
instance.setState = jest.fn();
instance.fetchCurrentUser(currentUser);
expect(instance.setState).toBeCalledWith({
currentUser,
currentUserError: undefined,
});
});
test('should get the user', () => {
instance.fetchCurrentUser();
expect(usersAPI.getUser).toBeCalled();
});
});
describe('fetchCurrentUserErrorCallback()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.errorCallback = jest.fn();
instance.fetchFeedItems = jest.fn();
instance.fetchCurrentUser = jest.fn();
});
test('should set a maskError if there is an error in fetching the current user', () => {
instance.fetchCurrentUserErrorCallback();
const inlineErrorState = wrapper.state().currentUserError.maskError;
expect(typeof currentUserErrorHeaderMessage).toBe('object');
expect(typeof defaultErrorMaskSubHeaderMessage).toBe('object');
expect(inlineErrorState.errorHeader).toEqual(currentUserErrorHeaderMessage);
expect(inlineErrorState.errorSubHeader).toEqual(defaultErrorMaskSubHeaderMessage);
});
});
describe('feedSuccessCallback()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
});
test('should fetch the feed items', () => {
instance.feedSuccessCallback();
expect(instance.fetchFeedItems).toBeCalled();
});
});
describe('feedErrorCallback()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
instance.errorCallback = jest.fn();
});
test('should invoke the generic error callback and fetch the items', () => {
instance.feedErrorCallback();
expect(instance.errorCallback).toBeCalled();
expect(instance.fetchFeedItems).toBeCalled();
});
});
describe('updateTask()', () => {
let instance;
let wrapper;
const taskObj = {
text: 'foo',
id: 'bar',
};
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
});
test('should call the update task API and fetch the items', () => {
instance.updateTask(taskObj);
expect(feedAPI.updateTaskNew).toBeCalled();
expect(instance.fetchFeedItems).toBeCalled();
});
});
describe('updateTaskAssignment()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
});
test('should call the update task assignment API and fetch the items', () => {
instance.updateTaskAssignment('1', '2', 'foo', 'bar');
expect(feedAPI.updateTaskCollaborator).toBeCalled();
expect(instance.fetchFeedItems).toBeCalled();
});
});
describe('createComment()', () => {
let instance;
let wrapper;
const message = 'foo';
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
});
test('should throw an error if missing current user', () => {
expect(() => instance.createComment(message, true)).toThrow('Bad box user!');
});
test('should call the create comment API and fetch the items', () => {
instance.setState({
currentUser,
});
instance.createComment(message, true);
expect(feedAPI.createComment).toBeCalled();
expect(instance.fetchFeedItems).toBeCalled();
});
});
describe('fetchFeedItems()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
});
test('should fetch the feed items', () => {
instance.fetchFeedItems();
expect(feedAPI.feedItems).toBeCalled();
});
test.each`
annotationsEnabled | appActivityEnabled | expectedAnnotations | expectedAppActivity
${false} | ${false} | ${false} | ${false}
${false} | ${true} | ${false} | ${true}
${true} | ${false} | ${true} | ${false}
${true} | ${true} | ${true} | ${true}
`(
'should fetch the feed items based on features: annotationsEnabled=$annotationsEnabled and appActivityEnabled=$appActivityEnabled',
({ annotationsEnabled, appActivityEnabled, expectedAnnotations, expectedAppActivity }) => {
wrapper = getWrapper({
features: {
activityFeed: {
annotations: { enabled: annotationsEnabled },
appActivity: { enabled: appActivityEnabled },
},
},
});
instance = wrapper.instance();
instance.errorCallback = jest.fn();
instance.fetchFeedItemsErrorCallback = jest.fn();
instance.fetchFeedItemsSuccessCallback = jest.fn();
instance.fetchFeedItems();
expect(feedAPI.feedItems).toHaveBeenCalledWith(
file,
false,
instance.fetchFeedItemsSuccessCallback,
instance.fetchFeedItemsErrorCallback,
instance.errorCallback,
{ shouldShowAnnotations: expectedAnnotations, shouldShowAppActivity: expectedAppActivity },
);
},
);
});
describe('fetchFeedItemsSuccessCallback()', () => {
let instance;
let wrapper;
const feedItems = 'foo';
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.setState = jest.fn();
});
test('should set the feedItems in the state', () => {
instance.fetchFeedItemsSuccessCallback(feedItems);
expect(instance.setState).toBeCalledWith({
feedItems,
activityFeedError: undefined,
});
});
});
describe('fetchFeedItemsErrorCallback()', () => {
let instance;
let wrapper;
const feedItems = 'foo';
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.setState = jest.fn();
});
test('should set the feedItems in the state', () => {
instance.fetchFeedItemsErrorCallback(feedItems);
expect(instance.setState).toBeCalledWith({
feedItems,
activityFeedError: activityFeedInlineError,
});
expect(onError).not.toHaveBeenCalled();
});
test('should call onError if errors is not empty', () => {
instance.fetchFeedItemsErrorCallback(feedItems, []);
expect(onError).not.toHaveBeenCalled();
instance.fetchFeedItemsErrorCallback(feedItems, [{ code: '0' }, { code: '1' }]);
expect(onError).toHaveBeenCalledWith(expect.any(Error), 'fetch_activity_error', {
showNotification: true,
errors: ['0', '1'],
});
});
});
describe('errorCallback()', () => {
let instance;
let wrapper;
let error;
const code = 'some_code';
const contextInfo = {
foo: 'bar',
};
beforeEach(() => {
error = new Error('foo');
onError = jest.fn();
wrapper = getWrapper({
onError,
});
instance = wrapper.instance();
jest.spyOn(global.console, 'error').mockImplementation();
});
afterEach(() => {
jest.restoreAllMocks();
});
test('should log the error', () => {
instance.errorCallback(error, code, contextInfo);
expect(onError).toHaveBeenCalledWith(error, code, contextInfo);
});
});
describe('fetchCurrentUserSuccessCallback()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.setState = jest.fn();
});
test('should set the feedItems in the state', () => {
instance.fetchCurrentUserSuccessCallback(currentUser);
expect(instance.setState).toBeCalledWith({
currentUser,
currentUserError: undefined,
});
});
});
describe('fetchCurrentUserSuccessCallback()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.setState = jest.fn();
});
test('should set the feedItems in the state', () => {
instance.fetchCurrentUserSuccessCallback(currentUser);
expect(instance.setState).toBeCalledWith({
currentUser,
currentUserError: undefined,
});
});
});
describe('getApproverWithQuery()', () => {
let instance;
let wrapper;
let getCollaboratorsSpy;
test('should get collaborators with groups', () => {
wrapper = getWrapper();
instance = wrapper.instance();
getCollaboratorsSpy = jest.spyOn(instance, 'getCollaborators');
const search = 'Santa Claus';
instance.getApproverWithQuery(search);
expect(getCollaboratorsSpy).toBeCalledWith(
instance.getApproverContactsSuccessCallback,
instance.errorCallback,
search,
{ includeGroups: true },
);
expect(fileCollaboratorsAPI.getFileCollaborators).toHaveBeenCalledWith(
file.id,
instance.getApproverContactsSuccessCallback,
instance.errorCallback,
{
filter_term: search,
include_groups: true,
include_uploader_collabs: false,
},
);
});
});
describe('getApproverContactsSuccessCallback()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.setState = jest.fn();
});
test('should set the feedItems in the state', () => {
instance.getApproverContactsSuccessCallback(collaborators);
expect(instance.setState).toBeCalledWith({
approverSelectorContacts: collaborators.entries,
});
});
});
describe('getMentionWithQuery()', () => {
let instance;
let wrapper;
let getCollaboratorsSpy;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
getCollaboratorsSpy = jest.spyOn(instance, 'getCollaborators');
});
test('should get collaborators without groups', () => {
const search = 'Santa Claus';
instance.getMentionWithQuery(search);
expect(getCollaboratorsSpy).toBeCalledWith(
instance.getMentionContactsSuccessCallback,
instance.errorCallback,
search,
);
expect(fileCollaboratorsAPI.getFileCollaborators).toHaveBeenCalledWith(
file.id,
instance.getMentionContactsSuccessCallback,
instance.errorCallback,
{
filter_term: search,
include_groups: false,
include_uploader_collabs: false,
},
);
});
});
describe('getMentionContactsSuccessCallback()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
instance.setState = jest.fn();
});
test('should set the feedItems in the state', () => {
instance.getMentionContactsSuccessCallback(collaborators);
expect(instance.setState).toBeCalledWith({
mentionSelectorContacts: collaborators.entries,
});
});
});
describe('fetchCurrentUserErrorCallback()', () => {
let wrapper;
let instance;
beforeEach(() => {
wrapper = getWrapper({
file,
});
instance = wrapper.instance();
instance.setState = jest.fn();
instance.errorCallback = jest.fn();
});
test('should set the current user error and call the error callback', () => {
instance.fetchCurrentUserErrorCallback({ status: 500 });
expect(instance.setState).toBeCalledWith({
currentUser: undefined,
currentUserError: expect.any(Object),
});
expect(instance.errorCallback).toBeCalled();
});
});
describe('getAvatarUrl()', () => {
let wrapper;
let instance;
beforeEach(() => {
wrapper = getWrapper({
file,
});
instance = wrapper.instance();
});
test('should set the current user error and call the error callback', () => {
const avatarUrl = instance.getAvatarUrl(currentUser.id);
expect(avatarUrl instanceof Promise).toBe(true);
expect(usersAPI.getAvatarUrlWithAccessToken).toBeCalledWith(currentUser.id, file.id);
});
});
describe('getCollaborators()', () => {
let wrapper;
let instance;
let successCb;
let errorCb;
beforeEach(() => {
successCb = jest.fn();
errorCb = jest.fn();
wrapper = getWrapper({
file,
});
instance = wrapper.instance();
});
test('should short circuit if there is no search string', () => {
instance.getCollaborators(successCb, errorCb);
instance.getCollaborators(successCb, errorCb, '');
instance.getCollaborators(successCb, errorCb, ' ');
expect(fileCollaboratorsAPI.getFileCollaborators).not.toHaveBeenCalled();
});
test('should call the file collaborators api', () => {
const searchStr = 'foo';
instance.getCollaborators(successCb, errorCb, searchStr);
expect(fileCollaboratorsAPI.getFileCollaborators).toHaveBeenCalledWith(file.id, successCb, errorCb, {
filter_term: searchStr,
include_groups: false,
include_uploader_collabs: false,
});
});
});
describe('refresh()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
});
test('should fetch the feed items when refresh is called', () => {
const fetchFeedItems = jest.fn();
instance.fetchFeedItems = fetchFeedItems;
instance.refresh();
expect(fetchFeedItems).toHaveBeenCalled();
expect(fetchFeedItems).toHaveBeenCalledWith(true);
});
});
describe('handleAnnotationSelect()', () => {
const annotatorState = { activeAnnotationId: '123' };
const emitAnnotatorActiveChangeEvent = jest.fn();
const getAnnotationsMatchPath = jest.fn().mockReturnValue({ params: { fileVersionId: '456' } });
const getAnnotationsPath = jest.fn().mockReturnValue('/activity/annotations/235/124');
const history = {
push: jest.fn(),
replace: jest.fn(),
};
const onAnnotationSelect = jest.fn();
const getAnnotationWrapper = () =>
getWrapper({
annotatorState,
emitAnnotatorActiveChangeEvent,
file,
getAnnotationsMatchPath,
getAnnotationsPath,
history,
onAnnotationSelect,
});
test('should call emitAnnotatorActiveChangeEvent and onAnnotatorSelect appropriately', () => {
const wrapper = getAnnotationWrapper();
const instance = wrapper.instance();
const annotation = { file_version: { id: '235' }, id: '124' };
instance.handleAnnotationSelect(annotation);
expect(emitAnnotatorActiveChangeEvent).toHaveBeenCalledWith('124');
expect(history.push).toHaveBeenCalledWith('/activity/annotations/235/124');
expect(onAnnotationSelect).toHaveBeenCalledWith(annotation);
});
test('should not call history.push if file versions are the same', () => {
const wrapper = getAnnotationWrapper();
const instance = wrapper.instance();
const annotation = { file_version: { id: '456' }, id: '124' };
instance.handleAnnotationSelect(annotation);
expect(emitAnnotatorActiveChangeEvent).toHaveBeenCalledWith('124');
expect(history.push).not.toHaveBeenCalled();
expect(onAnnotationSelect).toHaveBeenCalledWith(annotation);
});
test('should use current file version if match params returns null', () => {
const wrapper = getAnnotationWrapper();
const instance = wrapper.instance();
const annotation = { file_version: { id: '235' }, id: '124' };
getAnnotationsMatchPath.mockReturnValue({ params: { fileVersionId: undefined } });
instance.handleAnnotationSelect(annotation);
expect(emitAnnotatorActiveChangeEvent).toHaveBeenCalledWith('124');
expect(history.push).toHaveBeenCalledWith('/activity/annotations/235/124');
expect(onAnnotationSelect).toHaveBeenCalledWith(annotation);
});
test('should not call history.push if no file version id on the annotation', () => {
const wrapper = getAnnotationWrapper();
const instance = wrapper.instance();
const annotation = { file_version: null, id: '124' };
getAnnotationsMatchPath.mockReturnValue({ params: { fileVersionId: undefined } });
instance.handleAnnotationSelect(annotation);
expect(emitAnnotatorActiveChangeEvent).toHaveBeenCalledWith('124');
expect(history.push).not.toHaveBeenCalled();
expect(onAnnotationSelect).toHaveBeenCalledWith(annotation);
});
});
describe('handleAnnotationEdit()', () => {
test('should call updateAnnotation API', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
wrapper.instance().handleAnnotationEdit({
id: '123',
text: 'hello',
permissions: {
can_edit: true,
can_delete: true,
},
});
expect(api.getFeedAPI().updateAnnotation).toHaveBeenCalled();
expect(instance.fetchFeedItems).toHaveBeenCalled();
});
});
describe('handleAnnotationDelete()', () => {
test('should call deleteAnnotation API', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
wrapper.instance().handleAnnotationDelete({ id: '123' });
expect(api.getFeedAPI().deleteAnnotation).toBeCalled();
expect(instance.fetchFeedItems).toHaveBeenCalled();
});
});
describe('deleteAnnotationSuccess()', () => {
test('should handle successful annotation deletion', () => {
const mockEmitRemoveEvent = jest.fn();
const mockFeedSuccess = jest.fn();
const wrapper = getWrapper({ emitRemoveEvent: mockEmitRemoveEvent });
const instance = wrapper.instance();
instance.feedSuccessCallback = mockFeedSuccess;
instance.deleteAnnotationSuccess('123');
expect(mockEmitRemoveEvent).toBeCalledWith('123');
expect(mockFeedSuccess).toBeCalled();
});
});
});