cards/packages/cards-domain/tests/cloze.test.ts
Till JS 4b451f1b8d Phase 9i: Cloze-Hint-Anzeige
renderClozePrompt zeigt jetzt den Hint im aktiven Cluster anstelle
von „…", wenn der User die `{{c1::Antwort::Hinweis}}`-Syntax nutzt.
Beispiel: Prompt für `{{c1::Paris::Hauptstadt}}` wird "[Hauptstadt]"
statt "[…]". Nicht-aktive Cluster expandieren auf ihre Antwort —
der Hint bleibt unsichtbar, bis sein Cluster dran ist.

Neue Helper-Funktion hintForCluster(text, clusterId) liefert die
erste Hint-Annotation eines Clusters (deterministisches Verhalten
bei mehreren `{{c1::…}}`-Vorkommen mit unterschiedlichen Hints).

5 neue Tests in cloze.test.ts: hintForCluster (4 Cases), erweiterte
renderClozePrompt-Cases. Domain jetzt 46 Tests grün.

cloze_help in i18n DE/EN um die Hint-Syntax-Erklärung erweitert.
Live-Preview im Card-New/Edit nutzt die erweiterte Logik automatisch
(beide rufen renderClozePrompt aus @cards/domain).

svelte-check 379 files 0 errors, API-Tests unverändert grün
(48/9), Web-Tests 5/1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:26:00 +02:00

99 lines
3.1 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
extractClusterIds,
hintForCluster,
subIndexCountForCloze,
clusterIdForSubIndex,
renderClozePrompt,
renderClozeAnswer,
} from '../src/cloze.ts';
describe('extractClusterIds', () => {
it('liefert leere Liste, wenn kein Cluster-Markup', () => {
expect(extractClusterIds('Plain text without cloze')).toEqual([]);
});
it('extrahiert einen einzelnen Cluster', () => {
expect(extractClusterIds('The capital of France is {{c1::Paris}}.')).toEqual([1]);
});
it('extrahiert mehrere Cluster aufsteigend sortiert', () => {
expect(extractClusterIds('{{c2::B}} kommt nach {{c1::A}}')).toEqual([1, 2]);
});
it('dedupliziert mehrfach auftretende Cluster-IDs', () => {
expect(extractClusterIds('{{c1::a}} und {{c1::b}} und {{c2::c}}')).toEqual([1, 2]);
});
it('ignoriert malformed Cluster (kein numerisches N)', () => {
expect(extractClusterIds('{{cX::weird}} {{c1::ok}}')).toEqual([1]);
});
it('akzeptiert Cluster mit Hint (::hint wird gedroppt)', () => {
expect(extractClusterIds('Die {{c1::Hauptstadt::Land}} von Frankreich.')).toEqual([1]);
});
});
describe('subIndexCountForCloze', () => {
it('zählt distinct Cluster', () => {
expect(subIndexCountForCloze('{{c1::a}} {{c2::b}} {{c3::c}}')).toBe(3);
});
it('returniert 0 bei text ohne Cluster', () => {
expect(subIndexCountForCloze('keine cloze')).toBe(0);
});
});
describe('clusterIdForSubIndex', () => {
it('mapt subIndex auf sortierte Cluster-IDs', () => {
const text = '{{c3::c}} {{c1::a}} {{c2::b}}';
expect(clusterIdForSubIndex(text, 0)).toBe(1);
expect(clusterIdForSubIndex(text, 1)).toBe(2);
expect(clusterIdForSubIndex(text, 2)).toBe(3);
expect(clusterIdForSubIndex(text, 3)).toBe(null);
});
});
describe('hintForCluster', () => {
it('liefert den Hint, wenn vorhanden', () => {
expect(hintForCluster('Die {{c1::Antwort::Hinweis}}.', 1)).toBe('Hinweis');
});
it('liefert undefined ohne Hint-Annotation', () => {
expect(hintForCluster('Die {{c1::Antwort}}.', 1)).toBeUndefined();
});
it('liefert undefined für nicht-existierende Cluster', () => {
expect(hintForCluster('Die {{c1::Antwort::Hint}}.', 2)).toBeUndefined();
});
it('erste Hint-Annotation gewinnt bei mehreren Vorkommen', () => {
const text = '{{c1::a}} und {{c1::b::erster}} und {{c1::c::zweiter}}';
expect(hintForCluster(text, 1)).toBe('erster');
});
});
describe('renderClozePrompt', () => {
it('maskiert aktiven Cluster, expandiert andere', () => {
const out = renderClozePrompt('{{c1::A}} und {{c2::B}}', 1);
expect(out).toBe('[…] und B');
});
it('zeigt Hint im aktiven Cluster, wenn vorhanden', () => {
const out = renderClozePrompt('Die {{c1::Antwort::Hinweis}}.', 1);
expect(out).toBe('Die [Hinweis].');
});
it('expandiert nicht-aktiven Cluster auch wenn Hint vorhanden', () => {
const out = renderClozePrompt('{{c1::A::tipp}} und {{c2::B}}', 2);
expect(out).toBe('A und […]');
});
});
describe('renderClozeAnswer', () => {
it('expandiert alle Cluster, markiert aktiven mit Bold', () => {
const out = renderClozeAnswer('{{c1::A}} und {{c2::B}}', 1);
expect(out).toBe('**A** und B');
});
});