mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-12 14:22:21 +00:00
The CCTV camera check lost camera B from the picker when the flight to camera A changed the cameras in view; the picker now keeps focus while A is held. The oblique traffic wait compared exact seven-decimal view keys that a settled camera never reproduced; it compares the coverage with the current view within small tolerances and prints both keys on timeout.
108 lines
3.2 KiB
JavaScript
108 lines
3.2 KiB
JavaScript
import '../src/testSupport/registerDom.mjs';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import test from 'node:test';
|
|
import { createPinia } from 'pinia';
|
|
import { createApp, nextTick } from 'vue';
|
|
import { CctvControls } from '../src/ui/cctvControls.js';
|
|
import { useCctvStore } from '../src/ui/stores/cctv.js';
|
|
import CctvPanel from '../src/ui/views/panels/CctvPanel.vue';
|
|
|
|
const source = readFileSync(
|
|
new URL('./qa-camera-controls.mjs', import.meta.url),
|
|
'utf8',
|
|
);
|
|
|
|
test('the delayed-frame scenario keeps camera B selectable after the flight to A', async (t) => {
|
|
document.body.replaceChildren();
|
|
const host = document.body.appendChild(document.createElement('div'));
|
|
const pinia = createPinia();
|
|
const app = createApp(CctvPanel).use(pinia);
|
|
app.mount(host);
|
|
const select = document.getElementById('cctv-camera-select');
|
|
const controls = new CctvControls({
|
|
elements: {
|
|
_cctvPanel: document.getElementById('cctv-panel'),
|
|
_cctvSelect: select,
|
|
},
|
|
cctv: {},
|
|
actions: {
|
|
isEnabled: () => true,
|
|
setPanelCollapsed() {},
|
|
syncViewport() {},
|
|
},
|
|
store: useCctvStore(pinia),
|
|
});
|
|
t.after(() => {
|
|
controls.destroy();
|
|
app.unmount();
|
|
});
|
|
const [a, b] = ['a', 'b'].map((id) => ({
|
|
id: `qa-camera-${id}`,
|
|
name: id,
|
|
city: 'Austin',
|
|
}));
|
|
const render = async (activeCamera, cameraIndex) => {
|
|
controls._renderCctvState({
|
|
enabled: true,
|
|
count: 2,
|
|
activeCameraId: activeCamera.id,
|
|
activeCamera,
|
|
cameraIndex,
|
|
});
|
|
await nextTick();
|
|
};
|
|
await render(b, [a, b]);
|
|
const prepare = source.slice(
|
|
source.indexOf(' await page.setCacheEnabled(false);'),
|
|
source.indexOf(' delayCameraA = true;'),
|
|
);
|
|
await new Function('page', `return (async () => { ${prepare} })();`)({
|
|
setCacheEnabled: async () => {},
|
|
focus: async (selector) => document.querySelector(selector).focus(),
|
|
});
|
|
await render(a, [a]);
|
|
assert.ok([...select.options].some((option) => option.value === b.id));
|
|
select.value = b.id;
|
|
assert.equal(select.value, b.id);
|
|
select.blur();
|
|
await nextTick();
|
|
assert.ok(
|
|
![...select.options].some((option) => option.value === b.id),
|
|
'an unfocused picker drops B once the flight takes it out of view',
|
|
);
|
|
});
|
|
|
|
test('camera B fixtures respond while camera A frames remain held', async () => {
|
|
let route;
|
|
const delayedFrames = new Set();
|
|
const routing = source.slice(
|
|
source.indexOf(" page.on('request',"),
|
|
source.indexOf(' await page.goto('),
|
|
);
|
|
new Function('page', 'origin', 'delayCameraA', 'delayedFrames', routing)(
|
|
{ on: (event, handler) => (route = handler) },
|
|
'http://localhost:4321',
|
|
true,
|
|
delayedFrames,
|
|
);
|
|
const replies = [];
|
|
for (const id of ['a', 'b']) {
|
|
route({
|
|
url: () => `http://localhost:4321/api/cctv/frame/qa-camera-${id}?ts=1`,
|
|
respond: async (response) => replies.push({ id, response }),
|
|
});
|
|
}
|
|
assert.equal(delayedFrames.size, 1);
|
|
assert.deepEqual(
|
|
replies.map(({ id }) => id),
|
|
['b'],
|
|
);
|
|
assert.match(replies[0].response.body, /QA CAMERA B/);
|
|
await Promise.all([...delayedFrames].map((respond) => respond()));
|
|
assert.deepEqual(
|
|
replies.map(({ id }) => id),
|
|
['b', 'a'],
|
|
);
|
|
});
|