metadata-based-explorer1
Version:
Box UI Elements
1,422 lines (1,271 loc) • 54.4 kB
JavaScript
import commonMessages from '../../elements/common/messages';
import messages from '../messages';
import * as sorter from '../../utils/sorter';
import * as error from '../../utils/error';
import { IS_ERROR_DISPLAYED, TASK_NEW_NOT_STARTED } from '../../constants';
import Feed from '../Feed';
const mockTaskNew = {
created_by: {
type: 'task_collaborator',
target: { name: 'Jay-Z', id: '100' },
id: '000',
role: 'CREATOR',
status: TASK_NEW_NOT_STARTED,
},
created_at: '2019-01-01',
due_at: '2019-02-02',
id: '0',
name: 'task message',
type: 'task',
assigned_to: {
entries: [
{
id: '1',
target: { name: 'Beyonce', id: '2', avatar_url: '', type: 'user' },
status: TASK_NEW_NOT_STARTED,
permissions: {
can_delete: false,
can_update: false,
},
role: 'ASSIGNEE',
type: 'task_collaborator',
},
],
limit: 10,
next_marker: null,
},
permissions: {
can_update: false,
can_delete: false,
can_create_task_collaborator: false,
can_create_task_link: false,
},
task_links: {
entries: [
{
id: '03',
type: 'task_link',
target: {
type: 'file',
id: '4',
},
permissions: {
can_delete: false,
can_update: false,
},
},
],
limit: 1,
next_marker: null,
},
status: TASK_NEW_NOT_STARTED,
};
const mockFirstVersion = {
action: 'upload',
type: 'file_version',
id: 123,
created_at: 'Thu Sep 20 33658 19:45:39 GMT-0600 (CST)',
trashed_at: 1234567891,
modified_at: 1234567891,
modified_by: { name: 'Akon', id: 11 },
};
const mockCurrentVersion = {
action: 'restore',
type: 'file_version',
id: '123',
};
const deleted_version = {
action: 'delete',
type: 'file_version',
id: 234,
created_at: 'Thu Sep 20 33658 19:45:39 GMT-0600 (CST)',
trashed_at: 1234567891,
modified_at: 1234567891,
modified_by: { name: 'Akon', id: 11 },
};
const versions = {
total_count: 1,
entries: [mockFirstVersion, deleted_version],
};
const versionsWithCurrent = {
total_count: 3,
entries: [mockCurrentVersion, mockFirstVersion, deleted_version],
};
jest.mock('lodash/uniqueId', () => () => 'uniqueId');
jest.mock('../tasks/Tasks', () => {
const task = {
type: 'task',
id: '1234',
created_at: 'Thu Sep 25 33658 19:45:39 GMT-0600 (CST)',
modified_at: 'Thu Sep 25 33658 19:46:39 GMT-0600 (CST)',
tagged_message: 'test',
modified_by: { name: 'Jay-Z', id: 10 },
dueAt: 1234567891,
task_assignment_collection: {
entries: [{ assigned_to: { name: 'Akon', id: 11 } }],
total_count: 1,
},
};
return jest.fn().mockImplementation(() => ({
updateTask: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
createTask: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
deleteTask: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
getTasks: jest.fn().mockReturnValue(task),
getAssignments: jest.fn().mockImplementation((id, taskId, successCallback) => {
successCallback({
total_count: 1,
entries: [
{
type: 'task_assignment',
id: '48714317',
assigned_to: {
type: 'user',
id: '3086276240',
name: 'Test User',
login: 'testuser@foo.com',
},
status: 'completed',
},
],
});
}),
}));
});
jest.mock('../tasks/TasksNew', () => {
const task = mockTaskNew;
return jest.fn().mockImplementation(() => ({
createTask: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
updateTask: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
deleteTask: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
getTasksForFile: jest.fn().mockReturnValue({ entries: [task], next_marker: null, limit: 1000 }),
getTask: jest.fn(({ successCallback }) => {
successCallback(task);
}),
}));
});
jest.mock('../tasks/TaskCollaborators', () =>
jest.fn().mockImplementation(() => ({
createTaskCollaborator: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
updateTaskCollaborator: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
deleteTaskCollaborator: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
getTaskCollaborators: jest.fn().mockReturnValue({
entries: [
{
id: '1',
target: { name: 'Beyonce', id: '2', avatar_url: '', type: 'user' },
status: 'NOT_STARTED',
permissions: {
can_delete: false,
can_update: false,
},
role: 'ASSIGNEE',
type: 'task_collaborator',
},
],
next_marker: null,
limit: 1000,
}),
})),
);
jest.mock('../tasks/TaskLinks', () =>
jest.fn().mockImplementation(() => ({
createTaskLink: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
})),
);
jest.mock('../tasks/TaskAssignments', () =>
jest.fn().mockImplementation(() => ({
updateTaskAssignment: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
createTaskAssignment: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
})),
);
jest.mock('../Comments', () =>
jest.fn().mockImplementation(() => ({
getComments: jest.fn().mockReturnValue({
total_count: 1,
entries: [
{
type: 'comment',
id: '123',
created_at: 'Thu Sep 26 33658 19:46:39 GMT-0600 (CST)',
tagged_message: 'test @[123:Jeezy] @[10:Kanye West]',
created_by: { name: 'Akon', id: 11 },
},
],
}),
deleteComment: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
updateComment: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
createComment: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
})),
);
jest.mock('../Versions', () => {
return jest.fn().mockImplementation(() => ({
getVersions: jest.fn(() => mockFirstVersion),
getCurrentVersion: jest.fn(() => mockCurrentVersion),
}));
});
const MOCK_APP_ACTIVITY_ITEM = {
activity_template: {
id: '1',
type: 'activity_template',
},
app: {
icon_url: 'https://some.cdn.com/12345.png',
id: '123456',
name: 'App activities test',
type: 'app',
},
created_by: {
id: '1234556789876',
login: 'some-account@box.com',
name: 'John Doe',
type: 'user',
},
id: '3782',
occurred_at: '2019-02-21T04:00:00Z',
rendered_text: 'You shared this file in <a href="https://some-app.com" rel="noreferrer noopener">Some App</a>',
type: 'app_activity',
};
jest.mock('../AppActivity', () =>
jest.fn().mockImplementation(() => ({
getAppActivity: jest.fn().mockReturnValue({
total_count: 1,
entries: [MOCK_APP_ACTIVITY_ITEM],
}),
deleteAppActivity: jest.fn().mockImplementation(({ successCallback }) => {
successCallback();
}),
})),
);
describe('api/Feed', () => {
let feed;
const comments = {
total_count: 1,
entries: [
{
type: 'comment',
id: '123',
created_at: 'Thu Sep 26 33658 19:46:39 GMT-0600 (CST)',
tagged_message: 'test @[123:Jeezy] @[10:Kanye West]',
created_by: { name: 'Akon', id: 11 },
},
],
};
const tasks = {
total_count: 1,
entries: [
{
type: 'task',
id: '1234',
created_at: 'Thu Sep 25 33658 19:45:39 GMT-0600 (CST)',
modified_at: 'Thu Sep 25 33658 19:46:39 GMT-0600 (CST)',
tagged_message: 'test',
modified_by: { name: 'Jay-Z', id: 10 },
dueAt: 1234567891,
task_assignment_collection: {
entries: [{ assigned_to: { name: 'Akon', id: 11 } }],
total_count: 1,
},
},
],
};
const taskAssignments = {
total_count: 1,
entries: [
{
type: 'task_assignment',
id: '48714317',
assigned_to: {
type: 'user',
id: '3086276240',
name: 'Test User',
login: 'testuser@foo.com',
},
status: 'completed',
},
],
};
const tasksNew = {
entries: [mockTaskNew],
limit: 1000,
next_marker: null,
};
const appActivities = {
total_count: 1,
entries: [MOCK_APP_ACTIVITY_ITEM],
};
const feedItems = [...comments.entries, ...tasks.entries, ...versions.entries, ...appActivities.entries];
const feedItemsNew = [...comments.entries, ...tasksNew.entries, ...versions.entries, ...appActivities.entries];
const file = {
id: '12345',
permissions: {
can_comment: true,
},
modified_at: 1234567891,
file_version: {
id: 987,
},
restored_from: {
id: mockFirstVersion.id,
type: mockFirstVersion.type,
},
};
const user = { id: 'user1' };
const fileError = 'Bad box item!';
const errorCode = 'foo';
beforeEach(() => {
feed = new Feed({});
jest.spyOn(global.console, 'error').mockImplementation();
});
afterEach(() => {
jest.restoreAllMocks();
});
describe('getCacheKey()', () => {
test('should return the cache key', () => {
expect(feed.getCacheKey('1')).toBe('feedItems_1');
});
});
describe('getCachedItems()', () => {
beforeEach(() => {
feed.getCache = jest.fn().mockReturnValue({
set: jest.fn(),
get: jest.fn().mockReturnValue(feedItems),
});
feed.getCacheKey = jest.fn().mockReturnValue('baz');
});
test('should get the cached items', () => {
const id = '1';
const result = feed.getCachedItems(id);
expect(feed.getCacheKey).toHaveBeenCalledWith(id);
expect(result).toEqual(feedItems);
});
});
describe('setCachedItems()', () => {
let cache;
const cacheKey = 'baz';
beforeEach(() => {
cache = {
get: jest.fn().mockRejectedValue(feedItems),
set: jest.fn(),
};
feed.getCache = jest.fn().mockReturnValue(cache);
feed.getCacheKey = jest.fn().mockReturnValue(cacheKey);
});
test('should set the cached items', () => {
const id = '1';
feed.setCachedItems(id, feedItems);
expect(feed.getCacheKey).toHaveBeenCalledWith(id);
expect(cache.set).toHaveBeenCalledWith(cacheKey, {
items: feedItems,
hasError: false,
});
});
});
describe('feedItems()', () => {
const sortedItems = [
...versionsWithCurrent.entries,
...tasks.entries,
...comments.entries,
...appActivities.entries,
];
let successCb;
let errorCb;
beforeEach(() => {
feed.fetchVersions = jest.fn().mockResolvedValue(versions);
feed.fetchCurrentVersion = jest.fn().mockResolvedValue(mockCurrentVersion);
feed.fetchTasks = jest.fn().mockResolvedValue(tasks);
feed.fetchTasksNew = jest.fn().mockResolvedValue(tasksNew);
feed.fetchComments = jest.fn().mockResolvedValue(comments);
feed.fetchAppActivity = jest.fn().mockReturnValue(appActivities);
feed.setCachedItems = jest.fn();
feed.versionsAPI = {
getCurrentVersion: jest.fn().mockReturnValue(versions),
addCurrentVersion: jest.fn().mockReturnValue(versionsWithCurrent),
};
successCb = jest.fn();
errorCb = jest.fn();
feed.isDestroyed = jest.fn().mockReturnValue(false);
jest.spyOn(sorter, 'sortFeedItems').mockReturnValue(sortedItems);
});
afterEach(() => {
jest.restoreAllMocks();
});
test('should get feed items, sort, save to cache, and call the success callback', done => {
feed.feedItems(file, false, successCb, errorCb, jest.fn(), false, true);
setImmediate(() => {
expect(feed.versionsAPI.addCurrentVersion).toHaveBeenCalledWith(mockCurrentVersion, versions, file);
expect(sorter.sortFeedItems).toHaveBeenCalledWith(versionsWithCurrent, comments, tasks, appActivities);
expect(feed.setCachedItems).toHaveBeenCalledWith(file.id, sortedItems);
expect(successCb).toHaveBeenCalledWith(sortedItems);
done();
});
});
test('should get feed items, sort, save to cache, and call the error callback', done => {
feed.fetchVersions = function fetchVersionsWithError() {
this.hasError = true;
return [];
};
feed.feedItems(file, false, successCb, errorCb);
setImmediate(() => {
expect(errorCb).toHaveBeenCalledWith(sortedItems);
done();
});
});
test('should use the new task api if 6th arg is true', done => {
feed.feedItems(file, false, successCb, errorCb, errorCb, true);
setImmediate(() => {
expect(feed.fetchTasksNew).toHaveBeenCalled();
expect(feed.fetchTasks).not.toHaveBeenCalled();
done();
});
});
test('should use the app activity api if the seventh arg is true', done => {
feed.feedItems(file, false, successCb, errorCb, errorCb, false, true);
setImmediate(() => {
expect(feed.fetchAppActivity).toHaveBeenCalled();
done();
});
});
test('should note use the app activity api if the seventh arg is false', done => {
feed.feedItems(file, false, successCb, errorCb, errorCb, false, false);
setImmediate(() => {
expect(feed.fetchAppActivity).not.toHaveBeenCalled();
done();
});
});
test('should not call success or error callback if it is destroyed', done => {
feed.isDestroyed = jest.fn().mockReturnValue(true);
feed.feedItems(file, false, successCb, errorCb);
setImmediate(() => {
expect(successCb).not.toHaveBeenCalled();
expect(errorCb).not.toHaveBeenCalled();
done();
});
});
test('should return the cached items', () => {
feed.getCachedItems = jest.fn().mockReturnValue({
hasError: false,
items: feedItems,
});
feed.feedItems(file, false, successCb, errorCb);
expect(feed.getCachedItems).toHaveBeenCalledWith(file.id);
expect(successCb).toHaveBeenCalledWith(feedItems);
});
test('should refresh the cache after returning the cached items', done => {
feed.getCachedItems = jest.fn().mockReturnValue({
hasError: false,
items: feedItems,
});
feed.feedItems(file, true, successCb, errorCb);
expect(feed.getCachedItems).toHaveBeenCalledWith(file.id);
expect(successCb).toHaveBeenCalledTimes(1);
expect(successCb).toHaveBeenCalledWith(feedItems);
// refresh cache
setImmediate(() => {
expect(successCb).toHaveBeenCalledTimes(2);
done();
});
});
});
describe('fetchComments()', () => {
beforeEach(() => {
feed.file = file;
feed.fetchFeedItemErrorCallback = jest.fn();
});
test('should return a promise and call the comments api', () => {
const commentItems = feed.fetchComments();
expect(commentItems instanceof Promise).toBeTruthy();
expect(feed.commentsAPI.getComments).toBeCalled();
});
});
describe('Fetching Base Items', () => {
beforeEach(() => {
feed.file = file;
feed.fetchFeedItemErrorCallback = jest.fn();
});
describe('fetchVersions()', () => {
test('should return a promise and call the versions api', () => {
const versionItems = feed.fetchVersions();
expect(versionItems instanceof Promise).toBeTruthy();
expect(feed.versionsAPI.getVersions).toBeCalled();
});
});
describe('fetchCurrentVersion()', () => {
test('should return a promise and call the versions api', () => {
const currentVersion = feed.fetchCurrentVersion();
expect(currentVersion instanceof Promise).toBeTruthy();
expect(feed.versionsAPI.getCurrentVersion).toBeCalled();
});
});
describe('fetchTasks()', () => {
test('should return a promise and call the tasks api', () => {
const taskItems = feed.fetchTasks();
expect(taskItems instanceof Promise).toBeTruthy();
expect(feed.tasksAPI.getTasks).toBeCalled();
});
});
describe('fetchAppActivity()', () => {
test('should return a promise and call the app activity api', () => {
const activityItems = feed.fetchAppActivity();
expect(activityItems instanceof Promise).toBeTruthy();
expect(feed.appActivityAPI.getAppActivity).toBeCalled();
});
});
describe('fetchTasksNew()', () => {
test('should return a promise and call the tasks api', () => {
const taskItems = feed.fetchTasksNew();
expect(taskItems instanceof Promise).toBeTruthy();
expect(feed.tasksNewAPI.getTasksForFile).toBeCalled();
});
});
});
describe('updateTaskAssignment()', () => {
beforeEach(() => {
feed.updateFeedItem = jest.fn();
feed.updateTaskAssignmentSuccessCallback = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.updateTaskAssignment({})).toThrow(fileError);
});
test('should call the tasks assignments api and if successful, the success callback', () => {
feed.updateTaskAssignment(file);
expect(feed.taskAssignmentsAPI.length).toBe(1);
expect(feed.taskAssignmentsAPI.pop().updateTaskAssignment).toBeCalled();
expect(feed.updateTaskAssignmentSuccessCallback).toBeCalled();
});
});
describe('updateTaskAssignmentSuccessCallback()', () => {
beforeEach(() => {
feed.file = file;
feed.getCachedItems = jest.fn().mockReturnValue({
hasError: false,
items: feedItems,
});
feed.updateFeedItem = jest.fn();
});
test('should update the resolution state and call the success callback', () => {
const updatedState = 'completed';
const successCb = jest.fn();
const taskId = '1234';
feed.updateTaskAssignmentSuccessCallback(
taskId,
{
...tasks.entries[0].task_assignment_collection.entries[0],
status: updatedState,
},
successCb,
);
expect(feed.updateFeedItem.mock.calls[0][0].task_assignment_collection.entries[0].status).toBe(
updatedState,
);
expect(successCb).toBeCalled();
});
});
describe('updateTaskCollaborator()', () => {
beforeEach(() => {
feed.updateFeedItem = jest.fn();
feed.updateTaskCollaboratorSuccessCallback = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.updateTaskCollaborator({})).toThrow(fileError);
});
test('should call the tasks collaborators api and if successful, the success callback', () => {
feed.updateTaskCollaborator(file);
expect(feed.taskCollaboratorsAPI.length).toBe(1);
expect(feed.taskCollaboratorsAPI.pop().updateTaskCollaborator).toBeCalled();
expect(feed.updateTaskCollaboratorSuccessCallback).toBeCalled();
});
});
describe('updateTaskCollaboratorSuccessCallback()', () => {
beforeEach(() => {
feed.getCachedItems = jest.fn().mockReturnValue({
hasError: false,
items: feedItemsNew,
});
feed.updateFeedItem = jest.fn();
});
test('should refresh the task from the server and call the success callback', () => {
const updatedStatus = 'COMPLETED';
const successCb = jest.fn();
const taskId = mockTaskNew.id;
feed.updateTaskCollaboratorSuccessCallback(
taskId,
file,
{
...tasksNew.entries[0].assigned_to.entries[0],
status: updatedStatus,
},
successCb,
);
expect(feed.tasksNewAPI.getTask).toBeCalled();
expect(feed.updateFeedItem).toBeCalledWith({ isPending: false }, taskId);
expect(successCb).toBeCalled();
});
});
describe('updateTask()', () => {
beforeEach(() => {
feed.updateFeedItem = jest.fn();
feed.updateTaskSuccessCallback = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.updateTask({})).toThrow(fileError);
});
test('should call the tasks api and if successful, the success callback', () => {
feed.updateTask(file);
expect(feed.tasksAPI.updateTask).toBeCalled();
expect(feed.updateTaskSuccessCallback).toBeCalled();
});
});
describe('updateTaskNew()', () => {
beforeEach(() => {
feed.updateFeedItem = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.updateTaskNew({})).toThrow(fileError);
});
test('should call the error handling when unable to create new task collaborator', async () => {
const mockErrorCallback = jest.fn();
feed.createTaskCollaborator = jest.fn().mockRejectedValue(new Error('forced rejection'));
const task = {
id: '1',
description: 'updated description',
addedAssignees: [
{
type: 'user',
id: '3086276240',
name: 'Test User',
login: 'testuser@foo.com',
},
],
removedAssignees: [
{
type: 'task_collaborator',
id: '19283765',
target: { type: 'user', id: '19283765', name: 'remove Test User', login: 'testuser@foo.com' },
role: 'ASSIGNEE',
permissions: {
can_delete: true,
can_update: true,
},
status: 'incomplete',
},
],
};
feed.updateTaskNew(file, task, jest.fn(), mockErrorCallback);
await new Promise(r => setTimeout(r, 0));
expect(feed.tasksNewAPI.updateTask).not.toBeCalled();
expect(feed.updateFeedItem).toBeCalled();
expect(mockErrorCallback).toBeCalled();
});
test('should call the error handling when unable to delete existing task collaborator', async () => {
const mockErrorCallback = jest.fn();
feed.deleteTaskCollaborator = jest.fn().mockRejectedValue(new Error('forced rejection'));
const task = {
id: '1',
description: 'updated description',
addedAssignees: [
{
type: 'user',
id: '3086276240',
name: 'Test User',
login: 'testuser@foo.com',
},
],
removedAssignees: [
{
type: 'task_collaborator',
id: '19283765',
target: { type: 'user', id: '19283765', name: 'remove Test User', login: 'testuser@foo.com' },
role: 'ASSIGNEE',
permissions: {
can_delete: true,
can_update: true,
},
status: 'incomplete',
},
],
};
feed.updateTaskNew(file, task, jest.fn(), mockErrorCallback);
await new Promise(r => setTimeout(r, 0));
expect(feed.tasksNewAPI.updateTask).not.toBeCalled();
expect(feed.updateFeedItem).toBeCalled();
expect(mockErrorCallback).toBeCalled();
});
test('should call the new task api and if successful, the success callback', async () => {
const successCallback = jest.fn();
const task = {
id: '1',
description: 'updated description',
addedAssignees: [],
removedAssignees: [],
};
feed.updateTaskNew(file, task, successCallback, jest.fn());
expect(feed.file.id).toBe(file.id);
// push a new promise to trigger the promises in updateTaskNew
await new Promise(r => setTimeout(r, 0));
expect(feed.tasksNewAPI.updateTask).toBeCalled();
expect(feed.updateFeedItem).toBeCalled();
expect(successCallback).toBeCalled();
});
test('should call the appropriate new task APIs when adding new assignees', async () => {
const successCallback = jest.fn();
const task = {
id: '1',
description: 'updated description',
addedAssignees: [
{
type: 'user',
id: '3086276240',
name: 'Test User',
login: 'testuser@foo.com',
},
],
removedAssignees: [
{
type: 'task_collaborator',
id: '19283765',
target: { type: 'user', id: '19283765', name: 'remove Test User', login: 'testuser@foo.com' },
role: 'ASSIGNEE',
permissions: {
can_delete: true,
can_update: true,
},
status: 'incomplete',
},
],
};
feed.updateTaskNew(file, task, successCallback, jest.fn());
await new Promise(r => setTimeout(r, 0));
expect(feed.tasksNewAPI.updateTask).toBeCalled();
expect(feed.updateFeedItem).toBeCalled();
expect(successCallback).toBeCalled();
});
});
describe('updateTaskSuccessCallback()', () => {
let successCb;
beforeEach(() => {
feed.errorCallback = jest.fn();
feed.updateFeedItem = jest.fn();
successCb = jest.fn();
});
test('should call the success callback', () => {
const task = tasks.entries[0];
feed.updateTaskSuccessCallback(task, successCb);
const { task_assignment_collection, ...rest } = task;
expect(feed.updateFeedItem).toBeCalledWith(
{
...rest,
isPending: false,
},
task.id,
);
expect(successCb).toBeCalled();
});
test('should not call the success callback', () => {
feed.isDestroyed = jest.fn().mockReturnValue(true);
feed.updateTaskSuccessCallback(tasks.entries[0], successCb);
expect(successCb).not.toBeCalled();
});
});
describe('updateComment()', () => {
beforeEach(() => {
feed.updateFeedItem = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.updateComment({})).toThrow(fileError);
});
test('should call the comments api and update the feed items', () => {
const successCallback = jest.fn();
const comment = {
id: '1',
tagged_message: 'updated message',
message: 'update message',
permissions: { can_edit: true },
};
feed.updateComment(file, comment.id, comment.text, true, comment.permmissions, successCallback, jest.fn());
expect(feed.updateFeedItem).toBeCalled();
expect(successCallback).toBeCalled();
});
});
describe('deleteComment()', () => {
beforeEach(() => {
feed.updateFeedItem = jest.fn();
feed.deleteFeedItem = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.deleteComment({})).toThrow(fileError);
});
test('should call the comments api and if successful, the success callback', () => {
feed.deleteComment(file);
expect(feed.commentsAPI.deleteComment).toBeCalled();
expect(feed.deleteFeedItem).toBeCalled();
});
});
describe('deleteCommentErrorCallback()', () => {
const e = new Error('foo');
beforeEach(() => {
feed.updateFeedItem = jest.fn();
feed.createFeedError = jest.fn().mockReturnValue(error);
feed.feedErrorCallback = jest.fn();
});
test('should update the feed item and call the error callback', () => {
const commentId = '1';
feed.deleteCommentErrorCallback(e, errorCode, commentId);
expect(feed.updateFeedItem).toBeCalledWith(error, commentId);
expect(feed.feedErrorCallback).toBeCalledWith(true, e, errorCode);
});
});
describe('deleteAppActivity()', () => {
beforeEach(() => {
feed.updateFeedItem = jest.fn();
feed.deleteFeedItem = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.deleteAppActivity({})).toThrow(fileError);
});
test('should call the app activity api and if successful, the success callback', () => {
const activityItemId = '12345';
feed.deleteAppActivity(file, activityItemId);
expect(feed.appActivityAPI.deleteAppActivity).toBeCalled();
expect(feed.deleteFeedItem).toBeCalled();
});
});
describe('createTaskSuccessCallback()', () => {
let successCb;
let errorCb;
const assignees = ['foo', 'bar'];
beforeEach(() => {
feed.appendAssignmentsToTask = jest.fn();
feed.updateFeedItem = jest.fn();
feed.feedErrorCallback = jest.fn();
successCb = jest.fn();
errorCb = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.createTaskSuccessCallback()).toThrow(fileError);
});
test('should call the success callback', done => {
feed.file = file;
feed.createTaskAssignment = jest.fn().mockResolvedValue('foo');
feed.createTaskSuccessCallback(file, 'generated', tasks.entries[0], assignees, successCb, errorCb);
setImmediate(() => {
expect(feed.createTaskAssignment).toHaveBeenCalledTimes(assignees.length);
expect(feed.updateFeedItem).toBeCalled();
expect(successCb).toBeCalled();
done();
});
});
test('should call the error callback', done => {
feed.file = file;
feed.createTaskAssignment = jest.fn().mockRejectedValue('bar');
feed.createTaskSuccessCallback(file, 'generated', tasks.entries[0], assignees, successCb, errorCb);
setImmediate(() => {
expect(feed.createTaskAssignment).toHaveBeenCalledTimes(assignees.length);
expect(feed.feedErrorCallback).toBeCalled();
done();
});
});
});
describe('createTask()', () => {
let successCb;
let errorCb;
const message = 'foo';
const assignees = [{ id: '1234', name: 'A. User' }];
const currentUser = 'user1';
beforeEach(() => {
feed.addPendingItem = jest.fn();
feed.createTaskSuccessCallback = jest.fn();
successCb = jest.fn();
errorCb = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.createTask({})).toThrow(fileError);
});
test('should call the tasks api and if successful, the success callback', () => {
feed.createTask(file, currentUser, message, assignees, null, successCb, errorCb);
expect(feed.addPendingItem).toBeCalledWith(file.id, currentUser, {
id: 'uniqueId',
is_completed: false,
message,
task_assignment_collection: {
entries: [
{
assigned_to: assignees[0],
status: 'incomplete',
},
],
total_count: 1,
},
type: 'task',
});
expect(feed.tasksAPI.createTask).toBeCalled();
expect(feed.createTaskSuccessCallback).toBeCalled();
});
test('should set the due at string', () => {
const dueAt = 123456;
const dueDateString = new Date(dueAt).toISOString();
feed.createTask(file, currentUser, message, assignees, dueAt, successCb, errorCb);
expect(feed.addPendingItem).toBeCalledWith(file.id, currentUser, {
due_at: dueDateString,
id: 'uniqueId',
is_completed: false,
message,
task_assignment_collection: {
entries: [
{
assigned_to: assignees[0],
status: 'incomplete',
},
],
total_count: 1,
},
type: 'task',
});
});
});
describe('createTaskAssignment()', () => {
let errorCb;
beforeEach(() => {
feed.updateFeedItem = jest.fn();
feed.deleteFeedItem = jest.fn();
errorCb = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.deleteComment({})).toThrow(fileError);
});
test('should call the task assignment api and if successful, resolve', done => {
const promise = feed.createTaskAssignment(file, tasks.entries[0], 'foo', errorCb);
expect(feed.file.id).toBe(file.id);
expect(promise instanceof Promise).toBeTruthy();
expect(feed.taskAssignmentsAPI[0].createTaskAssignment).toBeCalled();
promise.then(() => {
done();
});
});
});
describe('deleteTask()', () => {
beforeEach(() => {
feed.updateFeedItem = jest.fn();
feed.deleteFeedItem = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.deleteTask({})).toThrow(fileError);
});
test('should call the comments api and if successful, the success callback', () => {
feed.deleteTask(file, '1');
expect(feed.file.id).toBe(file.id);
expect(feed.tasksAPI.deleteTask).toBeCalled();
expect(feed.deleteFeedItem).toBeCalled();
});
});
describe('deleteTaskNew()', () => {
beforeEach(() => {
feed.updateFeedItem = jest.fn();
feed.deleteFeedItem = jest.fn();
});
test('should throw if no file id', () => {
expect(() => feed.deleteTaskNew({})).toThrow(fileError);
});
test('should call the new task api and if successful, the success callback', () => {
feed.deleteTaskNew(file, { id: '1' });
expect(feed.file.id).toBe(file.id);
expect(feed.tasksNewAPI.deleteTask).toBeCalled();
expect(feed.deleteFeedItem).toBeCalled();
});
});
describe('deleteFeedItem()', () => {
let successCb;
const feedItemId = feedItems[0].id;
beforeEach(() => {
feed.setCachedItems = jest.fn();
feed.file = file;
successCb = jest.fn();
});
test('should delete the feed item and call success callback', () => {
feed.getCachedItems = jest.fn().mockReturnValue({
hasError: false,
items: feedItems,
});
feed.deleteFeedItem(feedItemId, successCb);
expect(feed.setCachedItems.mock.calls[0][1].length).toBe(feedItems.length - 1);
expect(successCb).toBeCalled();
});
test('not call the success callback', () => {
feed.isDestroyed = jest.fn().mockReturnValue(true);
feed.deleteFeedItem(feedItemId, successCb);
expect(successCb).not.toBeCalled();
});
});
describe('feedErrorCallback()', () => {
const code = 'foo';
const e = new Error('bar');
beforeEach(() => {
jest.spyOn(global.console, 'error').mockImplementation();
feed.errorCallback = jest.fn();
});
afterEach(() => {
jest.restoreAllMocks();
});
test('should log the error and set the hasError property if its not destroyed and hasError is set to true', () => {
const hasError = true;
feed.feedErrorCallback(hasError, e, code);
expect(global.console.error).toBeCalledWith(e);
expect(feed.hasError).toBe(true);
expect(feed.errorCallback).toHaveBeenCalledWith(e, code, {
error: e,
[IS_ERROR_DISPLAYED]: hasError,
});
});
test('should call the error callback with the value of hasError', () => {
const hasError = false;
feed.feedErrorCallback(hasError, e, code);
expect(feed.errorCallback).toHaveBeenCalledWith(e, code, {
error: e,
[IS_ERROR_DISPLAYED]: hasError,
});
});
test('should set hasError only if hasError is set', () => {
feed.hasError = null;
expect(feed.hasError).toBe(null);
const hasError = false;
feed.feedErrorCallback(hasError, e, code);
expect(feed.hasError).toBe(null);
feed.feedErrorCallback(true, e, code);
expect(feed.hasError).toBe(true);
});
});
describe('fetchTaskAssignments()', () => {
const tasksEntriesWithAssignments = {
...tasks.entries[0],
task_assignment_collection: {
...taskAssignments,
},
};
beforeEach(() => {
feed.file = file;
feed.appendAssignmentsToTask = jest.fn().mockReturnValue(tasksEntriesWithAssignments);
feed.errorCallback = jest.fn();
});
test('should fetch the task assignments', done => {
feed.fetchTaskAssignments(tasks).then(tasksWithAssignments => {
expect(feed.taskAssignmentsAPI.pop().getAssignments).toHaveBeenCalledTimes(tasks.entries.length);
expect(feed.appendAssignmentsToTask).toHaveBeenCalledTimes(tasks.entries.length);
expect(tasksWithAssignments).toEqual({
...tasks,
entries: [tasksEntriesWithAssignments],
});
done();
});
});
});
describe('appendAssignmentsToTask()', () => {
test('should correctly format a task with assignment with a message, increment assignment count', () => {
const task = {
task_assignment_collection: {
entries: [],
total_count: 0,
},
};
const assignments = [
{
id: '1',
assigned_to: { id: '1234' },
status: 'completed',
},
];
const expectedResult = {
task_assignment_collection: {
entries: [{ ...assignments[0], status: 'completed', type: 'task_assignment' }],
total_count: 1,
},
};
const result = feed.appendAssignmentsToTask(task, assignments);
expect(result.task_assignment_collection.total_count).toBe(1);
expect(result).toEqual(expectedResult);
});
test('should correctly format a task with assignment, increment assignment count', () => {
const task = {
task_assignment_collection: {
entries: [],
total_count: 0,
},
};
const assignments = [
{
id: '1',
assigned_to: { id: '1234' },
status: 'completed',
},
];
const expectedResult = {
task_assignment_collection: {
entries: [{ ...assignments[0], status: 'completed', type: 'task_assignment' }],
total_count: 1,
},
};
const result = feed.appendAssignmentsToTask(task, assignments);
expect(result.task_assignment_collection.total_count).toBe(1);
expect(result).toEqual(expectedResult);
});
});
describe('addPendingItem()', () => {
const itemBase = {
my_prop: 'yay',
};
beforeEach(() => {
feed.file = file;
feed.setCachedItems = jest.fn();
});
test('should create an item and add it to the feed with empty cache', () => {
feed.getCachedItems = jest.fn();
const item = feed.addPendingItem(file.id, user, itemBase);
expect(typeof item.created_at).toBe('string');
expect(item.created_by).toBe(user);
expect(typeof item.modified_at).toBe('string');
expect(item.isPending).toBe(true);
expect(feed.setCachedItems).toBeCalledWith(file.id, [item]);
});
test('should create an item and add it to the feed with populated cache', () => {
feed.getCachedItems = jest.fn().mockReturnValue({
hasError: false,
items: feedItems,
});
const item = feed.addPendingItem(file.id, user, itemBase);
expect(feed.setCachedItems).toBeCalledWith(file.id, [...feedItems, item]);
});
});
describe('createCommentSuccessCallback()', () => {
let successCb;
beforeEach(() => {
feed.updateFeedItem = jest.fn();
successCb = jest.fn();
});
test('should assign tagged_message of the comment with tagged_message value if it exists', () => {
const id = '0987654321';
const commentData = { tagged_message: 'yay' };
feed.createCommentSuccessCallback(commentData, id, successCb);
expect(feed.updateFeedItem).toBeCalledWith(
{
...commentData,
isPending: false,
},
id,
);
expect(successCb).toBeCalled();
});
test('should assign tagged_message of the comment with message value if it exists', () => {
const id = '0987654321';
const commentData = { message: 'yay' };
feed.createCommentSuccessCallback(commentData, id, successCb);
expect(feed.updateFeedItem).toBeCalledWith(
{
...commentData,
isPending: false,
},
id,
);
});
test('should not invoke success callback if destroyed', () => {
const id = '0987654321';
const commentData = { message: 'yay' };
feed.isDestroyed = jest.fn().mockReturnValue(true);
feed.createCommentSuccessCallback(commentData, id, successCb);
expect(successCb).not.toBeCalled();
});
});
describe('createFeedError()', () => {
test('should create a feed error with the message and title', () => {
const feedError = feed.createFeedError('foo', 'bar');
expect(feedError).toEqual({
error: {
message: 'foo',
title: 'bar',
},
});
});
test('should create a feed error with the message and default title', () => {
const feedError = feed.createFeedError('foo');
expect(feedError).toEqual({
error: {
message: 'foo',
title: commonMessages.errorOccured,
},
});
});
});
describe('updateFeedItem()', () => {
const { id } = comments.entries[0];
const updates = {
foo: 'bar',
id: 'foo',
};
beforeEach(() => {
feed.setCachedItems = jest.fn();
feed.getCachedItems = jest.fn().mockReturnValue({
hasError: false,
items: feedItems,
});
});
test('should throw if no file id', () => {
feed.file = {};
expect(() => feed.updateFeedItem({}, id)).toThrow(fileError);
});
test('should update the cache with the updated item', () => {
feed.file = file;
const updatedFeedItems = feed.updateFeedItem(updates, id);
expect(updatedFeedItems).not.toBeNull();
expect(feed.setCachedItems).toBeCalledWith(file.id, updatedFeedItems);
});
});
describe('createComment()', () => {
let successCb;
let errorCb;
const currentUser = {
id: 'bar',
};
const text = 'textfoo';
const hasMention = true;
beforeEach(() => {
successCb = jest.fn();
errorCb = jest.fn();
feed.addPendingItem = jest.fn();
feed.createCommentSuccessCallback = jest.fn();
feed.createCommentErrorCallback = jest.fn();
feed.createFeedError = jest.fn().mockReturnValue('foo');
});
test('should throw if no file id', () => {
expect(() => feed.createComment({}, currentUser, text, true, successCb, errorCb)).toThrow(fileError);
});
test('should create a pending item', () => {
feed.createComment(file, currentUser, text, true, successCb, errorCb);
expect(feed.addPendingItem).toBeCalledWith(file.id, currentUser, {
id: 'uniqueId',