import { afterEach, describe, expect, it } from 'vitest'; import { beschrifteTabelle, el } from '@/ui/dom'; import { MAX_TEXTFELD_ZEICHEN, textCell } from '@/ui/inputs'; /** * Laengengrenze der kompakten Textfelder, Befunde zur Fassung 5.10.0. * * BEFUND 40: Kein Eingabefeld trug eine Laengenbegrenzung - "maxlength" kam in * src/ nicht vor. Eine in das Feld "Bezeichnung" einer Signalgruppe eingefuegte * lange Zeichenkette ging ungekuerzt in das Zuordnungsbanner ueber der * Zwischenzeitenmatrix; ab rund 7 000 Zeichen laeuft jspdf-autotable dort in * `printFullRow` in eine Endlosteilung, und die Ausgabe bricht mit "Maximum * call stack size exceeded" ab. Es entsteht dann ueberhaupt keine * Planunterlage. Die Schranke in `matrixSatz` (src/services/export/pdf.ts) * verschmaelert den Ausschnitt nur bis auf eine Spalte und ist dann erschoepft. * * Die Bezeichnung ist eine Plankennzeichnung ("Bezeichnung im Plan, z. B. K1, * F2, R3"), keine Beschreibung. Sie wird beim Eintragen begrenzt und die * Kuerzung gemeldet - wie `ganzzahlAbrunden` es bei den Zahlenfeldern vormacht. */ afterEach(() => { document.body.replaceChildren(); }); function meldungen(): string[] { return [...document.querySelectorAll('.meldung-warnung')].map((m) => m.textContent ?? ''); } function tippe(feld: HTMLInputElement, text: string): void { feld.value = text; feld.dispatchEvent(new Event('change')); } describe('Textfelder: Bezeichnungen sind in der Länge begrenzt (Befund 40)', () => { it('trägt die Grenze als maxlength am Feld', () => { const feld = textCell('K1', () => {}); expect(feld.getAttribute('maxlength')).toBe(String(MAX_TEXTFELD_ZEICHEN)); }); it('kürzt eine zu lange Eingabe und meldet die Kürzung', () => { const angekommen: string[] = []; // Ohne Optionen - genau so, wie die acht Aufrufstellen der Anwendung // `textCell` benutzen (signalGroupsView, phasesView, projectView, // lageplanView; settingsView nur mit `placeholder`). const feld = textCell('K1', (wert) => angekommen.push(wert)); document.body.append(feld); tippe(feld, 'A'.repeat(10_000)); expect(angekommen).toHaveLength(1); expect(angekommen[0]).toHaveLength(MAX_TEXTFELD_ZEICHEN); // Auch im Feld steht danach, was uebernommen wurde - sonst zeigte die // Zelle einen Text, mit dem niemand mehr rechnet. expect(feld.value).toHaveLength(MAX_TEXTFELD_ZEICHEN); const gemeldet = meldungen(); expect(gemeldet).toHaveLength(1); expect(gemeldet[0]).toContain(String(MAX_TEXTFELD_ZEICHEN)); }); it('nennt in der Meldung die Spalte und die Zeile der Tabellenzelle', () => { /* * NACHTRAG (Fassung 5.11.0). Die Meldung bildete den Feldnamen aus den * OPTIONEN (`beschriftung ?? title ?? 'Der Text'`) - und kein einziger * `textCell`-Aufruf der Anwendung uebergibt eines von beiden. In der * Signalgruppentabelle mit ihren neunzehn Spalten und bis zu sechzehn * Zeilen hiess die Warnung deshalb immer nur "Der Text: ...", und welche * Bezeichnung gekuerzt wurde, stand nirgends. * * Den Namen bekommt das Feld erst beim Einhaengen, aus Spaltenkopf und * Zeilenname (`beschrifteTabelle` -> `benenneBedienelemente`, ui/dom.ts). * Genau dort liest ihn `numberCell` seit jeher - mit derselben Begruendung * ("bei einer Tabelle mit sechzehn Spalten waere unklar, welche"). */ const feld = textCell('K1', () => {}); const tabelle = el( 'table', {}, el('thead', {}, el('tr', {}, el('th', { text: 'Bezeichnung' }))), el('tbody', {}, el('tr', {}, el('td', {}, feld))), ); document.body.append(tabelle); beschrifteTabelle(tabelle, { beschriftung: 'Signalgruppen' }); expect(feld.getAttribute('aria-label'), 'Vorbedingung: die Tabelle benennt das Feld').toBe( 'Bezeichnung, K1', ); tippe(feld, 'A'.repeat(10_000)); const gemeldet = meldungen(); expect(gemeldet).toHaveLength(1); expect(gemeldet[0], 'die Meldung nennt das gekürzte Feld nicht').toContain('Bezeichnung, K1'); }); it('lässt einen Text bis zur Grenze unverändert und meldet nichts', () => { const angekommen: string[] = []; const feld = textCell('', (wert) => angekommen.push(wert)); document.body.append(feld); const gerade_noch = 'B'.repeat(MAX_TEXTFELD_ZEICHEN); tippe(feld, gerade_noch); expect(angekommen).toEqual([gerade_noch]); expect(meldungen()).toHaveLength(0); }); it('zählt die Länge nach dem Abschneiden der Leerzeichen', () => { const angekommen: string[] = []; const feld = textCell('', (wert) => angekommen.push(wert)); document.body.append(feld); tippe(feld, ` ${'C'.repeat(MAX_TEXTFELD_ZEICHEN)} `); expect(angekommen).toEqual(['C'.repeat(MAX_TEXTFELD_ZEICHEN)]); expect(meldungen()).toHaveLength(0); }); it('bleibt weit unter der Länge, an der die Zwischenzeitenmatrix im PDF abbricht', () => { // Gemessen wurde: 6 000 Zeichen laufen durch, ab rund 7 000 bricht die // Ausgabe ab. Der bestehende Test tests/export/pdfZwischenzeitenmatrix.test.ts // haelt fest, dass zwoelf Gruppen mit 260 Zeichen langen Namen einen // Ausdruck ergeben - die Grenze bleibt darunter. expect(MAX_TEXTFELD_ZEICHEN).toBeLessThanOrEqual(260); }); });