Remove React Virtualized from <Timeline>

This commit is contained in:
Evan Hahn 2022-03-03 14:23:10 -06:00 committed by GitHub
parent 1eafe79905
commit 0c31ad25ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 798 additions and 2512 deletions

View file

@ -1,299 +0,0 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { times } from 'lodash';
import { v4 as uuid } from 'uuid';
import {
fromItemIndexToRow,
fromRowToItemIndex,
getEphemeralRows,
getHeroRow,
getLastSeenIndicatorRow,
getRowCount,
getTypingBubbleRow,
} from '../../util/timelineUtil';
describe('<Timeline> utilities', () => {
const getItems = (count: number): Array<string> => times(count, () => uuid());
describe('fromItemIndexToRow', () => {
it('returns the same number under normal conditions', () => {
times(5, index => {
assert.strictEqual(
fromItemIndexToRow(index, { haveOldest: false }),
index
);
});
});
it('adds 1 (for the hero row) if you have the oldest messages', () => {
times(5, index => {
assert.strictEqual(
fromItemIndexToRow(index, { haveOldest: true }),
index + 1
);
});
});
it('adds 1 (for the unread indicator) once crossing the unread indicator index', () => {
const props = { haveOldest: false, oldestUnreadIndex: 2 };
[0, 1].forEach(index => {
assert.strictEqual(fromItemIndexToRow(index, props), index);
});
[2, 3, 4].forEach(index => {
assert.strictEqual(fromItemIndexToRow(index, props), index + 1);
});
});
it('can include the hero row and the unread indicator', () => {
const props = { haveOldest: true, oldestUnreadIndex: 2 };
[0, 1].forEach(index => {
assert.strictEqual(fromItemIndexToRow(index, props), index + 1);
});
[2, 3, 4].forEach(index => {
assert.strictEqual(fromItemIndexToRow(index, props), index + 2);
});
});
});
describe('fromRowToItemIndex', () => {
it('returns the item index under normal conditions', () => {
const props = { haveOldest: false, items: getItems(5) };
times(5, row => {
assert.strictEqual(fromRowToItemIndex(row, props), row);
});
assert.isUndefined(fromRowToItemIndex(5, props));
});
it('handles the unread indicator', () => {
const props = {
haveOldest: false,
items: getItems(4),
oldestUnreadIndex: 2,
};
[0, 1].forEach(row => {
assert.strictEqual(fromRowToItemIndex(row, props), row);
});
assert.isUndefined(fromRowToItemIndex(2, props));
[3, 4].forEach(row => {
assert.strictEqual(fromRowToItemIndex(row, props), row - 1);
});
assert.isUndefined(fromRowToItemIndex(5, props));
});
it('handles the hero row', () => {
const props = { haveOldest: true, items: getItems(3) };
assert.isUndefined(fromRowToItemIndex(0, props));
[1, 2, 3].forEach(row => {
assert.strictEqual(fromRowToItemIndex(row, props), row - 1);
});
assert.isUndefined(fromRowToItemIndex(4, props));
});
it('handles the whole enchilada', () => {
const props = {
haveOldest: true,
items: getItems(4),
oldestUnreadIndex: 2,
};
assert.isUndefined(fromRowToItemIndex(0, props));
[1, 2].forEach(row => {
assert.strictEqual(fromRowToItemIndex(row, props), row - 1);
});
assert.isUndefined(fromRowToItemIndex(3, props));
[4, 5].forEach(row => {
assert.strictEqual(fromRowToItemIndex(row, props), row - 2);
});
assert.isUndefined(fromRowToItemIndex(6, props));
});
});
describe('getRowCount', () => {
it('returns 1 (for the hero row) if the conversation is empty', () => {
assert.strictEqual(getRowCount({ haveOldest: true, items: [] }), 1);
});
it('returns the number of items under normal conditions', () => {
assert.strictEqual(
getRowCount({ haveOldest: false, items: getItems(4) }),
4
);
});
it('adds 1 (for the hero row) if you have the oldest messages', () => {
assert.strictEqual(
getRowCount({ haveOldest: true, items: getItems(4) }),
5
);
});
it('adds 1 (for the unread indicator) if you have unread messages', () => {
assert.strictEqual(
getRowCount({
haveOldest: false,
items: getItems(4),
oldestUnreadIndex: 2,
}),
5
);
});
it('adds 1 (for the typing contact) if you have unread messages', () => {
assert.strictEqual(
getRowCount({
haveOldest: false,
items: getItems(4),
typingContactId: uuid(),
}),
5
);
});
it('can have the whole enchilada', () => {
assert.strictEqual(
getRowCount({
haveOldest: true,
items: getItems(4),
oldestUnreadIndex: 2,
typingContactId: uuid(),
}),
7
);
});
});
describe('getHeroRow', () => {
it("returns undefined if there's no hero row", () => {
assert.isUndefined(getHeroRow({ haveOldest: false }));
});
it("returns 0 if there's a hero row", () => {
assert.strictEqual(getHeroRow({ haveOldest: true }), 0);
});
});
describe('getLastSeenIndicatorRow', () => {
it('returns undefined with no unread messages', () => {
assert.isUndefined(getLastSeenIndicatorRow({ haveOldest: false }));
assert.isUndefined(getLastSeenIndicatorRow({ haveOldest: true }));
});
it('returns the same number if the oldest messages are loaded', () => {
[0, 1, 2].forEach(oldestUnreadIndex => {
assert.strictEqual(
getLastSeenIndicatorRow({ haveOldest: false, oldestUnreadIndex }),
oldestUnreadIndex
);
});
});
it("increases the number by 1 if there's a hero row", () => {
[0, 1, 2].forEach(oldestUnreadIndex => {
assert.strictEqual(
getLastSeenIndicatorRow({ haveOldest: true, oldestUnreadIndex }),
oldestUnreadIndex + 1
);
});
});
});
describe('getTypingBubbleRow', () => {
it('returns undefined if nobody is typing', () => {
assert.isUndefined(
getTypingBubbleRow({ haveOldest: false, items: getItems(3) })
);
});
it('returns the last row if people are typing', () => {
[
{ haveOldest: true, items: [], typingContactId: uuid() },
{ haveOldest: false, items: getItems(3), typingContactId: uuid() },
{ haveOldest: true, items: getItems(3), typingContactId: uuid() },
{
haveOldest: false,
items: getItems(3),
oldestUnreadIndex: 2,
typingContactId: uuid(),
},
{
haveOldest: true,
items: getItems(3),
oldestUnreadIndex: 2,
typingContactId: uuid(),
},
].forEach(props => {
assert.strictEqual(getTypingBubbleRow(props), getRowCount(props) - 1);
});
});
});
describe('getEphemeralRows', () => {
function iterate<T>(iterator: Iterator<T>): Array<T> {
const result: Array<T> = [];
let iteration = iterator.next();
while (!iteration.done) {
result.push(iteration.value);
iteration = iterator.next();
}
return result;
}
it('yields each row under normal conditions', () => {
const result = getEphemeralRows({
haveOldest: false,
items: ['a', 'b', 'c'],
});
assert.deepStrictEqual(iterate(result), ['item:a', 'item:b', 'item:c']);
});
it('yields a hero row if there is one', () => {
const result = getEphemeralRows({ haveOldest: true, items: getItems(3) });
const iterated = iterate(result);
assert.lengthOf(iterated, 4);
assert.strictEqual(iterated[0], 'hero');
});
it('yields an unread indicator if there is one', () => {
const result = getEphemeralRows({
haveOldest: false,
items: getItems(3),
oldestUnreadIndex: 2,
});
const iterated = iterate(result);
assert.lengthOf(iterated, 4);
assert.strictEqual(iterated[2], 'oldest-unread');
});
it('yields a typing row if there is one', () => {
const result = getEphemeralRows({
haveOldest: false,
items: getItems(3),
typingContactId: uuid(),
});
const iterated = iterate(result);
assert.lengthOf(iterated, 4);
assert.strictEqual(iterated[3], 'typing-contact');
});
it('handles the whole enchilada', () => {
const result = getEphemeralRows({
haveOldest: true,
items: ['a', 'b', 'c'],
oldestUnreadIndex: 2,
typingContactId: uuid(),
});
assert.deepStrictEqual(iterate(result), [
'hero',
'item:a',
'item:b',
'oldest-unread',
'item:c',
'typing-contact',
]);
});
});
});

