Merge remote-tracking branch 'origin/main' into german/map-render-error-state

# Conflicts:
#	src/i18n/locales/ar.json
#	src/i18n/locales/de.json
#	src/i18n/locales/es.json
#	src/i18n/locales/fr.json
#	src/i18n/locales/he.json
#	src/i18n/locales/it.json
#	src/i18n/locales/ja.json
#	src/i18n/locales/pt.json
#	src/i18n/locales/ru.json
#	src/i18n/locales/uk.json
#	src/i18n/locales/zh-CN.json
#	src/i18n/locales/zh-Hant.json
This commit is contained in:
German Khokhlov committed 2026-11-11 19:33:49 +04:00
commit 897a74ea27
92 files changed
+11125 -124

No files matched your search

@@ -181,6 +181,18 @@ export function answerMapillaryRequest({ method, url, images }) {
: [];
return { ...json(200, { data }), known: true };
}
if (q.has('lat') && q.has('lng')) {
const at = { lat: Number(q.get('lat')), lon: Number(q.get('lng')) };
const radius = Math.min(50, Number(q.get('radius')) || 50);
const limit = Number(q.get('limit')) || 10;
// In no particular order, as the real API answers.
const data = photos
.filter((image) => metresApart(at, image) <= radius)
.reverse()
.slice(0, limit)
.map((image) => withFields(imageRecord(image), fields));
return { ...json(200, { data }), known: true };
}
if (q.has('s2')) return { ...json(200, { data: [] }), known: true };
return {
...json(400, { error: 'unsupported images query' }),
+1
View File
@@ -1314,6 +1314,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
+47
View File
@@ -79,6 +79,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -692,6 +693,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -741,6 +743,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -784,6 +787,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -833,6 +837,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -861,6 +866,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -892,6 +898,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -930,6 +937,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -1001,6 +1009,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -1029,6 +1038,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -1234,6 +1244,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -1433,6 +1444,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -1525,6 +1537,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -1592,6 +1605,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -1675,6 +1689,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -1713,6 +1728,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -1755,6 +1771,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -1796,6 +1813,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -1845,6 +1863,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -1898,6 +1917,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -1946,6 +1966,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -2010,6 +2031,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -2048,6 +2070,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -2106,6 +2129,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -2169,6 +2193,7 @@
"scripts/qa-street-level.mjs",
"src/i18n/index.js",
"src/i18n/locale.js",
"src/i18n/marker.js",
"src/i18n/locales/ar.json",
"src/i18n/locales/de.json",
"src/i18n/locales/es.json",
@@ -2178,6 +2203,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -2220,6 +2246,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -2271,6 +2298,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -2302,6 +2330,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -2367,6 +2396,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -2446,6 +2476,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -3421,6 +3452,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -4187,6 +4219,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -4373,6 +4406,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -4407,6 +4441,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -4462,6 +4497,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -4501,6 +4537,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -4608,6 +4645,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -4686,6 +4724,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -4981,6 +5020,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -5037,6 +5077,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -5101,6 +5142,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -5126,6 +5168,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -5197,6 +5240,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -5258,6 +5302,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json"
@@ -5466,6 +5511,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
@@ -5513,6 +5559,7 @@
"src/i18n/locales/ja.json",
"src/i18n/locales/pt.json",
"src/i18n/locales/ru.json",
"src/i18n/locales/sv.json",
"src/i18n/locales/uk.json",
"src/i18n/locales/zh-CN.json",
"src/i18n/locales/zh-Hant.json",
+244
View File
@@ -46,6 +46,15 @@ const VIEWPORT = { width: 1440, height: 900 };
/** Over downtown Sacramento, above the fixture photo line, looking down. */
const PARK = { lon: PHOTO_LINE.lon, lat: 38.5816, height: 900, pitch: -1.3 };
const DAY_MS = 86_400_000;
/** A Sacramento street with Street View, for the sl=google link step. */
const STREET_VIEW_STREET = Object.freeze({ lon: -121.49372, lat: 38.57986 });
/**
* What a Street View lookup and load cost: the Maps JS API's
* SingleImageSearch (the nearest lookup), GetMetadata and the panorama tiles.
* Script loads are not counted.
*/
const STREET_VIEW_REQUEST =
/MapsJsInternalService\/(SingleImageSearch|GetMetadata)|GeoPhotoService|streetviewpixels-pa\.googleapis\.com/;
/**
* Coverage for any street-zoom tile: a grid of 360°/flat, recent/old
@@ -234,6 +243,28 @@ async function boot(page, url) {
});
}
/**
* Open a share link as a fresh document. Changing only the hash of a live
* page and reloading races the app's share-link writer, which can put its own
* state back in the hash before the reload reads it. The page's fixtures (a
* CDP session on the page) carry over.
*/
async function bootLink(page, link) {
await page.goto('about:blank');
await boot(page, link);
}
/** The camera's position in degrees and metres. */
const cameraAt = (page) =>
page.evaluate(() => {
const c = window.__godsEyeView.viewer.camera.positionCartographic;
return {
lon: (c.longitude * 180) / Math.PI,
lat: (c.latitude * 180) / Math.PI,
height: c.height,
};
});
/** Run `test(uiState, arg)` in the page until it is true. */
const uiUntil = (page, test, arg = null, timeout = 30_000) =>
page.waitForFunction(
@@ -780,6 +811,219 @@ async function main() {
await uiUntil(page, litAlone, 'mapillary');
await park(page);
const base = url.split('#')[0];
await fixtureStep(
'a link to street-level photos (sl=) opens the photo at its point once the link restores',
async () => {
const photo = fixture.images[0];
// The camera sits ~330 m north of the photo: the lookup must use
// the link's point, not the view centre.
const cameraLat = Number((photo.lat + 0.003).toFixed(6));
await bootLink(
page,
`${base}#v=2&lat=${cameraLat}&lon=${photo.lon}&alt=600&heading=0&pitch=-60&roll=0&sl=mapillary,${photo.lat},${photo.lon}`,
);
await uiUntil(
page,
(u) => (u.street.imageId && !u.street.loading) || u.street.error,
null,
90_000,
);
const { street } = await getUI(page);
assert.equal(street.error, null, `viewer error: ${street.error}`);
assert.equal(street.providerId, 'mapillary');
assert.equal(
street.imageId,
photo.id,
`the photo at the link's point, not the one nearest the camera: ${street.imageId}`,
);
assert.equal(
await page.evaluate(() => location.hash.includes('sl=')),
false,
'the rewritten link no longer carries the one-shot request',
);
},
);
await fixtureStep(
'the show_street_level action opens the nearest photo at a point without a pointer',
async () => {
await page.evaluate(() =>
window.__godsEyeView.dataManager.layers
.get('street-level')
.module.closeViewer(),
);
const photo = fixture.images[0];
const result = await page.evaluate(
(at) =>
window.__godsEyeView.voiceCommands.runner('show_street_level', {
latitude: at.lat,
longitude: at.lon,
provider: 'mapillary',
fly: false,
}),
{ lat: photo.lat, lon: photo.lon },
);
assert.equal(
result.ok,
true,
`action failed: ${result.stage} ${result.error}`,
);
assert.equal(result.provider, 'mapillary');
assert.ok(
fixture.images.some((image) => image.id === result.imageId),
`a fixture photo opened: ${result.imageId}`,
);
assert.match(result.say || '', /^Mapillary at /);
},
);
await step(
'an sl=google link never loads a photo by itself: keyless it stops at the key, keyed it arms and waits for OPEN',
async ({ skipped }) => {
const point = STREET_VIEW_STREET;
// ~330 m north of the street: a flight would move the camera, and a
// press that ignored the armed point would open something else.
const cameraLat = Number((point.lat + 0.003).toFixed(6));
const googleRequests = [];
const onRequest = (request) => {
if (STREET_VIEW_REQUEST.test(request.url()))
googleRequests.push(request.url());
};
page.on('request', onRequest);
try {
await bootLink(
page,
`${base}#v=2&lat=${cameraLat}&lon=${point.lon}&alt=600&heading=0&pitch=-60&roll=0&map=osm&sl=google,${point.lat},${point.lon}`,
);
await page.waitForFunction(
(want) => {
const c =
window.__godsEyeView.viewer.camera.positionCartographic;
return Math.abs((c.latitude * 180) / Math.PI - want) < 0.0005;
},
{ timeout: 60_000 },
cameraLat,
);
const googleKey = await page.evaluate(() =>
Boolean(window.__GOOGLE_MAPS_API_KEY__),
);
if (!googleKey) {
// Without a key the action stops at the key, before any flight,
// map switch or source change, and the direct call says why.
const direct = await page.evaluate(
(at) =>
window.__godsEyeView.voiceCommands.runner(
'show_street_level',
{
latitude: at.lat,
longitude: at.lon,
provider: 'google',
fly: false,
autoOpen: false,
},
),
point,
);
assert.equal(direct.ok, false);
assert.equal(
direct.stage,
'key',
`${direct.stage} ${direct.error}`,
);
const after = await page.evaluate(() => {
const m =
window.__godsEyeView.dataManager.layers.get(
'street-level',
).module;
const u = m.getUIState();
return {
stack: window.__godsEyeView.mapStackController.getActiveId(),
google: m.getParams().google === true,
armed: u.street.armed,
open: u.street.open,
};
});
assert.deepEqual(
after,
{ stack: 'osm', google: false, armed: null, open: false },
'a keyless Street View link changes nothing',
);
const still = await cameraAt(page);
assert.ok(
Math.abs(still.lat - cameraLat) < 0.0005,
`the camera did not fly: ${still.lat}`,
);
assert.deepEqual(googleRequests, [], 'no Street View request');
return;
}
const photoreal = await page.evaluate(() =>
window.__godsEyeView.mapStackController.isStackAvailable(
'photoreal',
),
);
if (!photoreal) return skipped('no Google 3D');
await uiUntil(
page,
(u) =>
u.street.armed !== null &&
u.providers.some((p) => p.id === 'google' && p.on),
null,
90_000,
);
const beforePress = googleRequests.length;
const waiting = await getUI(page);
assert.equal(
waiting.street.open,
false,
'nothing open without a click',
);
assert.equal(waiting.street.imageId, null);
assert.deepEqual(
waiting.street.armed.point,
{ lat: point.lat, lon: point.lon },
'armed at the link point',
);
assert.equal(
beforePress,
0,
'no Street View request before the press',
);
await expandPanel(page);
await press(page, '#sl-nearest-btn');
await uiUntil(
page,
(u) => (u.street.imageId && !u.street.loading) || u.street.error,
null,
90_000,
);
const { street } = await getUI(page);
assert.equal(street.error, null, `viewer error: ${street.error}`);
assert.equal(street.providerId, 'google');
assert.ok(
googleRequests.length >= 1,
'the press made the Street View request',
);
const metres = Math.hypot(
(street.position.lat - point.lat) * 111_320,
(street.position.lon - point.lon) *
111_320 *
Math.cos((point.lat * Math.PI) / 180),
);
assert.ok(
metres <= 250,
`within 250 m of the link point: ${Math.round(metres)} m`,
);
// Back to Mapillary: the keyless page restores the saved layer
// and expects its KEY REQUIRED.
await press(page, '#sl-provider-chips [data-chip-id="mapillary"]');
await uiUntil(page, litAlone, 'mapillary');
} finally {
page.off('request', onRequest);
}
},
);
await fixtureStep(
// The fixture bundle carries a token, so this covers a keyless
// server behind a keyed bundle; the keyless default is checked above.
+1
View File
@@ -904,6 +904,7 @@ const SLOW_TOOLS = new Set([
'annotate_map',
'fly_to_location',
'select_nearest_aircraft',
'show_street_level',
'analyst_query',
'fly_route',
'frame_overhead',
+8
View File
@@ -35,6 +35,14 @@ export const CORE_PHRASES = Object.freeze([
{ phrase: 'Show me the whole earth', expect: 'zoom_to_globe' },
{ phrase: 'Frame the aircraft near us from overhead', expect: 'frame_overhead', typedArgs: [where('frame_overhead', (a) => a.target === 'flights')] },
// — street-level photos: one composite action, never fly + basemap —
{ phrase: 'Show me the Eiffel Tower on Street View', expect: 'show_street_level', typedArgs: [where('show_street_level', (a) => a.provider === 'google' && /eiffel/i.test(a.query || ''))] },
{ phrase: 'Street view of 10th Street in Sacramento', expect: 'show_street_level', typedArgs: [where('show_street_level', (a) => a.provider === 'google')] },
{ phrase: 'Show me Times Square on Mapillary', expect: 'show_street_level', typedArgs: [where('show_street_level', (a) => a.provider === 'mapillary')] },
{ phrase: 'Switch to Google Street View', expect: 'show_street_level', typedArgs: [where('show_street_level', (a) => a.provider === 'google' && !a.query)] },
{ phrase: 'Show me the Eiffel Tower', expect: 'fly_to_location' },
{ phrase: 'Turn on street level', expect: 'set_layer_visibility', typedArgs: [where('set_layer_visibility', (a) => a.layerId === 'street-level' && a.enabled === true)] },
// — the satellites trap: data layer, never basemap —
{ phrase: 'Show me the satellites', expect: { oneOf: ['set_layer_visibility', 'frame_overhead'] } },
{ phrase: 'Turn off the satellites', expect: 'set_layer_visibility', args: { layerId: 'satellites' }, typedArgs: [where('set_layer_visibility', (a) => a.layerId === 'satellites' && a.enabled === false)] },