diff --git a/src/app/utils/haptics.test.ts b/src/app/utils/haptics.test.ts index 01256bfee..93ffa77ee 100644 --- a/src/app/utils/haptics.test.ts +++ b/src/app/utils/haptics.test.ts @@ -2,32 +2,40 @@ import { test } from 'node:test'; import assert from 'node:assert/strict'; import { hapticsSupported, tick } from './haptics'; -const g = globalThis as { navigator?: unknown; window?: unknown }; +// Node 22 exposes a read-only `globalThis.navigator`; swap it via defineProperty. +const original = Object.getOwnPropertyDescriptor(globalThis, 'navigator'); +const setGlobal = (name: 'navigator' | 'window', value: unknown) => + Object.defineProperty(globalThis, name, { value, configurable: true, writable: true }); +const restore = () => { + if (original) Object.defineProperty(globalThis, 'navigator', original); + else delete (globalThis as { navigator?: unknown }).navigator; + delete (globalThis as { window?: unknown }).window; +}; test('tick is a no-op without the API, with the setting off, or under reduced motion', () => { - g.navigator = {}; + setGlobal('navigator', {}); assert.equal(hapticsSupported(), false); assert.equal(tick('ptt-on'), false); const calls: number[] = []; - g.navigator = { vibrate: (ms: number) => calls.push(ms) === 1 }; - g.window = { matchMedia: () => ({ matches: true }) }; + setGlobal('navigator', { vibrate: (ms: number) => calls.push(ms) === 1 }); + setGlobal('window', { matchMedia: () => ({ matches: true }) }); assert.equal(tick('ptt-on'), false); assert.deepEqual(calls, []); - g.window = { matchMedia: () => ({ matches: false }) }; + setGlobal('window', { matchMedia: () => ({ matches: false }) }); assert.equal(tick('ptt-on', false), false); assert.deepEqual(calls, []); + restore(); }); test('tick vibrates per kind when allowed', () => { const calls: number[] = []; - g.navigator = { vibrate: (ms: number) => calls.push(ms) > 0 }; - g.window = { matchMedia: () => ({ matches: false }) }; + setGlobal('navigator', { vibrate: (ms: number) => calls.push(ms) > 0 }); + setGlobal('window', { matchMedia: () => ({ matches: false }) }); assert.equal(tick('ptt-on'), true); assert.equal(tick('ptt-off'), true); assert.equal(tick('reaction'), true); assert.deepEqual(calls, [10, 10, 8]); - delete g.navigator; - delete g.window; + restore(); });