test(mapillary): provider, source, presentation, markup and QA-harness coverage

scripts/qa-mapillary.mjs drives a real browser through the rail panel,
the keyless gate, coverage, a ready-made feature plan, the viewer and its
expanded dialog at 1440×900 and 1280×800.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Fable 5.1 committed 2026-09-23 15:44:32 +01:00
1 parent 70ebdeb783
commit f6f8fd8fa8
11 files changed
+1558 -97

No files matched your search

+236
View File
@@ -0,0 +1,236 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
mapillaryImageUrl,
presentMapillaryPanel,
} from './mapillaryPresentation.js';
function snapshot(overrides = {}) {
const base = {
enabled: false,
keyRequired: false,
planner: true,
plannerModel: 'claude-opus-5',
coverage: {
zoom: null,
kind: null,
filter: { pano: 'all', sinceMs: null },
legend: [
{ key: 'recent', label: 'Recent (≤2 yr)', color: '#05cb63' },
{ key: 'older', label: 'Older', color: '#2e7d5b' },
{ key: 'pano', label: '360°', color: '#ff4fd8' },
{ key: 'selected', label: 'Selected', color: '#00d4ff' },
],
loading: false,
sequences: 0,
hint: '',
error: null,
},
sequence: { selectedId: null, images: 0, loading: false },
features: {
total: 0,
title: '',
loading: false,
progress: { done: 0, tiles: 0 },
counts: [],
truncated: false,
failed: 0,
iconMode: false,
hasBbox: false,
selectedId: null,
bbox: null,
},
street: {
open: false,
follow: false,
loading: false,
error: null,
imageId: null,
position: null,
bearing: null,
isPano: false,
capturedAt: null,
sequenceId: null,
creator: null,
renderMode: 'letterbox',
feature: null,
highlight: null,
},
query: {
busy: false,
stage: 'idle',
prompt: '',
place: null,
answer: '',
error: null,
title: '',
usage: null,
history: [],
},
};
return deepMerge(base, overrides);
}
function deepMerge(target, source) {
const out = { ...target };
for (const [key, value] of Object.entries(source)) {
out[key] =
value &&
typeof value === 'object' &&
!Array.isArray(value) &&
target[key] &&
typeof target[key] === 'object'
? deepMerge(target[key], value)
: value;
}
return out;
}
test('a missing Mapillary token gates every control and names the variable', () => {
const view = presentMapillaryPanel(snapshot({ keyRequired: true }));
assert.equal(view.keyless.visible, true);
assert.match(view.keyless.text, /MAPILLARY_CLIENT_TOKEN/);
assert.equal(view.controlsDisabled, true);
assert.deepEqual(view.status, { text: 'KEY REQUIRED', tone: 'warn' });
});
test('without an Anthropic key the query box explains itself and stays disabled', () => {
const view = presentMapillaryPanel(
snapshot({ planner: false, enabled: true }),
);
assert.equal(view.query.inputDisabled, true);
assert.equal(view.query.suggestionsDisabled, true);
assert.equal(view.query.hintWarn, true);
assert.match(view.query.hint, /Anthropic key/);
assert.ok(view.query.placeholder.length < 40, 'placeholder fits the field');
assert.equal(view.keyless.visible, false);
assert.equal(view.controlsDisabled, false);
});
test('a running query turns ASK into STOP and shows indeterminate progress per stage', () => {
const planning = presentMapillaryPanel(
snapshot({ enabled: true, query: { busy: true, stage: 'planning' } }),
);
assert.equal(planning.query.submitLabel, 'STOP');
assert.equal(planning.query.submitIsStop, true);
assert.deepEqual(planning.progress, {
visible: true,
indeterminate: true,
percent: 0,
label: 'Planning…',
});
assert.equal(planning.status.text, 'AI · PLANNING');
const resolving = presentMapillaryPanel(
snapshot({
enabled: true,
query: { busy: true, stage: 'resolving', place: 'Sacramento, CA' },
}),
);
assert.equal(resolving.progress.label, 'Finding Sacramento, CA…');
const fetching = presentMapillaryPanel(
snapshot({
enabled: true,
query: { busy: true, stage: 'fetching' },
features: { loading: true, progress: { done: 3, tiles: 12 }, total: 410 },
}),
);
assert.equal(fetching.progress.indeterminate, false);
assert.equal(fetching.progress.percent, 25);
assert.equal(fetching.progress.label, '3/12 tiles · 410 found');
});
test('idle copy depends on whether the layer is on', () => {
assert.match(
presentMapillaryPanel(snapshot()).answer,
/Turn on Street Level/,
);
assert.match(
presentMapillaryPanel(snapshot({ enabled: true })).answer,
/Click a green line/,
);
const view = presentMapillaryPanel(
snapshot({ enabled: true, query: { error: 'Planner unavailable' } }),
);
assert.equal(view.error, 'Planner unavailable');
assert.equal(view.answer, '');
});
test('result chips cap at eight and describe the overflow', () => {
const counts = Array.from({ length: 11 }, (_, i) => ({
value: `object--v${i}`,
label: `Class ${i}`,
color: '#fff',
count: 100 - i,
}));
const view = presentMapillaryPanel(
snapshot({ enabled: true, features: { total: 900, counts } }),
);
assert.equal(view.results.visible, true);
assert.equal(view.results.chips.length, 8);
assert.equal(view.results.more.count, 3);
assert.match(view.results.more.title, /Class 8 92, Class 9 91, Class 10 90/);
assert.equal(view.results.all.length, 11);
});
test('legend passes through in the layer’s order', () => {
const view = presentMapillaryPanel(snapshot());
assert.deepEqual(
view.legend.map((entry) => entry.key),
['recent', 'older', 'pano', 'selected'],
);
});
test('the meta line never mixes the visible-sequence count with the selected sequence', () => {
const browsing = presentMapillaryPanel(
snapshot({ enabled: true, coverage: { zoom: 14, sequences: 812 } }),
);
assert.match(browsing.meta, /^812 sequences in view · click a line/);
const selected = presentMapillaryPanel(
snapshot({
enabled: true,
coverage: { zoom: 14, sequences: 812 },
sequence: { selectedId: 'abc', images: 33 },
}),
);
assert.equal(selected.meta, '33 images in this sequence · Esc clears');
assert.doesNotMatch(selected.meta, /sequences in view/);
});
test('viewer caption reads "Image by" left and date right, with a deep link', () => {
const view = presentMapillaryPanel(
snapshot({
enabled: true,
street: {
open: true,
imageId: '1814275685699406',
creator: 'mapfool',
capturedAt: Date.UTC(2023, 9, 8),
bearing: 93.4,
isPano: true,
feature: { label: 'Fire hydrant', imageCount: 5 },
highlight: { count: 2 },
},
}),
);
assert.equal(
view.viewer.captionLeft,
'Fire hydrant · 5 sightings · 2 detections outlined · Image by mapfool',
);
assert.equal(view.viewer.captionRight, '360° · 93° · 2023-10-08');
assert.equal(
view.viewer.link,
'https://www.mapillary.com/app/?pKey=1814275685699406&focus=photo',
);
assert.equal(view.viewer.follow.disabled, false);
assert.equal(view.wantsOpen, true);
});
test('image deep links match the mapillary.com share format', () => {
assert.equal(
mapillaryImageUrl(1814275685699406),
'https://www.mapillary.com/app/?pKey=1814275685699406&focus=photo',
);
assert.equal(mapillaryImageUrl(' '), 'https://www.mapillary.com/app/');
});