fix(street-level): IC8 follow-ups for Google Street View

- An open after a failed one is no longer failed by the old panorama's
  status: only status_changed can reject; position_changed only resolves.
- A nearest lookup aborted, timed out or refused while Google's script
  loads no longer sends its getPanorama request once the script arrives.
- A panorama-details lookup that never answers times out after 10 s,
  cools down and retries; an unmount releases lookups in flight, and
  their late answers are dropped.
- Every Maps loader on a page shares one script load and ready callback,
  and either can retry a failed script.
- OPEN STREET VIEW aimed at the sky opens nothing and says to aim at a
  street, instead of looking up the camera footprint.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-12 13:52:39 +01:00
1 parent 76db4114ff
commit 10f1986e38
10 files changed
+279 -30

No files matched your search

@@ -206,6 +206,46 @@ test('aborting a lookup in flight gives up at once', async () => {
await assert.rejects(lookup, { name: 'AbortError' });
});
test('a lookup cancelled while Google\u2019s script loads asks Google nothing', async (t) => {
t.mock.timers.enable({ apis: ['setTimeout'] });
const { NEAREST_TIMEOUT_MS } = await import('./policy.js');
const fake = fakeStreetViewLibrary({
panoramas: PANORAMAS,
nearest: () => 'fresh',
});
const loader = fakeMapsLoader(fake.library, { deferred: true });
const instance = createGoogleProvider({
getApiKey: () => 'browser-key',
loader,
}).create({
services: {},
getFilter: () => ({ pano: 'all', sinceMs: null }),
isActive: () => true,
notify() {},
actions: { reportError() {} },
});
const flush = async () => {
for (let i = 0; i < 10; i++) await Promise.resolve();
};
// Aborted, then timed out, while the script still loads.
const controller = new AbortController();
const aborted = instance.nearestImage(
{ lat: 0, lon: 0 },
{ signal: controller.signal },
);
controller.abort();
await assert.rejects(aborted, { name: 'AbortError' });
const expired = instance.nearestImage({ lat: 0, lon: 0 });
t.mock.timers.tick(NEAREST_TIMEOUT_MS);
await assert.rejects(expired, /did not answer/);
// The script arrives: a current lookup still gets its answer.
const current = instance.nearestImage({ lat: 0, lon: 0 });
loader.release();
await flush();
assert.equal(await current, 'fresh');
assert.equal(fake.lookups.length, 1, 'only the current lookup asked Google');
});
test('a Google outage is an error, not "no imagery here"', async () => {
const { instance, fake } = setup();
fake.library.StreetViewService.prototype.getPanorama = () =>