Add "new conversation" composer for direct messages
This commit is contained in:
parent
84dc166b63
commit
06fb4fd0bc
61 changed files with 5960 additions and 3887 deletions
|
@ -1,208 +0,0 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { LeftPane, RowType, HeaderType } from '../../components/LeftPane';
|
||||
import { setup as setupI18n } from '../../../js/modules/i18n';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
describe('LeftPane', () => {
|
||||
const defaultProps = {
|
||||
archivedConversations: [],
|
||||
conversations: [],
|
||||
i18n,
|
||||
openConversationInternal: () => null,
|
||||
pinnedConversations: [],
|
||||
renderExpiredBuildDialog: () => <div />,
|
||||
renderMainHeader: () => <div />,
|
||||
renderMessageSearchResult: () => <div />,
|
||||
renderNetworkStatus: () => <div />,
|
||||
renderRelinkDialog: () => <div />,
|
||||
renderUpdateDialog: () => <div />,
|
||||
showArchivedConversations: () => null,
|
||||
showInbox: () => null,
|
||||
startNewConversation: () => null,
|
||||
};
|
||||
|
||||
describe('getRowFromIndex', () => {
|
||||
describe('given only pinned chats', () => {
|
||||
it('returns pinned chats, not headers', () => {
|
||||
const leftPane = new LeftPane({
|
||||
...defaultProps,
|
||||
pinnedConversations: [
|
||||
{
|
||||
id: 'philly-convo',
|
||||
isPinned: true,
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Philip Glass',
|
||||
type: 'direct',
|
||||
},
|
||||
{
|
||||
id: 'robbo-convo',
|
||||
isPinned: true,
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Robert Moog',
|
||||
type: 'direct',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
assert.deepEqual(leftPane.getRowFromIndex(0), {
|
||||
index: 0,
|
||||
type: RowType.PinnedConversation,
|
||||
});
|
||||
assert.deepEqual(leftPane.getRowFromIndex(1), {
|
||||
index: 1,
|
||||
type: RowType.PinnedConversation,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('given only non-pinned chats', () => {
|
||||
it('returns conversations, not headers', () => {
|
||||
const leftPane = new LeftPane({
|
||||
...defaultProps,
|
||||
conversations: [
|
||||
{
|
||||
id: 'fred-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Fred Willard',
|
||||
type: 'direct',
|
||||
},
|
||||
{
|
||||
id: 'robbo-convo',
|
||||
isPinned: false,
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Robert Moog',
|
||||
type: 'direct',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
assert.deepEqual(leftPane.getRowFromIndex(0), {
|
||||
index: 0,
|
||||
type: RowType.Conversation,
|
||||
});
|
||||
assert.deepEqual(leftPane.getRowFromIndex(1), {
|
||||
index: 1,
|
||||
type: RowType.Conversation,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('given only pinned and non-pinned chats', () => {
|
||||
it('returns headers and conversations', () => {
|
||||
const leftPane = new LeftPane({
|
||||
...defaultProps,
|
||||
conversations: [
|
||||
{
|
||||
id: 'fred-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Fred Willard',
|
||||
type: 'direct',
|
||||
},
|
||||
],
|
||||
pinnedConversations: [
|
||||
{
|
||||
id: 'philly-convo',
|
||||
isPinned: true,
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Philip Glass',
|
||||
type: 'direct',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
assert.deepEqual(leftPane.getRowFromIndex(0), {
|
||||
headerType: HeaderType.Pinned,
|
||||
type: RowType.Header,
|
||||
});
|
||||
assert.deepEqual(leftPane.getRowFromIndex(1), {
|
||||
index: 0,
|
||||
type: RowType.PinnedConversation,
|
||||
});
|
||||
assert.deepEqual(leftPane.getRowFromIndex(2), {
|
||||
headerType: HeaderType.Chats,
|
||||
type: RowType.Header,
|
||||
});
|
||||
assert.deepEqual(leftPane.getRowFromIndex(3), {
|
||||
index: 0,
|
||||
type: RowType.Conversation,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('given not showing archive with archived conversation', () => {
|
||||
it('returns an archive button last', () => {
|
||||
const leftPane = new LeftPane({
|
||||
...defaultProps,
|
||||
archivedConversations: [
|
||||
{
|
||||
id: 'jerry-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Jerry Jordan',
|
||||
type: 'direct',
|
||||
},
|
||||
],
|
||||
conversations: [
|
||||
{
|
||||
id: 'fred-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Fred Willard',
|
||||
type: 'direct',
|
||||
},
|
||||
],
|
||||
showArchived: false,
|
||||
});
|
||||
|
||||
assert.deepEqual(leftPane.getRowFromIndex(1), {
|
||||
type: RowType.ArchiveButton,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('given showing archive and archive chats', () => {
|
||||
it('returns archived conversations', () => {
|
||||
const leftPane = new LeftPane({
|
||||
...defaultProps,
|
||||
archivedConversations: [
|
||||
{
|
||||
id: 'fred-convo',
|
||||
isSelected: false,
|
||||
lastUpdated: Date.now(),
|
||||
markedUnread: false,
|
||||
title: 'Fred Willard',
|
||||
type: 'direct',
|
||||
},
|
||||
],
|
||||
showArchived: true,
|
||||
});
|
||||
|
||||
assert.deepEqual(leftPane.getRowFromIndex(0), {
|
||||
index: 0,
|
||||
type: RowType.ArchivedConversation,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
162
ts/test-node/components/leftPane/LeftPaneArchiveHelper_test.ts
Normal file
162
ts/test-node/components/leftPane/LeftPaneArchiveHelper_test.ts
Normal file
|
@ -0,0 +1,162 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { RowType } from '../../../components/ConversationList';
|
||||
import { FindDirection } from '../../../components/leftPane/LeftPaneHelper';
|
||||
|
||||
import { LeftPaneArchiveHelper } from '../../../components/leftPane/LeftPaneArchiveHelper';
|
||||
|
||||
describe('LeftPaneArchiveHelper', () => {
|
||||
const fakeConversation = () => ({
|
||||
id: uuid(),
|
||||
title: uuid(),
|
||||
type: 'direct' as const,
|
||||
});
|
||||
|
||||
describe('getRowCount', () => {
|
||||
it('returns the number of archived conversations', () => {
|
||||
assert.strictEqual(
|
||||
new LeftPaneArchiveHelper({ archivedConversations: [] }).getRowCount(),
|
||||
0
|
||||
);
|
||||
assert.strictEqual(
|
||||
new LeftPaneArchiveHelper({
|
||||
archivedConversations: [fakeConversation(), fakeConversation()],
|
||||
}).getRowCount(),
|
||||
2
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRowIndexToScrollTo', () => {
|
||||
it('returns undefined if no conversation is selected', () => {
|
||||
const helper = new LeftPaneArchiveHelper({
|
||||
archivedConversations: [fakeConversation(), fakeConversation()],
|
||||
});
|
||||
|
||||
assert.isUndefined(helper.getRowIndexToScrollTo(undefined));
|
||||
});
|
||||
|
||||
it('returns undefined if the selected conversation is not pinned or non-pinned', () => {
|
||||
const helper = new LeftPaneArchiveHelper({
|
||||
archivedConversations: [fakeConversation(), fakeConversation()],
|
||||
});
|
||||
|
||||
assert.isUndefined(helper.getRowIndexToScrollTo(uuid()));
|
||||
});
|
||||
|
||||
it("returns the archived conversation's index", () => {
|
||||
const archivedConversations = [fakeConversation(), fakeConversation()];
|
||||
const helper = new LeftPaneArchiveHelper({ archivedConversations });
|
||||
|
||||
assert.strictEqual(
|
||||
helper.getRowIndexToScrollTo(archivedConversations[0].id),
|
||||
0
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getRowIndexToScrollTo(archivedConversations[1].id),
|
||||
1
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRow', () => {
|
||||
it('returns each conversation as a row', () => {
|
||||
const archivedConversations = [fakeConversation(), fakeConversation()];
|
||||
const helper = new LeftPaneArchiveHelper({ archivedConversations });
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Conversation,
|
||||
conversation: archivedConversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: archivedConversations[1],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConversationAndMessageAtIndex', () => {
|
||||
it('returns the conversation at the given index when it exists', () => {
|
||||
const archivedConversations = [fakeConversation(), fakeConversation()];
|
||||
const helper = new LeftPaneArchiveHelper({ archivedConversations });
|
||||
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(0)?.conversationId,
|
||||
archivedConversations[0].id
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(1)?.conversationId,
|
||||
archivedConversations[1].id
|
||||
);
|
||||
});
|
||||
|
||||
it('when requesting an index out of bounds, returns the last conversation', () => {
|
||||
const archivedConversations = [fakeConversation(), fakeConversation()];
|
||||
const helper = new LeftPaneArchiveHelper({ archivedConversations });
|
||||
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(2)?.conversationId,
|
||||
archivedConversations[1].id
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(99)?.conversationId,
|
||||
archivedConversations[1].id
|
||||
);
|
||||
|
||||
// This is mostly a resilience measure in case we're ever called with an invalid
|
||||
// index.
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(-1)?.conversationId,
|
||||
archivedConversations[1].id
|
||||
);
|
||||
});
|
||||
|
||||
it('returns undefined if there are no archived conversations', () => {
|
||||
const helper = new LeftPaneArchiveHelper({ archivedConversations: [] });
|
||||
|
||||
assert.isUndefined(helper.getConversationAndMessageAtIndex(0));
|
||||
assert.isUndefined(helper.getConversationAndMessageAtIndex(1));
|
||||
assert.isUndefined(helper.getConversationAndMessageAtIndex(-1));
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConversationAndMessageInDirection', () => {
|
||||
it('returns the next conversation when searching downward', () => {
|
||||
const archivedConversations = [fakeConversation(), fakeConversation()];
|
||||
const helper = new LeftPaneArchiveHelper({ archivedConversations });
|
||||
|
||||
assert.deepEqual(
|
||||
helper.getConversationAndMessageInDirection(
|
||||
{ direction: FindDirection.Down, unreadOnly: false },
|
||||
archivedConversations[0].id,
|
||||
undefined
|
||||
),
|
||||
{ conversationId: archivedConversations[1].id }
|
||||
);
|
||||
});
|
||||
|
||||
// Additional tests are found with `getConversationInDirection`.
|
||||
});
|
||||
|
||||
describe('shouldRecomputeRowHeights', () => {
|
||||
it('always returns false because row heights are constant', () => {
|
||||
const helper = new LeftPaneArchiveHelper({
|
||||
archivedConversations: [fakeConversation(), fakeConversation()],
|
||||
});
|
||||
|
||||
assert.isFalse(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
archivedConversations: [fakeConversation()],
|
||||
})
|
||||
);
|
||||
assert.isFalse(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
archivedConversations: [fakeConversation(), fakeConversation()],
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
144
ts/test-node/components/leftPane/LeftPaneComposeHelper_test.ts
Normal file
144
ts/test-node/components/leftPane/LeftPaneComposeHelper_test.ts
Normal file
|
@ -0,0 +1,144 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { RowType } from '../../../components/ConversationList';
|
||||
import { FindDirection } from '../../../components/leftPane/LeftPaneHelper';
|
||||
|
||||
import { LeftPaneComposeHelper } from '../../../components/leftPane/LeftPaneComposeHelper';
|
||||
|
||||
describe('LeftPaneComposeHelper', () => {
|
||||
const fakeContact = () => ({
|
||||
id: uuid(),
|
||||
title: uuid(),
|
||||
type: 'direct' as const,
|
||||
});
|
||||
|
||||
describe('getRowCount', () => {
|
||||
it('returns the number of contacts if not searching for a phone number', () => {
|
||||
assert.strictEqual(
|
||||
new LeftPaneComposeHelper({
|
||||
composeContacts: [],
|
||||
regionCode: 'US',
|
||||
searchTerm: 'foo bar',
|
||||
}).getRowCount(),
|
||||
0
|
||||
);
|
||||
assert.strictEqual(
|
||||
new LeftPaneComposeHelper({
|
||||
composeContacts: [fakeContact(), fakeContact()],
|
||||
regionCode: 'US',
|
||||
searchTerm: '',
|
||||
}).getRowCount(),
|
||||
2
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the number of contacts + 1 if searching for a phone number', () => {
|
||||
assert.strictEqual(
|
||||
new LeftPaneComposeHelper({
|
||||
composeContacts: [fakeContact(), fakeContact()],
|
||||
regionCode: 'US',
|
||||
searchTerm: '+16505551234',
|
||||
}).getRowCount(),
|
||||
3
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRow', () => {
|
||||
it('returns each contact as a row if not searching for a phone number', () => {
|
||||
const composeContacts = [fakeContact(), fakeContact()];
|
||||
const helper = new LeftPaneComposeHelper({
|
||||
composeContacts,
|
||||
regionCode: 'US',
|
||||
searchTerm: '',
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Contact,
|
||||
contact: composeContacts[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Contact,
|
||||
contact: composeContacts[1],
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a "start new conversation" row if searching for a phone number', () => {
|
||||
const composeContacts = [fakeContact(), fakeContact()];
|
||||
const helper = new LeftPaneComposeHelper({
|
||||
composeContacts,
|
||||
regionCode: 'US',
|
||||
searchTerm: '+16505551234',
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.StartNewConversation,
|
||||
phoneNumber: '+16505551234',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Contact,
|
||||
contact: composeContacts[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.Contact,
|
||||
contact: composeContacts[1],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConversationAndMessageAtIndex', () => {
|
||||
it('returns undefined because keyboard shortcuts are not supported', () => {
|
||||
const helper = new LeftPaneComposeHelper({
|
||||
composeContacts: [fakeContact(), fakeContact()],
|
||||
regionCode: 'US',
|
||||
searchTerm: 'foo bar',
|
||||
});
|
||||
|
||||
assert.isUndefined(helper.getConversationAndMessageAtIndex(0));
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConversationAndMessageInDirection', () => {
|
||||
it('returns undefined because keyboard shortcuts are not supported', () => {
|
||||
const helper = new LeftPaneComposeHelper({
|
||||
composeContacts: [fakeContact(), fakeContact()],
|
||||
regionCode: 'US',
|
||||
searchTerm: 'foo bar',
|
||||
});
|
||||
|
||||
assert.isUndefined(
|
||||
helper.getConversationAndMessageInDirection(
|
||||
{ direction: FindDirection.Down, unreadOnly: false },
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldRecomputeRowHeights', () => {
|
||||
it('always returns false because row heights are constant', () => {
|
||||
const helper = new LeftPaneComposeHelper({
|
||||
composeContacts: [fakeContact(), fakeContact()],
|
||||
regionCode: 'US',
|
||||
searchTerm: 'foo bar',
|
||||
});
|
||||
|
||||
assert.isFalse(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
composeContacts: [fakeContact()],
|
||||
searchTerm: 'foo bar',
|
||||
})
|
||||
);
|
||||
assert.isFalse(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
composeContacts: [fakeContact(), fakeContact(), fakeContact()],
|
||||
searchTerm: '',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
635
ts/test-node/components/leftPane/LeftPaneInboxHelper_test.ts
Normal file
635
ts/test-node/components/leftPane/LeftPaneInboxHelper_test.ts
Normal file
|
@ -0,0 +1,635 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { RowType } from '../../../components/ConversationList';
|
||||
import { FindDirection } from '../../../components/leftPane/LeftPaneHelper';
|
||||
|
||||
import { LeftPaneInboxHelper } from '../../../components/leftPane/LeftPaneInboxHelper';
|
||||
|
||||
describe('LeftPaneInboxHelper', () => {
|
||||
const fakeConversation = () => ({
|
||||
id: uuid(),
|
||||
title: uuid(),
|
||||
type: 'direct' as const,
|
||||
});
|
||||
|
||||
describe('getRowCount', () => {
|
||||
it('returns 0 if there are no conversations', () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [],
|
||||
pinnedConversations: [],
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(helper.getRowCount(), 0);
|
||||
});
|
||||
|
||||
it('returns 1 if there are only archived conversations', () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [],
|
||||
pinnedConversations: [],
|
||||
archivedConversations: [fakeConversation()],
|
||||
});
|
||||
|
||||
assert.strictEqual(helper.getRowCount(), 1);
|
||||
});
|
||||
|
||||
it("returns the number of non-pinned conversations if that's all there is", () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
],
|
||||
pinnedConversations: [],
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(helper.getRowCount(), 3);
|
||||
});
|
||||
|
||||
it("returns the number of pinned conversations if that's all there is", () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [],
|
||||
pinnedConversations: [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
],
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(helper.getRowCount(), 3);
|
||||
});
|
||||
|
||||
it('adds 2 rows for each header if there are pinned and non-pinned conversations,', () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
],
|
||||
pinnedConversations: [fakeConversation()],
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(helper.getRowCount(), 6);
|
||||
});
|
||||
|
||||
it('adds 1 row for the archive button if there are any archived conversations', () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
],
|
||||
pinnedConversations: [],
|
||||
archivedConversations: [fakeConversation()],
|
||||
});
|
||||
|
||||
assert.strictEqual(helper.getRowCount(), 4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRowIndexToScrollTo', () => {
|
||||
it('returns undefined if no conversation is selected', () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [fakeConversation(), fakeConversation()],
|
||||
pinnedConversations: [fakeConversation()],
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.isUndefined(helper.getRowIndexToScrollTo(undefined));
|
||||
});
|
||||
|
||||
it('returns undefined if the selected conversation is not pinned or non-pinned', () => {
|
||||
const archivedConversations = [fakeConversation()];
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [fakeConversation(), fakeConversation()],
|
||||
pinnedConversations: [fakeConversation()],
|
||||
archivedConversations,
|
||||
});
|
||||
|
||||
assert.isUndefined(
|
||||
helper.getRowIndexToScrollTo(archivedConversations[0].id)
|
||||
);
|
||||
});
|
||||
|
||||
it("returns the pinned conversation's index if there are only pinned conversations", () => {
|
||||
const pinnedConversations = [fakeConversation(), fakeConversation()];
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [],
|
||||
pinnedConversations,
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
helper.getRowIndexToScrollTo(pinnedConversations[0].id),
|
||||
0
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getRowIndexToScrollTo(pinnedConversations[1].id),
|
||||
1
|
||||
);
|
||||
});
|
||||
|
||||
it("returns the conversation's index if there are only non-pinned conversations", () => {
|
||||
const conversations = [fakeConversation(), fakeConversation()];
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations,
|
||||
pinnedConversations: [],
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(helper.getRowIndexToScrollTo(conversations[0].id), 0);
|
||||
assert.strictEqual(helper.getRowIndexToScrollTo(conversations[1].id), 1);
|
||||
});
|
||||
|
||||
it("returns the pinned conversation's index + 1 (for the header) if there are both pinned and non-pinned conversations", () => {
|
||||
const pinnedConversations = [fakeConversation(), fakeConversation()];
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [fakeConversation()],
|
||||
pinnedConversations,
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
helper.getRowIndexToScrollTo(pinnedConversations[0].id),
|
||||
1
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getRowIndexToScrollTo(pinnedConversations[1].id),
|
||||
2
|
||||
);
|
||||
});
|
||||
|
||||
it("returns the non-pinned conversation's index + pinnedConversations.length + 2 (for the headers) if there are both pinned and non-pinned conversations", () => {
|
||||
const conversations = [fakeConversation(), fakeConversation()];
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations,
|
||||
pinnedConversations: [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
],
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(helper.getRowIndexToScrollTo(conversations[0].id), 5);
|
||||
assert.strictEqual(helper.getRowIndexToScrollTo(conversations[1].id), 6);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRow', () => {
|
||||
it('returns the archive button if there are only archived conversations', () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [],
|
||||
pinnedConversations: [],
|
||||
archivedConversations: [fakeConversation(), fakeConversation()],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.ArchiveButton,
|
||||
archivedConversationsCount: 2,
|
||||
});
|
||||
assert.isUndefined(helper.getRow(1));
|
||||
});
|
||||
|
||||
it("returns pinned conversations if that's all there are", () => {
|
||||
const pinnedConversations = [fakeConversation(), fakeConversation()];
|
||||
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [],
|
||||
pinnedConversations,
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[1],
|
||||
});
|
||||
assert.isUndefined(helper.getRow(2));
|
||||
});
|
||||
|
||||
it('returns pinned conversations and an archive button if there are no non-pinned conversations', () => {
|
||||
const pinnedConversations = [fakeConversation(), fakeConversation()];
|
||||
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [],
|
||||
pinnedConversations,
|
||||
archivedConversations: [fakeConversation()],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.ArchiveButton,
|
||||
archivedConversationsCount: 1,
|
||||
});
|
||||
assert.isUndefined(helper.getRow(3));
|
||||
});
|
||||
|
||||
it("returns non-pinned conversations if that's all there are", () => {
|
||||
const conversations = [fakeConversation(), fakeConversation()];
|
||||
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations,
|
||||
pinnedConversations: [],
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[1],
|
||||
});
|
||||
assert.isUndefined(helper.getRow(2));
|
||||
});
|
||||
|
||||
it('returns non-pinned conversations and an archive button if there are no pinned conversations', () => {
|
||||
const conversations = [fakeConversation(), fakeConversation()];
|
||||
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations,
|
||||
pinnedConversations: [],
|
||||
archivedConversations: [fakeConversation()],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.ArchiveButton,
|
||||
archivedConversationsCount: 1,
|
||||
});
|
||||
assert.isUndefined(helper.getRow(3));
|
||||
});
|
||||
|
||||
it('returns headers if there are both pinned and non-pinned conversations', () => {
|
||||
const conversations = [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
];
|
||||
const pinnedConversations = [fakeConversation(), fakeConversation()];
|
||||
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations,
|
||||
pinnedConversations,
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'LeftPane--pinned',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'LeftPane--chats',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(5), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(6), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[2],
|
||||
});
|
||||
assert.isUndefined(helper.getRow(7));
|
||||
});
|
||||
|
||||
it('returns headers if there are both pinned and non-pinned conversations, and an archive button', () => {
|
||||
const conversations = [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
];
|
||||
const pinnedConversations = [fakeConversation(), fakeConversation()];
|
||||
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations,
|
||||
pinnedConversations,
|
||||
archivedConversations: [fakeConversation()],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'LeftPane--pinned',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.Conversation,
|
||||
conversation: pinnedConversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'LeftPane--chats',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(5), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(6), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[2],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(7), {
|
||||
type: RowType.ArchiveButton,
|
||||
archivedConversationsCount: 1,
|
||||
});
|
||||
assert.isUndefined(helper.getRow(8));
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConversationAndMessageAtIndex', () => {
|
||||
it('returns pinned converastions, then non-pinned conversations', () => {
|
||||
const conversations = [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
];
|
||||
const pinnedConversations = [fakeConversation(), fakeConversation()];
|
||||
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations,
|
||||
pinnedConversations,
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(0)?.conversationId,
|
||||
pinnedConversations[0].id
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(1)?.conversationId,
|
||||
pinnedConversations[1].id
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(2)?.conversationId,
|
||||
conversations[0].id
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(3)?.conversationId,
|
||||
conversations[1].id
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(4)?.conversationId,
|
||||
conversations[2].id
|
||||
);
|
||||
});
|
||||
|
||||
it("when requesting an index out of bounds, returns the last pinned conversation when that's all there is", () => {
|
||||
const pinnedConversations = [fakeConversation(), fakeConversation()];
|
||||
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [],
|
||||
pinnedConversations,
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(2)?.conversationId,
|
||||
pinnedConversations[1].id
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(99)?.conversationId,
|
||||
pinnedConversations[1].id
|
||||
);
|
||||
// This is mostly a resilience measure in case we're ever called with an invalid
|
||||
// index.
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(-1)?.conversationId,
|
||||
pinnedConversations[1].id
|
||||
);
|
||||
});
|
||||
|
||||
it("when requesting an index out of bounds, returns the last non-pinned conversation when that's all there is", () => {
|
||||
const conversations = [fakeConversation(), fakeConversation()];
|
||||
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations,
|
||||
pinnedConversations: [],
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(2)?.conversationId,
|
||||
conversations[1].id
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(99)?.conversationId,
|
||||
conversations[1].id
|
||||
);
|
||||
// This is mostly a resilience measure in case we're ever called with an invalid
|
||||
// index.
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(-1)?.conversationId,
|
||||
conversations[1].id
|
||||
);
|
||||
});
|
||||
|
||||
it('when requesting an index out of bounds, returns the last non-pinned conversation when there are both pinned and non-pinned conversations', () => {
|
||||
const conversations = [fakeConversation(), fakeConversation()];
|
||||
const pinnedConversations = [fakeConversation(), fakeConversation()];
|
||||
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations,
|
||||
pinnedConversations,
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(4)?.conversationId,
|
||||
conversations[1].id
|
||||
);
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(99)?.conversationId,
|
||||
conversations[1].id
|
||||
);
|
||||
// This is mostly a resilience measure in case we're ever called with an invalid
|
||||
// index.
|
||||
assert.strictEqual(
|
||||
helper.getConversationAndMessageAtIndex(-1)?.conversationId,
|
||||
conversations[1].id
|
||||
);
|
||||
});
|
||||
|
||||
it('returns undefined if there are no conversations', () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [],
|
||||
pinnedConversations: [],
|
||||
archivedConversations: [fakeConversation()],
|
||||
});
|
||||
|
||||
assert.isUndefined(helper.getConversationAndMessageAtIndex(0));
|
||||
assert.isUndefined(helper.getConversationAndMessageAtIndex(1));
|
||||
assert.isUndefined(helper.getConversationAndMessageAtIndex(-1));
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConversationAndMessageInDirection', () => {
|
||||
it('returns the next conversation when searching downward', () => {
|
||||
const pinnedConversations = [fakeConversation(), fakeConversation()];
|
||||
const conversations = [fakeConversation()];
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations,
|
||||
pinnedConversations,
|
||||
archivedConversations: [],
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
helper.getConversationAndMessageInDirection(
|
||||
{ direction: FindDirection.Down, unreadOnly: false },
|
||||
pinnedConversations[1].id,
|
||||
undefined
|
||||
),
|
||||
{ conversationId: conversations[0].id }
|
||||
);
|
||||
});
|
||||
|
||||
// Additional tests are found with `getConversationInDirection`.
|
||||
});
|
||||
|
||||
describe('shouldRecomputeRowHeights', () => {
|
||||
it("returns false if the number of conversations in each section doesn't change", () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
],
|
||||
pinnedConversations: [fakeConversation(), fakeConversation()],
|
||||
archivedConversations: [fakeConversation()],
|
||||
});
|
||||
|
||||
assert.isFalse(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
conversations: [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
],
|
||||
pinnedConversations: [fakeConversation(), fakeConversation()],
|
||||
archivedConversations: [fakeConversation(), fakeConversation()],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns false if the only thing changed is whether conversations are archived', () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
],
|
||||
pinnedConversations: [fakeConversation(), fakeConversation()],
|
||||
archivedConversations: [fakeConversation()],
|
||||
});
|
||||
|
||||
assert.isFalse(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
conversations: [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
],
|
||||
pinnedConversations: [fakeConversation(), fakeConversation()],
|
||||
archivedConversations: [],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns false if the only thing changed is the number of non-pinned conversations', () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
],
|
||||
pinnedConversations: [fakeConversation(), fakeConversation()],
|
||||
archivedConversations: [fakeConversation()],
|
||||
});
|
||||
|
||||
assert.isFalse(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
conversations: [fakeConversation()],
|
||||
pinnedConversations: [fakeConversation(), fakeConversation()],
|
||||
archivedConversations: [fakeConversation(), fakeConversation()],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns true if the number of pinned conversations changes', () => {
|
||||
const helper = new LeftPaneInboxHelper({
|
||||
conversations: [fakeConversation()],
|
||||
pinnedConversations: [fakeConversation(), fakeConversation()],
|
||||
archivedConversations: [fakeConversation()],
|
||||
});
|
||||
|
||||
assert.isTrue(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
conversations: [fakeConversation()],
|
||||
pinnedConversations: [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
],
|
||||
archivedConversations: [fakeConversation()],
|
||||
})
|
||||
);
|
||||
assert.isTrue(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
conversations: [fakeConversation()],
|
||||
pinnedConversations: [fakeConversation()],
|
||||
archivedConversations: [fakeConversation()],
|
||||
})
|
||||
);
|
||||
assert.isTrue(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
conversations: [fakeConversation()],
|
||||
pinnedConversations: [],
|
||||
archivedConversations: [fakeConversation()],
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
331
ts/test-node/components/leftPane/LeftPaneSearchHelper_test.ts
Normal file
331
ts/test-node/components/leftPane/LeftPaneSearchHelper_test.ts
Normal file
|
@ -0,0 +1,331 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { RowType } from '../../../components/ConversationList';
|
||||
|
||||
import { LeftPaneSearchHelper } from '../../../components/leftPane/LeftPaneSearchHelper';
|
||||
|
||||
describe('LeftPaneSearchHelper', () => {
|
||||
const fakeConversation = () => ({
|
||||
id: uuid(),
|
||||
title: uuid(),
|
||||
type: 'direct' as const,
|
||||
});
|
||||
|
||||
const fakeMessage = () => ({
|
||||
id: uuid(),
|
||||
conversationId: uuid(),
|
||||
});
|
||||
|
||||
describe('getRowCount', () => {
|
||||
it('returns 0 when there are no search results', () => {
|
||||
const helper = new LeftPaneSearchHelper({
|
||||
conversationResults: { isLoading: false, results: [] },
|
||||
contactResults: { isLoading: false, results: [] },
|
||||
messageResults: { isLoading: false, results: [] },
|
||||
searchTerm: 'foo',
|
||||
});
|
||||
|
||||
assert.strictEqual(helper.getRowCount(), 0);
|
||||
});
|
||||
|
||||
it("returns 2 rows for each section of search results that's loading", () => {
|
||||
const helper = new LeftPaneSearchHelper({
|
||||
conversationResults: { isLoading: true },
|
||||
contactResults: { isLoading: false, results: [] },
|
||||
messageResults: { isLoading: true },
|
||||
searchTerm: 'foo',
|
||||
});
|
||||
|
||||
assert.strictEqual(helper.getRowCount(), 4);
|
||||
});
|
||||
|
||||
it('returns 1 + the number of results, dropping empty sections', () => {
|
||||
const helper = new LeftPaneSearchHelper({
|
||||
conversationResults: {
|
||||
isLoading: false,
|
||||
results: [fakeConversation(), fakeConversation()],
|
||||
},
|
||||
contactResults: { isLoading: false, results: [] },
|
||||
messageResults: { isLoading: false, results: [fakeMessage()] },
|
||||
searchTerm: 'foo',
|
||||
});
|
||||
|
||||
assert.strictEqual(helper.getRowCount(), 5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRow', () => {
|
||||
it('returns header + spinner for loading sections', () => {
|
||||
const helper = new LeftPaneSearchHelper({
|
||||
conversationResults: { isLoading: true },
|
||||
contactResults: { isLoading: true },
|
||||
messageResults: { isLoading: true },
|
||||
searchTerm: 'foo',
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'conversationsHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Spinner,
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Spinner,
|
||||
});
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'messagesHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(5), {
|
||||
type: RowType.Spinner,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns header + results when all sections have loaded with results', () => {
|
||||
const conversations = [fakeConversation(), fakeConversation()];
|
||||
const contacts = [fakeConversation()];
|
||||
const messages = [fakeMessage(), fakeMessage()];
|
||||
|
||||
const helper = new LeftPaneSearchHelper({
|
||||
conversationResults: {
|
||||
isLoading: false,
|
||||
results: conversations,
|
||||
},
|
||||
contactResults: { isLoading: false, results: contacts },
|
||||
messageResults: { isLoading: false, results: messages },
|
||||
searchTerm: 'foo',
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'conversationsHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.Conversation,
|
||||
conversation: contacts[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(5), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'messagesHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(6), {
|
||||
type: RowType.MessageSearchResult,
|
||||
messageId: messages[0].id,
|
||||
});
|
||||
assert.deepEqual(helper.getRow(7), {
|
||||
type: RowType.MessageSearchResult,
|
||||
messageId: messages[1].id,
|
||||
});
|
||||
});
|
||||
|
||||
it('omits conversations when there are no conversation results', () => {
|
||||
const contacts = [fakeConversation()];
|
||||
const messages = [fakeMessage(), fakeMessage()];
|
||||
|
||||
const helper = new LeftPaneSearchHelper({
|
||||
conversationResults: {
|
||||
isLoading: false,
|
||||
results: [],
|
||||
},
|
||||
contactResults: { isLoading: false, results: contacts },
|
||||
messageResults: { isLoading: false, results: messages },
|
||||
searchTerm: 'foo',
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: contacts[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'messagesHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.MessageSearchResult,
|
||||
messageId: messages[0].id,
|
||||
});
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.MessageSearchResult,
|
||||
messageId: messages[1].id,
|
||||
});
|
||||
});
|
||||
|
||||
it('omits contacts when there are no contact results', () => {
|
||||
const conversations = [fakeConversation(), fakeConversation()];
|
||||
const messages = [fakeMessage(), fakeMessage()];
|
||||
|
||||
const helper = new LeftPaneSearchHelper({
|
||||
conversationResults: {
|
||||
isLoading: false,
|
||||
results: conversations,
|
||||
},
|
||||
contactResults: { isLoading: false, results: [] },
|
||||
messageResults: { isLoading: false, results: messages },
|
||||
searchTerm: 'foo',
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'conversationsHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'messagesHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.MessageSearchResult,
|
||||
messageId: messages[0].id,
|
||||
});
|
||||
assert.deepEqual(helper.getRow(5), {
|
||||
type: RowType.MessageSearchResult,
|
||||
messageId: messages[1].id,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('omits messages when there are no message results', () => {
|
||||
const conversations = [fakeConversation(), fakeConversation()];
|
||||
const contacts = [fakeConversation()];
|
||||
|
||||
const helper = new LeftPaneSearchHelper({
|
||||
conversationResults: {
|
||||
isLoading: false,
|
||||
results: conversations,
|
||||
},
|
||||
contactResults: { isLoading: false, results: contacts },
|
||||
messageResults: { isLoading: false, results: [] },
|
||||
searchTerm: 'foo',
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'conversationsHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[0],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), {
|
||||
type: RowType.Conversation,
|
||||
conversation: conversations[1],
|
||||
});
|
||||
assert.deepEqual(helper.getRow(3), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'contactsHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(4), {
|
||||
type: RowType.Conversation,
|
||||
conversation: contacts[0],
|
||||
});
|
||||
assert.isUndefined(helper.getRow(5));
|
||||
});
|
||||
|
||||
describe('shouldRecomputeRowHeights', () => {
|
||||
it("returns false if the number of results doesn't change", () => {
|
||||
const helper = new LeftPaneSearchHelper({
|
||||
conversationResults: {
|
||||
isLoading: false,
|
||||
results: [fakeConversation(), fakeConversation()],
|
||||
},
|
||||
contactResults: { isLoading: false, results: [] },
|
||||
messageResults: {
|
||||
isLoading: false,
|
||||
results: [fakeMessage(), fakeMessage(), fakeMessage()],
|
||||
},
|
||||
searchTerm: 'foo',
|
||||
});
|
||||
|
||||
assert.isFalse(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
conversationResults: {
|
||||
isLoading: false,
|
||||
results: [fakeConversation(), fakeConversation()],
|
||||
},
|
||||
contactResults: { isLoading: false, results: [] },
|
||||
messageResults: {
|
||||
isLoading: false,
|
||||
results: [fakeMessage(), fakeMessage(), fakeMessage()],
|
||||
},
|
||||
searchTerm: 'bar',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns false when a section goes from loading to loaded with 1 result', () => {
|
||||
const helper = new LeftPaneSearchHelper({
|
||||
conversationResults: { isLoading: true },
|
||||
contactResults: { isLoading: true },
|
||||
messageResults: { isLoading: true },
|
||||
searchTerm: 'foo',
|
||||
});
|
||||
|
||||
assert.isFalse(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
conversationResults: {
|
||||
isLoading: false,
|
||||
results: [fakeConversation()],
|
||||
},
|
||||
contactResults: { isLoading: true },
|
||||
messageResults: { isLoading: true },
|
||||
searchTerm: 'bar',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns true if the number of results in a section changes', () => {
|
||||
const helper = new LeftPaneSearchHelper({
|
||||
conversationResults: {
|
||||
isLoading: false,
|
||||
results: [fakeConversation(), fakeConversation()],
|
||||
},
|
||||
contactResults: { isLoading: false, results: [] },
|
||||
messageResults: { isLoading: false, results: [] },
|
||||
searchTerm: 'foo',
|
||||
});
|
||||
|
||||
assert.isTrue(
|
||||
helper.shouldRecomputeRowHeights({
|
||||
conversationResults: {
|
||||
isLoading: false,
|
||||
results: [fakeConversation()],
|
||||
},
|
||||
contactResults: { isLoading: true },
|
||||
messageResults: { isLoading: true },
|
||||
searchTerm: 'bar',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,199 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import {
|
||||
FindDirection,
|
||||
ToFindType,
|
||||
} from '../../../components/leftPane/LeftPaneHelper';
|
||||
|
||||
import { getConversationInDirection } from '../../../components/leftPane/getConversationInDirection';
|
||||
|
||||
describe('getConversationInDirection', () => {
|
||||
const fakeConversation = (markedUnread = false) => ({
|
||||
id: uuid(),
|
||||
title: uuid(),
|
||||
type: 'direct' as const,
|
||||
markedUnread,
|
||||
});
|
||||
|
||||
const fakeConversations = [
|
||||
fakeConversation(),
|
||||
fakeConversation(true),
|
||||
fakeConversation(true),
|
||||
fakeConversation(),
|
||||
];
|
||||
|
||||
describe('searching for any conversation', () => {
|
||||
const up: ToFindType = {
|
||||
direction: FindDirection.Up,
|
||||
unreadOnly: false,
|
||||
};
|
||||
const down: ToFindType = {
|
||||
direction: FindDirection.Down,
|
||||
unreadOnly: false,
|
||||
};
|
||||
|
||||
it('returns undefined if there are no conversations', () => {
|
||||
assert.isUndefined(getConversationInDirection([], up, undefined));
|
||||
assert.isUndefined(getConversationInDirection([], down, undefined));
|
||||
});
|
||||
|
||||
it('if no conversation is selected, returns the last conversation when going up', () => {
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(fakeConversations, up, undefined),
|
||||
{ conversationId: fakeConversations[3].id }
|
||||
);
|
||||
});
|
||||
|
||||
it('if no conversation is selected, returns the first conversation when going down', () => {
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(fakeConversations, down, undefined),
|
||||
{ conversationId: fakeConversations[0].id }
|
||||
);
|
||||
});
|
||||
|
||||
it('if the first conversation is selected, returns the last conversation when going up', () => {
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(
|
||||
fakeConversations,
|
||||
up,
|
||||
fakeConversations[0].id
|
||||
),
|
||||
{ conversationId: fakeConversations[3].id }
|
||||
);
|
||||
});
|
||||
|
||||
it('if the last conversation is selected, returns the first conversation when going down', () => {
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(
|
||||
fakeConversations,
|
||||
down,
|
||||
fakeConversations[3].id
|
||||
),
|
||||
{ conversationId: fakeConversations[0].id }
|
||||
);
|
||||
});
|
||||
|
||||
it('goes up one conversation in normal cases', () => {
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(
|
||||
fakeConversations,
|
||||
up,
|
||||
fakeConversations[2].id
|
||||
),
|
||||
{ conversationId: fakeConversations[1].id }
|
||||
);
|
||||
});
|
||||
|
||||
it('goes down one conversation in normal cases', () => {
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(
|
||||
fakeConversations,
|
||||
down,
|
||||
fakeConversations[0].id
|
||||
),
|
||||
{ conversationId: fakeConversations[1].id }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('searching for unread conversations', () => {
|
||||
const up: ToFindType = {
|
||||
direction: FindDirection.Up,
|
||||
unreadOnly: true,
|
||||
};
|
||||
const down: ToFindType = {
|
||||
direction: FindDirection.Down,
|
||||
unreadOnly: true,
|
||||
};
|
||||
|
||||
const noUnreads = [
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
fakeConversation(),
|
||||
];
|
||||
|
||||
it('returns undefined if there are no conversations', () => {
|
||||
assert.isUndefined(getConversationInDirection([], up, undefined));
|
||||
assert.isUndefined(getConversationInDirection([], down, undefined));
|
||||
});
|
||||
|
||||
it('if no conversation is selected, finds the last unread conversation (if it exists) when searching up', () => {
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(fakeConversations, up, undefined),
|
||||
{ conversationId: fakeConversations[2].id }
|
||||
);
|
||||
assert.isUndefined(getConversationInDirection(noUnreads, up, undefined));
|
||||
});
|
||||
|
||||
it('if no conversation is selected, finds the first unread conversation (if it exists) when searching down', () => {
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(fakeConversations, down, undefined),
|
||||
{ conversationId: fakeConversations[1].id }
|
||||
);
|
||||
assert.isUndefined(
|
||||
getConversationInDirection(noUnreads, down, undefined)
|
||||
);
|
||||
});
|
||||
|
||||
it("searches up for unread conversations, returning undefined if no conversation exists (doesn't wrap around)", () => {
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(
|
||||
fakeConversations,
|
||||
up,
|
||||
fakeConversations[3].id
|
||||
),
|
||||
{ conversationId: fakeConversations[2].id }
|
||||
);
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(
|
||||
fakeConversations,
|
||||
up,
|
||||
fakeConversations[2].id
|
||||
),
|
||||
{ conversationId: fakeConversations[1].id }
|
||||
);
|
||||
assert.isUndefined(
|
||||
getConversationInDirection(
|
||||
fakeConversations,
|
||||
up,
|
||||
fakeConversations[1].id
|
||||
)
|
||||
);
|
||||
assert.isUndefined(
|
||||
getConversationInDirection(noUnreads, up, noUnreads[2].id)
|
||||
);
|
||||
});
|
||||
|
||||
it("searches down for unread conversations, returning undefined if no conversation exists (doesn't wrap around)", () => {
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(
|
||||
fakeConversations,
|
||||
down,
|
||||
fakeConversations[0].id
|
||||
),
|
||||
{ conversationId: fakeConversations[1].id }
|
||||
);
|
||||
assert.deepEqual(
|
||||
getConversationInDirection(
|
||||
fakeConversations,
|
||||
down,
|
||||
fakeConversations[1].id
|
||||
),
|
||||
{ conversationId: fakeConversations[2].id }
|
||||
);
|
||||
assert.isUndefined(
|
||||
getConversationInDirection(
|
||||
fakeConversations,
|
||||
down,
|
||||
fakeConversations[2].id
|
||||
)
|
||||
);
|
||||
assert.isUndefined(
|
||||
getConversationInDirection(noUnreads, down, noUnreads[1].id)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue