Files
gods-eye-view/src/voice/analystEmptyNotes.test.mjs
T
German KhokhlovandClaude Opus 5.5 20f4cf3763 i18n: fix the LGTM review findings and tidy the branch texts
- i18n scanner: lastError sink, no false sinks on defaults and ternaries
- shared src/i18n/marker.js for modules that only mark strings
- wind field messages move to wind/fields.js; CITY_POIS imports from cityPois.js
- drop review IDs, change history and stale references from comments and docs

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 21:00:28 +04:00

115 lines
3.2 KiB
JavaScript

// The analyst card's empty notes can be a layer's status line: Local ADS-B
// with every decoder feed stale, Transit with a quiet feed ("(95s old)") or
// its getStats().coverage text. The card shows them in the UI language; the
// model gets the English.
import assert from 'node:assert/strict';
import test from 'node:test';
import { setLocale } from '../i18n/index.js';
import { attachVoiceResult } from './speech.js';
import {
initialVoiceCardState,
reduceVoiceCard,
voiceCardView,
} from './voiceCardPresentation.js';
function viewFor(layer, caveat) {
const shaped = attachVoiceResult(
'analyst_query',
{
ok: true,
count: 0,
complete: true,
scopeLabel: 'in view',
feedState: 'nominal',
unanswered: [],
coverage: { layersQueried: [{ ...layer, status: 'empty' }] },
display: { scope: 'in view', caveat },
items: [],
},
{},
);
return voiceCardView(
[
{
type: 'action-call',
callId: 'c1',
name: 'analyst_query',
arguments: {},
},
{
type: 'action-result',
callId: 'c1',
name: 'analyst_query',
result: shaped,
},
].reduce(reduceVoiceCard, initialVoiceCardState()),
);
}
function notesFor(layerKey, caveat) {
return viewFor({ layerKey }, caveat).result.notes;
}
function inLocale(locale, run) {
setLocale(locale);
try {
run();
} finally {
setLocale('en');
}
}
// Caveat = `${layerKey}: ${getEmptyNote(layerKey)}` (data/analystEngine.js;
// gevActions.js getEmptyNote → stats.coverage || stats.statusMessage).
const cases = [
['local-adsb', 'local-adsb: feeds 1090, 978 stale · 0 heard'],
['local-adsb', 'local-adsb: feed 1090 stale · 0 heard'],
['transit', 'transit: TTC 0 (95s old)'],
['transit', 'transit: TTC 0 · GO Transit 0 (95s old)'],
];
// Transit's coverage text when nothing is in view (transit/queries.js).
const transitCoverage = [
'transit: Fly below 3,000 km to a covered region',
'transit: No feed here yet · 12 regions available',
];
for (const locale of ['es', 'ru']) {
for (const [layerKey, caveat] of cases) {
test(`${locale}: analyst note "${caveat}" is translated`, () => {
inLocale(locale, () => {
const notes = notesFor(layerKey, caveat);
assert.ok(notes.length > 0, 'the note is shown');
assert.doesNotMatch(notes.join(' | '), /\b(stale|heard|old)\b/);
});
});
}
test(`${locale}: Transit's empty note is translated on the analyst card`, () => {
inLocale(locale, () => {
for (const caveat of transitCoverage) {
const view = viewFor(
{ layerKey: 'transit', source: 'GTFS-RT' },
caveat,
);
const text = JSON.stringify(view.result);
assert.doesNotMatch(
text,
/Fly below|No feed here yet|regions available/,
text,
);
}
});
});
}
for (const [layerKey, caveat] of cases) {
test(`en: analyst note "${caveat}" keeps its status line`, () => {
setLocale('en');
const line = caveat.slice(caveat.indexOf(': ') + 2);
assert.ok(
notesFor(layerKey, caveat).some((note) => note.endsWith(`: ${line}`)),
);
});
}