feat: <gm-shell> wiring, styles, and AFTERIMAGE boot
Adds the gm-shell root element that owns store/clock/stamps/scenario, mounts director-rail and dice-tray, handles reached (stamp + persist) and open-tool (toggle dice tray), and boots the AFTERIMAGE scenario in main.js. Also adds tests/setup.js + a vite.config.js setupFiles entry: Node's newer built-in global `localStorage` shadows jsdom's per-window implementation in vitest's jsdom environment, leaving localStorage undefined in tests until replaced with a real jsdom-backed instance.
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import '../../src/components/gm-shell.js';
|
||||
import afterimage from '../../src/scenarios/afterimage.js';
|
||||
|
||||
describe('<gm-shell>', () => {
|
||||
let el, t;
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
document.body.innerHTML = '';
|
||||
t = 0;
|
||||
el = document.createElement('gm-shell');
|
||||
el.now = () => t;
|
||||
document.body.appendChild(el);
|
||||
el.loadScenario(afterimage);
|
||||
});
|
||||
|
||||
it('mounts a director-rail for the loaded scenario', () => {
|
||||
expect(el.querySelector('director-rail')).not.toBe(null);
|
||||
expect(el.querySelector('director-rail [data-role=here]').textContent).toContain('The pier');
|
||||
});
|
||||
|
||||
it('stamps the next beat on "reached" and persists it', () => {
|
||||
el.startSession(); // start at t=0
|
||||
t = 6 * 60000; // 6 minutes later
|
||||
el.querySelector('[data-role=reached]').dispatchEvent(new CustomEvent('reached', { bubbles: true }));
|
||||
expect(el.stamps['a1-open']).toBeCloseTo(6, 5);
|
||||
const persisted = JSON.parse(localStorage.getItem('gmd.afterimage.stamps'));
|
||||
expect(persisted['a1-open']).toBeCloseTo(6, 5);
|
||||
// rail now shows the reached beat as current
|
||||
expect(el.querySelector('director-rail [data-role=here]').textContent).toContain('The pier');
|
||||
});
|
||||
|
||||
it('reveals the dice tray on open-tool', () => {
|
||||
expect(el.querySelector('dice-tray').hidden).toBe(true);
|
||||
el.dispatchEvent(new CustomEvent('open-tool', { detail: { tool: 'dice' }, bubbles: true }));
|
||||
expect(el.querySelector('dice-tray').hidden).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
// Workaround: Node's own experimental global `localStorage` (added in recent
|
||||
// Node versions) shadows jsdom's per-window localStorage before vitest's
|
||||
// jsdom environment can install it, leaving `localStorage`/`window.localStorage`
|
||||
// undefined in tests. Replace it with a real jsdom-backed Storage instance.
|
||||
import { JSDOM } from 'jsdom';
|
||||
|
||||
if (typeof localStorage === 'undefined' || typeof localStorage.setItem !== 'function') {
|
||||
const { window } = new JSDOM('', { url: 'http://localhost/' });
|
||||
Object.defineProperty(globalThis, 'localStorage', {
|
||||
value: window.localStorage,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user