test: markdown parser subsystem (58) + custom-emoji readers (32)
Via subagents, probe-verified against real output, no bugs: - markdown: internal/utils (11), inline/runner (7), inline/parser (21 — bold/ italic/underline/strike/code/spoiler/link, nesting, precedence, URL lookbehind), block/parser (19 — headings/code-fences/quotes/lists/<br>/escapes). Closes the biggest coverage hole (core message rendering). - custom-emoji: PackMetaReader (6), PackImageReader (7), PackImagesReader (4), utils equality+makeImagePacks (5), recent-emoji promote/increment/100-cap (10). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { PackImageReader } from './PackImageReader';
|
||||
import { ImageUsage, PackImage } from './types';
|
||||
|
||||
test('fromPackImage returns undefined when url is missing or not a string', () => {
|
||||
assert.equal(PackImageReader.fromPackImage('cat', {} as unknown as PackImage), undefined);
|
||||
assert.equal(
|
||||
PackImageReader.fromPackImage('cat', { url: 123 } as unknown as PackImage),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
test('fromPackImage builds a reader with shortcode and url', () => {
|
||||
const reader = PackImageReader.fromPackImage('cat', { url: 'mxc://x/cat' });
|
||||
assert.ok(reader);
|
||||
assert.equal(reader?.shortcode, 'cat');
|
||||
assert.equal(reader?.url, 'mxc://x/cat');
|
||||
});
|
||||
|
||||
test('body reads only string values', () => {
|
||||
assert.equal(PackImageReader.fromPackImage('c', { url: 'u', body: 'Cat' })?.body, 'Cat');
|
||||
assert.equal(PackImageReader.fromPackImage('c', { url: 'u' })?.body, undefined);
|
||||
assert.equal(
|
||||
PackImageReader.fromPackImage('c', { url: 'u', body: 5 } as unknown as PackImage)?.body,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
test('info is passed through unchanged', () => {
|
||||
const info = { w: 10, h: 10 };
|
||||
assert.equal(
|
||||
PackImageReader.fromPackImage('c', { url: 'u', info } as unknown as PackImage)?.info,
|
||||
info,
|
||||
);
|
||||
assert.equal(PackImageReader.fromPackImage('c', { url: 'u' })?.info, undefined);
|
||||
});
|
||||
|
||||
test('usage filters to known values and returns undefined when none/non-array', () => {
|
||||
assert.equal(PackImageReader.fromPackImage('c', { url: 'u' })?.usage, undefined);
|
||||
assert.equal(
|
||||
PackImageReader.fromPackImage('c', {
|
||||
url: 'u',
|
||||
usage: 'emoticon' as unknown as ImageUsage[],
|
||||
})?.usage,
|
||||
undefined,
|
||||
);
|
||||
assert.equal(
|
||||
PackImageReader.fromPackImage('c', {
|
||||
url: 'u',
|
||||
usage: ['bogus'] as unknown as ImageUsage[],
|
||||
})?.usage,
|
||||
undefined,
|
||||
);
|
||||
assert.deepEqual(
|
||||
PackImageReader.fromPackImage('c', { url: 'u', usage: [ImageUsage.Sticker] })?.usage,
|
||||
[ImageUsage.Sticker],
|
||||
);
|
||||
assert.deepEqual(
|
||||
PackImageReader.fromPackImage('c', {
|
||||
url: 'u',
|
||||
usage: [ImageUsage.Emoticon, 'x', ImageUsage.Sticker] as unknown as ImageUsage[],
|
||||
})?.usage,
|
||||
[ImageUsage.Emoticon, ImageUsage.Sticker],
|
||||
);
|
||||
});
|
||||
|
||||
test('content reconstructs the PackImage from url and raw image fields', () => {
|
||||
const info = { w: 1, h: 1 };
|
||||
const reader = PackImageReader.fromPackImage('c', {
|
||||
url: 'u',
|
||||
body: 'Cat',
|
||||
usage: [ImageUsage.Emoticon],
|
||||
info,
|
||||
} as unknown as PackImage);
|
||||
assert.deepEqual(reader?.content, {
|
||||
url: 'u',
|
||||
body: 'Cat',
|
||||
usage: [ImageUsage.Emoticon],
|
||||
info,
|
||||
});
|
||||
});
|
||||
|
||||
test('content preserves raw usage even when usage getter filters it', () => {
|
||||
// content uses the raw image.usage, not the filtered usage getter
|
||||
const reader = PackImageReader.fromPackImage('c', {
|
||||
url: 'u',
|
||||
usage: ['bogus'] as unknown as ImageUsage[],
|
||||
});
|
||||
assert.deepEqual(reader?.content.usage, ['bogus'] as unknown as ImageUsage[]);
|
||||
assert.equal(reader?.usage, undefined);
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { PackImagesReader } from './PackImagesReader';
|
||||
import { PackImages, ImageUsage } from './types';
|
||||
|
||||
test('collection builds a shortcode -> reader map', () => {
|
||||
const images: PackImages = {
|
||||
cat: { url: 'mxc://x/cat', usage: [ImageUsage.Emoticon] },
|
||||
dog: { url: 'mxc://x/dog' },
|
||||
};
|
||||
const reader = new PackImagesReader(images);
|
||||
const collection = reader.collection;
|
||||
|
||||
assert.equal(collection.size, 2);
|
||||
assert.equal(collection.get('cat')?.url, 'mxc://x/cat');
|
||||
assert.equal(collection.get('cat')?.shortcode, 'cat');
|
||||
assert.equal(collection.get('dog')?.url, 'mxc://x/dog');
|
||||
});
|
||||
|
||||
test('collection drops invalid images (missing/non-string url)', () => {
|
||||
const images = {
|
||||
good: { url: 'mxc://x/good' },
|
||||
noUrl: {},
|
||||
badUrl: { url: 5 },
|
||||
} as unknown as PackImages;
|
||||
const collection = new PackImagesReader(images).collection;
|
||||
|
||||
assert.equal(collection.size, 1);
|
||||
assert.ok(collection.has('good'));
|
||||
assert.equal(collection.has('noUrl'), false);
|
||||
assert.equal(collection.has('badUrl'), false);
|
||||
});
|
||||
|
||||
test('collection of an empty image set is an empty map', () => {
|
||||
const collection = new PackImagesReader({}).collection;
|
||||
assert.equal(collection.size, 0);
|
||||
});
|
||||
|
||||
test('collection is memoized (same Map instance returned)', () => {
|
||||
const reader = new PackImagesReader({ cat: { url: 'u' } });
|
||||
const first = reader.collection;
|
||||
const second = reader.collection;
|
||||
assert.equal(first, second);
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { PackMetaReader } from './PackMetaReader';
|
||||
import { ImageUsage, PackMeta } from './types';
|
||||
|
||||
const reader = (meta: PackMeta) => new PackMetaReader(meta);
|
||||
|
||||
test('name reads display_name only when it is a string', () => {
|
||||
assert.equal(reader({ display_name: 'Cats' }).name, 'Cats');
|
||||
assert.equal(reader({}).name, undefined);
|
||||
assert.equal(reader({ display_name: 42 } as unknown as PackMeta).name, undefined);
|
||||
});
|
||||
|
||||
test('avatar reads avatar_url only when it is a string', () => {
|
||||
assert.equal(reader({ avatar_url: 'mxc://x/y' }).avatar, 'mxc://x/y');
|
||||
assert.equal(reader({}).avatar, undefined);
|
||||
assert.equal(reader({ avatar_url: {} } as unknown as PackMeta).avatar, undefined);
|
||||
});
|
||||
|
||||
test('attribution reads attribution only when it is a string', () => {
|
||||
assert.equal(reader({ attribution: 'me' }).attribution, 'me');
|
||||
assert.equal(reader({}).attribution, undefined);
|
||||
assert.equal(reader({ attribution: 0 } as unknown as PackMeta).attribution, undefined);
|
||||
});
|
||||
|
||||
test('usage falls back when missing, non-array, or empty/unknown', () => {
|
||||
const fallback = [ImageUsage.Emoticon, ImageUsage.Sticker];
|
||||
assert.deepEqual(reader({}).usage, fallback);
|
||||
assert.deepEqual(reader({ usage: 'emoticon' } as unknown as PackMeta).usage, fallback);
|
||||
assert.deepEqual(reader({ usage: [] }).usage, fallback);
|
||||
assert.deepEqual(reader({ usage: ['bogus'] as unknown as ImageUsage[] }).usage, fallback);
|
||||
});
|
||||
|
||||
test('usage filters to only known values', () => {
|
||||
assert.deepEqual(reader({ usage: [ImageUsage.Emoticon] }).usage, [ImageUsage.Emoticon]);
|
||||
assert.deepEqual(reader({ usage: [ImageUsage.Sticker] }).usage, [ImageUsage.Sticker]);
|
||||
assert.deepEqual(
|
||||
reader({
|
||||
usage: [ImageUsage.Emoticon, 'bogus', ImageUsage.Sticker] as unknown as ImageUsage[],
|
||||
}).usage,
|
||||
[ImageUsage.Emoticon, ImageUsage.Sticker],
|
||||
);
|
||||
});
|
||||
|
||||
test('content returns the original meta object', () => {
|
||||
const meta: PackMeta = { display_name: 'Cats', usage: [ImageUsage.Emoticon] };
|
||||
assert.equal(reader(meta).content, meta);
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { MatrixEvent } from 'matrix-js-sdk';
|
||||
import { packAddressEqual, imageUsageEqual, packMetaEqual, makeImagePacks } from './utils';
|
||||
import { PackAddress } from './PackAddress';
|
||||
import { PackMetaReader } from './PackMetaReader';
|
||||
import { ImageUsage, PackContent, PackMeta } from './types';
|
||||
|
||||
test('packAddressEqual handles undefined and value equality', () => {
|
||||
assert.equal(packAddressEqual(undefined, undefined), true);
|
||||
assert.equal(packAddressEqual(new PackAddress('!r', 'k'), undefined), false);
|
||||
assert.equal(packAddressEqual(undefined, new PackAddress('!r', 'k')), false);
|
||||
assert.equal(packAddressEqual(new PackAddress('!r', 'k'), new PackAddress('!r', 'k')), true);
|
||||
assert.equal(packAddressEqual(new PackAddress('!r', 'k'), new PackAddress('!r', 'j')), false);
|
||||
assert.equal(packAddressEqual(new PackAddress('!r', 'k'), new PackAddress('!s', 'k')), false);
|
||||
});
|
||||
|
||||
test('imageUsageEqual compares set membership and length', () => {
|
||||
assert.equal(imageUsageEqual([], []), true);
|
||||
assert.equal(imageUsageEqual([ImageUsage.Emoticon], [ImageUsage.Emoticon]), true);
|
||||
assert.equal(
|
||||
imageUsageEqual(
|
||||
[ImageUsage.Emoticon, ImageUsage.Sticker],
|
||||
[ImageUsage.Sticker, ImageUsage.Emoticon],
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert.equal(imageUsageEqual([ImageUsage.Emoticon], [ImageUsage.Sticker]), false);
|
||||
assert.equal(
|
||||
imageUsageEqual([ImageUsage.Emoticon], [ImageUsage.Emoticon, ImageUsage.Sticker]),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
const meta = (m: PackMeta) => new PackMetaReader(m);
|
||||
|
||||
test('packMetaEqual compares name, avatar, attribution and usage', () => {
|
||||
assert.equal(packMetaEqual(meta({ display_name: 'A' }), meta({ display_name: 'A' })), true);
|
||||
assert.equal(packMetaEqual(meta({ display_name: 'A' }), meta({ display_name: 'B' })), false);
|
||||
assert.equal(packMetaEqual(meta({ avatar_url: 'u' }), meta({ avatar_url: 'v' })), false);
|
||||
assert.equal(packMetaEqual(meta({ attribution: 'x' }), meta({ attribution: 'y' })), false);
|
||||
// both fall back to default usage => equal
|
||||
assert.equal(packMetaEqual(meta({}), meta({})), true);
|
||||
assert.equal(
|
||||
packMetaEqual(meta({ usage: [ImageUsage.Emoticon] }), meta({ usage: [ImageUsage.Sticker] })),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
const eventStub = (id: string | undefined, content: PackContent = {}): MatrixEvent =>
|
||||
({
|
||||
getId: () => id,
|
||||
getRoomId: () => '!room:server',
|
||||
getStateKey: () => 'state-key',
|
||||
getContent: () => content,
|
||||
}) as unknown as MatrixEvent;
|
||||
|
||||
test('makeImagePacks builds packs and skips events without an id', () => {
|
||||
const events = [
|
||||
eventStub('$1', { pack: { display_name: 'One' } }),
|
||||
eventStub(undefined, { pack: { display_name: 'Skip' } }),
|
||||
eventStub('$2', { images: { cat: { url: 'mxc://x/cat' } } }),
|
||||
];
|
||||
const packs = makeImagePacks(events);
|
||||
|
||||
assert.equal(packs.length, 2);
|
||||
assert.equal(packs[0].id, '$1');
|
||||
assert.equal(packs[0].meta.name, 'One');
|
||||
assert.equal(packs[0].address?.roomId, '!room:server');
|
||||
assert.equal(packs[0].address?.stateKey, 'state-key');
|
||||
assert.equal(packs[1].id, '$2');
|
||||
assert.equal(packs[1].images.collection.get('cat')?.url, 'mxc://x/cat');
|
||||
});
|
||||
|
||||
test('makeImagePacks returns empty for empty input', () => {
|
||||
assert.deepEqual(makeImagePacks([]), []);
|
||||
});
|
||||
Reference in New Issue
Block a user