mattermost-redux
Version:
Common code (API client, Redux stores, logic, utility functions) for building a Mattermost client
1,325 lines (1,154 loc) • 69.2 kB
JavaScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import assert from 'assert';
import {Posts, Preferences} from '../constants';
import deepFreeze from 'utils/deep_freeze';
import {getPreferenceKey} from 'utils/preference_utils';
import {
COMBINED_USER_ACTIVITY,
combineUserActivitySystemPost,
comparePostTypes,
DATE_LINE,
getDateForDateLine,
getFirstPostId,
getLastPostId,
getLastPostIndex,
getPostIdsForCombinedUserActivityPost,
isCombinedUserActivityPost,
isDateLine,
makeCombineUserActivityPosts,
makeFilterPostsAndAddSeparators,
makeGenerateCombinedPost,
postTypePriority,
START_OF_NEW_MESSAGES,
} from './post_list';
describe('makeFilterPostsAndAddSeparators', () => {
it('filter join/leave posts', () => {
const filterPostsAndAddSeparators = makeFilterPostsAndAddSeparators();
const time = Date.now();
const today = new Date(time);
let state = {
entities: {
general: {
config: {},
},
posts: {
posts: {
1001: {id: '1001', create_at: time, type: ''},
1002: {id: '1002', create_at: time + 1, type: Posts.POST_TYPES.JOIN_CHANNEL},
},
},
preferences: {
myPreferences: {},
},
users: {
currentUserId: '1234',
profiles: {
1234: {id: '1234', username: 'user'},
},
},
},
};
const lastViewedAt = Number.POSITIVE_INFINITY;
const postIds = ['1002', '1001'];
const indicateNewMessages = true;
// Defaults to show post
let now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages});
assert.deepEqual(now, [
'1002',
'1001',
'date-' + today.getTime(),
]);
// Show join/leave posts
state = {
...state,
entities: {
...state.entities,
preferences: {
...state.entities.preferences,
myPreferences: {
...state.entities.preferences.myPreferences,
[getPreferenceKey(Preferences.CATEGORY_ADVANCED_SETTINGS, Preferences.ADVANCED_FILTER_JOIN_LEAVE)]: {
category: Preferences.CATEGORY_ADVANCED_SETTINGS,
name: Preferences.ADVANCED_FILTER_JOIN_LEAVE,
value: 'true',
},
},
},
},
};
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages});
assert.deepEqual(now, [
'1002',
'1001',
'date-' + today.getTime(),
]);
// Hide join/leave posts
state = {
...state,
entities: {
...state.entities,
preferences: {
...state.entities.preferences,
myPreferences: {
...state.entities.preferences.myPreferences,
[getPreferenceKey(Preferences.CATEGORY_ADVANCED_SETTINGS, Preferences.ADVANCED_FILTER_JOIN_LEAVE)]: {
category: Preferences.CATEGORY_ADVANCED_SETTINGS,
name: Preferences.ADVANCED_FILTER_JOIN_LEAVE,
value: 'false',
},
},
},
},
};
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages});
assert.deepEqual(now, [
'1001',
'date-' + today.getTime(),
]);
// always show join/leave posts for the current user
state = {
...state,
entities: {
...state.entities,
posts: {
...state.entities.posts,
posts: {
...state.entities.posts.posts,
1002: {id: '1002', create_at: time + 1, type: Posts.POST_TYPES.JOIN_CHANNEL, props: {username: 'user'}},
},
},
},
};
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages});
assert.deepEqual(now, [
'1002',
'1001',
'date-' + today.getTime(),
]);
});
it('new messages indicator', () => {
const filterPostsAndAddSeparators = makeFilterPostsAndAddSeparators();
const time = Date.now();
const today = new Date(time);
const state = {
entities: {
general: {
config: {},
},
posts: {
posts: {
1000: {id: '1000', create_at: time + 1000, type: ''},
1005: {id: '1005', create_at: time + 1005, type: ''},
1010: {id: '1010', create_at: time + 1010, type: ''},
},
},
preferences: {
myPreferences: {},
},
users: {
currentUserId: '1234',
profiles: {
1234: {id: '1234', username: 'user'},
},
},
},
};
const postIds = ['1010', '1005', '1000']; // Remember that we list the posts backwards
// Do not show new messages indicator before all posts
let now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt: 0, indicateNewMessages: true});
assert.deepEqual(now, [
'1010',
'1005',
'1000',
'date-' + (today.getTime() + 1000),
]);
now = filterPostsAndAddSeparators(state, {postIds, indicateNewMessages: true});
assert.deepEqual(now, [
'1010',
'1005',
'1000',
'date-' + (today.getTime() + 1000),
]);
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt: time + 999, indicateNewMessages: false});
assert.deepEqual(now, [
'1010',
'1005',
'1000',
'date-' + (today.getTime() + 1000),
]);
// Show new messages indicator before all posts
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt: time + 999, indicateNewMessages: true});
assert.deepEqual(now, [
'1010',
'1005',
'1000',
START_OF_NEW_MESSAGES,
'date-' + (today.getTime() + 1000),
]);
// Show indicator between posts
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt: time + 1003, indicateNewMessages: true});
assert.deepEqual(now, [
'1010',
'1005',
START_OF_NEW_MESSAGES,
'1000',
'date-' + (today.getTime() + 1000),
]);
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt: time + 1006, indicateNewMessages: true});
assert.deepEqual(now, [
'1010',
START_OF_NEW_MESSAGES,
'1005',
'1000',
'date-' + (today.getTime() + 1000),
]);
// Don't show indicator when all posts are read
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt: time + 1020});
assert.deepEqual(now, [
'1010',
'1005',
'1000',
'date-' + (today.getTime() + 1000),
]);
});
it('memoization', () => {
const filterPostsAndAddSeparators = makeFilterPostsAndAddSeparators();
const time = Date.now();
const today = new Date(time);
const tomorrow = new Date((24 * 60 * 60 * 1000) + today.getTime());
// Posts 7 hours apart so they should appear on multiple days
const initialPosts = {
1001: {id: '1001', create_at: time, type: ''},
1002: {id: '1002', create_at: time + 5, type: ''},
1003: {id: '1003', create_at: time + 10, type: ''},
1004: {id: '1004', create_at: tomorrow, type: ''},
1005: {id: '1005', create_at: tomorrow + 5, type: ''},
1006: {id: '1006', create_at: tomorrow + 10, type: Posts.POST_TYPES.JOIN_CHANNEL},
};
let state = {
entities: {
general: {
config: {},
},
posts: {
posts: initialPosts,
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_ADVANCED_SETTINGS, Preferences.ADVANCED_FILTER_JOIN_LEAVE)]: {
category: Preferences.CATEGORY_ADVANCED_SETTINGS,
name: Preferences.ADVANCED_FILTER_JOIN_LEAVE,
value: 'true',
},
},
},
users: {
currentUserId: '1234',
profiles: {
1234: {id: '1234', username: 'user'},
},
},
},
};
let postIds = [
'1006',
'1004',
'1003',
'1001',
];
let lastViewedAt = initialPosts['1001'].create_at + 1;
let now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
assert.deepEqual(now, [
'1006',
'1004',
'date-' + tomorrow.getTime(),
'1003',
START_OF_NEW_MESSAGES,
'1001',
'date-' + today.getTime(),
]);
// No changes
let prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
assert.equal(now, prev);
assert.deepEqual(now, [
'1006',
'1004',
'date-' + tomorrow.getTime(),
'1003',
START_OF_NEW_MESSAGES,
'1001',
'date-' + today.getTime(),
]);
// lastViewedAt changed slightly
lastViewedAt = initialPosts['1001'].create_at + 2;
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
assert.equal(now, prev);
assert.deepEqual(now, [
'1006',
'1004',
'date-' + tomorrow.getTime(),
'1003',
START_OF_NEW_MESSAGES,
'1001',
'date-' + today.getTime(),
]);
// lastViewedAt changed a lot
lastViewedAt = initialPosts['1003'].create_at + 1;
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
assert.notEqual(now, prev);
assert.deepEqual(now, [
'1006',
'1004',
START_OF_NEW_MESSAGES,
'date-' + tomorrow.getTime(),
'1003',
'1001',
'date-' + today.getTime(),
]);
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
assert.equal(now, prev);
assert.deepEqual(now, [
'1006',
'1004',
START_OF_NEW_MESSAGES,
'date-' + tomorrow.getTime(),
'1003',
'1001',
'date-' + today.getTime(),
]);
// postIds changed, but still shallowly equal
postIds = [...postIds];
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
assert.equal(now, prev);
assert.deepEqual(now, [
'1006',
'1004',
START_OF_NEW_MESSAGES,
'date-' + tomorrow.getTime(),
'1003',
'1001',
'date-' + today.getTime(),
]);
// Post changed, not in postIds
state = {
...state,
entities: {
...state.entities,
posts: {
...state.entities.posts,
posts: {
...state.entities.posts.posts,
1007: {id: '1007', create_at: 7 * 60 * 60 * 7 * 1000},
},
},
},
};
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
assert.equal(now, prev);
assert.deepEqual(now, [
'1006',
'1004',
START_OF_NEW_MESSAGES,
'date-' + tomorrow.getTime(),
'1003',
'1001',
'date-' + today.getTime(),
]);
// Post changed, in postIds
state = {
...state,
entities: {
...state.entities,
posts: {
...state.entities.posts,
posts: {
...state.entities.posts.posts,
1006: {...state.entities.posts.posts['1006'], message: 'abcd'},
},
},
},
};
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
assert.equal(now, prev);
assert.deepEqual(now, [
'1006',
'1004',
START_OF_NEW_MESSAGES,
'date-' + tomorrow.getTime(),
'1003',
'1001',
'date-' + today.getTime(),
]);
// Filter changed
state = {
...state,
entities: {
...state.entities,
preferences: {
...state.entities.preferences,
myPreferences: {
...state.entities.preferences.myPreferences,
[getPreferenceKey(Preferences.CATEGORY_ADVANCED_SETTINGS, Preferences.ADVANCED_FILTER_JOIN_LEAVE)]: {
category: Preferences.CATEGORY_ADVANCED_SETTINGS,
name: Preferences.ADVANCED_FILTER_JOIN_LEAVE,
value: 'false',
},
},
},
},
};
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
assert.notEqual(now, prev);
assert.deepEqual(now, [
'1004',
START_OF_NEW_MESSAGES,
'date-' + tomorrow.getTime(),
'1003',
'1001',
'date-' + today.getTime(),
]);
prev = now;
now = filterPostsAndAddSeparators(state, {postIds, lastViewedAt, indicateNewMessages: true});
assert.equal(now, prev);
assert.deepEqual(now, [
'1004',
START_OF_NEW_MESSAGES,
'date-' + tomorrow.getTime(),
'1003',
'1001',
'date-' + today.getTime(),
]);
});
});
describe('makeCombineUserActivityPosts', () => {
test('should do nothing if no post IDs are provided', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
const postIds = [];
const state = {
entities: {
posts: {
posts: {},
},
},
};
const result = combineUserActivityPosts(state, postIds);
expect(result).toBe(postIds);
expect(result).toEqual([]);
});
test('should do nothing if there are no user activity posts', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
const postIds = deepFreeze([
'post1',
START_OF_NEW_MESSAGES,
'post2',
DATE_LINE + '1001',
'post3',
DATE_LINE + '1000',
]);
const state = {
entities: {
posts: {
posts: {
post1: {id: 'post1'},
post2: {id: 'post2'},
post3: {id: 'post3'},
},
},
},
};
const result = combineUserActivityPosts(state, postIds);
expect(result).toBe(postIds);
});
test('should combine adjacent user activity posts', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
const postIds = deepFreeze([
'post1',
'post2',
'post3',
]);
const state = {
entities: {
posts: {
posts: {
post1: {id: 'post1', type: Posts.POST_TYPES.JOIN_CHANNEL},
post2: {id: 'post2', type: Posts.POST_TYPES.LEAVE_CHANNEL},
post3: {id: 'post3', type: Posts.POST_TYPES.ADD_TO_CHANNEL},
},
},
},
};
const result = combineUserActivityPosts(state, postIds);
expect(result).not.toBe(postIds);
expect(result).toEqual([
COMBINED_USER_ACTIVITY + 'post1_post2_post3',
]);
});
test('should "combine" a single activity post', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
const postIds = deepFreeze([
'post1',
'post2',
'post3',
]);
const state = {
entities: {
posts: {
posts: {
post1: {id: 'post1'},
post2: {id: 'post2', type: Posts.POST_TYPES.LEAVE_CHANNEL},
post3: {id: 'post3'},
},
},
},
};
const result = combineUserActivityPosts(state, postIds);
expect(result).not.toBe(postIds);
expect(result).toEqual([
'post1',
COMBINED_USER_ACTIVITY + 'post2',
'post3',
]);
});
test('should not combine with regular messages', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
const postIds = deepFreeze([
'post1',
'post2',
'post3',
'post4',
'post5',
]);
const state = {
entities: {
posts: {
posts: {
post1: {id: 'post1', type: Posts.POST_TYPES.JOIN_CHANNEL},
post2: {id: 'post2', type: Posts.POST_TYPES.JOIN_CHANNEL},
post3: {id: 'post3'},
post4: {id: 'post4', type: Posts.POST_TYPES.ADD_TO_CHANNEL},
post5: {id: 'post5', type: Posts.POST_TYPES.ADD_TO_CHANNEL},
},
},
},
};
const result = combineUserActivityPosts(state, postIds);
expect(result).not.toBe(postIds);
expect(result).toEqual([
COMBINED_USER_ACTIVITY + 'post1_post2',
'post3',
COMBINED_USER_ACTIVITY + 'post4_post5',
]);
});
test('should not combine with other system messages', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
const postIds = deepFreeze([
'post1',
'post2',
'post3',
]);
const state = {
entities: {
posts: {
posts: {
post1: {id: 'post1', type: Posts.POST_TYPES.JOIN_CHANNEL},
post2: {id: 'post2', type: Posts.POST_TYPES.PURPOSE_CHANGE},
post3: {id: 'post3', type: Posts.POST_TYPES.ADD_TO_CHANNEL},
},
},
},
};
const result = combineUserActivityPosts(state, postIds);
expect(result).not.toBe(postIds);
expect(result).toEqual([
COMBINED_USER_ACTIVITY + 'post1',
'post2',
COMBINED_USER_ACTIVITY + 'post3',
]);
});
test('should not combine across non-post items', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
const postIds = deepFreeze([
'post1',
START_OF_NEW_MESSAGES,
'post2',
'post3',
DATE_LINE + '1001',
'post4',
]);
const state = {
entities: {
posts: {
posts: {
post1: {id: 'post1', type: Posts.POST_TYPES.JOIN_CHANNEL},
post2: {id: 'post2', type: Posts.POST_TYPES.LEAVE_CHANNEL},
post3: {id: 'post3', type: Posts.POST_TYPES.ADD_TO_CHANNEL},
post4: {id: 'post4', type: Posts.POST_TYPES.JOIN_CHANNEL},
},
},
},
};
const result = combineUserActivityPosts(state, postIds);
expect(result).not.toBe(postIds);
expect(result).toEqual([
COMBINED_USER_ACTIVITY + 'post1',
START_OF_NEW_MESSAGES,
COMBINED_USER_ACTIVITY + 'post2_post3',
DATE_LINE + '1001',
COMBINED_USER_ACTIVITY + 'post4',
]);
});
test('should not combine more than 100 posts', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
const postIds = [];
const posts = {};
for (let i = 0; i < 110; i++) {
const postId = `post${i}`;
postIds.push(postId);
posts[postId] = {id: postId, type: Posts.POST_TYPES.JOIN_CHANNEL};
}
const state = {
entities: {
posts: {
posts,
},
},
};
const result = combineUserActivityPosts(state, postIds);
expect(result).toHaveLength(2);
});
describe('memoization', () => {
const initialPostIds = ['post1', 'post2'];
const initialState = {
entities: {
posts: {
posts: {
post1: {id: 'post1', type: Posts.POST_TYPES.JOIN_CHANNEL},
post2: {id: 'post2', type: Posts.POST_TYPES.JOIN_CHANNEL},
},
},
},
};
test('should not recalculate when nothing has changed', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
expect(combineUserActivityPosts.recomputations()).toBe(0);
combineUserActivityPosts(initialState, initialPostIds);
expect(combineUserActivityPosts.recomputations()).toBe(1);
combineUserActivityPosts(initialState, initialPostIds);
expect(combineUserActivityPosts.recomputations()).toBe(1);
});
test('should recalculate when the post IDs change', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
let postIds = initialPostIds;
combineUserActivityPosts(initialState, postIds);
expect(combineUserActivityPosts.recomputations()).toBe(1);
postIds = ['post1'];
combineUserActivityPosts(initialState, postIds);
expect(combineUserActivityPosts.recomputations()).toBe(2);
});
test('should not recalculate when an unrelated state change occurs', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
let state = initialState;
combineUserActivityPosts(state, initialPostIds);
expect(combineUserActivityPosts.recomputations()).toBe(1);
state = {
...state,
entities: {
...state.entities,
posts: {
...state.entities.posts,
selectedPostId: 'post2',
},
},
};
combineUserActivityPosts(state, initialPostIds);
expect(combineUserActivityPosts.recomputations()).toBe(1);
});
test('should recalculate if any post changes, but should return the same results if possible', () => {
const combineUserActivityPosts = makeCombineUserActivityPosts();
let state = initialState;
const initialResult = combineUserActivityPosts(state, initialPostIds);
expect(combineUserActivityPosts.recomputations()).toBe(1);
// An unrelated post changed
state = {
...state,
entities: {
...state.entities,
posts: {
...state.entities.posts,
posts: {
...state.entities.posts.posts,
post3: {id: 'post3'},
},
},
},
};
let result = combineUserActivityPosts(state, initialPostIds);
expect(combineUserActivityPosts.recomputations()).toBe(2);
expect(result).toBe(initialResult);
// One of the posts was updated, but post type didn't change
state = {
...state,
entities: {
...state.entities,
posts: {
...state.entities.posts,
posts: {
...state.entities.posts.posts,
post2: {...state.entities.posts.posts.post2, update_at: 1234},
},
},
},
};
result = combineUserActivityPosts(state, initialPostIds);
expect(combineUserActivityPosts.recomputations()).toBe(3);
expect(result).toBe(initialResult);
// One of the posts changed type
state = {
...state,
entities: {
...state.entities,
posts: {
...state.entities.posts,
posts: {
...state.entities.posts.posts,
post2: {...state.entities.posts.posts.post2, type: ''},
},
},
},
};
result = combineUserActivityPosts(state, initialPostIds);
expect(combineUserActivityPosts.recomputations()).toBe(4);
expect(result).not.toBe(initialResult);
});
});
});
describe('isDateLine', () => {
test('should correctly identify date line items', () => {
expect(isDateLine('')).toBe(false);
expect(isDateLine('date')).toBe(false);
expect(isDateLine('date-')).toBe(true);
expect(isDateLine('date-0')).toBe(true);
expect(isDateLine('date-1531152392')).toBe(true);
expect(isDateLine('date-1531152392-index')).toBe(true);
});
});
describe('getDateForDateLine', () => {
test('should get date correctly without suffix', () => {
expect(getDateForDateLine('date-1234')).toBe(1234);
});
test('should get date correctly with suffix', () => {
expect(getDateForDateLine('date-1234-suffix')).toBe(1234);
});
});
describe('isCombinedUserActivityPost', () => {
test('should correctly identify combined user activity posts', () => {
expect(isCombinedUserActivityPost('post1')).toBe(false);
expect(isCombinedUserActivityPost('date-1234')).toBe(false);
expect(isCombinedUserActivityPost('user-activity-post1')).toBe(true);
expect(isCombinedUserActivityPost('user-activity-post1_post2')).toBe(true);
expect(isCombinedUserActivityPost('user-activity-post1_post2_post4')).toBe(true);
});
});
describe('getPostIdsForCombinedUserActivityPost', () => {
test('should get IDs correctly', () => {
expect(getPostIdsForCombinedUserActivityPost('user-activity-post1_post2_post3')).toEqual(['post1', 'post2', 'post3']);
});
});
describe('getFirstPostId', () => {
test('should return the first item if it is a post', () => {
expect(getFirstPostId(['post1', 'post2', 'post3'])).toBe('post1');
});
test('should return the first ID from a combined post', () => {
expect(getFirstPostId(['user-activity-post2_post3', 'post4', 'user-activity-post5_post6'])).toBe('post2');
});
test('should skip date separators', () => {
expect(getFirstPostId(['date-1234', 'user-activity-post1_post2', 'post3', 'post4', 'date-1000'])).toBe('post1');
});
test('should skip the new message line', () => {
expect(getFirstPostId([START_OF_NEW_MESSAGES, 'post2', 'post3', 'post4'])).toBe('post2');
});
});
describe('getLastPostId', () => {
test('should return the last item if it is a post', () => {
expect(getLastPostId(['post1', 'post2', 'post3'])).toBe('post3');
});
test('should return the last ID from a combined post', () => {
expect(getLastPostId(['user-activity-post2_post3', 'post4', 'user-activity-post5_post6'])).toBe('post6');
});
test('should skip date separators', () => {
expect(getLastPostId(['date-1234', 'user-activity-post1_post2', 'post3', 'post4', 'date-1000'])).toBe('post4');
});
test('should skip the new message line', () => {
expect(getLastPostId(['post2', 'post3', 'post4', START_OF_NEW_MESSAGES])).toBe('post4');
});
});
describe('getLastPostIndex', () => {
test('should return index of last post for list of all regular posts', () => {
expect(getLastPostIndex(['post1', 'post2', 'post3'])).toBe(2);
});
test('should return index of last combined post', () => {
expect(getLastPostIndex(['user-activity-post2_post3', 'post4', 'user-activity-post5_post6'])).toBe(2);
});
test('should skip date separators and return index of last post', () => {
expect(getLastPostIndex(['date-1234', 'user-activity-post1_post2', 'post3', 'post4', 'date-1000'])).toBe(3);
});
test('should skip the new message line and return index of last post', () => {
expect(getLastPostIndex(['post2', 'post3', 'post4', START_OF_NEW_MESSAGES])).toBe(2);
});
});
describe('makeGenerateCombinedPost', () => {
test('should output a combined post', () => {
const generateCombinedPost = makeGenerateCombinedPost();
const state = {
entities: {
posts: {
posts: {
post1: {
id: 'post1',
channel_id: 'channel1',
create_at: 1002,
delete_at: 0,
message: 'joe added to the channel by bill.',
props: {
addedUsername: 'joe',
addedUserId: 'user2',
username: 'bill',
userId: 'user1',
},
type: Posts.POST_TYPES.ADD_TO_CHANNEL,
user_id: 'user1',
metadata: {},
},
post2: {
id: 'post2',
channel_id: 'channel1',
create_at: 1001,
delete_at: 0,
message: 'alice added to the channel by bill.',
props: {
addedUsername: 'alice',
addedUserId: 'user3',
username: 'bill',
userId: 'user1',
},
type: Posts.POST_TYPES.ADD_TO_CHANNEL,
user_id: 'user1',
metadata: {},
},
post3: {
id: 'post3',
channel_id: 'channel1',
create_at: 1000,
delete_at: 0,
message: 'bill joined the channel.',
props: {
username: 'bill',
userId: 'user1',
},
type: Posts.POST_TYPES.JOIN_CHANNEL,
user_id: 'user1',
metadata: {},
},
},
},
},
};
const combinedId = 'user-activity-post1_post2_post3';
const result = generateCombinedPost(state, combinedId);
expect(result).toEqual({
id: combinedId,
root_id: '',
channel_id: 'channel1',
create_at: 1000,
delete_at: 0,
message: 'joe added to the channel by bill.\nalice added to the channel by bill.\nbill joined the channel.',
props: {
messages: [
'joe added to the channel by bill.',
'alice added to the channel by bill.',
'bill joined the channel.',
],
user_activity: {
allUserIds: ['user2', 'user3', 'user1'],
allUsernames: [],
messageData: [
{
postType: Posts.POST_TYPES.JOIN_CHANNEL,
userIds: ['user1'],
},
{
postType: Posts.POST_TYPES.ADD_TO_CHANNEL,
userIds: ['user2', 'user3'],
actorId: 'user1',
},
],
},
},
state: '',
system_post_ids: ['post1', 'post2', 'post3'],
type: Posts.POST_TYPES.COMBINED_USER_ACTIVITY,
user_activity_posts: [
state.entities.posts.posts.post1,
state.entities.posts.posts.post2,
state.entities.posts.posts.post3,
],
user_id: '',
metadata: {},
});
});
describe('memoization', () => {
const initialState = {
entities: {
posts: {
posts: {
post1: {id: 'post1'},
post2: {id: 'post2'},
},
},
},
};
const initialCombinedId = 'user-activity-post1_post2';
test('should not recalculate when called twice with the same ID', () => {
const generateCombinedPost = makeGenerateCombinedPost();
expect(generateCombinedPost.recomputations()).toBe(0);
generateCombinedPost(initialState, initialCombinedId);
expect(generateCombinedPost.recomputations()).toBe(1);
generateCombinedPost(initialState, initialCombinedId);
expect(generateCombinedPost.recomputations()).toBe(1);
});
test('should recalculate when called twice with different IDs', () => {
const generateCombinedPost = makeGenerateCombinedPost();
expect(generateCombinedPost.recomputations()).toBe(0);
let combinedId = initialCombinedId;
generateCombinedPost(initialState, combinedId);
expect(generateCombinedPost.recomputations()).toBe(1);
combinedId = 'user-activity-post2';
generateCombinedPost(initialState, combinedId);
expect(generateCombinedPost.recomputations()).toBe(2);
});
test('should not recalculate when a different post changes', () => {
const generateCombinedPost = makeGenerateCombinedPost();
expect(generateCombinedPost.recomputations()).toBe(0);
let state = initialState;
generateCombinedPost(state, initialCombinedId);
expect(generateCombinedPost.recomputations()).toBe(1);
state = {
...state,
entities: {
...state.entities,
posts: {
...state.entities.posts,
posts: {
...state.entities.posts.posts,
post3: {id: 'post3'},
},
},
},
};
generateCombinedPost(state, initialCombinedId);
expect(generateCombinedPost.recomputations()).toBe(1);
});
test('should recalculate when one of the included posts change', () => {
const generateCombinedPost = makeGenerateCombinedPost();
expect(generateCombinedPost.recomputations()).toBe(0);
let state = initialState;
generateCombinedPost(state, initialCombinedId);
expect(generateCombinedPost.recomputations()).toBe(1);
state = {
...state,
entities: {
...state.entities,
posts: {
...state.entities.posts,
posts: {
...state.entities.posts.posts,
post2: {id: 'post2', update_at: 1234},
},
},
},
};
generateCombinedPost(state, initialCombinedId);
expect(generateCombinedPost.recomputations()).toBe(2);
});
});
});
describe('combineUserActivitySystemPost', () => {
const PostTypes = Posts.POST_TYPES;
it('should return null', () => {
assert.equal(Boolean(combineUserActivitySystemPost()), false);
assert.equal(Boolean(combineUserActivitySystemPost([])), false);
});
const postAddToChannel1 = {type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_1', addedUsername: 'added_username_1'}};
const postAddToChannel2 = {type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_2', addedUsername: 'added_username_2'}};
const postAddToChannel3 = {type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_3', addedUsername: 'added_username_3'}};
const postAddToChannel4 = {type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_2', props: {addedUserId: 'added_user_id_4', addedUsername: 'added_username_4'}};
const postAddToChannel5 = {type: PostTypes.ADD_TO_CHANNEL, user_id: 'user_id_1', props: {addedUsername: 'added_username_1'}};
it('should match return for ADD_TO_CHANNEL', () => {
const out1 = {
allUserIds: ['added_user_id_1', 'user_id_1'],
allUsernames: [],
messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1']}],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToChannel1]), out1);
const out2 = {
allUserIds: ['added_user_id_1', 'added_user_id_2', 'user_id_1'],
allUsernames: [],
messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1', 'added_user_id_2']}],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToChannel1, postAddToChannel2]), out2);
const out3 = {
allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1'],
allUsernames: [],
messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']}],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToChannel1, postAddToChannel2, postAddToChannel3]), out3);
const out4 = {
allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1', 'added_user_id_4', 'user_id_2'],
allUsernames: [],
messageData: [
{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']},
{actorId: 'user_id_2', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_4']},
],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToChannel1, postAddToChannel2, postAddToChannel3, postAddToChannel4]), out4);
const out5 = {
allUserIds: ['user_id_1'],
allUsernames: ['added_username_1'],
messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_username_1']}],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToChannel5]), out5);
const out6 = {
allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1', 'added_user_id_4', 'user_id_2'],
allUsernames: ['added_username_1'],
messageData: [
{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_username_1', 'added_user_id_1', 'added_user_id_2', 'added_user_id_3']},
{actorId: 'user_id_2', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_4']},
],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToChannel1, postAddToChannel2, postAddToChannel3, postAddToChannel4, postAddToChannel5]), out6);
});
it('should match return for ADD_TO_CHANNEL, backward compatibility with addedUsername', () => {
const out1 = {
allUserIds: ['user_id_1'],
allUsernames: ['added_user_name_1'],
messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_name_1']}],
};
assert.deepEqual(combineUserActivitySystemPost([{...postAddToChannel1, props: {addedUsername: 'added_user_name_1'}}]), out1);
const out2 = {
allUserIds: ['added_user_id_2', 'user_id_1'],
allUsernames: ['added_user_name_1'],
messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_name_1', 'added_user_id_2']}],
};
assert.deepEqual(combineUserActivitySystemPost([{...postAddToChannel1, props: {addedUsername: 'added_user_name_1'}}, postAddToChannel2]), out2);
const out3 = {
allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1', 'user_id_2'],
allUsernames: ['added_user_name_4'],
messageData: [
{actorId: 'user_id_1', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']},
{actorId: 'user_id_2', postType: PostTypes.ADD_TO_CHANNEL, userIds: ['added_user_name_4']},
],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToChannel1, postAddToChannel2, postAddToChannel3, {...postAddToChannel4, props: {addedUsername: 'added_user_name_4'}}]), out3);
});
const postAddToTeam1 = {type: PostTypes.ADD_TO_TEAM, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_1'}};
const postAddToTeam2 = {type: PostTypes.ADD_TO_TEAM, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_2'}};
const postAddToTeam3 = {type: PostTypes.ADD_TO_TEAM, user_id: 'user_id_1', props: {addedUserId: 'added_user_id_3'}};
const postAddToTeam4 = {type: PostTypes.ADD_TO_TEAM, user_id: 'user_id_2', props: {addedUserId: 'added_user_id_4'}};
it('should match return for ADD_TO_TEAM', () => {
const out1 = {
allUserIds: ['added_user_id_1', 'user_id_1'],
allUsernames: [],
messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_1']}],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToTeam1]), out1);
const out2 = {
allUserIds: ['added_user_id_1', 'added_user_id_2', 'user_id_1'],
allUsernames: [],
messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_1', 'added_user_id_2']}],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToTeam1, postAddToTeam2]), out2);
const out3 = {
allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1'],
allUsernames: [],
messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']}],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToTeam1, postAddToTeam2, postAddToTeam3]), out3);
const out4 = {
allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1', 'added_user_id_4', 'user_id_2'],
allUsernames: [],
messageData: [
{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']},
{actorId: 'user_id_2', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_4']},
],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToTeam1, postAddToTeam2, postAddToTeam3, postAddToTeam4]), out4);
});
it('should match return for ADD_TO_TEAM, backward compatibility with addedUsername', () => {
const out1 = {
allUserIds: ['user_id_1'],
allUsernames: ['added_user_name_1'],
messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_name_1']}],
};
assert.deepEqual(combineUserActivitySystemPost([{...postAddToTeam1, props: {addedUsername: 'added_user_name_1'}}]), out1);
const out2 = {
allUserIds: ['added_user_id_2', 'user_id_1'],
allUsernames: ['added_user_name_1'],
messageData: [{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_name_1', 'added_user_id_2']}],
};
assert.deepEqual(combineUserActivitySystemPost([{...postAddToTeam1, props: {addedUsername: 'added_user_name_1'}}, postAddToTeam2]), out2);
const out3 = {
allUserIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3', 'user_id_1', 'user_id_2'],
allUsernames: ['added_user_name_4'],
messageData: [
{actorId: 'user_id_1', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_id_1', 'added_user_id_2', 'added_user_id_3']},
{actorId: 'user_id_2', postType: PostTypes.ADD_TO_TEAM, userIds: ['added_user_name_4']},
],
};
assert.deepEqual(combineUserActivitySystemPost([postAddToTeam1, postAddToTeam2, postAddToTeam3, {...postAddToTeam4, props: {addedUsername: 'added_user_name_4'}}]), out3);
});
const postJoinChannel1 = {type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_1'};
const postJoinChannel2 = {type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_2'};
const postJoinChannel3 = {type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_3'};
const postJoinChannel4 = {type: PostTypes.JOIN_CHANNEL, user_id: 'user_id_4'};
it('should match return for JOIN_CHANNEL', () => {
const out1 = {
allUserIds: ['user_id_1'],
allUsernames: [],
messageData: [{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1']}],
};
assert.deepEqual(combineUserActivitySystemPost([postJoinChannel1]), out1);
const out2 = {
allUserIds: ['user_id_1', 'user_id_2'],
allUsernames: [],
messageData: [{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1', 'user_id_2']}],
};
assert.deepEqual(combineUserActivitySystemPost([postJoinChannel1, postJoinChannel2]), out2);
const out3 = {
allUserIds: ['user_id_1', 'user_id_2', 'user_id_3'],
allUsernames: [],
messageData: [{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1', 'user_id_2', 'user_id_3']}],
};
assert.deepEqual(combineUserActivitySystemPost([postJoinChannel1, postJoinChannel2, postJoinChannel3]), out3);
const out4 = {
allUserIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4'],
allUsernames: [],
messageData: [{postType: PostTypes.JOIN_CHANNEL, userIds: ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4']}],
};
assert.deepEqual(combineUserActivitySystemPost([postJoinChannel1, postJoinChannel2, postJoinChannel3, postJoinChannel4]), out4);
});
const postJoinTeam1 = {type: PostTypes.JOIN_TEAM, user_id: 'user_id_1'};
const postJoinTeam2 = {type: PostTypes.JOIN_TEAM, user_id: 'user_id_2'};
const postJoinTeam3 = {type: PostTypes.JOIN_TEAM, user_id: 'user_id_3'};
const postJoinTeam4 = {type: PostTypes.JOIN_TEAM, user_id: 'user_id_4'};
it('should match return for JOIN_TEAM', () => {
const out1 = {
allUserId