105 lines
3.0 KiB
TypeScript
105 lines
3.0 KiB
TypeScript
import { test } from 'node:test';
|
|||
|
|
import assert from 'node:assert/strict';
|
||
|
|
import { sortBookmarks, groupBookmarksByRoom } from './bookmarks';
|
||
|
|
import { Bookmark } from '../hooks/useBookmarks';
|
||
|
|
|
||
|
|
const bk = (eventId: string, roomId: string, savedAt: number, roomName = roomId): Bookmark => ({
|
||
|
|
eventId,
|
||
|
|
roomId,
|
||
|
|
savedAt,
|
||
|
|
roomName,
|
||
|
|
previewText: eventId,
|
||
|
|
});
|
||
|
|
|
||
|
|
test('sortBookmarks newest orders by savedAt descending', () => {
|
||
|
|
const input = [bk('a', 'r1', 100), bk('b', 'r1', 300), bk('c', 'r1', 200)];
|
||
|
|
const out = sortBookmarks(input, 'newest');
|
||
|
|
assert.deepEqual(
|
||
|
|
out.map((b) => b.eventId),
|
||
|
|
['b', 'c', 'a'],
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('sortBookmarks oldest orders by savedAt ascending', () => {
|
||
|
|
const input = [bk('a', 'r1', 100), bk('b', 'r1', 300), bk('c', 'r1', 200)];
|
||
|
|
const out = sortBookmarks(input, 'oldest');
|
||
|
|
assert.deepEqual(
|
||
|
|
out.map((b) => b.eventId),
|
||
|
|
['a', 'c', 'b'],
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('sortBookmarks room falls back to newest for a flat list', () => {
|
||
|
|
const input = [bk('a', 'r1', 100), bk('b', 'r2', 300)];
|
||
|
|
const out = sortBookmarks(input, 'room');
|
||
|
|
assert.deepEqual(
|
||
|
|
out.map((b) => b.eventId),
|
||
|
|
['b', 'a'],
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('sortBookmarks breaks savedAt ties deterministically by eventId', () => {
|
||
|
|
const input = [bk('z', 'r1', 100), bk('a', 'r1', 100), bk('m', 'r1', 100)];
|
||
|
|
const newest = sortBookmarks(input, 'newest');
|
||
|
|
const oldest = sortBookmarks(input, 'oldest');
|
||
|
|
// Equal timestamps → ascending eventId in both directions.
|
||
|
|
assert.deepEqual(
|
||
|
|
newest.map((b) => b.eventId),
|
||
|
|
['a', 'm', 'z'],
|
||
|
|
);
|
||
|
|
assert.deepEqual(
|
||
|
|
oldest.map((b) => b.eventId),
|
||
|
|
['a', 'm', 'z'],
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('sortBookmarks does not mutate its input', () => {
|
||
|
|
const input = [bk('a', 'r1', 100), bk('b', 'r1', 300)];
|
||
|
|
const before = input.map((b) => b.eventId);
|
||
|
|
sortBookmarks(input, 'oldest');
|
||
|
|
assert.deepEqual(
|
||
|
|
input.map((b) => b.eventId),
|
||
|
|
before,
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('groupBookmarksByRoom buckets by room, newest-first within a group', () => {
|
||
|
|
const input = [
|
||
|
|
bk('a', 'r1', 100),
|
||
|
|
bk('b', 'r2', 500),
|
||
|
|
bk('c', 'r1', 300),
|
||
|
|
bk('d', 'r2', 200),
|
||
|
|
];
|
||
|
|
const groups = groupBookmarksByRoom(input);
|
||
|
|
assert.equal(groups.length, 2);
|
||
|
|
const r1 = groups.find((g) => g.roomId === 'r1')!;
|
||
|
|
const r2 = groups.find((g) => g.roomId === 'r2')!;
|
||
|
|
assert.deepEqual(
|
||
|
|
r1.items.map((b) => b.eventId),
|
||
|
|
['c', 'a'],
|
||
|
|
);
|
||
|
|
assert.deepEqual(
|
||
|
|
r2.items.map((b) => b.eventId),
|
||
|
|
['b', 'd'],
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('groupBookmarksByRoom orders groups by most-recent save (active rooms first)', () => {
|
||
|
|
const input = [bk('a', 'r1', 100), bk('c', 'r1', 300), bk('b', 'r2', 500)];
|
||
|
|
// r2's newest is 500, r1's newest is 300 → r2 first.
|
||
|
|
const groups = groupBookmarksByRoom(input);
|
||
|
|
assert.deepEqual(
|
||
|
|
groups.map((g) => g.roomId),
|
||
|
|
['r2', 'r1'],
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('groupBookmarksByRoom carries the stored room name', () => {
|
||
|
|
const groups = groupBookmarksByRoom([bk('a', 'r1', 100, 'General')]);
|
||
|
|
assert.equal(groups[0].roomName, 'General');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('groupBookmarksByRoom returns empty for empty input', () => {
|
||
|
|
assert.deepEqual(groupBookmarksByRoom([]), []);
|
||
|
|
});
|