Files
gods-eye-view/src/ui/toastView.test.mjs
T
Sameh Khamis f846aaed4c feat(ui): render the page toast from the status store
ShellFeedback and the scene director show toasts through createToast(),
each on its own timer as before, and the scene chrome renders the toast.
Every show rewrites the text, so a repeated message is announced again.
2026-10-31 19:58:59 -07:00

80 lines
3.1 KiB
JavaScript

// src/ui/toastView.test.mjs — the page toast renders what its owners'
// createToast() publish, each owner on its own timer.
import '../testSupport/registerDom.mjs';
import test from 'node:test';
import assert from 'node:assert/strict';
import { createApp, nextTick } from 'vue';
import { createPinia } from 'pinia';
import { useStatusStore } from './stores/status.js';
import { createToast } from './toast.js';
import SceneChromeView from './views/SceneChromeView.vue';
function fixture(t) {
t.mock.timers.enable({ apis: ['setTimeout'] });
document.body.replaceChildren();
const pinia = createPinia();
const app = createApp(SceneChromeView).use(pinia);
app.mount(document.body.appendChild(document.createElement('div')));
t.after(() => app.unmount());
const store = useStatusStore(pinia);
const toast = () => document.getElementById('toast');
return { store, toast };
}
test('a toast shows its message, then fades out keeping its text', async (t) => {
const { store, toast } = fixture(t);
assert.equal(toast().textContent, '');
assert.equal(toast().classList.contains('visible'), false);
const owner = createToast({ durationMs: 2000, store });
owner.show('Link copied');
await nextTick();
assert.equal(toast().textContent, 'Link copied');
assert.equal(toast().classList.contains('visible'), true);
assert.equal(toast().getAttribute('role'), 'status');
t.mock.timers.tick(1999);
await nextTick();
assert.equal(toast().classList.contains('visible'), true);
t.mock.timers.tick(1);
await nextTick();
assert.equal(toast().classList.contains('visible'), false);
assert.equal(toast().textContent, 'Link copied');
});
test('a newer toast restarts the time, and another owner hides it on its own time', async (t) => {
const { store, toast } = fixture(t);
const shell = createToast({ durationMs: 2000, store });
const scenes = createToast({ durationMs: 2600, store });
scenes.show('Scene not saved');
t.mock.timers.tick(1000);
shell.show('Saved');
t.mock.timers.tick(1600);
await nextTick();
assert.equal(toast().textContent, 'Saved');
assert.equal(toast().classList.contains('visible'), false);
shell.show('Again');
scenes.cancel();
shell.hide();
await nextTick();
assert.equal(toast().classList.contains('visible'), false);
assert.equal(toast().textContent, 'Again');
t.mock.timers.tick(5000);
assert.equal(store.toast.text, 'Again');
assert.equal(store.toast.visible, false);
});
test('a repeated message is written again so the live region announces it', async (t) => {
const { store, toast } = fixture(t);
const owner = createToast({ durationMs: 2000, store });
owner.show('Fly to a POI first');
await nextTick();
const first = toast().firstChild;
t.mock.timers.tick(2000);
await nextTick();
assert.ok(toast().firstChild === first, 'fading out leaves the text alone');
owner.show('Fly to a POI first');
await nextTick();
assert.equal(toast().textContent, 'Fly to a POI first');
assert.ok(toast().firstChild !== first, 'the repeat is a new text node');
assert.equal(toast().classList.contains('visible'), true);
});