mattermost-redux
Version:
Common code (API client, Redux stores, logic, utility functions) for building a Mattermost client
1,391 lines (1,200 loc) • 142 kB
JavaScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import assert from 'assert';
import expect from 'expect';
import {
ChannelTypes,
GeneralTypes,
PostTypes,
} from 'action_types';
import {Posts} from '../../constants';
import * as reducers from 'reducers/entities/posts';
import deepFreeze from 'utils/deep_freeze';
describe('posts', () => {
for (const actionType of [
PostTypes.RECEIVED_POST,
PostTypes.RECEIVED_NEW_POST,
]) {
describe(`received a single post (${actionType})`, () => {
it('should add a new post', () => {
const state = deepFreeze({
post1: {id: 'post1'},
});
const nextState = reducers.handlePosts(state, {
type: actionType,
data: {id: 'post2'},
});
expect(nextState).not.toBe(state);
expect(nextState.post1).toBe(state.post1);
expect(nextState).toEqual({
post1: {id: 'post1'},
post2: {id: 'post2'},
});
});
it('should add a new pending post', () => {
const state = deepFreeze({
post1: {id: 'post1'},
});
const nextState = reducers.handlePosts(state, {
type: actionType,
data: {id: 'post2', pending_post_id: 'post2'},
});
expect(nextState).not.toBe(state);
expect(nextState.post1).toBe(state.post1);
expect(nextState).toEqual({
post1: {id: 'post1'},
post2: {id: 'post2', pending_post_id: 'post2'},
});
});
it('should update an existing post', () => {
const state = deepFreeze({
post1: {id: 'post1', message: '123'},
});
const nextState = reducers.handlePosts(state, {
type: actionType,
data: {id: 'post1', message: 'abc'},
});
expect(nextState).not.toBe(state);
expect(nextState.post1).not.toBe(state.post1);
expect(nextState).toEqual({
post1: {id: 'post1', message: 'abc'},
});
});
it('should remove any pending posts when receiving the actual post', () => {
const state = deepFreeze({
pending: {id: 'pending'},
});
const nextState = reducers.handlePosts(state, {
type: actionType,
data: {id: 'post1', pending_post_id: 'pending'},
});
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
post1: {id: 'post1', pending_post_id: 'pending'},
});
});
});
}
describe('received multiple posts', () => {
it('should do nothing when post list is empty', () => {
const state = deepFreeze({
post1: {id: 'post1'},
});
const nextState = reducers.handlePosts(state, {
type: PostTypes.RECEIVED_POSTS,
data: {
order: [],
posts: {},
},
});
expect(nextState).toBe(state);
});
it('should add new posts', () => {
const state = deepFreeze({
post1: {id: 'post1'},
});
const nextState = reducers.handlePosts(state, {
type: PostTypes.RECEIVED_POSTS,
data: {
order: ['post2', 'post3'],
posts: {
post2: {id: 'post2'},
post3: {id: 'post3'},
},
},
});
expect(nextState).not.toBe(state);
expect(nextState.post1).toBe(state.post1);
expect(nextState).toEqual({
post1: {id: 'post1'},
post2: {id: 'post2'},
post3: {id: 'post3'},
});
});
it('should update existing posts unless we have a more recent version', () => {
const state = deepFreeze({
post1: {id: 'post1', message: '123', update_at: 1000},
post2: {id: 'post2', message: '456', update_at: 1000},
});
const nextState = reducers.handlePosts(state, {
type: PostTypes.RECEIVED_POSTS,
data: {
order: ['post1', 'post2'],
posts: {
post1: {id: 'post1', message: 'abc', update_at: 2000},
post2: {id: 'post2', message: 'def', update_at: 500},
},
},
});
expect(nextState).not.toBe(state);
expect(nextState.post1).not.toBe(state.post1);
expect(nextState.post2).toBe(state.post2);
expect(nextState).toEqual({
post1: {id: 'post1', message: 'abc', update_at: 2000},
post2: {id: 'post2', message: '456', update_at: 1000},
});
});
it('should set state for deleted posts', () => {
const state = deepFreeze({
post1: {id: 'post1', message: '123', delete_at: 0, file_ids: ['file']},
post2: {id: 'post2', message: '456', delete_at: 0, has_reactions: true},
});
const nextState = reducers.handlePosts(state, {
type: PostTypes.RECEIVED_POSTS,
data: {
order: ['post1', 'post2'],
posts: {
post1: {id: 'post1', message: '123', delete_at: 2000, file_ids: ['file']},
post2: {id: 'post2', message: '456', delete_at: 500, has_reactions: true},
},
},
});
expect(nextState).not.toBe(state);
expect(nextState.post1).not.toBe(state.post1);
expect(nextState.post2).not.toBe(state.post2);
expect(nextState).toEqual({
post1: {id: 'post1', message: '123', delete_at: 2000, file_ids: [], has_reactions: false, state: Posts.POST_DELETED},
post2: {id: 'post2', message: '456', delete_at: 500, file_ids: [], has_reactions: false, state: Posts.POST_DELETED},
});
});
it('should remove any pending posts when receiving the actual post', () => {
const state = deepFreeze({
pending1: {id: 'pending1'},
pending2: {id: 'pending2'},
});
const nextState = reducers.handlePosts(state, {
type: PostTypes.RECEIVED_POSTS,
data: {
order: ['post1', 'post2'],
posts: {
post1: {id: 'post1', pending_post_id: 'pending1'},
post2: {id: 'post2', pending_post_id: 'pending2'},
},
},
});
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
post1: {id: 'post1', pending_post_id: 'pending1'},
post2: {id: 'post2', pending_post_id: 'pending2'},
});
});
it('should not add channelId entity to postsInChannel if there were no posts in channel and it has receivedNewPosts on action', () => {
const state = deepFreeze({
posts: {},
postsInChannel: {},
});
const action = {
type: PostTypes.RECEIVED_POSTS,
data: {
order: ['postId'],
posts: {
postId: {
id: 'postId',
},
},
},
channelId: 'channelId',
receivedNewPosts: true,
};
const nextState = reducers.handlePosts(state, action);
assert.deepEqual(nextState.postsInChannel, {});
});
});
describe(`deleting a post (${PostTypes.POST_DELETED})`, () => {
it('should mark the post as deleted and remove the rest of the thread', () => {
const state = deepFreeze({
post1: {id: 'post1', file_ids: ['file'], has_reactions: true},
comment1: {id: 'comment1', root_id: 'post1'},
comment2: {id: 'comment2', root_id: 'post1'},
});
const nextState = reducers.handlePosts(state, {
type: PostTypes.POST_DELETED,
data: {id: 'post1'},
});
expect(nextState).not.toBe(state);
expect(nextState.post1).not.toBe(state.post1);
expect(nextState).toEqual({
post1: {id: 'post1', file_ids: [], has_reactions: false, state: Posts.POST_DELETED},
});
});
it('should not remove the rest of the thread when deleting a comment', () => {
const state = deepFreeze({
post1: {id: 'post1'},
comment1: {id: 'comment1', root_id: 'post1'},
comment2: {id: 'comment2', root_id: 'post1'},
});
const nextState = reducers.handlePosts(state, {
type: PostTypes.POST_DELETED,
data: {id: 'comment1'},
});
expect(nextState).not.toBe(state);
expect(nextState.post1).toBe(state.post1);
expect(nextState.comment1).not.toBe(state.comment1);
expect(nextState.comment2).toBe(state.comment2);
expect(nextState).toEqual({
post1: {id: 'post1'},
comment1: {id: 'comment1', root_id: 'post1', file_ids: [], has_reactions: false, state: Posts.POST_DELETED},
comment2: {id: 'comment2', root_id: 'post1'},
});
});
it('should do nothing if the post is not loaded', () => {
const state = deepFreeze({
post1: {id: 'post1', file_ids: ['file'], has_reactions: true},
});
const nextState = reducers.handlePosts(state, {
type: PostTypes.POST_DELETED,
data: {id: 'post2'},
});
expect(nextState).toBe(state);
expect(nextState.post1).toBe(state.post1);
});
});
describe(`removing a post (${PostTypes.POST_REMOVED})`, () => {
it('should remove the post and the rest and the rest of the thread', () => {
const state = deepFreeze({
post1: {id: 'post1', file_ids: ['file'], has_reactions: true},
comment1: {id: 'comment1', root_id: 'post1'},
comment2: {id: 'comment2', root_id: 'post1'},
post2: {id: 'post2'},
});
const nextState = reducers.handlePosts(state, {
type: PostTypes.POST_REMOVED,
data: {id: 'post1'},
});
expect(nextState).not.toBe(state);
expect(nextState.post2).toBe(state.post2);
expect(nextState).toEqual({
post2: {id: 'post2'},
});
});
it('should not remove the rest of the thread when removing a comment', () => {
const state = deepFreeze({
post1: {id: 'post1'},
comment1: {id: 'comment1', root_id: 'post1'},
comment2: {id: 'comment2', root_id: 'post1'},
post2: {id: 'post2'},
});
const nextState = reducers.handlePosts(state, {
type: PostTypes.POST_REMOVED,
data: {id: 'comment1'},
});
expect(nextState).not.toBe(state);
expect(nextState.post1).toBe(state.post1);
expect(nextState.comment1).not.toBe(state.comment1);
expect(nextState.comment2).toBe(state.comment2);
expect(nextState).toEqual({
post1: {id: 'post1'},
comment2: {id: 'comment2', root_id: 'post1'},
post2: {id: 'post2'},
});
});
it('should do nothing if the post is not loaded', () => {
const state = deepFreeze({
post1: {id: 'post1', file_ids: ['file'], has_reactions: true},
});
const nextState = reducers.handlePosts(state, {
type: PostTypes.POST_REMOVED,
data: {id: 'post2'},
});
expect(nextState).toBe(state);
expect(nextState.post1).toBe(state.post1);
});
});
for (const actionType of [
ChannelTypes.RECEIVED_CHANNEL_DELETED,
ChannelTypes.DELETE_CHANNEL_SUCCESS,
ChannelTypes.LEAVE_CHANNEL,
]) {
describe(`when a channel is deleted (${actionType})`, () => {
it('should remove any posts in that channel', () => {
const state = deepFreeze({
post1: {id: 'post1', channel_id: 'channel1'},
post2: {id: 'post2', channel_id: 'channel1'},
post3: {id: 'post3', channel_id: 'channel2'},
});
const nextState = reducers.handlePosts(state, {
type: actionType,
data: {
id: 'channel1',
viewArchivedChannels: false,
},
});
expect(nextState).not.toBe(state);
expect(nextState.post3).toBe(state.post3);
expect(nextState).toEqual({
post3: {id: 'post3', channel_id: 'channel2'},
});
});
it('should do nothing if no posts in that channel are loaded', () => {
const state = deepFreeze({
post1: {id: 'post1', channel_id: 'channel1'},
post2: {id: 'post2', channel_id: 'channel1'},
post3: {id: 'post3', channel_id: 'channel2'},
});
const nextState = reducers.handlePosts(state, {
type: actionType,
data: {
id: 'channel3',
viewArchivedChannels: false,
},
});
expect(nextState).toBe(state);
expect(nextState.post1).toBe(state.post1);
expect(nextState.post2).toBe(state.post2);
expect(nextState.post3).toBe(state.post3);
});
it('should not remove any posts with viewArchivedChannels enabled', () => {
const state = deepFreeze({
post1: {id: 'post1', channel_id: 'channel1'},
post2: {id: 'post2', channel_id: 'channel1'},
post3: {id: 'post3', channel_id: 'channel2'},
});
const nextState = reducers.handlePosts(state, {
type: actionType,
data: {
id: 'channel1',
viewArchivedChannels: true,
},
});
expect(nextState).toBe(state);
expect(nextState.post1).toBe(state.post1);
expect(nextState.post2).toBe(state.post2);
expect(nextState.post3).toBe(state.post3);
});
});
}
});
describe('pendingPostIds', () => {
describe('making a new pending post', () => {
it('should add new entries for pending posts', () => {
const state = deepFreeze(['1234']);
const nextState = reducers.handlePendingPosts(state, {
type: PostTypes.RECEIVED_NEW_POST,
data: {
pending_post_id: 'abcd',
},
});
expect(nextState).not.toBe(state);
expect(nextState).toEqual(['1234', 'abcd']);
});
it('should not add duplicate entries', () => {
const state = deepFreeze(['1234']);
const nextState = reducers.handlePendingPosts(state, {
type: PostTypes.RECEIVED_NEW_POST,
data: {
pending_post_id: '1234',
},
});
expect(nextState).toBe(state);
expect(nextState).toEqual(['1234']);
});
it('should do nothing for regular posts', () => {
const state = deepFreeze(['1234']);
const nextState = reducers.handlePendingPosts(state, {
type: PostTypes.RECEIVED_NEW_POST,
data: {
id: 'abcd',
},
});
expect(nextState).toBe(state);
expect(nextState).toEqual(['1234']);
});
});
describe('removing a pending post', () => {
it('should remove an entry when its post is deleted', () => {
const state = deepFreeze(['1234', 'abcd']);
const nextState = reducers.handlePendingPosts(state, {
type: PostTypes.POST_REMOVED,
data: {
id: 'abcd',
},
});
expect(nextState).not.toBe(state);
expect(nextState).toEqual(['1234']);
});
it('should do nothing without an entry for the post', () => {
const state = deepFreeze(['1234', 'abcd']);
const nextState = reducers.handlePendingPosts(state, {
type: PostTypes.POST_REMOVED,
data: {
id: 'wxyz',
},
});
expect(nextState).toBe(state);
expect(nextState).toEqual(['1234', 'abcd']);
});
});
describe('marking a pending post as completed', () => {
it('should remove an entry when its post is successfully created', () => {
const state = deepFreeze(['1234', 'abcd']);
const nextState = reducers.handlePendingPosts(state, {
type: PostTypes.RECEIVED_POST,
data: {
id: 'post',
pending_post_id: 'abcd',
},
});
expect(nextState).not.toBe(state);
expect(nextState).toEqual(['1234']);
});
it('should do nothing without an entry for the post', () => {
const state = deepFreeze(['1234', 'abcd']);
const nextState = reducers.handlePendingPosts(state, {
type: PostTypes.RECEIVED_POST,
data: {
id: 'post',
pending_post_id: 'wxyz',
},
});
expect(nextState).toBe(state);
expect(nextState).toEqual(['1234', 'abcd']);
});
it('should do nothing when receiving a non-pending post', () => {
const state = deepFreeze(['1234', 'abcd']);
const nextState = reducers.handlePendingPosts(state, {
type: PostTypes.RECEIVED_POST,
data: {
id: 'post',
},
});
expect(nextState).toBe(state);
expect(nextState).toEqual(['1234', 'abcd']);
});
});
});
describe('postsInChannel', () => {
describe('receiving a new post', () => {
it('should do nothing without posts loaded for the channel', () => {
const state = deepFreeze({});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_NEW_POST,
data: {id: 'post1', channel_id: 'channel1'},
}, {}, {});
expect(nextState).toBe(state);
expect(nextState).toEqual({});
});
it('should store the new post when the channel is empty', () => {
const state = deepFreeze({
channel1: [],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_NEW_POST,
data: {id: 'post1', channel_id: 'channel1'},
});
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1'], recent: true},
],
});
});
it('should store the new post when the channel has recent posts', () => {
const state = deepFreeze({
channel1: [
{order: ['post2', 'post3'], recent: true},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_NEW_POST,
data: {id: 'post1', channel_id: 'channel1'},
});
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true},
],
});
});
it('should not store the new post when the channel only has older posts', () => {
const state = deepFreeze({
channel1: [
{order: ['post2', 'post3'], recent: false},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_NEW_POST,
data: {id: 'post1', channel_id: 'channel1'},
});
expect(nextState).toEqual({
channel1: [
{order: ['post2', 'post3'], recent: false}, {order: ['post1'], recent: true},
],
});
});
it('should do nothing for a duplicate post', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_NEW_POST,
data: {id: 'post1', channel_id: 'channel1'},
});
expect(nextState).toBe(state);
});
it('should remove a previously pending post', () => {
const state = deepFreeze({
channel1: [
{order: ['pending', 'post2', 'post1'], recent: true},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_NEW_POST,
data: {id: 'post3', channel_id: 'channel1', pending_post_id: 'pending'},
}, {}, {post1: {create_at: 1}, post2: {create_at: 2}, post3: {create_at: 3}});
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post3', 'post2', 'post1'], recent: true},
],
});
});
it('should just add the new post if the pending post was already removed', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'post2'], recent: true},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_NEW_POST,
data: {id: 'post3', channel_id: 'channel1', pending_post_id: 'pending'},
});
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post3', 'post1', 'post2'], recent: true},
],
});
});
it('should not include a previously removed post', () => {
const state = deepFreeze({
channel1: [
{order: ['post1'], recent: true},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.POST_REMOVED,
data: {id: 'post1', channel_id: 'channel1'},
});
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [{
order: [],
recent: true,
}],
});
});
});
describe('receiving a single post', () => {
it('should replace a previously pending post', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'pending', 'post2'], recent: true},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POST,
data: {id: 'post3', channel_id: 'channel1', pending_post_id: 'pending'},
});
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post3', 'post2'], recent: true},
],
});
});
it('should do nothing for a pending post that was already removed', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'post2'], recent: true},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POST,
data: {id: 'post3', channel_id: 'channel1', pending_post_id: 'pending'},
});
expect(nextState).toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2'], recent: true},
],
});
});
it('should do nothing for a post that was not previously pending', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'pending', 'post2'], recent: true},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POST,
data: {id: 'post3', channel_id: 'channel1'},
});
expect(nextState).toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'pending', 'post2'], recent: true},
],
});
});
it('should do nothing for a post without posts loaded for the channel', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'post2'], recent: true},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POST,
data: {id: 'post3', channel_id: 'channel2', pending_post_id: 'pending'},
});
expect(nextState).toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2'], recent: true},
],
});
});
});
describe('receiving consecutive recent posts in the channel', () => {
it('should save posts in the correct order', () => {
const state = deepFreeze({
channel1: [
{order: ['post2', 'post4'], recent: true},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post1: nextPosts.post1,
post3: nextPosts.post3,
},
order: ['post1', 'post3'],
},
recent: true,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3', 'post4'], recent: true},
],
});
});
it('should not save duplicate posts', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post2: nextPosts.post2,
post4: nextPosts.post4,
},
order: ['post2', 'post4'],
},
recent: true,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3', 'post4'], recent: true},
],
});
});
it('should do nothing when receiving no posts for loaded channel', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {},
order: [],
},
recent: true,
}, null, {});
expect(nextState).toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true},
],
});
});
it('should make entry for channel with no posts', () => {
const state = deepFreeze({});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {},
order: [],
},
recent: true,
}, null, {});
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [{
order: [],
recent: true,
}],
});
});
it('should not save posts that are not in data.order', () => {
const state = deepFreeze({
channel1: [
{order: ['post2', 'post3'], recent: true},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post1: nextPosts.post1,
post2: nextPosts.post2,
post3: nextPosts.post3,
post4: nextPosts.post4,
},
order: ['post1', 'post2'],
},
recent: true,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true},
],
});
});
it('should not save posts in an older block, even if they may be adjacent', () => {
const state = deepFreeze({
channel1: [
{order: ['post3', 'post4'], recent: false},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post1: nextPosts.post1,
post2: nextPosts.post2,
},
order: ['post1', 'post2'],
},
recent: true,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post3', 'post4'], recent: false},
{order: ['post1', 'post2'], recent: true},
],
});
});
it('should not save posts in the recent block even if new posts may be adjacent', () => {
const state = deepFreeze({
channel1: [
{order: ['post3', 'post4'], recent: true},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post1: nextPosts.post1,
post2: nextPosts.post2,
},
order: ['post1', 'post2'],
},
recent: true,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post3', 'post4'], recent: false},
{order: ['post1', 'post2'], recent: true},
],
});
});
it('should add posts to non-recent block if there is overlap', () => {
const state = deepFreeze({
channel1: [
{order: ['post2', 'post3'], recent: false},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post1: nextPosts.post1,
post2: nextPosts.post2,
},
order: ['post1', 'post2'],
},
recent: true,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true},
],
});
});
});
describe('receiving consecutive posts in the channel that are not recent', () => {
it('should save posts in the correct order', () => {
const state = deepFreeze({
channel1: [
{order: ['post2', 'post4'], recent: false},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post1: nextPosts.post1,
post3: nextPosts.post3,
},
order: ['post1', 'post3'],
},
recent: false,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3', 'post4'], recent: false},
],
});
});
it('should not save duplicate posts', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: false},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post2: nextPosts.post2,
post4: nextPosts.post4,
},
order: ['post2', 'post4'],
},
recent: false,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3', 'post4'], recent: false},
],
});
});
it('should do nothing when receiving no posts for loaded channel', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true},
],
});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {},
order: [],
},
recent: false,
}, null, {});
expect(nextState).toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true},
],
});
});
it('should make entry for channel with no posts', () => {
const state = deepFreeze({});
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {},
order: [],
},
recent: false,
}, null, {});
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [{
order: [],
recent: false,
}],
});
});
it('should not save posts that are not in data.order', () => {
const state = deepFreeze({
channel1: [
{order: ['post2', 'post3'], recent: false},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post1: nextPosts.post1,
post2: nextPosts.post2,
post3: nextPosts.post3,
post4: nextPosts.post4,
},
order: ['post1', 'post2'],
},
recent: false,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: false},
],
});
});
it('should not save posts in another block without overlap', () => {
const state = deepFreeze({
channel1: [
{order: ['post3', 'post4'], recent: false},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post1: nextPosts.post1,
post2: nextPosts.post2,
},
order: ['post1', 'post2'],
},
recent: false,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post3', 'post4'], recent: false},
{order: ['post1', 'post2'], recent: false},
],
});
});
it('should add posts to recent block if there is overlap', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'post2'], recent: true},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post2: nextPosts.post2,
post3: nextPosts.post3,
},
order: ['post2', 'post3'],
},
recent: false,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true},
],
});
});
it('should save with chunk as oldest', () => {
const state = deepFreeze({
channel1: [
{order: ['post1', 'post2'], recent: true},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_IN_CHANNEL,
channelId: 'channel1',
data: {
posts: {
post2: nextPosts.post2,
post3: nextPosts.post3,
},
order: ['post2', 'post3'],
},
recent: false,
oldest: true,
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true, oldest: true},
],
});
});
});
describe('receiving posts since', () => {
it('should save posts in the channel in the correct order', () => {
const state = deepFreeze({
channel1: [
{order: ['post3', 'post4'], recent: true},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_SINCE,
channelId: 'channel1',
data: {
posts: {
post1: nextPosts.post1,
post2: nextPosts.post2,
},
order: ['post1', 'post2'],
},
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3', 'post4'], recent: true},
],
});
});
it('should not save older posts', () => {
const state = deepFreeze({
channel1: [
{order: ['post2', 'post3'], recent: true},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_SINCE,
channelId: 'channel1',
data: {
posts: {
post1: nextPosts.post1,
post4: nextPosts.post4,
},
order: ['post1', 'post4'],
},
}, null, nextPosts);
expect(nextState).not.toBe(state);
expect(nextState).toEqual({
channel1: [
{order: ['post1', 'post2', 'post3'], recent: true},
],
});
});
it('should save any posts in between', () => {
const state = deepFreeze({
channel1: [
{order: ['post2', 'post4'], recent: true},
],
});
const nextPosts = {
post1: {id: 'post1', channel_id: 'channel1', create_at: 4000},
post2: {id: 'post2', channel_id: 'channel1', create_at: 3000},
post3: {id: 'post3', channel_id: 'channel1', create_at: 2000},
post4: {id: 'post4', channel_id: 'channel1', create_at: 1000},
post5: {id: 'post5', channel_id: 'channel1', create_at: 500},
post6: {id: 'post6', channel_id: 'channel1', create_at: 300},
};
const nextState = reducers.postsInChannel(state, {
type: PostTypes.RECEIVED_POSTS_SINCE,