All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 5m30s
CI / Build container images (pull_request) Successful in 10s
CI / Auth e2e pack (pull_request) Successful in 7m39s
CI / Import/export fidelity gate (pull_request) Successful in 56s
CD / Build and push images (push) Successful in 17s
CD / Deploy to Test (push) Successful in 14s
CD / Smoke tests against Test (push) Successful in 1m14s
CD / Promote to Int (push) Successful in 10s
CI / Lint, typecheck, test (push) Successful in 4m46s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 7m1s
CI / Import/export fidelity gate (push) Successful in 54s
Release / Build release images and notes (push) Successful in 1m11s
Release / Release-candidate operations QA (push) Successful in 44s
Prod deploy / Deploy the released images to Prod (push) Successful in 15s
Neues Referenz-Plugin packages/plugins/chordpro nach dem mermaid-Muster: bewusst ohne Fremdbibliothek (Supply-Chain-Lehre aus #136) — eigener minimaler ChordPro-Parser (Direktiven title/subtitle/ artist/key/capo/tempo/comment, Chorus-Fences, [Akkord]-Marker, #-Kommentare) plus SVG-Formatter mit Monospace-Raster: Akkorde über dem Text, Titelkopf, Chorus-Einrückung, XML-escaped. Persistenz {source, svg} — der Snapshot bedient Lesemodus, Public-Ansicht und Exporte über den PluginFallbackRenderer. Edit-Modus: Textarea + debounced Live-Preview. ZIP 20 KB (Limits 64/256 MiB), 4 Parser-/Formatter-Tests, i18n de+en, Doku-Listen (site-admin en+de, plugin-architecture) ergänzt. Prod-Installation wie üblich per Site-Admin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155v2aT8AG1kZDQEZiCLBWC
60 lines
1.9 KiB
TypeScript
60 lines
1.9 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import { chordProToSvg, parseChordPro } from './chordpro';
|
|
|
|
const SHEET = `{title: Bühnenlied}
|
|
{artist: Nadia Morgenstern}
|
|
{key: C}
|
|
# internes Kommentar — unsichtbar
|
|
|
|
[C]Hallo du [G]schöne [Am]Welt
|
|
Ohne Akkorde geht es [F]auch
|
|
|
|
{start_of_chorus}
|
|
{comment: Refrain}
|
|
[C]La la [G]la
|
|
{end_of_chorus}`;
|
|
|
|
describe('chordpro parser + svg formatter (issue #155)', () => {
|
|
it('parses directives, chords with columns, comments, and chorus', () => {
|
|
const sheet = parseChordPro(SHEET);
|
|
expect(sheet.meta).toMatchObject({
|
|
title: 'Bühnenlied',
|
|
subtitle: 'Nadia Morgenstern',
|
|
key: 'C',
|
|
});
|
|
const first = sheet.lines[0]!;
|
|
expect(first.lyrics).toBe('Hallo du schöne Welt');
|
|
expect(first.chords).toEqual([
|
|
{ at: 0, chord: 'C' },
|
|
{ at: 9, chord: 'G' },
|
|
{ at: 16, chord: 'Am' },
|
|
]);
|
|
const chorusLine = sheet.lines.find((l) => l.chorus && l.kind === 'pair')!;
|
|
expect(chorusLine.lyrics).toBe('La la la');
|
|
const comment = sheet.lines.find((l) => l.kind === 'comment')!;
|
|
expect(comment.lyrics).toBe('Refrain');
|
|
// `#` lines never show up.
|
|
expect(sheet.lines.some((l) => l.lyrics.includes('internes'))).toBe(false);
|
|
});
|
|
|
|
it('renders a self-contained SVG with title, chords, and umlauts escaped safely', () => {
|
|
const svg = chordProToSvg(SHEET);
|
|
expect(svg).toMatch(/^<svg xmlns="http:\/\/www\.w3\.org\/2000\/svg"/);
|
|
expect(svg).toContain('Bühnenlied');
|
|
expect(svg).toContain('>Am</text>');
|
|
expect(svg).toContain('Hallo du schöne Welt');
|
|
expect(svg).not.toContain('<script');
|
|
});
|
|
|
|
it('escapes XML-hostile input', () => {
|
|
const svg = chordProToSvg('{title: <img src=x>}\n[C]a & b < c');
|
|
expect(svg).toContain('<img src=x>');
|
|
expect(svg).toContain('a & b < c');
|
|
});
|
|
|
|
it('throws on an empty sheet so the plugin can show its hint', () => {
|
|
expect(() => chordProToSvg(' \n# nur Kommentar')).toThrow();
|
|
});
|
|
});
|