mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
- Voice layer manifest (src/voice/layerManifest.js) generates the layer enums, aliases and analyst field hints; a test fails when a catalog layer has no entry. - analyst_query validates fields, operators, units, values and scopes, counts up to 250,000 loaded records per layer, reports complete/partial/unanswered, tells LAYER_OFF / NOT_READY / FEED_UNAVAILABLE apart from zero, refuses mismatched follow-ups and never lets a superseded query replace follow-up memory. Seven more layers expose analyst records. - Tool results carry a code-built say line and a display card; the voice card in the mic control shows captions, plan steps and the result (src/voice/resultDisplay.js is the one adapter). Slow tools narrate their current step out of band; holding Space while the assistant talks cuts it off. Turn spans go to the debug log; caption transcription is metered; the log omits what was said unless GEV_VOICE_LOG_CONTENT=1. - Point and ask: the Space-down pointer snapshot resolves "this", "that one" and "around here" through 'pointer' targets, and "the second one" through referent:n from the last numbered list. - Prompt states each policy once (instructions + tools 48.5k -> 46.7k chars). - Voice benchmark harness (scripts/qa-voice-bench.mjs, scripts/voice-bench/) shares its phrase table and grading with scripts/qa-voice-routing.mjs.
104 lines
3.6 KiB
JavaScript
104 lines
3.6 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { readStylesheet } from '../testSupport/readStylesheet.mjs';
|
|
import { PointerReticle, RETICLE_LINGER_MS, RETICLE_MAX_MS } from './pointerReticle.js';
|
|
|
|
const css = readStylesheet(new URL('../../style.css', import.meta.url));
|
|
|
|
function fakeDoc() {
|
|
const appended = [];
|
|
return {
|
|
appended,
|
|
body: { appendChild: (node) => appended.push(node) },
|
|
createElement: (tag) => ({
|
|
tag,
|
|
hidden: false,
|
|
style: {},
|
|
dataset: {},
|
|
attributes: {},
|
|
setAttribute(name, value) {
|
|
this.attributes[name] = value;
|
|
},
|
|
removed: false,
|
|
remove() {
|
|
this.removed = true;
|
|
},
|
|
}),
|
|
};
|
|
}
|
|
|
|
function harness() {
|
|
const doc = fakeDoc();
|
|
const timers = new Map();
|
|
let next = 1;
|
|
const reticle = new PointerReticle({
|
|
doc,
|
|
rectOf: () => ({ left: 10, top: 20 }),
|
|
setTimer: (fn, ms) => {
|
|
const id = next++;
|
|
timers.set(id, { fn, ms });
|
|
return id;
|
|
},
|
|
clearTimer: (id) => timers.delete(id),
|
|
});
|
|
const fire = () => {
|
|
const [[id, timer]] = [...timers];
|
|
timers.delete(id);
|
|
timer.fn();
|
|
};
|
|
return { doc, timers, reticle, fire };
|
|
}
|
|
|
|
const pointer = { kind: 'this', label: 'UPS793', screenPx: { x: 600, y: 200 } };
|
|
|
|
test('reticle: shows at the pointed pixel and caps its lifetime', () => {
|
|
const { doc, timers, reticle, fire } = harness();
|
|
reticle.handle({ type: 'pointer', pointer });
|
|
const node = doc.appended[0];
|
|
assert.equal(node.className, 'gev-pointer-reticle');
|
|
assert.equal(node.attributes['aria-hidden'], 'true');
|
|
assert.equal(node.hidden, false);
|
|
assert.equal(node.style.left, '610px');
|
|
assert.equal(node.style.top, '220px');
|
|
assert.equal(node.dataset.target, 'entity');
|
|
assert.equal([...timers.values()][0].ms, RETICLE_MAX_MS);
|
|
fire();
|
|
assert.equal(node.hidden, true);
|
|
});
|
|
|
|
test('reticle: leaves when a tool moves the camera, lingers after a reply', () => {
|
|
const { doc, timers, reticle, fire } = harness();
|
|
reticle.handle({ type: 'pointer', pointer: { ...pointer, kind: 'here' } });
|
|
const node = doc.appended[0];
|
|
assert.equal(node.dataset.target, 'ground');
|
|
reticle.handle({ type: 'action-call', name: 'analyst_query', callId: 'c1' });
|
|
reticle.handle({ type: 'action-call', name: 'analyst_query', callId: 'c2' });
|
|
reticle.handle({ type: 'action-result', name: 'analyst_query', callId: 'c1' });
|
|
reticle.handle({ type: 'completion' });
|
|
assert.equal([...timers.values()][0].ms, RETICLE_MAX_MS, 'a sibling call still running keeps it');
|
|
reticle.handle({ type: 'action-result', name: 'analyst_query', callId: 'c2' });
|
|
reticle.handle({ type: 'completion' });
|
|
assert.equal([...timers.values()][0].ms, RETICLE_LINGER_MS);
|
|
fire();
|
|
assert.equal(node.hidden, true);
|
|
reticle.handle({ type: 'pointer', pointer });
|
|
reticle.handle({ type: 'action-call', name: 'track_entity' });
|
|
assert.equal(node.hidden, true, 'camera tools hide it at once');
|
|
reticle.handle({ type: 'pointer', pointer });
|
|
reticle.handle({ type: 'state', state: 'idle' });
|
|
assert.equal(node.hidden, true, 'session end hides it');
|
|
assert.equal(timers.size, 0);
|
|
reticle.destroy();
|
|
reticle.destroy();
|
|
assert.equal(node.removed, true);
|
|
reticle.handle({ type: 'pointer', pointer });
|
|
assert.equal(doc.appended.length, 1, 'destroyed reticle ignores events');
|
|
});
|
|
|
|
test('reticle css: overlay is inert and hidden in Clean-UI and recording', () => {
|
|
assert.match(css, /\.gev-pointer-reticle\s*\{[^}]*pointer-events:\s*none/);
|
|
assert.match(css, /body\.ui-clean-view \.gev-pointer-reticle/);
|
|
assert.match(css, /body\.recording-mode \.gev-pointer-reticle/);
|
|
assert.match(css, /\.gev-pointer-reticle\[hidden\]/);
|
|
});
|