import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' import { describe, it } from 'node:test' // Two files hold one decision between them. The grip takes focus when it is dragged — otherwise the // arrow keys it advertises are only reachable by tabbing the length of the rail — and a scripted // `focus()` is precisely the case a UA may read as keyboard-driven, so `:focus-visible` can leave a // ring down the edge of a rail somebody has just dragged. `RailResizer` therefore says whether the // focus came from the keyboard and `app.css` draws the ring only when it did. Remove either half and // the ring is back on every drag, quietly and only under the pointer. So they are checked against // each other. const src = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'src') const read = (name) => readFileSync(resolve(src, name), 'utf8') describe('the grip focus ring', () => { const css = read('app.css') const component = read('lib/components/RailResizer.svelte') it('is drawn from the attribute the component sets', () => { assert.match(component, /data-ring=\{/) assert.match(css, /\.rail-resize\[data-ring\][\s,][^{}]*\{[^}]*outline: 2px/) }) it('is the only thing that draws it', () => { assert.ok(css.includes('.rail-resize:not([data-ring]):focus-visible { outline: 0; }')) // The opt-out above is the whole point; a rule that paints the grip from `:focus-visible` alone // would put the drag's ring back beside it. (Selectors spanning lines — the shared ring rule the // first test reads — do not match, which is right: that one is keyed on the attribute.) for (const selector of css.match(/[^\n{}]*\.rail-resize[^\n{}]*:focus-visible[^\n{}]*/g) ?? []) { assert.ok( selector.includes(':not([data-ring])'), `\`${selector.trim()}\` gives the grip a ring the pointer can trip`, ) } }) })