import test from 'node:test' import assert from 'node:assert/strict' import { loadModule } from './load.mjs' // Fokus nach Pult-Aktionen (Endprüfung P4, W3): ein gesperrter Knopf verliert den Fokus an ; die Zeile gibt ihn danach zurück, // aber nur, wenn er wirklich verloren ging. Reine Logik mit einer document-Attrappe, kein DOM nötig. const { activeControl, focusLost, restoreFocus } = await loadModule('src/lib/workersFocus.js') function fakeDoc() { const body = { name: 'body' } const doc = { body, documentElement: { name: 'html' }, activeElement: body } const el = (name, extra = {}) => ({ name, isConnected: true, disabled: false, focusCalls: 0, focusArgs: [], focus(...args) { this.focusCalls += 1; this.focusArgs.push(args); doc.activeElement = this }, ...extra }) return { doc, body, el } } test('focusLost: body, Wurzel, nichts, gesperrtes oder ausgehängtes Element gelten als verloren, ein bedienbares Element nicht', () => { const { doc, body, el } = fakeDoc() assert.equal(focusLost(doc), true) // activeElement = body doc.activeElement = doc.documentElement; assert.equal(focusLost(doc), true) for (const nothing of [null, undefined]) { doc.activeElement = nothing; assert.equal(focusLost(doc), true, String(nothing)) } doc.activeElement = el('a', { disabled: true }); assert.equal(focusLost(doc), true) // der Browser lässt es erst beim nächsten Takt los doc.activeElement = el('b', { isConnected: false }); assert.equal(focusLost(doc), true) doc.activeElement = el('c'); assert.equal(focusLost(doc), false) // ohne document (Node, Server) gibt es nichts zurückzugeben for (const none of [null, undefined]) assert.equal(focusLost(none), false) assert.equal(body.name, 'body') }) test('activeControl: das fokussierte Bedienelement, sonst null (body und Wurzel zählen nicht)', () => { const { doc, el } = fakeDoc() assert.equal(activeControl(doc), null) doc.activeElement = doc.documentElement; assert.equal(activeControl(doc), null) doc.activeElement = null; assert.equal(activeControl(doc), null) const button = el('button'); doc.activeElement = button assert.equal(activeControl(doc), button) assert.equal(activeControl(null), null); assert.equal(activeControl(undefined), null) }) test('restoreFocus: nimmt den ersten Kandidaten, der noch da und frei ist; greift nur ein, wenn der Fokus verloren ging', () => { const { doc, body, el } = fakeDoc() const gone = el('gone', { isConnected: false }) const locked = el('locked', { disabled: true }) const first = el('first') const second = el('second') assert.equal(restoreFocus(doc, [null, undefined, gone, locked, first, second]), true) assert.equal(doc.activeElement, first); assert.deepEqual([first.focusCalls, second.focusCalls, gone.focusCalls, locked.focusCalls], [1, 0, 0, 0]) // der Fokus liegt woanders (die Person hat inzwischen selbst gewählt): nichts wird angefasst const other = el('other'); doc.activeElement = other assert.equal(restoreFocus(doc, [first]), false) assert.equal(doc.activeElement, other); assert.equal(first.focusCalls, 1) // kein brauchbarer Kandidat, kein document, keine Liste: false, nichts passiert doc.activeElement = body assert.equal(restoreFocus(doc, [null, gone, locked]), false); assert.equal(doc.activeElement, body) assert.equal(restoreFocus(null, [first]), false); assert.equal(restoreFocus(doc, []), false) // Dinge ohne focus() (Attrappen, Textknoten) werden übersprungen assert.equal(restoreFocus(doc, [{ isConnected: true }, 'text', 7, second]), true); assert.equal(doc.activeElement, second) }) // Die Meldung oben (oder eine Zeile weit unten in einer langen Liste) darf den Fokus bekommen, ohne dass die Seite dorthin springt (Nachrunde R8) test('restoreFocus: fokussiert mit preventScroll, damit die Seite nicht zur Meldung oder Zeile springt', () => { const { doc, el } = fakeDoc() const target = el('meldung') assert.equal(restoreFocus(doc, [target]), true) assert.equal(doc.activeElement, target) assert.deepEqual(target.focusArgs, [[{ preventScroll: true }]]) // genau ein Aufruf, genau diese Option })