| 1 | import assert from 'node:assert/strict'; |
| 2 | import { test } from 'node:test'; |
| 3 | import { buildGroups, lastOwnSent, lastSenderIs } from '../src/lib/timeline-groups.ts'; |
| 4 | import type { TimelineEvent, TimelineItem } from '../src/lib/types.ts'; |
| 5 | |
| 6 | const MINUTE = 60 * 1000; |
| 7 | |
| 8 | function event(id: string, sender: string, timestamp_ms: number, extra: Partial<TimelineEvent> = {}): TimelineItem { |
| 9 | return { |
| 10 | kind: 'event', |
| 11 | event: { item_id: id, event_id: id, timestamp_ms, own: false, send_state: { kind: 'Sent' }, sender: { user_id: sender }, ...extra } as TimelineEvent |
| 12 | }; |
| 13 | } |
| 14 | |
| 15 | test('a sender run shares one header until a gap or another sender', () => { |
| 16 | const groups = buildGroups([ |
| 17 | event('a', '@x', 0), |
| 18 | event('b', '@x', MINUTE), |
| 19 | event('c', '@x', 10 * MINUTE), |
| 20 | event('d', '@y', 11 * MINUTE), |
| 21 | { kind: 'day', timestamp_ms: 12 * MINUTE }, |
| 22 | event('e', '@y', 12 * MINUTE) |
| 23 | ]); |
| 24 | assert.deepEqual(groups.map((g) => g.showSender), [true, false, true, true, true, true]); |
| 25 | }); |
| 26 | |
| 27 | test('gap rows are keyed by their id so pagination keeps them mounted', () => { |
| 28 | const gap = { kind: 'historyGap', gap: { id: 'g1', older_boundary: null, newer_boundary: null, lifecycle: 'Idle', continuity: 'Unresolved', failure: null } } as TimelineItem; |
| 29 | assert.equal(buildGroups([event('a', '@x', 0), gap])[1].key, 'gap-g1'); |
| 30 | }); |
| 31 | |
| 32 | test('the latest own sent event and the latest sender are found from the end', () => { |
| 33 | const list = [event('a', '@me', 0, { own: true }), event('b', '@x', 1), event('c', '@me', 2, { own: true, send_state: { kind: 'Sending' } })]; |
| 34 | assert.equal(lastOwnSent(list), 'a'); |
| 35 | assert.equal(lastSenderIs(list, '@me'), true); |
| 36 | assert.equal(lastSenderIs([], '@me'), false); |
| 37 | }); |