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>
99 lines
3.1 KiB
TypeScript
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');
|
|
});
|
|
});
|