View file

@ -187,44 +187,42 @@ describe('timestamp', () => {
});
describe('formatTime', () => {
useFakeTimers();
it('returns "Now" for times within the last minute, including unexpected times in the future', () => {
[
Date.now(),
moment().subtract(1, 'second'),
moment().subtract(59, 'seconds'),
moment().add(1, 'minute'),
moment().add(1, 'year'),
FAKE_NOW,
moment(FAKE_NOW).subtract(1, 'second'),
moment(FAKE_NOW).subtract(59, 'seconds'),
moment(FAKE_NOW).add(1, 'minute'),
moment(FAKE_NOW).add(1, 'year'),
].forEach(timestamp => {
assert.strictEqual(formatTime(i18n, timestamp), 'Now');
assert.strictEqual(formatTime(i18n, timestamp, FAKE_NOW), 'Now');
});
});
it('returns "X minutes ago" for times in the last hour, but older than 1 minute', () => {
assert.strictEqual(
formatTime(i18n, moment().subtract(1, 'minute')),
formatTime(i18n, moment(FAKE_NOW).subtract(1, 'minute'), FAKE_NOW),
'1m'
);
assert.strictEqual(
formatTime(i18n, moment().subtract(30, 'minutes')),
formatTime(i18n, moment(FAKE_NOW).subtract(30, 'minutes'), FAKE_NOW),
'30m'
);
assert.strictEqual(
formatTime(i18n, moment().subtract(59, 'minutes')),
formatTime(i18n, moment(FAKE_NOW).subtract(59, 'minutes'), FAKE_NOW),
'59m'
);
});
it('returns hh:mm-like times for times older than 1 hour from now', () => {
const oneHourAgo = new Date('2020-01-23T03:56:00.000');
assert.deepEqual(formatTime(i18n, oneHourAgo), '3:56 AM');
assert.deepEqual(formatTime(i18n, oneHourAgo, FAKE_NOW), '3:56 AM');
const oneDayAgo = new Date('2020-01-22T04:56:00.000');
assert.deepEqual(formatTime(i18n, oneDayAgo), '4:56 AM');
assert.deepEqual(formatTime(i18n, oneDayAgo, FAKE_NOW), '4:56 AM');
const oneYearAgo = new Date('2019-01-23T04:56:00.000');
assert.deepEqual(formatTime(i18n, oneYearAgo), '4:56 AM');
assert.deepEqual(formatTime(i18n, oneYearAgo, FAKE_NOW), '4:56 AM');
});
});