@@ -410,3 +410,94 @@ for (const route of ROUTEN) {
410410 expect ( results . violations , `${ route . pfad } : ${ JSON . stringify ( results . violations ) } ` ) . toEqual ( [ ] ) ;
411411 } ) ;
412412}
413+
414+ /**
415+ * Die Tokens erreichen die GEBAUTE Anwendung (EYT-80 Inkrement 2).
416+ *
417+ * Die Datei-Waechter in `apps/web/test/basisdesign-tokens.test.ts` und
418+ * `packages/ui/test/basisdesign-tokens.test.ts` lesen Quelltext. Sie koennen
419+ * nicht sehen, ob Next den `@import` aufloest, ob das Paket seine CSS-Datei
420+ * ueberhaupt ausliefert oder ob ein Bundler sie als nebenwirkungsfrei
421+ * wegwirft. Dieser Fall liest den BERECHNETEN Wert im Chromium.
422+ *
423+ * Verglichen wird die FARBE, nicht die Schreibweise. Gemessen 27.08.2026
424+ * verkuerzt der Minifier des Produktionsbaus `#ffffff` zu `#fff` — und
425+ * `getComputedStyle` normalisiert den Wert einer Custom Property NICHT, weil
426+ * sie bis zur Verwendung ein beliebiges Token ist. Ein Zeichenkettenvergleich
427+ * haette hier also den Minifier geprueft und nicht die Farbe. Darum wird der
428+ * rohe Wert einer Sonde als `color` zugewiesen und von Chromium selbst als
429+ * `rgb(…)` zurueckgelesen: das ist unabhaengig von Schreibweise, Minifier und
430+ * kuenftigen Formatwechseln.
431+ *
432+ * Drei Routen, weil die Zusage „auf den realen Produktflaechen" lautet und
433+ * nicht „auf der Startseite". Im `web-smoke` laeuft keine API; alle drei
434+ * antworten trotzdem mit 200 und tragen dieselbe CSS-Datei. Geprueft werden
435+ * hier die TOKENWERTE, nicht der angemeldete Zustand — der gehoert dem
436+ * auth-journey.
437+ *
438+ * Hellmodus: Playwright startet ohne `colorScheme`-Angabe, und der Standard
439+ * ist `light`. Die Dunkelwerte sind hier bewusst NICHT gemessen; sie haengen
440+ * an `packages/ui/test/basisdesign-tokens.test.ts`.
441+ */
442+ const KANONISCHE_ROLLEN : ReadonlyArray < readonly [ string , string ] > = [
443+ [ "--eyt-bg-canvas" , "rgb(246, 244, 239)" ] ,
444+ [ "--eyt-bg-surface" , "rgb(255, 255, 255)" ] ,
445+ [ "--eyt-text-primary" , "rgb(29, 27, 24)" ] ,
446+ [ "--eyt-text-secondary" , "rgb(91, 86, 78)" ] ,
447+ [ "--eyt-border-default" , "rgb(216, 212, 203)" ] ,
448+ [ "--eyt-action-primary" , "rgb(30, 82, 49)" ] ,
449+ // Auch die repo-eigene und die abgeleitete Rolle werden AUSGELIEFERT —
450+ // dass ihre Werte richtig SIND, bewacht packages/ui; dass sie im gebauten
451+ // Browser ankommen, kann nur dieser Fall sehen (PO-Review PR #96).
452+ [ "--eyt-action-primary-contrast" , "rgb(255, 255, 255)" ] ,
453+ [ "--eyt-state-published-bg" , "rgb(225, 235, 226)" ] ,
454+ [ "--eyt-state-published-text" , "rgb(30, 82, 49)" ] ,
455+ [ "--eyt-state-draft-text" , "rgb(122, 83, 0)" ] ,
456+ [ "--eyt-state-draft-bg" , "rgb(244, 232, 206)" ] ,
457+ [ "--eyt-state-danger-text" , "rgb(155, 44, 31)" ] ,
458+ [ "--eyt-state-danger-bg" , "rgb(247, 227, 223)" ] ,
459+ [ "--eyt-state-info-text" , "rgb(21, 94, 117)" ] ,
460+ [ "--eyt-state-info-bg" , "rgb(222, 237, 242)" ] ,
461+ ] ;
462+
463+ for ( const pfad of [ "/" , "/planung" , "/kosten" ] ) {
464+ test ( `${ pfad } liefert die kanonischen Basisdesign-Tokens aus` , async ( { page } ) => {
465+ await page . goto ( pfad ) ;
466+ await expect ( page . getByRole ( "main" ) ) . toBeVisible ( ) ;
467+
468+ const gemessen = await page . evaluate (
469+ ( namen ) => {
470+ const wurzel = getComputedStyle ( document . documentElement ) ;
471+ const sonde = document . createElement ( "span" ) ;
472+ document . body . append ( sonde ) ;
473+ try {
474+ return Object . fromEntries (
475+ namen . map ( ( name ) => {
476+ const roh = wurzel . getPropertyValue ( name ) . trim ( ) ;
477+ // Erst leeren, dann setzen: lehnt der CSS-Parser den Wert ab,
478+ // bleibt `style.color` leer — und `angenommen` deckt genau den
479+ // Fall auf, in dem eine fehlende Rolle sonst die geerbte Farbe
480+ // der Sonde gemessen haette und zufaellig gruen waere.
481+ sonde . style . color = "" ;
482+ sonde . style . color = roh ;
483+ const angenommen = sonde . style . color !== "" ;
484+ return [ name , { roh, angenommen, farbe : getComputedStyle ( sonde ) . color } ] ;
485+ } ) ,
486+ ) ;
487+ } finally {
488+ sonde . remove ( ) ;
489+ }
490+ } ,
491+ KANONISCHE_ROLLEN . map ( ( [ name ] ) => name ) ,
492+ ) ;
493+
494+ for ( const [ name , farbe ] of KANONISCHE_ROLLEN ) {
495+ const wert = gemessen [ name ] ;
496+ expect ( wert ?. roh , `${ pfad } : ${ name } ist nicht deklariert` ) . toBeTruthy ( ) ;
497+ expect ( wert ?. angenommen , `${ pfad } : ${ name } = "${ wert ?. roh } " ist keine gueltige Farbe` ) . toBe (
498+ true ,
499+ ) ;
500+ expect ( wert ?. farbe , `${ pfad } : ${ name } (roh: "${ wert ?. roh } ")` ) . toBe ( farbe ) ;
501+ }
502+ } ) ;
503+ }
0 commit comments