import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { EventState, LED_COLOR_BY_STATE, ledBackgroundColor, } from '../../frontend/src/app/core/services/event-status.pure'; describe('LED state -> background-color mapping', () => { it.each([ ['running', 'var(--color-success)'], ['countdown', 'var(--color-warning)'], ['stopped', 'var(--color-danger)'], ['unconfigured', 'var(--color-secondary)'], ] as const)('maps %s to %s', (state, expected) => { expect(ledBackgroundColor(state)).toBe(expected); expect(LED_COLOR_BY_STATE[state]).toBe(expected); }); it('covers every EventState variant with a non-transparent token', () => { for (const state of Object.keys(LED_COLOR_BY_STATE) as EventState[]) { const color = ledBackgroundColor(state); expect(color).toMatch(/^var\(--color-/); expect(color).not.toBe('transparent'); expect(color).not.toBe(''); } }); }); describe('ShellHeaderComponent LED wiring', () => { const sourcePath = resolve( __dirname, '../../frontend/src/app/features/shell/header/shell-header.component.ts', ); const source = readFileSync(sourcePath, 'utf8'); it('does not declare a transparent background on the LED base', () => { const baseBlock = source.match(/\.led\s*\{[^}]*\}/); expect(baseBlock).not.toBeNull(); expect(baseBlock?.[0]).not.toMatch(/background-color\s*:\s*transparent/); }); it('keeps the component shape (size, radius, no border, inline-block, vertical-align)', () => { const baseBlock = source.match(/\.led\s*\{[^}]*\}/); expect(baseBlock?.[0]).toMatch(/display:\s*inline-block/); expect(baseBlock?.[0]).toMatch(/width:\s*10px/); expect(baseBlock?.[0]).toMatch(/height:\s*10px/); expect(baseBlock?.[0]).toMatch(/border-radius:\s*50%/); expect(baseBlock?.[0]).toMatch(/border:\s*0/); expect(baseBlock?.[0]).toMatch(/vertical-align:\s*middle/); }); it('binds the LED background color directly from the state signal', () => { expect(source).toMatch(/\[style\.background-color\]="ledColor\(\)"/); expect(source).toMatch(/ledColor\s*=\s*computed\(/); expect(source).toMatch(/ledBackgroundColor\(this\.eventState\(\)\)/); }); it('keeps the four state class bindings, testid, and accessible state label', () => { expect(source).toContain('[class.led-running]="eventState() === \'running\'"'); expect(source).toContain('[class.led-countdown]="eventState() === \'countdown\'"'); expect(source).toContain('[class.led-stopped]="eventState() === \'stopped\'"'); expect(source).toContain('[class.led-unconfigured]="eventState() === \'unconfigured\'"'); expect(source).toContain('data-testid="shell-led"'); expect(source).toContain("[attr.aria-label]=\"'Event status: ' + eventState()\""); }); it('does not redeclare LED state colors in the component styles', () => { expect(source).not.toMatch(/\.led-running\s*\{[^}]*background-color/); expect(source).not.toMatch(/\.led-countdown\s*\{[^}]*background-color/); expect(source).not.toMatch(/\.led-stopped\s*\{[^}]*background-color/); expect(source).not.toMatch(/\.led-unconfigured\s*\{[^}]*background-color/); }); });