Files
cinny/src/app/state/recentForwardTargets.test.ts
T
jaredandClaude Opus 4.8 629db9724f feat(forward): message preview, optional comment, recent targets
The Forward dialog forwarded blind. Add three things (design-system
cleanup included):

- Preview: a compact read-only preview at the top of the dialog shows the
  sender + body, with a thumbnail for image/video (reuses ThumbnailContent
  and the getMemberName/getMemberAvatarMxc/trimReplyFromBody helpers). We
  already hold mEvent, so nothing is fetched.

- Comment: an optional "Add a comment" field sends a short m.text note to
  each target room, sequenced BEFORE the forwarded message per room so the
  note reads above the quoted content. The existing per-room failure /
  retry logic is preserved (a room fails if either send rejects).

- Recent targets: a "Recent" chip row (hidden while searching) offers
  one-tap selection of rooms you last forwarded to. Successful targets are
  recorded most-recent-first, deduped, capped at 8, in localStorage via the
  pure, unit-tested addRecentForwardTarget (state/recentForwardTargets.ts).
  Rooms you've since left are filtered out of the row.

Also replaces the hardcoded rgba(0,0,0,0.35) sending scrim with a
token-free opacity dim of the list (design-system rule: no hardcoded
colors).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 00:37:49 -04:00

48 lines
1.8 KiB
TypeScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
// This module uses atomWithStorage(..., { getOnInit: true }), so it reads
// `localStorage` the moment it's evaluated. node has none, so install a no-op
// mock, then import dynamically (a static import would be hoisted above the
// mock assignment and evaluate the module too early).
(globalThis as { localStorage?: unknown }).localStorage = {
getItem: () => null,
setItem: () => undefined,
removeItem: () => undefined,
};
const { addRecentForwardTarget } = await import('./recentForwardTargets');
test('addRecentForwardTarget prepends a new roomId', () => {
assert.deepEqual(addRecentForwardTarget(['!a', '!b'], '!c'), ['!c', '!a', '!b']);
});
test('addRecentForwardTarget dedupes and moves the roomId to the front', () => {
assert.deepEqual(addRecentForwardTarget(['!a', '!b', '!c'], '!b'), ['!b', '!a', '!c']);
});
test('addRecentForwardTarget ignores an empty roomId', () => {
assert.deepEqual(addRecentForwardTarget(['!a', '!b'], ''), ['!a', '!b']);
});
test('addRecentForwardTarget caps the list at 8 entries, dropping the oldest', () => {
const eight = Array.from({ length: 8 }, (_, i) => `!r${i}`);
const result = addRecentForwardTarget(eight, '!new');
assert.equal(result.length, 8);
assert.equal(result[0], '!new');
// the oldest entry (last) is dropped
assert.equal(result.includes('!r7'), false);
assert.deepEqual(result.slice(1), eight.slice(0, 7));
});
test('addRecentForwardTarget does not mutate its input', () => {
const input = ['!a', '!b'];
const before = [...input];
addRecentForwardTarget(input, '!c');
assert.deepEqual(input, before);
});
test('addRecentForwardTarget on an empty history returns a single-element list', () => {
assert.deepEqual(addRecentForwardTarget([], '!first'), ['!first']);
});