120 lines
3.6 KiB
TypeScript
120 lines
3.6 KiB
TypeScript
import { describe, it } from 'node:test';
|
|||
|
|
import assert from 'node:assert/strict';
|
||
|
|
import { GroupCandidate, findMediaRun, planMediaGroups, splitRun } from './mediaGroups';
|
||
|
|
|
||
|
|
const media = (
|
||
|
|
sender: string,
|
||
|
|
ts: number,
|
||
|
|
extra: Partial<GroupCandidate> = {},
|
||
|
|
): GroupCandidate => ({
|
||
|
|
sender,
|
||
|
|
ts,
|
||
|
|
kind: 'media',
|
||
|
|
hasRelation: false,
|
||
|
|
redacted: false,
|
||
|
|
mustEnd: false,
|
||
|
|
...extra,
|
||
|
|
});
|
||
|
|
const text = (sender: string, ts: number): GroupCandidate => ({
|
||
|
|
...media(sender, ts),
|
||
|
|
kind: 'other',
|
||
|
|
});
|
||
|
|
const skip = (ts: number): GroupCandidate => ({ ...media('x', ts), kind: 'skip' });
|
||
|
|
const seq = (items: GroupCandidate[]) => (i: number) => items[i];
|
||
|
|
|
||
|
|
describe('findMediaRun', () => {
|
||
|
|
it('collects contiguous same-sender media within the gap, from any member', () => {
|
||
|
|
const at = seq([
|
||
|
|
text('a', 0),
|
||
|
|
media('a', 1000),
|
||
|
|
media('a', 2000),
|
||
|
|
media('a', 3000),
|
||
|
|
text('a', 4000),
|
||
|
|
]);
|
||
|
|
assert.deepEqual(findMediaRun(at, 1), [1, 2, 3]);
|
||
|
|
assert.deepEqual(findMediaRun(at, 2), [1, 2, 3]);
|
||
|
|
assert.deepEqual(findMediaRun(at, 3), [1, 2, 3]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('breaks on a different sender, text in between, or a long gap', () => {
|
||
|
|
const at = seq([
|
||
|
|
media('a', 0),
|
||
|
|
media('b', 1000),
|
||
|
|
media('a', 2000),
|
||
|
|
text('a', 2500),
|
||
|
|
media('a', 3000),
|
||
|
|
media('a', 70_000),
|
||
|
|
]);
|
||
|
|
assert.deepEqual(findMediaRun(at, 0), [0]);
|
||
|
|
assert.deepEqual(findMediaRun(at, 2), [2]);
|
||
|
|
assert.deepEqual(findMediaRun(at, 4), [4]);
|
||
|
|
assert.deepEqual(findMediaRun(at, 5), [5]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('skips invisible filler such as reactions and edits', () => {
|
||
|
|
const at = seq([media('a', 0), skip(100), skip(200), media('a', 1000)]);
|
||
|
|
assert.deepEqual(findMediaRun(at, 0), [0, 3]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('never groups replies, thread messages, edits or redacted events', () => {
|
||
|
|
const at = seq([
|
||
|
|
media('a', 0),
|
||
|
|
media('a', 500, { hasRelation: true }),
|
||
|
|
media('a', 1000),
|
||
|
|
media('a', 1500, { redacted: true }),
|
||
|
|
]);
|
||
|
|
assert.deepEqual(findMediaRun(at, 0), [0]);
|
||
|
|
assert.deepEqual(findMediaRun(at, 1), []);
|
||
|
|
assert.deepEqual(findMediaRun(at, 2), [2]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('measures the gap between consecutive members, not from the first', () => {
|
||
|
|
const at = seq([media('a', 0), media('a', 50_000), media('a', 100_000)]);
|
||
|
|
assert.deepEqual(findMediaRun(at, 0), [0, 1, 2]);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('splitRun', () => {
|
||
|
|
it('caps group size and drops singles', () => {
|
||
|
|
const items = Array.from({ length: 12 }, (_, i) => media('a', i * 1000));
|
||
|
|
const groups = splitRun(
|
||
|
|
Array.from({ length: 12 }, (_, i) => i),
|
||
|
|
seq(items),
|
||
|
|
10,
|
||
|
|
);
|
||
|
|
assert.deepEqual(groups, [
|
||
|
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||
|
|
[10, 11],
|
||
|
|
]);
|
||
|
|
assert.deepEqual(splitRun([0, 1, 2, 3], seq(items), 3), [[0, 1, 2]]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('closes a group at a member with reactions so they stay visible', () => {
|
||
|
|
const items = [
|
||
|
|
media('a', 0),
|
||
|
|
media('a', 1000, { mustEnd: true }),
|
||
|
|
media('a', 2000),
|
||
|
|
media('a', 3000),
|
||
|
|
];
|
||
|
|
assert.deepEqual(splitRun([0, 1, 2, 3], seq(items)), [
|
||
|
|
[0, 1],
|
||
|
|
[2, 3],
|
||
|
|
]);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('planMediaGroups', () => {
|
||
|
|
it('marks only the last member as the renderer', () => {
|
||
|
|
const at = seq([media('a', 0), media('a', 1000), media('a', 2000)]);
|
||
|
|
const plan = planMediaGroups(at, 1);
|
||
|
|
assert.equal(plan.get(0)?.renders, false);
|
||
|
|
assert.equal(plan.get(1)?.renders, false);
|
||
|
|
assert.equal(plan.get(2)?.renders, true);
|
||
|
|
assert.deepEqual(plan.get(2)?.members, [0, 1, 2]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('returns an empty plan for a lone image', () => {
|
||
|
|
assert.equal(planMediaGroups(seq([text('a', 0), media('a', 1000)]), 1).size, 0);
|
||
|
|
});
|
||
|
|
});
|