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
63 lines
2.1 KiB
TypeScript
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']);
|
|
});
|
|
});
|