doxa-maps/internal/check-map-fly-reduced-motion.mjs
Doxa Maps 3cd8a8cd30
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
Update from source c2af6b16
2026-09-26 07:01:59 +00:00

42 lines
2.7 KiB
JavaScript

#!/usr/bin/env node
/**
* check-map-fly-reduced-motion — useMapFly honours prefers-reduced-motion for EVERY camera move, headless.
*
* A reader who asks the OS for reduced motion must not get flying/easing maps (a photosensitivity issue).
* useMapFly decides it itself (cam()), so a caller cannot forget it; flyToCoords also honours caller
* options (it used to take none, silently dropping a caller's { duration: 0 }).
* Runs the real composable against a recording fake map, with matchMedia reporting reduce / no-preference.
* USAGE node internal/check-map-fly-reduced-motion.mjs EXIT 0 pass · 1 fail
*/
let reduce = false
globalThis.window = { matchMedia: (q) => ({ matches: reduce && /prefers-reduced-motion:\s*reduce/.test(q) }), devicePixelRatio: 1 }
const { useMapFly } = await import('../library/composables/useMapFly.js')
const calls = []
const map = new Proxy({}, { get: (_, k) => (k === 'getZoom' ? () => 3 : (...a) => { calls.push({ fn: String(k), a }) }) })
const fly = useMapFly({ getMap: () => map })
let bad = 0
const check = (name, ok, got) => { if (!ok) bad++; console.log(` ${ok ? 'ok ' : 'FAIL'} ${name}${ok ? '' : ' — got ' + JSON.stringify(got)}`) }
const run = (fn) => { calls.length = 0; fn(); return calls.slice() }
const B = [[0, 0], [10, 10]]
console.log('MAP-FLY-REDUCED-MOTION: the real useMapFly against a recording map (no browser)')
reduce = false
let c = run(() => fly.flyTo([1, 2], { zoom: 4 }))
check('motion allowed: flyTo animates', c[0]?.fn === 'flyTo', c)
c = run(() => fly.fitBounds(B))
check('motion allowed: fitBounds eases (duration > 0)', c[0]?.fn === 'fitBounds' && c[0].a[1].duration > 0, c)
c = run(() => fly.flyToCoords({ longitude: 1, latitude: 2 }, 5, { duration: 0 }))
check('flyToCoords passes caller options through ({ duration: 0 })', c[0]?.fn === 'flyTo' && c[0].a[0].duration === 0 && c[0].a[0].zoom === 5, c)
reduce = true
c = run(() => fly.flyTo([1, 2], { zoom: 4 }))
check('reduced motion: flyTo becomes jumpTo', c.length === 1 && c[0].fn === 'jumpTo' && c[0].a[0].zoom === 4, c)
c = run(() => fly.flyToCoords({ longitude: 1, latitude: 2 }))
check('reduced motion: flyToCoords becomes jumpTo', c.length === 1 && c[0].fn === 'jumpTo', c)
c = run(() => fly.fitBounds(B))
check('reduced motion: fitBounds is instant (animate:false, duration 0)', c[0]?.fn === 'fitBounds' && c[0].a[1].animate === false && c[0].a[1].duration === 0, c)
c = run(() => fly.resetView())
check('reduced motion: resetView makes no flyTo/easeTo', !c.some((x) => x.fn === 'flyTo' || x.fn === 'easeTo'), c)
if (bad) { console.log(`MAP-FLY-REDUCED-MOTION: FAIL — ${bad} case(s).`); process.exit(1) }
console.log('MAP-FLY-REDUCED-MOTION: PASS — every camera move in useMapFly is instant under reduced motion; caller options reach flyToCoords.')