mattermost-redux
Version:
Common code (API client, Redux stores, logic, utility functions) for building a Mattermost client
1,196 lines (1,004 loc) • 66.6 kB
JavaScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {General, Preferences} from '../../constants';
import {CategoryTypes} from '../../constants/channel_categories';
import {MarkUnread} from '../../constants/channels';
import {getCurrentChannelId, getMyChannelMemberships} from 'selectors/entities/channels';
import {getConfig} from 'selectors/entities/general';
import {getLastPostPerChannel} from 'selectors/entities/posts';
import {getMyPreferences} from 'selectors/entities/preferences';
import {getCurrentUserId} from 'selectors/entities/users';
import mergeObjects from 'test/merge_objects';
import {CategorySorting} from 'types/channel_categories';
import {isGroupOrDirectChannelVisible} from 'utils/channel_utils';
import {getPreferenceKey} from 'utils/preference_utils';
import * as Selectors from './channel_categories';
describe('getCategoryInTeamByType', () => {
const favoritesCategory1 = {id: 'favoritesCategory1', team_id: 'team1', type: CategoryTypes.FAVORITES};
const channelsCategory1 = {id: 'channelsCategory1', team_id: 'team1', type: CategoryTypes.CHANNELS};
const directMessagesCategory1 = {id: 'directMessagesCategory1', team_id: 'team1', type: CategoryTypes.DIRECT_MESSAGES};
const channelsCategory2 = {id: 'channelsCategory2', team_id: 'team2', type: CategoryTypes.CHANNELS};
const state = {
entities: {
channelCategories: {
byId: {
channelsCategory1,
channelsCategory2,
directMessagesCategory1,
favoritesCategory1,
},
},
},
};
test('should return categories from each team', () => {
expect(Selectors.getCategoryInTeamByType(state, 'team1', CategoryTypes.FAVORITES)).toBe(favoritesCategory1);
expect(Selectors.getCategoryInTeamByType(state, 'team1', CategoryTypes.CHANNELS)).toBe(channelsCategory1);
expect(Selectors.getCategoryInTeamByType(state, 'team1', CategoryTypes.DIRECT_MESSAGES)).toBe(directMessagesCategory1);
expect(Selectors.getCategoryInTeamByType(state, 'team2', CategoryTypes.CHANNELS)).toBe(channelsCategory2);
});
test('should return null for a team that does not exist', () => {
expect(Selectors.getCategoryInTeamByType(state, 'team3', CategoryTypes.CHANNELS)).toBeUndefined();
});
test('should return null for a category that does not exist', () => {
expect(Selectors.getCategoryInTeamByType(state, 'team2', CategoryTypes.FAVORITES)).toBeUndefined();
});
});
describe('getCategoryInTeamWithChannel', () => {
const category1 = {id: 'category1', team_id: 'team1', channel_ids: ['channel1', 'channel2']};
const category2 = {id: 'category2', team_id: 'team1', channel_ids: ['dmChannel1']};
const category3 = {id: 'category3', team_id: 'team2', channel_ids: ['dmChannel1']};
const state = {
entities: {
channelCategories: {
byId: {
category1,
category2,
category3,
},
},
},
};
test('should return the category containing a given channel', () => {
expect(Selectors.getCategoryInTeamWithChannel(state, 'team1', 'channel1')).toBe(category1);
expect(Selectors.getCategoryInTeamWithChannel(state, 'team1', 'channel2')).toBe(category1);
});
test('should return the category on the correct team for a cross-team channel', () => {
expect(Selectors.getCategoryInTeamWithChannel(state, 'team1', 'dmChannel1')).toBe(category2);
expect(Selectors.getCategoryInTeamWithChannel(state, 'team2', 'dmChannel1')).toBe(category3);
});
});
describe('makeGetCategoriesForTeam', () => {
const category1 = {id: 'category1', display_name: 'Category One', type: CategoryTypes.CUSTOM};
const category2 = {id: 'category2', display_name: 'Category Two', type: CategoryTypes.CUSTOM};
const state = {
entities: {
channelCategories: {
byId: {
category1,
category2,
},
orderByTeam: {
team1: [category2.id, category1.id],
},
},
},
};
test('should return categories for team in order', () => {
const getCategoriesForTeam = Selectors.makeGetCategoriesForTeam();
expect(getCategoriesForTeam(state, 'team1')).toEqual([
state.entities.channelCategories.byId.category2,
state.entities.channelCategories.byId.category1,
]);
});
test('should memoize properly', () => {
const getCategoriesForTeam = Selectors.makeGetCategoriesForTeam();
const result = getCategoriesForTeam(state, 'team1');
// Repeat calls should return the same array
expect(getCategoriesForTeam(state, 'team1')).toBe(result);
// Calls to a difference instance of the selector won't return the same array
expect(result).not.toBe(Selectors.makeGetCategoriesForTeam()(state, 'team1'));
// Calls with different arguments won't return the same array
expect(getCategoriesForTeam(state, 'team2')).not.toBe(result);
// Calls after different argumetns won't return the same array
expect(getCategoriesForTeam(state, 'team1')).not.toBe(result);
});
});
describe('legacyMakeFilterAutoclosedDMs', () => {
const currentUser = {id: 'currentUser'};
const baseState = {
entities: {
channels: {
currentChannelId: 'channel1',
myMembers: {
channel1: {},
},
},
general: {
config: {
CloseUnusedDirectMessages: 'true',
},
},
posts: {
posts: {},
postsInChannel: {
channel1: [],
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_AUTOCLOSE_DMS)]: {value: Preferences.AUTOCLOSE_DMS_ENABLED},
},
},
users: {
currentUserId: currentUser.id,
profiles: {
currentUser,
},
},
},
};
const now = Date.now();
const cutoff = now - (7 * 24 * 60 * 60 * 1000);
function isChannelVisiblePrecondition(state, channel) {
return isGroupOrDirectChannelVisible(
channel,
getMyChannelMemberships(state),
getConfig(state),
getMyPreferences(state),
getCurrentUserId(state),
state.entities.users.profiles,
getLastPostPerChannel(state),
getCurrentChannelId(state),
now,
);
}
test('should hide an inactive GM channel', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const gmChannel = {id: 'gmChannel', type: General.GM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
},
},
},
});
expect(isChannelVisiblePrecondition(state, gmChannel)).toBe(false);
expect(filterAutoclosedDMs(state, [gmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([]);
});
test('should show a GM channel if it was opened recently', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const gmChannel = {id: 'gmChannel', type: General.GM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, gmChannel.id)]: {value: `${cutoff + 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
},
},
},
});
expect(isChannelVisiblePrecondition(state, gmChannel)).toBe(true);
expect(filterAutoclosedDMs(state, [gmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([gmChannel]);
});
test('should show a GM channel if it was viewed recently', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const gmChannel = {id: 'gmChannel', type: General.GM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, gmChannel.id)]: {value: `${cutoff + 1}`},
},
},
},
});
expect(isChannelVisiblePrecondition(state, gmChannel)).toBe(true);
expect(filterAutoclosedDMs(state, [gmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([gmChannel]);
});
test('should show a GM channel if it had an unloaded post made recently', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const gmChannel = {id: 'gmChannel', type: General.GM_CHANNEL, last_post_at: cutoff + 1};
const state = mergeObjects(baseState, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
},
},
},
});
expect(isChannelVisiblePrecondition(state, gmChannel)).toBe(true);
expect(filterAutoclosedDMs(state, [gmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([gmChannel]);
});
test('should show a GM channel if it had a loaded post made recently', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const gmChannel = {id: 'gmChannel', type: General.GM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
posts: {
posts: {
post1: {id: 'post1', channel_id: gmChannel, create_at: cutoff + 1},
},
postsInChannel: {
gmChannel: [{order: ['post1'], recent: true}],
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
},
},
},
});
expect(isChannelVisiblePrecondition(state, gmChannel)).toBe(true);
expect(filterAutoclosedDMs(state, [gmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([gmChannel]);
});
test('should show an inactive GM channel if autoclosing DMs is disabled for the user', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const gmChannel = {id: 'gmChannel', type: General.GM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_AUTOCLOSE_DMS)]: {value: ''},
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
},
},
},
});
expect(isChannelVisiblePrecondition(state, gmChannel)).toBe(true);
expect(filterAutoclosedDMs(state, [gmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([gmChannel]);
});
test('should show an inactive GM channel if autoclosing DMs is disabled for the server', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const gmChannel = {id: 'gmChannel', type: General.GM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
general: {
config: {
CloseUnusedDirectMessages: 'false',
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_AUTOCLOSE_DMS)]: {value: Preferences.AUTOCLOSE_DMS_ENABLED},
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
},
},
},
});
expect(isChannelVisiblePrecondition(state, gmChannel)).toBe(true);
expect(filterAutoclosedDMs(state, [gmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([gmChannel]);
});
test('should show a GM channel if it has unread messages', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const gmChannel = {id: 'gmChannel', type: General.GM_CHANNEL, total_msg_count: 1};
const state = mergeObjects(baseState, {
entities: {
channels: {
myMembers: {
gmChannel: {msg_count: 0},
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, gmChannel.id)]: {value: `${cutoff - 1}`},
},
},
},
});
expect(isChannelVisiblePrecondition(state, gmChannel)).toBe(true);
expect(filterAutoclosedDMs(state, [gmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([gmChannel]);
});
test('should hide an inactive DM channel', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const otherUser = {id: 'otherUser', delete_at: 0};
const dmChannel = {id: 'dmChannel', name: `${currentUser.id}__${otherUser.id}`, type: General.DM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUser.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, dmChannel.id)]: {value: `${cutoff - 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, dmChannel.id)]: {value: `${cutoff - 1}`},
},
},
users: {
profiles: {
otherUser,
},
},
},
});
expect(isChannelVisiblePrecondition(state, dmChannel)).toBe(false);
expect(filterAutoclosedDMs(state, [dmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([]);
});
test('should show a DM channel if it was opened recently', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const otherUser = {id: 'otherUser', delete_at: 0};
const dmChannel = {id: 'dmChannel', name: `${currentUser.id}__${otherUser.id}`, type: General.DM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUser.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, dmChannel.id)]: {value: `${cutoff + 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, dmChannel.id)]: {value: `${cutoff - 1}`},
},
},
users: {
profiles: {
otherUser,
},
},
},
});
expect(isChannelVisiblePrecondition(state, dmChannel)).toBe(true);
expect(filterAutoclosedDMs(state, [dmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([dmChannel]);
});
test('should show a DM channel with a deactivated user if its the current channel', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const otherUser = {id: 'otherUser', delete_at: cutoff + 2};
const dmChannel = {id: 'dmChannel', name: `${currentUser.id}__${otherUser.id}`, type: General.DM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
channels: {
currentChannelId: 'dmChannel',
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUser.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, dmChannel.id)]: {value: `${cutoff + 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, dmChannel.id)]: {value: `${cutoff - 1}`},
},
},
users: {
profiles: {
otherUser,
},
},
},
});
expect(isChannelVisiblePrecondition(state, dmChannel)).toBe(true);
expect(filterAutoclosedDMs(state, [dmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([dmChannel]);
});
test('should hide a DM channel with a deactivated user if it is not the current channel', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const otherUser = {id: 'otherUser', delete_at: cutoff + 2};
const dmChannel = {id: 'dmChannel', name: `${currentUser.id}__${otherUser.id}`, type: General.DM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
channels: {
currentChannelId: 'channel2',
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUser.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, dmChannel.id)]: {value: `${cutoff + 1}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, dmChannel.id)]: {value: `${cutoff - 1}`},
},
},
users: {
profiles: {
otherUser,
},
},
},
});
expect(isChannelVisiblePrecondition(state, dmChannel)).toBe(false);
expect(filterAutoclosedDMs(state, [dmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([]);
});
test('should show a DM channel with a deactivated user if it is not the current channel but it has been opened since the user was deactivated', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const otherUser = {id: 'otherUser', delete_at: cutoff + 2};
const dmChannel = {id: 'dmChannel', name: `${currentUser.id}__${otherUser.id}`, type: General.DM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
channels: {
currentChannelId: 'dmChannel',
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUser.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, dmChannel.id)]: {value: `${cutoff + 3}`},
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, dmChannel.id)]: {value: `${cutoff - 1}`},
},
},
users: {
profiles: {
otherUser,
},
},
},
});
expect(isChannelVisiblePrecondition(state, dmChannel)).toBe(true);
expect(filterAutoclosedDMs(state, [dmChannel], CategoryTypes.DIRECT_MESSAGES)).toEqual([dmChannel]);
});
test('should return the original array when no items are removed', () => {
const filterAutoclosedDMs = Selectors.legacyMakeFilterAutoclosedDMs(() => cutoff);
const channel1 = {id: 'channel1', type: General.PUBLIC_CHANNEL};
const state = baseState;
const channels = [channel1];
expect(filterAutoclosedDMs(state, channels, CategoryTypes.DIRECT_MESSAGES)).toBe(channels);
});
});
describe('makeFilterAutoclosedDMs', () => {
const currentUser = {id: 'currentUser'};
const tigerKing = {id: 'tigerKing'};
const bojackHorseman = {id: 'bojackHorseman'};
const jeffWinger = {id: 'jeffWinger'};
const baseState = {
entities: {
channels: {
currentChannelId: 'channel1',
myMembers: {
channel2: {
channel_id: 'channel2',
last_viewed_at: 0,
},
channel1: {},
channel3: {},
},
},
general: {
config: {
CloseUnusedDirectMessages: 'true',
},
},
posts: {
posts: {},
postsInChannel: {
channel1: [],
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_AUTOCLOSE_DMS)]: {value: Preferences.AUTOCLOSE_DMS_ENABLED},
[getPreferenceKey(Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.LIMIT_VISIBLE_DMS_GMS)]: {value: '0'},
},
},
users: {
currentUserId: currentUser.id,
profiles: {
currentUser,
tigerKing,
bojackHorseman,
jeffWinger,
},
},
},
};
test('Should always show an unread channel', () => {
const filterAutoclosedDMs = Selectors.makeFilterAutoclosedDMs();
const gmChannel1 = {id: 'gmChannel1', type: General.GM_CHANNEL, total_msg_count: 5};
const gmChannel2 = {id: 'gmChannel2', type: General.GM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
channels: {
myMembers: {
gmChannel1: {msg_count: 1, notify_props: {mark_unread: MarkUnread.ALL}},
gmChannel2: {msg_count: 0, notify_props: {mark_unread: MarkUnread.ALL}},
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.LIMIT_VISIBLE_DMS_GMS)]: {value: '1'},
},
},
},
});
expect(filterAutoclosedDMs(state, [gmChannel1, gmChannel2], CategoryTypes.DIRECT_MESSAGES)).toEqual([gmChannel1]);
});
test('Should always show the current channel', () => {
const filterAutoclosedDMs = Selectors.makeFilterAutoclosedDMs();
const dmChannel1 = {id: 'dmChannel1', type: General.DM_CHANNEL, name: `${currentUser.id}__${jeffWinger.id}`};
const gmChannel1 = {id: 'gmChannel1', type: General.GM_CHANNEL};
let state = mergeObjects(baseState, {
entities: {
channels: {
currentChannelId: dmChannel1.id,
myMembers: {
[gmChannel1.id]: {last_viewed_at: 1000},
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.LIMIT_VISIBLE_DMS_GMS)]: {value: '1'},
},
},
},
});
expect(filterAutoclosedDMs(state, [dmChannel1, gmChannel1], CategoryTypes.DIRECT_MESSAGES)).toEqual([dmChannel1]);
state = mergeObjects(baseState, {
entities: {
channels: {
currentChannelId: gmChannel1.id,
myMembers: {
[dmChannel1.id]: {last_viewed_at: 1000},
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.LIMIT_VISIBLE_DMS_GMS)]: {value: '1'},
},
},
},
});
expect(filterAutoclosedDMs(state, [gmChannel1, dmChannel1], CategoryTypes.DIRECT_MESSAGES)).toEqual([gmChannel1]);
});
describe('Should always show the exact number of channels specified by the user', () => {
const filterAutoclosedDMs = Selectors.makeFilterAutoclosedDMs();
const dmChannel1 = {id: 'dmChannel1', type: General.DM_CHANNEL, name: `${currentUser.id}__${tigerKing.id}`};
const gmChannel1 = {id: 'gmChannel1', type: General.GM_CHANNEL, name: 'WhatsApp'};
const gmChannel2 = {id: 'gmChannel2', type: General.GM_CHANNEL, name: 'Telegram'};
const dmChannel2 = {id: 'dmChannel2', type: General.DM_CHANNEL, name: `${currentUser.id}__${bojackHorseman.id}`};
const dmChannel3 = {id: 'dmChannel3', type: General.DM_CHANNEL, name: `${currentUser.id}__${jeffWinger.id}`};
test('User specified 5 DMs to be shown', () => {
const state = mergeObjects(baseState, {
entities: {
channels: {
currentChannelId: dmChannel1.id,
myMembers: {
[dmChannel1.id]: {last_viewed_at: 1000},
[dmChannel2.id]: {last_viewed_at: 500},
[dmChannel3.id]: {last_viewed_at: 0},
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.LIMIT_VISIBLE_DMS_GMS)]: {value: '5'},
},
},
},
});
expect(filterAutoclosedDMs(state, [dmChannel1, gmChannel1, gmChannel2, dmChannel2, dmChannel3], CategoryTypes.DIRECT_MESSAGES)).toEqual([dmChannel1, gmChannel1, gmChannel2, dmChannel2, dmChannel3]);
});
test('User specified 2 DMs to be shown', () => {
const state = mergeObjects(baseState, {
entities: {
channels: {
currentChannelId: dmChannel1.id,
myMembers: {
[dmChannel1.id]: {last_viewed_at: 1000},
[dmChannel2.id]: {last_viewed_at: 500},
[dmChannel3.id]: {last_viewed_at: 0},
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.LIMIT_VISIBLE_DMS_GMS)]: {value: '2'},
},
},
},
});
expect(filterAutoclosedDMs(state, [dmChannel1, gmChannel1, gmChannel2, dmChannel2, dmChannel3], CategoryTypes.DIRECT_MESSAGES)).toEqual([dmChannel1, dmChannel2]);
});
});
test('should consider approximate view time and open time preferences for most recently viewed channel', () => {
const filterAutoclosedDMs = Selectors.makeFilterAutoclosedDMs();
const dmChannel1 = {id: 'dmChannel1', type: General.DM_CHANNEL, name: `${currentUser.id}__${tigerKing.id}`};
const dmChannel2 = {id: 'dmChannel2', type: General.DM_CHANNEL, name: `${currentUser.id}__${bojackHorseman.id}`};
const dmChannel3 = {id: 'dmChannel3', type: General.DM_CHANNEL, name: `${currentUser.id}__${jeffWinger.id}`};
let state = mergeObjects(baseState, {
entities: {
channels: {
channels: {
dmChannel1,
dmChannel2,
dmChannel3,
},
myMembers: {
[dmChannel1.id]: {last_viewed_at: 1000},
[dmChannel2.id]: {last_viewed_at: 500},
[dmChannel3.id]: {last_viewed_at: 0},
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.LIMIT_VISIBLE_DMS_GMS)]: {value: '2'},
},
},
},
});
expect(filterAutoclosedDMs(state, [dmChannel1, dmChannel2, dmChannel3], CategoryTypes.DIRECT_MESSAGES)).toEqual([dmChannel1, dmChannel2]);
state = mergeObjects(state, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_OPEN_TIME, dmChannel3.id)]: {value: '3000'},
},
},
},
});
expect(filterAutoclosedDMs(state, [dmChannel1, dmChannel2, dmChannel3], CategoryTypes.DIRECT_MESSAGES)).toEqual([dmChannel1, dmChannel3]);
state = mergeObjects(state, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_CHANNEL_APPROXIMATE_VIEW_TIME, dmChannel2.id)]: {value: '2000'},
},
},
},
});
expect(filterAutoclosedDMs(state, [dmChannel1, dmChannel2, dmChannel3], CategoryTypes.DIRECT_MESSAGES)).toEqual([dmChannel2, dmChannel3]);
});
});
describe('makeFilterManuallyClosedDMs', () => {
const currentUser = {id: 'currentUser'};
const otherUser1 = {id: 'otherUser1'};
const otherUser2 = {id: 'otherUser2'};
const baseState = {
entities: {
channels: {
myMembers: {},
},
preferences: {
myPreferences: {},
},
users: {
currentUserId: currentUser.id,
},
},
};
test('should filter DMs based on preferences', () => {
const filterManuallyClosedDMs = Selectors.makeFilterManuallyClosedDMs();
const dmChannel1 = {id: 'dmChannel1', type: General.DM_CHANNEL, name: `${currentUser.id}__${otherUser1.id}`};
const dmChannel2 = {id: 'dmChannel2', type: General.DM_CHANNEL, name: `${currentUser.id}__${otherUser2.id}`};
const state = mergeObjects(baseState, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUser1.id)]: {value: 'false'},
[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUser2.id)]: {value: 'true'},
},
},
},
});
expect(filterManuallyClosedDMs(state, [dmChannel1, dmChannel2])).toMatchObject([dmChannel2]);
});
test('should filter GMs based on preferences', () => {
const filterManuallyClosedDMs = Selectors.makeFilterManuallyClosedDMs();
const gmChannel1 = {id: 'gmChannel1', type: General.GM_CHANNEL};
const gmChannel2 = {id: 'gmChannel2', type: General.GM_CHANNEL};
const state = mergeObjects(baseState, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel1.id)]: {value: 'true'},
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel2.id)]: {value: 'false'},
},
},
},
});
expect(filterManuallyClosedDMs(state, [gmChannel1, gmChannel2])).toMatchObject([gmChannel1]);
});
test('should show unread DMs and GMs, regardless of preferences', () => {
const filterManuallyClosedDMs = Selectors.makeFilterManuallyClosedDMs();
const dmChannel1 = {id: 'dmChannel1', type: General.DM_CHANNEL, name: `${currentUser.id}__${otherUser1.id}`, total_msg_count: 1};
const dmChannel2 = {id: 'dmChannel2', type: General.DM_CHANNEL, name: `${currentUser.id}__${otherUser2.id}`, total_msg_count: 0};
const gmChannel1 = {id: 'gmChannel1', type: General.GM_CHANNEL, total_msg_count: 1};
const gmChannel2 = {id: 'gmChannel2', type: General.GM_CHANNEL, total_msg_count: 0};
const state = mergeObjects(baseState, {
entities: {
channels: {
myMembers: {
dmChannel1: {msg_count: 0},
dmChannel2: {msg_count: 0},
gmChannel1: {msg_count: 0},
gmChannel2: {msg_count: 0},
},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUser1.id)]: {value: 'false'},
[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUser2.id)]: {value: 'false'},
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel1.id)]: {value: 'false'},
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel2.id)]: {value: 'false'},
},
},
},
});
expect(filterManuallyClosedDMs(state, [dmChannel1, dmChannel2, gmChannel1, gmChannel2])).toEqual([dmChannel1, gmChannel1]);
});
test('should show the current channel, regardless of preferences', () => {
const filterManuallyClosedDMs = Selectors.makeFilterManuallyClosedDMs();
const dmChannel1 = {id: 'dmChannel1', type: General.DM_CHANNEL, name: `${currentUser.id}__${otherUser1.id}`};
const gmChannel1 = {id: 'gmChannel1', type: General.GM_CHANNEL};
let state = mergeObjects(baseState, {
entities: {
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, otherUser1.id)]: {value: 'false'},
[getPreferenceKey(Preferences.CATEGORY_GROUP_CHANNEL_SHOW, gmChannel1.id)]: {value: 'false'},
},
},
},
});
expect(filterManuallyClosedDMs(state, [dmChannel1, gmChannel1])).toEqual([]);
state = mergeObjects(baseState, {
entities: {
channels: {
currentChannelId: dmChannel1.id,
},
},
});
expect(filterManuallyClosedDMs(state, [dmChannel1, gmChannel1])).toEqual([dmChannel1]);
state = mergeObjects(baseState, {
entities: {
channels: {
currentChannelId: gmChannel1.id,
},
},
});
expect(filterManuallyClosedDMs(state, [dmChannel1, gmChannel1])).toEqual([gmChannel1]);
});
test('should not filter other channels', () => {
const filterManuallyClosedDMs = Selectors.makeFilterManuallyClosedDMs();
const channel1 = {id: 'channel1', type: General.OPEN_CHANNEL};
const channel2 = {id: 'channel2', type: General.PRIVATE_CHANNEL};
const state = baseState;
const channels = [channel1, channel2];
expect(filterManuallyClosedDMs(state, channels)).toBe(channels);
});
});
describe('makeSortChannelsByName', () => {
const currentUser = {id: 'currentUser', locale: 'en'};
const baseState = {
entities: {
channels: {
myMembers: {},
},
users: {
currentUserId: currentUser.id,
profiles: {
currentUser,
},
},
},
};
test('should sort channels by display name', () => {
const sortChannelsByName = Selectors.makeSortChannelsByName();
const channel1 = {id: 'channel1', display_name: 'Carrot'};
const channel2 = {id: 'channel2', display_name: 'Apple'};
const channel3 = {id: 'channel3', display_name: 'Banana'};
const channels = [channel1, channel2, channel3];
expect(sortChannelsByName(baseState, channels)).toEqual([channel2, channel3, channel1]);
});
test('should sort channels by display name with numbers', () => {
const sortChannelsByName = Selectors.makeSortChannelsByName();
const channel1 = {id: 'channel1', display_name: 'Channel 10'};
const channel2 = {id: 'channel2', display_name: 'Channel 1'};
const channel3 = {id: 'channel3', display_name: 'Channel 11'};
const channel4 = {id: 'channel4', display_name: 'Channel 1a'};
const channels = [channel1, channel2, channel3, channel4];
expect(sortChannelsByName(baseState, channels)).toEqual([channel2, channel4, channel1, channel3]);
});
test('should sort muted channels last', () => {
const sortChannelsByName = Selectors.makeSortChannelsByName();
const state = mergeObjects(baseState, {
entities: {
channels: {
myMembers: {
channel1: {notify_props: {mark_unread: MarkUnread.MENTION}},
channel3: {notify_props: {mark_unread: MarkUnread.MENTION}},
channel4: {notify_props: {mark_unread: MarkUnread.ALL}},
},
},
},
});
const channel1 = {id: 'channel1', display_name: 'Carrot'};
const channel2 = {id: 'channel2', display_name: 'Apple'};
const channel3 = {id: 'channel3', display_name: 'Banana'};
const channel4 = {id: 'channel4', display_name: 'Dragonfruit'};
const channels = [channel1, channel2, channel3, channel4];
expect(sortChannelsByName(state, channels)).toEqual([channel2, channel4, channel3, channel1]);
});
});
describe('makeSortChannelsByNameWithDMs', () => {
const currentUser = {id: 'currentUser', username: 'currentUser', first_name: 'Current', last_name: 'User', locale: 'en'};
const otherUser1 = {id: 'otherUser1', username: 'otherUser1', first_name: 'Other', last_name: 'User', locale: 'en'};
const otherUser2 = {id: 'otherUser2', username: 'otherUser2', first_name: 'Another', last_name: 'User', locale: 'en'};
const channel1 = {id: 'channel1', type: General.OPEN_CHANNEL, display_name: 'Zebra'};
const channel2 = {id: 'channel2', type: General.PRIVATE_CHANNEL, display_name: 'Aardvark'};
const channel3 = {id: 'channel3', type: General.OPEN_CHANNEL, display_name: 'Bear'};
const dmChannel1 = {id: 'dmChannel1', type: General.DM_CHANNEL, display_name: '', name: `${currentUser.id}__${otherUser1.id}`};
const dmChannel2 = {id: 'dmChannel2', type: General.DM_CHANNEL, display_name: '', name: `${otherUser2.id}__${currentUser.id}`};
const gmChannel1 = {id: 'gmChannel1', type: General.GM_CHANNEL, display_name: `${currentUser.username}, ${otherUser1.username}, ${otherUser2.username}`, name: 'gmChannel1'};
const baseState = {
entities: {
channels: {
myMembers: {},
},
general: {
config: {},
},
preferences: {
myPreferences: {
[getPreferenceKey(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.NAME_NAME_FORMAT)]: {value: Preferences.DISPLAY_PREFER_FULL_NAME},
},
},
users: {
currentUserId: currentUser.id,
profiles: {
currentUser,
otherUser1,
otherUser2,
},
},
},
};
test('should sort regular channels by display name', () => {
const sortChannelsByNameWithDMs = Selectors.makeSortChannelsByNameWithDMs();
expect(sortChannelsByNameWithDMs(baseState, [
channel1,
channel2,
channel3,
])).toMatchObject([
channel2, // Aardvark
channel3, // Bear
channel1, // Zebra
]);
});
test('should sort DM channels by the display name of the other user', () => {
const sortChannelsByNameWithDMs = Selectors.makeSortChannelsByNameWithDMs();
expect(sortChannelsByNameWithDMs(baseState, [
channel1,
channel2,
channel3,
dmChannel1,
dmChannel2,
])).toMatchObject([
channel2, // Aardvark
dmChannel2, // Another User
channel3, // Bear
dmChannel1, // Other User
channel1, // Zebra
]);
});
test('should sort GM channels by the display name of the other users', () => {
const sortChannelsByNameWithDMs = Selectors.makeSortChannelsByNameWithDMs();
let state = baseState;
expect(sortChannelsByNameWithDMs(state, [
channel1,
channel2,
channel3,
gmChannel1,
])).toMatchObject([
channel2, // Aardvark
gmChannel1, // Another User, Other User
channel3, // Bear
channel1, // Zebra
]);
state = mergeObjects(state, {
entities: {
users: {
currentUserId: otherUser2.id,
},
},
});
expect(sortChannelsByNameWithDMs(state, [
channel1,
channel2,
channel3,
gmChannel1,
])).toMatchObject([
channel2, // Aardvark
channel3, // Bear
gmChannel1, // Current User, Other User
channel1, // Zebra
]);
});
test('should sort muted channels last', () => {
const sortChannelsByNameWithDMs = Selectors.makeSortChannelsByNameWithDMs();
const state = mergeObjects(baseState, {
entities: {
channels: {
myMembers: {
channel3: {notify_props: {mark_unread: MarkUnread.MENTION}},
dmChannel1: {notify_props: {mark_unread: MarkUnread.MENTION}},
dmChannel2: {notify_props: {mark_unread: MarkUnread.ALL}},
gmChannel1: {notify_props: {mark_unread: MarkUnread.MENTION}},
},
},
},
});
expect(sortChannelsByNameWithDMs(state, [
channel1,
channel2,
channel3,
dmChannel1,
dmChannel2,
gmChannel1,
])).toMatchObject([
channel2, // Aardvark
dmChannel2, // Another User
channel1, // Zebra
gmChannel1, // Another User, Other User (Muted)
channel3, // Bear (Muted)
dmChannel1, // Other User (Muted)
]);
});
});
describe('makeSortChannelsByRecency', () => {
const channel1 = {id: 'channel1', display_name: 'Apple', last_post_at: 1000};
const channel2 = {id: 'channel2', display_name: 'Banana', last_post_at: 2000};
const channel3 = {id: 'channel3', display_name: 'Zucchini', last_post_at: 3000};
const baseState = {
entities: {
posts: {
posts: {},
postsInChannel: {},
},
},
};
test('should sort channels by their last_post_at when no posts are loaded', () => {
const sortChannelsByRecency = Selectors.makeSortChannelsByRecency();
const state = baseState;
expect(sortChannelsByRecency(state, [channel1, channel2, channel3])).toMatchObject([channel3, channel2, channel1]);
expect(sortChannelsByRecency(state, [channel3, channel2, channel1])).toMatchObject([channel3, channel2, channel1]);
});
test('should sort channels by their latest post when possible', () => {
const sortChannelsByRecency = Selectors.makeSortChannelsByRecency();
const state = mergeObjects(baseState, {
entities: {
posts: {
posts: {
post1: {id: 'post1', channel_id: 'channel1', create_at: 2500},
},
postsInChannel: {
channel1: [
{order: ['post1'], recent: true},
],
},
},
},
});
expect(sortChannelsByRecency(state, [channel1, channel2, channel3])).toMatchObject([channel3, channel1, channel2]);
expect(sortChannelsByRecency(state, [channel3, channel2, channel1])).toMatchObject([channel3, channel1, channel2]);
});
});
describe('makeGetChannelsForCategory', () => {
const currentUser = {id: 'currentUser', username: 'currentUser', first_name: 'Current', last_name: 'User', locale: 'en'};
const otherUser1 = {id: 'otherUser1', username: 'otherUser1', first_name: 'Other', last_name: 'User', locale: 'en'};
const otherUser2 = {id: 'otherUser2', username: 'otherUser2', first_name: 'Another', last_name: 'User', locale: 'en'};
const channel1 = {id: 'channel1', type: General.OPEN_CHANNEL, team_id: 'team1', display_name: 'Zebra', delete_at: 0};
const channel2 = {id: 'channel2', type: General.PRIVATE_CHANNEL, team_id: 'team1', display_name: 'Aardvark', delete_at: 0};
const channel3 = {id: 'channel3', type: General.OPEN_CHANNEL, team_id: 'team1', display_name: 'Bear', delete_at: 0};
const dmChannel1 = {id: 'dmChannel1', type: General.DM_CHANNEL, team_id: '', display_name: '', name: `${currentUser.id}__${otherUser1.id}`, delete_at: 0, last_post_at: 2000};
const dmChannel2 = {id: 'dmChannel2', type: General.DM_CHANNEL, team_id: '', display_name: '', name: `${otherUser2.id}__${currentUser.id}`, delete_at: 0};
const gmChannel1 = {id: 'gmChannel1', type: General.GM_CHANNEL, team_id: '', display_name: `${currentUser.username}, ${otherUser1.username}, ${otherUser2.username}`, name: 'gmChannel1', delete_at: 0};
const baseState = {
entities: {
channels: {
channels: {
channel1,
channel2,
channel3,
dmChannel1,
dmChannel2,
gmChannel1,
},
myMembers: {
[channel1.id]: {},
[channel2.id]: {},
[channel3.id]: {},
[dmChannel1.id]: {},
[dmChannel2.id]: