import { test } from 'node:test' import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' // Task G1: der Webshop ist im Kundenbereich verdrahtet (dashboard.html, app.js, funktionen.js, shop.css, shop.js). Rein statisch, ohne DOM. const lies = (pfad) => readFileSync(new URL(`../${pfad}`, import.meta.url), 'utf8') test('dashboard.html: shop.css nach termine.css, Abschnitt #view-webshop mit Kopf „Webshop“ und #webshop-body', () => { const html = lies('public/dashboard.html') assert.match(html, /\s*/) assert.match(html, /
[\s\S]*?

Webshop<\/h1>[\s\S]*?
[\s\S]*?<\/section>/) assert.equal([...html.matchAll(/id="view-webshop"/g)].length, 1) assert.doesNotMatch(html, /data-view="webshop"/, 'kein eigener Navigationseintrag: Unterseite von Funktionen') }) test('app.js: VIEW_HASHES, viewRefreshers, FEATURE_LABELS und APP_PARENT kennen den Webshop', () => { const app = lies('public/app.js') assert.match(app, /VIEW_HASHES = \{[^}]*webshop: 'webshop'/) assert.match(app, /FEATURE_LABELS = \{[^}]*shop: 'Webshop'/) assert.match(app, /webshop: \(\) => import\('\/shop\.js'\)\.then\(\(m\) => m\.initShop\(document\.getElementById\('webshop-body'\)\)\)/) // APP_PARENT: die Reihenfolge der bisherigen Einträge bleibt (termineView.test.js), webshop folgt, zeiten (Öffnungszeiten live) steht am Ende assert.match(app, /APP_PARENT = \{ news: 'funktionen', analyse: 'funktionen', termine: 'funktionen', /) assert.match(app, /APP_PARENT = \{[^}]*projekt: 'projects', webshop: 'funktionen', zeiten: 'funktionen' \}/) // die Ansicht ist vom Hash aus erreichbar: kein zweiter Schlüssel mit demselben Hash const hashes = app.match(/VIEW_HASHES = \{([^}]*)\}/)[1] assert.equal([...hashes.matchAll(/'webshop'/g)].length, 1) }) test('shop.js und shop.css: die Hülle exportiert initShop, die Styles hängen an #view-webshop und sh-', () => { const js = lies('public/shop.js') assert.match(js, /export function initShop\(root, \{ projectId \} = \{\}\)/) assert.doesNotMatch(js.replace(/\/\*[\s\S]*?\*\//g, ''), /innerHTML|insertAdjacentHTML|outerHTML|document\.write/, 'Text nie als HTML') const css = lies('public/shop.css') assert.match(css, /#view-webshop/) assert.doesNotMatch(css, /(^|[}\s,])\.tm-/m, 'keine Klassen des Terminplaners neu definieren') }) test('funktionen.js: Webshop-Kachel inline, die erlaubten Importe bleiben (zwei absolute, ein relativer, kein dynamischer)', () => { const fn = lies('public/funktionen.js') assert.deepEqual(fn.match(/from '\/[^']+'/g), ["from '/fn-util.js'", "from '/analyse-view.js'"]) assert.deepEqual(fn.match(/from '\.[^']+'/g), ["from './vertraege-link.js'"]) assert.doesNotMatch(fn, /import\s*\(/) assert.match(fn, /data-fn-tile="shop"/) assert.match(fn, /getElementById\('webshop-body'\)/) }) test('Dateinamen: Webshop-Dateien beginnen nie mit termine (eigene Regeln der termine*.js-Tests)', async () => { const { readdirSync } = await import('node:fs') const dateien = readdirSync(new URL('../public/', import.meta.url)).filter((f) => /^shop.*\.(js|css)$/.test(f)) assert.ok(dateien.includes('shop.js') && dateien.includes('shop.css')) })