@orfeas126/box-ui-elements
Version:
Box UI Elements
1,284 lines (1,110 loc) • 68.8 kB
JavaScript
import * as React from 'react';
import { shallow, mount } from 'enzyme';
import cloneDeep from 'lodash/cloneDeep';
import { ActivitySidebarComponent, activityFeedInlineError } from '../ActivitySidebar';
import ActivitySidebarFilter from '../ActivitySidebarFilter';
import { filterableActivityFeedItems, formattedReplies } from '../fixtures';
import { FEED_ITEM_TYPE_COMMENT } from '../../../constants';
jest.mock('lodash/debounce', () => jest.fn(i => i));
jest.mock('lodash/uniqueId', () => () => 'uniqueId');
// const mockReplace = jest.fn();
// jest.mock('lodash/uniqueId', () => () => 'uniqueId');
const userError = 'Bad box user!';
describe('elements/content-sidebar/ActivitySidebar', () => {
const feedAPI = {
createComment: jest.fn(),
createReply: jest.fn(),
createTaskNew: jest.fn(),
createThreadedComment: jest.fn(),
deleteAnnotation: jest.fn(),
deleteComment: jest.fn(),
deleteReply: jest.fn(),
deleteTaskNew: jest.fn(),
deleteThreadedComment: jest.fn(),
feedItems: jest.fn(),
fetchReplies: jest.fn(),
fetchThreadedComment: jest.fn(),
updateAnnotation: jest.fn(),
updateComment: jest.fn(),
updateFeedItem: jest.fn(),
updateReply: jest.fn(),
updateTaskCollaborator: jest.fn(),
updateTaskNew: jest.fn(),
updateThreadedComment: jest.fn(),
};
const usersAPI = {
get: jest.fn(),
getAvatarUrlWithAccessToken: jest.fn().mockResolvedValue('foo'),
getUser: jest.fn(),
};
const fileCollaboratorsAPI = {
getCollaboratorsWithQuery: jest.fn(),
};
const api = {
getUsersAPI: () => usersAPI,
getFeedAPI: () => feedAPI,
getFileCollaboratorsAPI: () => fileCollaboratorsAPI,
};
const file = {
id: 'I_AM_A_FILE',
file_version: {
id: '123',
},
};
const currentUser = {
id: '123',
name: 'foo bar',
};
const collaborators = {
entries: [
{
id: '1',
name: 'foo',
item: {},
},
],
};
let onError = jest.fn();
const getWrapper = (props = {}) =>
shallow(
<ActivitySidebarComponent
api={api}
currentUser={currentUser}
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;
beforeEach(() => {
jest.spyOn(ActivitySidebarComponent.prototype, 'fetchFeedItems');
wrapper = getWrapper();
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);
});
});
describe('render()', () => {
test('should render the activity feed sidebar', () => {
const wrapper = getWrapper();
expect(wrapper).toMatchSnapshot();
});
});
describe('createTask()', () => {
test('should throw an error if there is not the current user in the state', () => {
const wrapper = getWrapper({ currentUser: undefined });
const instance = wrapper.instance();
expect(() => instance.createTask()).toThrow('Bad box user!');
});
test('should create the task and fetch the feed items', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
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()', () => {
test.each`
hasReplies
${undefined}
${false}
`(
'should call the deleteComment API if it exists when hasReplies prop equals to $hasReplies',
({ hasReplies }) => {
const wrapper = getWrapper({ hasReplies });
const instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
const id = '1';
const permissions = {
can_edit: false,
can_delete: true,
};
instance.deleteComment({ id, permissions });
expect(feedAPI.deleteComment).toBeCalledWith(
file,
id,
permissions,
expect.any(Function),
expect.any(Function),
);
expect(instance.fetchFeedItems).toBeCalled();
},
);
test('should call the deleteThreadedComment API if it exists when hasReplies prop equals to true', () => {
const wrapper = getWrapper({ hasReplies: true });
const instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
const id = '1';
const permissions = {
can_edit: false,
can_delete: true,
};
instance.deleteComment({ id, permissions });
expect(feedAPI.deleteThreadedComment).toBeCalledWith(
file,
id,
permissions,
expect.any(Function),
expect.any(Function),
);
expect(instance.fetchFeedItems).toBeCalled();
});
});
describe('deleteReply()', () => {
test('should call the deleteReply API and call emitAnnotationReplyDeleteEvent', () => {
const mockEmitAnnotationReplyDeleteEvent = jest.fn();
const wrapper = getWrapper({ emitAnnotationReplyDeleteEvent: mockEmitAnnotationReplyDeleteEvent });
const instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
const id = '1';
const parentId = '123';
const permissions = {
can_edit: false,
can_delete: true,
};
instance.deleteReply({ id, parentId, permissions });
expect(feedAPI.deleteReply).toBeCalledWith(
file,
id,
parentId,
permissions,
expect.any(Function),
expect.any(Function),
);
expect(instance.fetchFeedItems).toBeCalled();
expect(mockEmitAnnotationReplyDeleteEvent).toBeCalledWith(id, parentId, true);
});
});
describe('deleteReplySuccessCallback()', () => {
test('should call the feedSuccessCallback and emitAnnotationReplyDeleteEvent', () => {
const mockEmitAnnotationReplyDeleteEvent = jest.fn();
const wrapper = getWrapper({ emitAnnotationReplyDeleteEvent: mockEmitAnnotationReplyDeleteEvent });
const instance = wrapper.instance();
instance.feedSuccessCallback = jest.fn();
const id = '1';
const parentId = '123';
instance.deleteReplySuccessCallback(id, parentId);
expect(instance.feedSuccessCallback).toBeCalled();
expect(mockEmitAnnotationReplyDeleteEvent).toBeCalledWith(id, parentId);
});
});
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;
let onTaskAssignmentUpdate;
beforeEach(() => {
onTaskAssignmentUpdate = jest.fn();
wrapper = getWrapper({ onTaskAssignmentUpdate, file });
instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
instance.feedSuccessCallback = jest.fn();
instance.feedErrorCallback = jest.fn();
});
test('should call the update task assignment API and fetch the items', () => {
instance.updateTaskAssignment('1', '2', 'foo', 'bar');
expect(feedAPI.updateTaskCollaborator).toHaveBeenCalledWith(
file,
'1',
'2',
'foo',
expect.any(Function),
instance.feedErrorCallback,
);
expect(instance.fetchFeedItems).toBeCalled();
const successCallback = feedAPI.updateTaskCollaborator.mock.calls[0][4];
successCallback();
expect(onTaskAssignmentUpdate).toHaveBeenCalledWith('1', '2', 'foo', '123');
});
});
describe('createComment()', () => {
test('should throw an error if missing current user', () => {
const wrapper = getWrapper({ currentUser: null });
const instance = wrapper.instance();
const message = 'foo';
expect(() => instance.createComment(message, true)).toThrow('Bad box user!');
});
test.each`
hasReplies
${undefined}
${false}
`(
'should call the createComment API and fetch the items when hasReplies prop equals to $hasReplies',
({ hasReplies }) => {
const wrapper = getWrapper({ hasReplies });
const instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
const message = 'foo';
const hasMention = true;
instance.createComment(message, hasMention);
expect(feedAPI.createComment).toBeCalledWith(
file,
currentUser,
message,
hasMention,
expect.any(Function),
expect.any(Function),
);
expect(instance.fetchFeedItems).toBeCalled();
},
);
test('should call the createThreadedComment API and fetch the items when hasReplies prop equals to true', () => {
const wrapper = getWrapper({ hasReplies: true });
const instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
const message = 'foo';
const hasMention = true;
instance.createComment(message, hasMention);
expect(feedAPI.createThreadedComment).toBeCalledWith(
file,
currentUser,
message,
expect.any(Function),
expect.any(Function),
);
expect(instance.fetchFeedItems).toBeCalled();
});
});
describe('createReply()', () => {
test('should throw an error if missing current user', () => {
const wrapper = getWrapper({ currentUser: undefined });
const instance = wrapper.instance();
expect(() => instance.createReply('123', FEED_ITEM_TYPE_COMMENT, 'abc', true)).toThrow(userError);
});
test('should call the createReply API, fetch the items and call emitAnnotationReplyCreateEvent', () => {
const mockEmitAnnotationReplyCreateEvent = jest.fn();
const wrapper = getWrapper({ emitAnnotationReplyCreateEvent: mockEmitAnnotationReplyCreateEvent });
const instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
const parentId = '123';
const parentType = FEED_ITEM_TYPE_COMMENT;
const message = 'abc';
instance.setState({
currentUser,
});
instance.createReply(parentId, parentType, message);
expect(feedAPI.createReply).toBeCalledWith(
file,
currentUser,
parentId,
parentType,
message,
expect.any(Function),
expect.any(Function),
);
expect(instance.fetchFeedItems).toBeCalled();
expect(mockEmitAnnotationReplyCreateEvent).toBeCalledWith(
{ tagged_message: message },
'uniqueId',
parentId,
true,
);
});
});
describe('createReplySuccessCallback()', () => {
test('should call the feedSuccessCallback and emitAnnotationReplyCreateEvent', () => {
const mockEmitAnnotationReplyCreateEvent = jest.fn();
const wrapper = getWrapper({ emitAnnotationReplyCreateEvent: mockEmitAnnotationReplyCreateEvent });
const instance = wrapper.instance();
instance.feedSuccessCallback = jest.fn();
const reply = { id: '1', status: 'resolved' };
const parentId = '123';
const eventRequestId = 'comment_123';
instance.createReplySuccessCallback(eventRequestId, parentId, reply);
expect(instance.feedSuccessCallback).toBeCalled();
expect(mockEmitAnnotationReplyCreateEvent).toBeCalledWith(reply, eventRequestId, parentId);
});
});
describe('updateComment()', () => {
test.each`
hasReplies
${undefined}
${false}
`('should call updateComment API when hasReplies prop equals to $hasReplies', ({ hasReplies }) => {
const wrapper = getWrapper({ hasReplies });
const instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
wrapper.instance().updateComment('123', 'hello', undefined, false, {
can_edit: true,
can_delete: true,
});
expect(api.getFeedAPI().updateComment).toBeCalledWith(
file,
'123',
'hello',
false,
{ can_edit: true, can_delete: true },
expect.any(Function),
expect.any(Function),
);
expect(instance.fetchFeedItems).toBeCalled();
});
describe('should call updateThreadedComment API when hasReplies prop equals to true', () => {
test.each`
status | text | expectedStatus | expectedText
${undefined} | ${undefined} | ${undefined} | ${undefined}
${'open'} | ${'foo'} | ${'open'} | ${'foo'}
`('given status=$status and text=$text', ({ status, text, expectedStatus, expectedText }) => {
const wrapper = getWrapper({ hasReplies: true });
const instance = wrapper.instance();
instance.fetchFeedItems = jest.fn();
wrapper.instance().updateComment('123', text, status, false, {
can_edit: true,
can_delete: true,
});
expect(api.getFeedAPI().updateThreadedComment).toBeCalledWith(
file,
'123',
expectedText,
expectedStatus,
{ can_edit: true, can_delete: true },
expect.any(Function),
expect.any(Function),
);
expect(instance.fetchFeedItems).toBeCalled();
});
});
});
describe('updateReply()', () => {
test('should call updateReply API and call emitAnnotationReplyUpdateEvent', () => {
const mockEmitAnnotationReplyUpdateEvent = jest.fn();
const wrapper = getWrapper({ emitAnnotationReplyUpdateEvent: mockEmitAnnotationReplyUpdateEvent });
const instance = wrapper.instance();
const parentId = '123';
const text = 'abc';
const reply = {
id: '1',
permissions: { can_edit: true },
};
instance.fetchFeedItems = jest.fn();
wrapper.instance().updateReply(reply.id, parentId, text, reply.permissions);
expect(api.getFeedAPI().updateReply).toBeCalledWith(
file,
reply.id,
parentId,
text,
reply.permissions,
expect.any(Function),
expect.any(Function),
);
expect(instance.fetchFeedItems).toBeCalled();
expect(mockEmitAnnotationReplyUpdateEvent).toBeCalledWith(
{ id: reply.id, tagged_message: text },
parentId,
true,
);
});
});
describe('updateReplies()', () => {
test('should call updateFeedItem API', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
wrapper.setState({
feedItems: [],
});
const id = '123';
const replies = cloneDeep(formattedReplies);
instance.fetchFeedItems = jest.fn();
instance.updateReplies(id, replies);
expect(api.getFeedAPI().updateFeedItem).toBeCalledWith({ replies }, id);
expect(instance.fetchFeedItems).toBeCalled();
});
test('should disable active item if replies are being hidden and activeFeedEntryId belongs to a reply that is in currently being updated parent', () => {
const historyReplace = jest.fn();
const activeReplyId = '123';
const wrapper = getWrapper({
activeFeedEntryId: activeReplyId,
history: { replace: historyReplace },
});
const instance = wrapper.instance();
instance.getActiveCommentPath = jest.fn();
const itemId = '999';
const lastReplyId = '456';
const replies = [{ id: lastReplyId }];
wrapper.setState({
feedItems: [{ id: itemId, replies: [{ id: activeReplyId }, { id: lastReplyId }], type: 'comment' }],
});
instance.updateReplies(itemId, replies);
expect(instance.getActiveCommentPath).toBeCalledWith();
expect(historyReplace).toBeCalled();
});
});
describe('fetchRepliesForFeedItems()', () => {
test('should not call getActiveFeedEntryData if activeFeedEntryId is not set', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
instance.getActiveFeedEntryData = jest.fn();
instance.fetchRepliesForFeedItems([]);
expect(instance.getActiveFeedEntryData).not.toBeCalled();
});
test('should call getActiveFeedEntryData with given feedItems if activeFeedEntryId is set', () => {
const wrapper = getWrapper({
activeFeedEntryId: '123',
});
const instance = wrapper.instance();
instance.getActiveFeedEntryData = jest.fn().mockImplementation(() => Promise.resolve());
const feedItems = [{ id: '123' }];
instance.fetchRepliesForFeedItems(feedItems);
expect(instance.getActiveFeedEntryData).toBeCalledWith(feedItems);
});
test.each`
id | type | isActiveEntryInFeedResult
${undefined} | ${undefined} | ${true}
${'123'} | ${'comment'} | ${true}
${undefined} | ${undefined} | ${false}
${'123'} | ${'task'} | ${false}
`(
'should not call getFeedItemsWithReplies if isActiveEntryInFeed results with $isActiveEntryInFeedResult and getActiveFeedEntryData resolves with id=$id and type=$type',
async ({ id, type, isActiveEntryInFeedResult }) => {
const wrapper = getWrapper({
activeFeedEntryId: '123',
});
const instance = wrapper.instance();
instance.getFeedItemsWithReplies = jest.fn();
instance.isActiveEntryInFeed = jest.fn().mockImplementation(() => isActiveEntryInFeedResult);
instance.getActiveFeedEntryData = jest.fn().mockImplementation(() => Promise.resolve({ id, type }));
const feedItems = [{ id: '123' }];
await instance.fetchRepliesForFeedItems(feedItems);
expect(instance.getFeedItemsWithReplies).not.toBeCalled();
},
);
test('should call getFeedItemsWithReplies if getActiveFeedEntryData resolves with id and type of feed item that is not the feed ', async () => {
const wrapper = getWrapper({
activeFeedEntryId: '123',
});
const instance = wrapper.instance();
instance.isActiveEntryInFeed = jest.fn().mockImplementation(() => false);
instance.getActiveFeedEntryData = jest
.fn()
.mockImplementation(() => Promise.resolve({ id: '123', type: 'comment' }));
instance.getFeedItemsWithReplies = jest.fn().mockImplementation(() => Promise.resolve());
const feedItems = [{ id: '123', replies: [{ id: '456' }] }];
await instance.fetchRepliesForFeedItems(feedItems);
expect(instance.getFeedItemsWithReplies).toBeCalledWith(feedItems, '123', 'comment');
});
});
describe('updateReplySuccessCallback()', () => {
test('should call the feedSuccessCallback and emitAnnotationReplyUpdateEvent', () => {
const mockEmitAnnotationReplyUpdateEvent = jest.fn();
const wrapper = getWrapper({ emitAnnotationReplyUpdateEvent: mockEmitAnnotationReplyUpdateEvent });
const instance = wrapper.instance();
instance.feedSuccessCallback = jest.fn();
const onSuccess = jest.fn();
const reply = { id: '1', status: 'resolved' };
const parentId = '123';
instance.updateReplySuccessCallback(parentId, onSuccess, reply);
expect(instance.feedSuccessCallback).toBeCalled();
expect(mockEmitAnnotationReplyUpdateEvent).toBeCalledWith(reply, parentId);
expect(onSuccess).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 | repliesEnabled | tasksEnabled | versionsEnabled | uaaIntegrationEnabled | expectedAnnotations | expectedAppActivity | expectedReplies | expectedTasks | expectedVersions | expectedUseUAA
${false} | ${false} | ${false} | ${false} | ${false} | ${false} | ${false} | ${false} | ${false} | ${false} | ${false} | ${false}
${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
`(
'should fetch the feed items based on features: annotationsEnabled=$annotationsEnabled, appActivityEnabled=$appActivityEnabled, repliesEnabled=$repliesEnabled, tasksEnabled=$tasksEnabled, versionsEnabled=$versionsEnabled and uaaIntegrationEnabled=$uaaIntegrationEnabled',
({
annotationsEnabled,
appActivityEnabled,
repliesEnabled,
tasksEnabled,
versionsEnabled,
uaaIntegrationEnabled,
expectedAnnotations,
expectedAppActivity,
expectedReplies,
expectedTasks,
expectedVersions,
expectedUseUAA,
}) => {
wrapper = getWrapper({
features: {
activityFeed: {
annotations: { enabled: annotationsEnabled },
appActivity: { enabled: appActivityEnabled },
uaaIntegration: { enabled: uaaIntegrationEnabled },
},
},
hasReplies: repliesEnabled,
hasTasks: tasksEnabled,
hasVersions: versionsEnabled,
});
instance = wrapper.instance();
instance.errorCallback = jest.fn();
instance.fetchFeedItemsErrorCallback = jest.fn();
instance.fetchFeedItemsSuccessCallback = jest.fn();
instance.logAPIParity = jest.fn();
instance.fetchFeedItems();
expect(feedAPI.feedItems).toHaveBeenCalledWith(
file,
false,
instance.fetchFeedItemsSuccessCallback,
instance.fetchFeedItemsErrorCallback,
instance.errorCallback,
{
shouldShowAnnotations: expectedAnnotations,
shouldShowAppActivity: expectedAppActivity,
shouldShowReplies: expectedReplies,
shouldShowTasks: expectedTasks,
shouldShowVersions: expectedVersions,
shouldUseUAA: expectedUseUAA,
},
expectedUseUAA ? instance.logAPIParity : undefined,
);
},
);
test('should call feedItems with fetchRepliesForFeedItems as success callback instead of fetchFeedItemsSuccessCallback when shouldShowReplies and shouldRefreshCache are true and active comment is set, ', () => {
const shouldShowReplies = true;
const shouldRefreshCache = true;
wrapper = getWrapper({
activeFeedEntryId: '123',
activeFeedEntryType: 'comment',
hasReplies: shouldShowReplies,
});
instance = wrapper.instance();
instance.errorCallback = jest.fn();
instance.fetchFeedItemsErrorCallback = jest.fn();
instance.fetchRepliesForFeedItems = jest.fn();
instance.fetchFeedItems(shouldRefreshCache);
expect(feedAPI.feedItems).toHaveBeenCalledWith(
file,
true,
instance.fetchRepliesForFeedItems,
instance.fetchFeedItemsErrorCallback,
instance.errorCallback,
{
shouldShowAnnotations: false,
shouldShowAppActivity: false,
shouldShowReplies: true,
shouldShowTasks: true,
shouldShowVersions: true,
shouldUseUAA: false,
},
undefined,
);
});
});
describe('fetchFeedItemsSuccessCallback()', () => {
const feedItems = ['foo'];
let instance;
let logger;
let wrapper;
beforeEach(() => {
logger = { onDataReadyMetric: jest.fn(), onReadyMetric: jest.fn() };
wrapper = getWrapper({ logger });
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,
});
});
test('should not call onDataReadyMetric if feedItems is <= 1', () => {
instance.fetchFeedItemsSuccessCallback(feedItems);
expect(logger.onDataReadyMetric).not.toHaveBeenCalled();
});
test('should call onDataReadyMetric if feedItems is > 1', () => {
instance.fetchFeedItemsSuccessCallback(['foo', 'bar']);
expect(logger.onDataReadyMetric).toHaveBeenCalledWith(
{
endMarkName: 'activity_sidebar_data_ready',
startMarkName: 'activity_sidebar_data_loading',
},
file.id,
);
});
});
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('getCommentFeedItemWithReplies()', () => {
test('should return given feedItem with given replies', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const item = { id: '123' };
const replies = [{ id: '456' }];
const expectedItem = { id: '123', replies: [{ id: '456' }] };
expect(instance.getCommentFeedItemWithReplies(item, replies)).toMatchObject(expectedItem);
});
});
describe('getFeedItemsWithReplies()', () => {
test.each`
id | type
${undefined} | ${'comment'}
${'123'} | ${undefined}
${undefined} | ${undefined}
`('should resolve with given feedItems if id=$id and type=$type', async ({ id, type }) => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const feedItems = [{ id: '123' }];
const result = await instance.getFeedItemsWithReplies(feedItems, id, type);
expect(result).toMatchObject(feedItems);
});
test('should call fetchReplies feed api and resolve with updated feedItems', async () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const feedItems = [{ id: '123', type: 'comment' }];
const id = '123';
const type = 'comment';
const replies = [{ id: '456' }, { id: '789' }];
const expectedItem = { id: '123', type: 'comment', replies };
const expectedItems = [expectedItem];
instance.isItemTypeComment = jest.fn().mockImplementation(() => true);
instance.getCommentFeedItemWithReplies = jest.fn().mockImplementation(() => expectedItem);
api.getFeedAPI().fetchReplies = jest
.fn()
.mockImplementationOnce((fileParam, idParam, typeParam, successCallback) => {
successCallback(replies);
});
const result = await instance.getFeedItemsWithReplies(feedItems, id, type);
expect(api.getFeedAPI().fetchReplies).toBeCalledWith(
file,
id,
type,
expect.any(Function),
expect.any(Function),
);
expect(result).toMatchObject(expectedItems);
});
test('should reject with error if fetchReplies is called and fails', async () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const feedItems = [{ id: '123', type: 'comment' }];
const id = '123';
const type = 'comment';
const error = { status: '500' };
api.getFeedAPI().fetchReplies = jest
.fn()
.mockImplementationOnce((fileParam, idParam, typeParam, successCallback, errorCallback) => {
errorCallback(error);
});
await expect(instance.getFeedItemsWithReplies(feedItems, id, type)).rejects.toMatchObject(error);
});
});
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('getApprover()', () => {
let instance;
let wrapper;
test('should get collaborators with groups', () => {
wrapper = getWrapper();
instance = wrapper.instance();
const search = 'Santa Claus';
instance.getApprover(search);
expect(api.getFileCollaboratorsAPI().getCollaboratorsWithQuery).toBeCalledWith(
file.id,
instance.getApproverContactsSuccessCallback,
instance.errorCallback,
search,
{
includeGroups: true,
},
);
});
});
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('getMention()', () => {
let instance;
let wrapper;
test('should get collaborators without groups', () => {
wrapper = getWrapper();
instance = wrapper.instance();
const search = 'Santa Claus';
instance.getMention(search);
expect(api.getFileCollaboratorsAPI().getCollaboratorsWithQuery).toBeCalledWith(
file.id,
instance.getMentionContactsSuccessCallback,
instance.errorCallback,
search,
);
});
});
describe('getFocusableFeedItemById()', () => {
test.each`
feedItems | itemId | expected
${[]} | ${undefined} | ${undefined}
${[]} | ${'123'} | ${undefined}
${[{ id: '123', type: 'comment' }]} | ${'123'} | ${{ id: '123', type: 'comment' }}
${[{ id: '123', type: 'annotation' }]} | ${'123'} | ${{ id: '123', type: 'annotation' }}
${[{ id: '123', type: 'task' }]} | ${'123'} | ${{ id: '123', type: 'task' }}
${[{ id: '123', type: 'app_activity' }]} | ${'123'} | ${undefined}
${[{ id: '123', type: 'comment' }]} | ${'456'} | ${undefined}
`(
'given feedItems=$feedItems and itemId=$itemId should return $expected',
({ feedItems, itemId, expected }) => {
const wrapper = getWrapper();
const instance = wrapper.instance();
expect(instance.getFocusableFeedItemById(feedItems, itemId)).toEqual(expected);
},
);
});
describe('getCommentFeedItemByReplyId()', () => {
test.each`
feedItems | itemId | expected
${[]} | ${'123'} | ${undefined}
${[]} | ${undefined} | ${undefined}
${[{ id: '123', type: 'comment', replies: [{ id: '456' }] }]} | ${'123'} | ${undefined}
${[{ id: '123', type: 'comment', replies: [{ id: '456' }] }]} | ${'999'} | ${undefined}
${[{ id: '123', type: 'comment', replies: [{ id: '456' }] }]} | ${'456'} | ${{ id: '123', type: 'comment', replies: [{ id: '456' }] }}
${[{ id: '123', type: 'comment' }]} | ${'456'} | ${undefined}
${[{ id: '123', type: 'task', replies: [{ id: '456' }] }]} | ${'456'} | ${undefined}
`(
'given feedItems=$feedItems and itemId=$itemId should return $expected',
({ feedItems, itemId, expected }) => {
const wrapper = getWrapper();
const instance = wrapper.instance();
expect(instance.getCommentFeedItemByReplyId(feedItems, itemId)).toEqual(expected);
},
);
});
describe('isActiveEntryInFeed()', () => {
test.each`
getFocusableFeedItemByIdResult | getCommentFeedItemByReplyIdResult | expected
${false} | ${false} | ${false}
${true} | ${false} | ${true}
${false} | ${true} | ${true}
`(
'should return $expected when getFocusableFeedItemByIdResult returns $getFocusableFeedItemByIdResult and getCommentFeedItemByReplyId returns $getCommentFeedItemByReplyIdResult',
({ getFocusableFeedItemByIdResult, getCommentFeedItemByReplyIdResult, expected }) => {
const wrapper = getWrapper();
const instance = wrapper.instance();
instance.getFocusableFeedItemById = jest
.fn()
.mockImplementationOnce(() => getFocusableFeedItemByIdResult);
instance.getCommentFeedItemByReplyId = jest
.fn()
.mockImplementationOnce(() => getCommentFeedItemByReplyIdResult);
expect(instance.isActiveEntryInFeed([], '123')).toEqual(expected);
},
);
});
describe('isItemTypeFocusable()', () => {
test.each`
type | expected
${'annotation'} | ${true}
${'app_activity'} | ${false}
${'comment'} | ${true}
${'file_version'} | ${false}
${'task'} | ${true}
`('should return $expected given type=$type', ({ type, expected }) => {
const wrapper = getWrapper();
const instance = wrapper.instance();
expect(instance.isItemTypeFocusable(type)).toEqual(expected);
});
});
describe('isItemTypeComment()', () => {
test.each`
type | expected
${'annotation'} | ${true}
${'app_activity'} | ${false}
${'comment'} | ${true}
${'file_version'} | ${false}
${'task'} | ${false}
`('should return $expected given type=$type', ({ type, expected }) => {
const wrapper = getWrapper();
const instance = wrapper.instance();
expect(instance.isItemTypeComment(type)).toEqual(expected);
});
});
describe('getActiveFeedEntryData()', () => {
test('should resolve with active feed entry data if active entry is a first level Feed item', async () => {
const wrapper = getWrapper({
activeFeedEntryId: '123',
activeFeedEntryType: 'annotation',
});
const instance = wrapper.instance();
const data = { id: '123', type: 'comment' };
instance.getFocusableFeedItemById = jest.fn().mockImplementation(() => data);
const result = await instance.getActiveFeedEntryData([]);
expect(result).toEqual(data);
});
test('should resolve with active feed entry data if active entry is within replies of any first level Feed items', async () => {
const wrapper = getWrapper({
activeFeedEntryId: '123',
activeFeedEntryType: 'annotation',
});
const instance = wrapper.instance();
const data = { id: '123', type: 'comment' };
instance.getFocusableFeedItemById = jest.fn().mockImplementation(() => undefined);
instance.getCommentFeedItemByReplyId = jest.fn().mockImplementation(() => data);
const result = await instance.getActiveFeedEntryData([]);
expect(result).toEqual(data);
});
test('should call fetchThreadedComment from feed api if the active entry could not be found within feed items', async () => {
const wrapper = getWrapper({
activeFeedEntryId: '123',
activeFeedEntryType: 'comment',
});
const instance = wrapper.instance();
const expectedData = { id: '123', type: 'comment' };
const parentItem = { id: '123', type: 'comment', replies: [{ id: '456' }] };
const feedItems = [parentItem];
instance.getFocusableFeedItemById = jest
.fn()
.mockImplementation(() => parentItem)
.mockImplementationOnce(() => undefined);
instance.getCommentFeedItemByReplyId = jest.fn().mockImplementation(() => undefined);
api.getFeedAPI().fetchThreadedComment = jest
.fn()
.mockImplementationOnce((f, commentId, successCallback) => {
successCallback({ parent: {} });
});
const result = await instance.getActiveFeedEntryData(feedItems);
expect(api.getFeedAPI().fetchThreadedComment).toBeCalledWith(
file,
'123',
expect.any(Function),
expect.any(Function),
);
expect(result).toMatchObject(expectedData);
});
test('if fetchThreadedComment is called unsuccessfuly and error status is 404, should resolve with {}', async () => {
const wrapper = getWrapper({
activeFeedEntryId: '123',
activeFeedEntryType: 'comment',
});
const instance = wrapper.instance();
const feedItems = [{ id: '123', type: 'comment' }];
const error = { status: 404 };
api.getFeedAPI().fetchThreadedComment = jest
.fn()
.mockImplementationOnce((f, commentId, successCallback, errorCallback) => {
errorCallback(error);
});
instance.getFocusableFeedItemById = jest.fn().mockImplementation(() => undefined);
instance.getCommentFeedItemByReplyId = jest.fn().mockImplementation(() => undefined);
const result = await instance.getActiveFeedEntryData(feedItems);
expect(result).toMatchObject({});
});
test('if fetchThreadedComment is called unsuccessfuly and error status != 404, should reject with received error', async () => {
const wrapper = getWrapper({
activeFeedEntryId: '123',
activeFeedEntryType: 'comment',
});
const instance = wrapper.instance();
const feedItems = [{ id: '123', type: 'comment' }];
const error = { status: 500 };
api.getFeedAPI().fetchThreadedComment = jest
.fn()
.mockImplementationOnce((f, commentId, successCallback, errorCallback) => {
errorCallback(error);
});
instance.getFocusableFeedItemById = jest.fn().mockImplementation(() => undefined);
instance.getCommentFeedItemByReplyId = jest.fn().mockImplementation(() => undefined);
await expect(instance.getActiveFeedEntryData(feedItems)).rejects.toMatchObject(error);
});
});
describe('getActiveCommentPath()', () => {
test.each`
commentId | expected
${undefined} | ${'/activity'}
${'123'} | ${'/activity/comments/123'}
${'456'} | ${'/activity/comments/456'}
`('should return $expected given commentId=$commentId', ({ commentId, expected }) => {
const wrapper = getWrapper();
const instance = wrapper.instance();
expect(instance.getActiveCommentPath(commentId)).toEqual(expected);
});
});
describe('getMentionContactsSuccessCallback()', () => {
let instance;
let wrapper;
beforeEach(() => {
wrapper = getWrapper();
instance = wrapper.instance();
});
test('should dinamycally set as false contacts loading state', () => {
instance.setState = jest.fn();
instance.getMentionContactsSuccessCallback(collaborators);
expect(instance.setState).toBeCalledWith(
{
contactsLoaded: false,
},
expect.any(Function),
);
});
test('should set the feedItems in the state', () => {
instance.getMentionContactsSuccessCallback(collaborators);
expect(wrapper.state('contactsLoaded')).toBeTruthy();
expect(wrapper.state('mentionSelectorContacts')).toEqual(collaborators.entries);
});
});
describe('getReplies()', () => {
test('should call fetchReplies API', () => {
const wrapper = getWrapper();
const instance = wrapper.instance();
const itemId = '123';
const itemType = FEED_ITEM_TYPE_COMMENT;
instance.fetchFeedItems = jest.fn();
wrapper.instance().getReplies(itemId, itemType);
expect(api.