dorfteich/packages/shared/src/editor-schema/mention.test.ts
Claude Fable 5 7471fc70f7 #150: @-Mentions — Inline-Node, instanzweite User-Suche, Autocomplete
Neuer Inline-Atom mention {userId, username}: Markdown-Regel @username
(E-Mail-sicher über Wortgrenzen), Serializer, HTML-Span dt-mention,
Plain-Text für die Suche, Extraktor extractMentionUserIds. Neue
Endpoints GET /users/search (auth, min. 2 Zeichen, Limit 10,
Rate-Limit) und GET /users/brief (Batch-Auflösung für live
Anzeigenamen; gelöschte Nutzer → toter Chip). Editor: MentionView mit
Live-displayName, MentionAutocomplete (Klon des Wikilink-Musters),
Chip-CSS. 5 Unit-Tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155v2aT8AG1kZDQEZiCLBWC
2026-07-20 00:56:07 +02:00

63 lines
2.1 KiB
TypeScript

import { Node } from 'prosemirror-model';
import { describe, expect, it } from 'vitest';
import { docToHtml } from './html';
import { docToMarkdown, markdownToDoc } from './markdown';
import { extractMentionUserIds } from './mentions';
import { docToPlainText } from './plain-text';
import { editorSchema } from './schema';
function firstMention(doc: Node): Node | null {
let found: Node | null = null;
doc.descendants((node) => {
if (!found && node.type.name === 'mention') found = node;
});
return found;
}
describe('@username mention (issue #150)', () => {
it('parses @username to a mention node and round-trips', () => {
const doc = markdownToDoc('Hallo @nadia, schau mal.');
const node = firstMention(doc);
expect(node).not.toBeNull();
expect(node!.attrs.username).toBe('nadia');
expect(node!.attrs.userId).toBe('');
expect(docToMarkdown(doc)).toContain('@nadia');
});
it('leaves e-mail addresses untouched', () => {
const doc = markdownToDoc('Schreib an stefan@example.org bitte.');
expect(firstMention(doc)).toBeNull();
expect(docToMarkdown(doc)).toContain('stefan@example.org');
});
it('renders HTML with the username and optional user id', () => {
const doc = markdownToDoc('Ping @zoraya-shahin!');
expect(docToHtml(doc)).toContain('class="dt-mention" data-mention="zoraya-shahin"');
expect(docToHtml(doc)).toContain('@zoraya-shahin');
});
it('appears in the plain text for search', () => {
expect(docToPlainText(markdownToDoc('Frag @nadia.'))).toContain('@nadia');
});
it('extracts only resolved user ids', () => {
const doc = Node.fromJSON(editorSchema, {
type: 'doc',
content: [
{
type: 'paragraph',
content: [
{ type: 'mention', attrs: { userId: 'u-1', username: 'nadia' } },
{ type: 'text', text: ' und ' },
{ type: 'mention', attrs: { userId: '', username: 'import-only' } },
{ type: 'text', text: ' und nochmal ' },
{ type: 'mention', attrs: { userId: 'u-1', username: 'nadia' } },
],
},
],
});
expect(extractMentionUserIds(doc)).toEqual(['u-1']);
});
});