Terminplaner: Portal-Ansicht – Nachbesserungen (1/2: Module getrennt)

public/termine.js in Huelle und Module zerlegt, Verhalten unveraendert (R48):
termine-dom.js (Helfer ohne Zustand), termine-tab-termine.js (Liste, Kopf, Zeilen),
termine-aktionen.js (Aktionen mit Rueckfrage), termine-formulare.js (Uhrzeiten,
Termin eintragen, Zeit sperren). Reiter bekommen Zustand und aktuell() nur ueber
ctx; die Huelle haengt sie ueber die Registry REITER ein. Statiktest prueft alle
public/termine*.js.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-03 08:17:44 +00:00
1 parent 43e4826ae3
commit 9c07f6a8c5
7 files changed
+697 -645

No files matched your search

+28 -10
View File
@@ -1,6 +1,6 @@
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { readFileSync, readdirSync } from 'node:fs'
import {
FILTER, ABSAGE_GRUENDE, anfragenText, bereichFuer, datumKurz, datumLang, endeZeit, fensterParsen, fensterText, filterStatus, gruppiereNachTag,
fehltText, heuteBerlin, istSperre, monatVon, moeglicheAktionen, ressourcenWort, statusLabel, statusTon, tagPlus, tagUeberschrift, verschiebeTag, wocheVon, zeitNormal, zeitraumTitel,
@@ -230,13 +230,31 @@ test('Portal: Ansicht #termine ist verdrahtet, funktionen.js behält seinen eine
assert.doesNotMatch(fn, /import\s*\(/, 'auch kein dynamischer Import')
})
test('termine.js: Fremdtext nur als Text, keine Browser-Dialoge, Du-Form', () => {
const quelle = lies('public/termine.js').replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|\s)\/\/.*$/gm, '') // ohne Kommentare
assert.doesNotMatch(quelle, /innerHTML|insertAdjacentHTML|outerHTML|document\.write/, 'Daten nie als HTML')
assert.doesNotMatch(quelle, /\bconfirm\(|\balert\(|\bprompt\(/, 'Rückfragen stehen in der Seite')
assert.doesNotMatch(quelle, /\bSie\b|\bIhr(e[nmrs]?)?\b/, 'Portaltexte in der Du-Form')
// Links nur auf die eigene Export-Schnittstelle, nie auf Kundentext
const hrefs = quelle.match(/href: `[^`]*`/g) || []
assert.deepEqual(hrefs, ['href: `/api/termine/export.csv?projectId=${encodeURIComponent(projectId)}`'])
assert.doesNotMatch(quelle, /href: [^`]|setAttribute\('href'/, 'sonst kein href')
test('Ansicht-Module (public/termine*.js): Fremdtext nur als Text, keine Browser-Dialoge, Du-Form, ein einziger Link', () => {
const dateien = readdirSync(new URL('../public/', import.meta.url)).filter((f) => /^termine.*\.js$/.test(f)).sort()
assert.deepEqual(dateien, ['termine-aktionen.js', 'termine-dom.js', 'termine-formulare.js', 'termine-tab-termine.js', 'termine-view.js', 'termine.js'])
const ohneKommentare = (text) => text.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|\s)\/\/.*$/gm, '')
const quellen = dateien.map((f) => [f, ohneKommentare(lies(`public/${f}`))])
for (const [datei, quelle] of quellen) {
assert.doesNotMatch(quelle, /innerHTML|insertAdjacentHTML|outerHTML|document\.write/, `${datei}: Daten nie als HTML`)
assert.doesNotMatch(quelle, /\bconfirm\(|\balert\(|\bprompt\(/, `${datei}: Rückfragen stehen in der Seite`)
assert.doesNotMatch(quelle, /\bSie\b|\bIhr(e[nmrs]?)?\b/, `${datei}: Portaltexte in der Du-Form`)
assert.doesNotMatch(quelle, /localStorage|sessionStorage|document\.cookie/, `${datei}: kein Browserspeicher`)
}
// Links nur auf die eigene Export-Schnittstelle (exportUrl), nie auf Kundentext
const hrefs = quellen.flatMap(([datei, quelle]) => (quelle.match(/href: [^,}]+|setAttribute\('href'/g) || []).map((m) => `${datei}: ${m}`))
assert.deepEqual(hrefs, ['termine.js: href: exportUrl(projectId)'])
const dom = quellen.find(([f]) => f === 'termine-dom.js')[1]
assert.ok(dom.includes('`/api/termine/export.csv?projectId=${encodeURIComponent(projectId)}`'), 'exportUrl codiert die Projekt-ID')
})
test('Ansicht-Module: Reiter sind Module mit render(panel, ctx), die Huelle kennt sie nur ueber REITER', () => {
const huelle = lies('public/termine.js')
assert.match(huelle, /import \{ render as renderTermine \} from '\.\/termine-tab-termine\.js'/)
assert.match(huelle, /const REITER = \[/)
assert.match(lies('public/termine-tab-termine.js'), /export function render\(panel, ctx\)/)
// Reiter lesen Zustand nur aus ctx, nicht aus der Huelle
for (const datei of ['termine-tab-termine.js', 'termine-aktionen.js', 'termine-formulare.js']) {
assert.doesNotMatch(lies(`public/${datei}`), /from '\.\/termine\.js'/, `${datei} importiert die Huelle nicht`)
}
})