diff --git a/src/app/utils/sort.test.ts b/src/app/utils/sort.test.ts new file mode 100644 index 000000000..22cb23016 --- /dev/null +++ b/src/app/utils/sort.test.ts @@ -0,0 +1,56 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import type { MatrixClient } from 'matrix-js-sdk'; +import { + byTsOldToNew, + byOrderKey, + factoryRoomIdByUnreadCount, + factoryRoomIdByActivity, + factoryRoomIdByAtoZ, +} from './sort'; + +test('byTsOldToNew sorts ascending by timestamp', () => { + assert.ok(byTsOldToNew(1, 2) < 0); + assert.ok(byTsOldToNew(5, 3) > 0); + assert.equal(byTsOldToNew(2, 2), 0); + assert.deepEqual([30, 10, 20].sort(byTsOldToNew), [10, 20, 30]); +}); + +test('byOrderKey: undefined sorts last, otherwise lexical', () => { + assert.equal(byOrderKey(undefined, undefined), 0); + assert.equal(byOrderKey('a', undefined), -1); // defined before undefined + assert.equal(byOrderKey(undefined, 'a'), 1); + assert.equal(byOrderKey('a', 'b'), -1); + assert.equal(byOrderKey('b', 'a'), 1); + // equal non-empty keys return 1 (not 0) — there is no equality branch for two + // present keys, so a stable sort keeps input order for equal keys. + assert.equal(byOrderKey('a', 'a'), 1); + assert.deepEqual(['c', undefined, 'a', 'b'].sort(byOrderKey), ['a', 'b', 'c', undefined]); +}); + +test('factoryRoomIdByUnreadCount sorts by unread count descending', () => { + const counts: Record = { r1: 0, r2: 5, r3: 2 }; + const cmp = factoryRoomIdByUnreadCount((id) => counts[id]); + assert.deepEqual(['r1', 'r2', 'r3'].sort(cmp), ['r2', 'r3', 'r1']); +}); + +test('factoryRoomIdByActivity sorts most-recently-active first', () => { + const ts: Record = { old: 100, new: 300, mid: 200 }; + const mx = { + getRoom: (id: string) => (id in ts ? { getLastActiveTimestamp: () => ts[id] } : null), + } as unknown as MatrixClient; + const cmp = factoryRoomIdByActivity(mx); + assert.deepEqual(['old', 'new', 'mid'].sort(cmp), ['new', 'mid', 'old']); + // a room the client can't resolve sinks to the bottom + assert.deepEqual(['missing', 'new'].sort(cmp), ['new', 'missing']); +}); + +test('factoryRoomIdByAtoZ sorts case-insensitively and ignores leading #', () => { + const names: Record = { a: 'Banana', b: 'apple', c: '#Cherry' }; + const mx = { + getRoom: (id: string) => ({ name: names[id] ?? '' }), + } as unknown as MatrixClient; + const cmp = factoryRoomIdByAtoZ(mx); + // apple < Banana < Cherry (# stripped, case-insensitive) + assert.deepEqual(['a', 'b', 'c'].sort(cmp), ['b', 'a', 'c']); +});