You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/arts/adom/test/reduced-motion.test.ts

58 lines
1.7 KiB

feat(uix): persistence + a11ySemantic + polymorphic core (book §5.3, §6, §9) Three book canon items codified in code: **Persistence (libro §6.1)** — separa `hold` (perceptual min) de `persistence` (lifecycle real): - New `SignalPersistence = 'transient' | 'untilAction' | 'untilFix' | 'stateBound'` - New `SEMA_HOLDS_BY_INTENT` canonical lookup table (`src/uix/sema/holds.ts`) - `EngineSemantic.emit()` returns the resolved signal id; keeps projection alive past hold for non-transient. Exposes `clear(id)`, `clearTarget(target)`, `hasActive(id)`. - `SomaRuntime.trigger()` returns `TriggerResult { id?, persistence? }`. Exposes `clearSignal(id)`, `clearTarget(target)`, `partRef(part)`. **a11ySemantic (libro §9.1)**: - New `MorfoA11ySemantic` (requiresPersistentTrace, requiresLiveRegion, requiresFocusMove, keyboardEquivalent, reducedMotionFallback) on MorfoEvent. - `ActiveDom.prefersReducedMotion`: reactive tracker via media query (`src/arts/adom/reduced-motion.svelte.ts`). - `ActiveUix.announce(msg, priority?, timeout?)`: lazy-created live region via `dom.writeNode`. - `Soma.runtime()` auto-wires `sources.announce`. - `SomaRuntime.trigger()` honors a11ySemantic after emit (live region, focus, reduced-motion fallback including `channels: []` for 'state'). **Polymorphic events (libro §5.3)** — ADITIVO sobre shape concreto: - `allowedFamilies?: readonly SemaFamily[]` opcional en MorfoEventSemantic. - Provider override via `runtime.trigger(name, { semantic })`. - Default family of the morfo is implicitly allowed. - New `SomaRuntimePolymorphicError`. - New `isPolymorphicSemantic` type guard. Synthetic `prewriteFixtureMorfo` (src/uix/morfo/test-fixtures.ts) decouples compile + runtime tests from the production morfo catalogue. 671 tests pass in sema + morfo + soma + adom scopes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
// @vitest-environment jsdom
import { describe, expect, it } from 'vitest';
import { createReducedMotionTracker } from '../reduced-motion.svelte';
describe('ReducedMotionTracker', () => {
it('initializes from the current matchMedia value', () => {
const tracker = createReducedMotionTracker();
// jsdom matchMedia defaults to matches=false for unknown queries.
expect(tracker.matches).toBe(false);
tracker.dispose();
});
it('dispose detaches the listener (idempotent)', () => {
const tracker = createReducedMotionTracker();
expect(() => tracker.dispose()).not.toThrow();
// second dispose is a no-op (no throw)
expect(() => tracker.dispose()).not.toThrow();
});
it('falls back to false when window.matchMedia is missing', () => {
const fakeWin = {} as unknown as Window;
const tracker = createReducedMotionTracker(fakeWin);
expect(tracker.matches).toBe(false);
// dispose without listeners is a no-op (no throw)
tracker.dispose();
});
it('reads from a custom target window', () => {
let attachedListeners = 0;
const fakeWin = {
matchMedia: (q: string) => {
expect(q).toBe('(prefers-reduced-motion: reduce)');
return {
matches: true,
media: q,
onchange: null,
addEventListener: () => {
attachedListeners++;
},
removeEventListener: () => {
attachedListeners--;
},
addListener: () => {},
removeListener: () => {},
dispatchEvent: () => false
} as MediaQueryList;
}
} as unknown as Window;
const tracker = createReducedMotionTracker(fakeWin);
expect(tracker.matches).toBe(true);
expect(attachedListeners).toBe(1);
tracker.dispose();
expect(attachedListeners).toBe(0);
});
});

Powered by TurnKey Linux.