Ohne die Option springt die Seite beim Zurückgeben des Fokus zum Ziel: bei einer Zeile weit unten in
einer langen Liste zur Meldung oben, und die Person verliert ihre Stelle. restoreFocus ruft jetzt
target.focus({ preventScroll: true }); der Fokus (und damit die Ansage) wandert, die Ansicht bleibt stehen.
Die Attrappe in workersFocus.test.mjs merkt sich die Argumente, ein neuer Test pinnt genau einen Aufruf
mit dieser Option.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
69 lines
4.0 KiB
JavaScript
69 lines
4.0 KiB
JavaScript
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 <body>; 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
|
|
})
|