mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
ContextControls presents the selected mode, the transition busy state and the nearby sites search through a publish option, and ContextView renders them from the gev-context store. ./ui/context stays free of Vue. The Radio QA reads the search button's aria-busy, the state it actually carries.
164 lines
5.8 KiB
JavaScript
164 lines
5.8 KiB
JavaScript
// src/ui/contextPanel.test.mjs — the Context mode switch renders what
|
|
// ContextControls presents (`_presentContext()`), over happy-dom.
|
|
import '../testSupport/registerDom.mjs';
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { createPinia } from 'pinia';
|
|
import { createApp, nextTick } from 'vue';
|
|
import { useContextStore } from './stores/context.js';
|
|
import ContextView from './views/ContextView.vue';
|
|
import { ContextControls } from './contextControls.js';
|
|
|
|
const turn = () => new Promise((resolve) => setImmediate(resolve));
|
|
|
|
function fixture(t) {
|
|
document.body.replaceChildren();
|
|
const host = document.body.appendChild(document.createElement('div'));
|
|
const pinia = createPinia();
|
|
const app = createApp(ContextView).use(pinia);
|
|
app.mount(host);
|
|
const $ = (id) => document.getElementById(id);
|
|
const store = useContextStore(pinia);
|
|
const controls = new ContextControls({
|
|
elements: {
|
|
_globalContextFlightsBtn: $('global-context-flights-btn'),
|
|
_globalContextMissionsBtn: $('global-context-missions-btn'),
|
|
_installationsSearchBtn: $('installations-search-btn'),
|
|
},
|
|
installations: {},
|
|
actions: {
|
|
getCockpit: () => null,
|
|
claimVisualAuthority() {},
|
|
showToast() {},
|
|
setClearBusy() {},
|
|
syncDetection() {},
|
|
scheduleLayout() {},
|
|
refreshRadio() {},
|
|
setPanelCollapsed() {},
|
|
},
|
|
publish: (view) => store.publish(view),
|
|
});
|
|
t.after(() => {
|
|
controls.stop();
|
|
controls.disconnect();
|
|
app.unmount();
|
|
});
|
|
const sync = async () => {
|
|
controls._syncContextModeButtons();
|
|
await nextTick();
|
|
};
|
|
return { $, controls, sync };
|
|
}
|
|
|
|
test('before ContextControls renders, the switch shows its initial markup', (t) => {
|
|
const { $ } = fixture(t);
|
|
const panel = $('global-context-panel');
|
|
assert.equal(panel.className, 'panel-collapsible collapsed');
|
|
assert.equal(panel.hasAttribute('data-context-mode'), false);
|
|
for (const id of [
|
|
'global-context-flights-btn',
|
|
'global-context-missions-btn',
|
|
]) {
|
|
const tab = $(id);
|
|
assert.equal(tab.className, 'context-mode-button');
|
|
assert.equal(tab.getAttribute('aria-selected'), 'false');
|
|
assert.equal(tab.getAttribute('tabindex'), '0');
|
|
assert.equal(tab.hasAttribute('aria-busy'), false);
|
|
}
|
|
assert.equal($('context-mode-standby').hidden, false);
|
|
assert.equal($('context-flights-view').hidden, true);
|
|
assert.equal($('context-missions-view').hidden, true);
|
|
assert.equal($('installations-search-btn').hasAttribute('aria-busy'), false);
|
|
});
|
|
|
|
test('a transition keeps focus and Tab on both tabs until it settles', async (t) => {
|
|
const { $, controls, sync } = fixture(t);
|
|
const contacts = $('global-context-flights-btn');
|
|
const missions = $('global-context-missions-btn');
|
|
missions.focus();
|
|
controls._contextModeChanging = true;
|
|
await sync();
|
|
assert.equal(
|
|
document.activeElement === missions,
|
|
true,
|
|
'busy render keeps focus',
|
|
);
|
|
for (const tab of [contacts, missions]) {
|
|
assert.equal(tab.disabled, false);
|
|
assert.equal(tab.getAttribute('tabindex'), '0');
|
|
assert.equal(tab.getAttribute('aria-disabled'), 'true');
|
|
assert.equal(tab.getAttribute('aria-busy'), 'true');
|
|
}
|
|
|
|
controls._contextModeChanging = false;
|
|
controls._contextMode = 'space-missions';
|
|
await sync();
|
|
assert.equal(
|
|
document.activeElement === missions,
|
|
true,
|
|
'settled render keeps focus',
|
|
);
|
|
for (const tab of [contacts, missions]) {
|
|
assert.equal(tab.disabled, false);
|
|
assert.equal(tab.getAttribute('aria-disabled'), 'false');
|
|
assert.equal(tab.getAttribute('aria-busy'), 'false');
|
|
}
|
|
assert.equal(missions.classList.contains('active'), true);
|
|
assert.equal(missions.getAttribute('aria-selected'), 'true');
|
|
assert.equal(contacts.classList.contains('active'), false);
|
|
assert.equal(contacts.getAttribute('aria-selected'), 'false');
|
|
assert.equal($('context-mode-standby').hidden, true);
|
|
assert.equal($('context-flights-view').hidden, true);
|
|
assert.equal($('context-missions-view').hidden, false);
|
|
});
|
|
|
|
test('the panel carries the mode beside the classes its chrome writes', async (t) => {
|
|
const { $, controls, sync } = fixture(t);
|
|
const panel = $('global-context-panel');
|
|
panel.classList.remove('collapsed');
|
|
controls._contextMode = 'flights';
|
|
await sync();
|
|
assert.equal(panel.className, 'panel-collapsible context-enabled');
|
|
assert.equal(panel.getAttribute('data-context-mode'), 'flights');
|
|
assert.equal($('context-flights-view').hidden, false);
|
|
|
|
panel.classList.add('collapsed');
|
|
controls._contextMode = null;
|
|
await sync();
|
|
assert.equal(panel.className, 'panel-collapsible collapsed');
|
|
assert.equal(panel.getAttribute('data-context-mode'), 'none');
|
|
assert.equal($('context-mode-standby').hidden, false);
|
|
assert.equal($('context-flights-view').hidden, true);
|
|
});
|
|
|
|
test('the nearby sites search is busy, without native disable, until it settles', async (t) => {
|
|
const { $, controls } = fixture(t);
|
|
const button = $('installations-search-btn');
|
|
let release;
|
|
let enables = 0;
|
|
controls._dataManager = {
|
|
layers: new Map([['military-installations', {}]]),
|
|
setEnabled: () => {
|
|
enables += 1;
|
|
return new Promise((resolve) => (release = resolve));
|
|
},
|
|
isEnabled: () => true,
|
|
};
|
|
controls.installations.searchNearby = async () => true;
|
|
button.focus();
|
|
button.click();
|
|
await nextTick();
|
|
assert.equal(button.getAttribute('aria-busy'), 'true');
|
|
assert.equal(button.getAttribute('aria-disabled'), 'true');
|
|
assert.equal(button.disabled, false);
|
|
assert.equal(document.activeElement === button, true);
|
|
button.click();
|
|
assert.equal(enables, 1, 'a busy search ignores a second click');
|
|
|
|
release(true);
|
|
await turn();
|
|
await nextTick();
|
|
assert.equal(button.getAttribute('aria-busy'), 'false');
|
|
assert.equal(button.getAttribute('aria-disabled'), 'false');
|
|
});
|