mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
fix(street-level): IC8 review of the Street View provider
Google's Maps Platform terms forbid Street View imagery beside non-Google maps, so the provider declares requiresMapStack: 'photoreal': off Google 3D its chip is dashed with the reason, lookups and opens skip it, and leaving Google 3D closes its panorama. - Ground clicks: only a click on the map itself (scenePick.isSurfacePick: nothing picked or Google 3D tile content) at street zoom opens Street View, and the camera stays put; higher up the hint says to zoom in. scenePick.pickGroundPosition replaces the copy in Directions. - Viewer: poses go out at once and again when the date and address arrive, so an open never hangs on that lookup; one lookup per panorama, seeded by the nearest lookup; a close settles a pending open; failed lookups are asked again; poses follow position changes, not pano_changed. - Loader: the gm_authFailure handler is installed once, so a retried load reports a refusal once. - Viewer host: a prewarm that lands after another provider took the host is released; FIT/FILL availability is set on every open. - Provider: a refusal while switched off reports no error over another photo; SINCE compares against the end of Google's month; messages name the referrer and network causes; FLAT says Street View is 360° only. - The layer row names the key of the providers that are on; the MCP panel CSP admits Street View's tiles, control images and photos. - Tests for each, against fakes that now follow Google's event order; the browser gate restores its state, waits instead of sleeping, aims its panel clicks and corner drags, and asserts the camera stays put. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
22b0009138
commit
7bfcf177ac
35 files changed
+1108
-388
No files matched your search
@@ -12,6 +12,7 @@ import { PROVIDER_COLORS } from '../../policy.js';
|
||||
import { requiresKeyIdFor, validateProviders } from '../../registry.js';
|
||||
import { createMapillaryProvider } from '../mapillary/index.js';
|
||||
import {
|
||||
fakeHost,
|
||||
fakeMapsLoader,
|
||||
fakeStreetViewLibrary,
|
||||
} from '../../../../testSupport/googleStreetViewFakes.mjs';
|
||||
@@ -28,13 +29,27 @@ function setup({ key = 'browser-key', nearest = () => 'fresh' } = {}) {
|
||||
const def = createGoogleProvider({ getApiKey: () => apiKey, loader });
|
||||
const reported = [];
|
||||
let filter = { pano: 'all', sinceMs: null };
|
||||
let active = false;
|
||||
const context = {
|
||||
services: {},
|
||||
getFilter: () => filter,
|
||||
isActive: () => active,
|
||||
notify() {},
|
||||
actions: { reportError: (message) => reported.push(message) },
|
||||
};
|
||||
const instance = def.create(context);
|
||||
const created = def.create(context);
|
||||
// The core switches it on and off; mirror that for isActive().
|
||||
const instance = {
|
||||
...created,
|
||||
activate() {
|
||||
active = true;
|
||||
created.activate();
|
||||
},
|
||||
deactivate() {
|
||||
active = false;
|
||||
created.deactivate();
|
||||
},
|
||||
};
|
||||
return {
|
||||
def,
|
||||
instance,
|
||||
@@ -106,7 +121,7 @@ test('no panorama nearby, or one the imagery filter excludes, is null', async ()
|
||||
assert.equal(await instance.nearestImage({ lat: 0, lon: 0 }), 'old');
|
||||
});
|
||||
|
||||
test('an aborted lookup gives up before and after asking Google', async () => {
|
||||
test('an aborted lookup gives up before asking Google', async () => {
|
||||
const { instance } = setup();
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
@@ -190,3 +205,52 @@ test('aborting a lookup in flight gives up at once', async () => {
|
||||
controller.abort();
|
||||
await assert.rejects(lookup, { name: 'AbortError' });
|
||||
});
|
||||
|
||||
test('a Google outage is an error, not "no imagery here"', async () => {
|
||||
const { instance, fake } = setup();
|
||||
fake.library.StreetViewService.prototype.getPanorama = () =>
|
||||
Promise.reject(
|
||||
Object.assign(new Error('UNKNOWN_ERROR'), {
|
||||
code: 'UNKNOWN_ERROR',
|
||||
endpoint: 'STREETVIEW_GET_PANORAMA',
|
||||
}),
|
||||
);
|
||||
await assert.rejects(instance.nearestImage({ lat: 0, lon: 0 }), {
|
||||
code: 'UNKNOWN_ERROR',
|
||||
});
|
||||
});
|
||||
|
||||
test('a refusal while the provider is off reports no error over another photo', () => {
|
||||
const { instance, loader, reported } = setup();
|
||||
loader.refuseKey();
|
||||
assert.deepEqual(reported, []);
|
||||
assert.equal(
|
||||
instance.coverageStats().keyRejected,
|
||||
true,
|
||||
'its chip still says so',
|
||||
);
|
||||
});
|
||||
|
||||
test('a since window keeps a panorama taken any time in its cut-off month', async () => {
|
||||
const { instance, setFilter } = setup({ nearest: () => 'fresh' }); // 2025-06
|
||||
setFilter({ pano: 'all', sinceMs: Date.UTC(2025, 5, 20) });
|
||||
assert.equal(await instance.nearestImage({ lat: 0, lon: 0 }), 'fresh');
|
||||
setFilter({ pano: 'all', sinceMs: Date.UTC(2025, 6, 1) });
|
||||
assert.equal(await instance.nearestImage({ lat: 0, lon: 0 }), null);
|
||||
});
|
||||
|
||||
test('the panorama a lookup found is dated without a second lookup', async () => {
|
||||
const { instance, fake } = setup();
|
||||
await instance.nearestImage({ lat: 38.58, lon: -121.49 });
|
||||
const host = fakeHost();
|
||||
const poses = [];
|
||||
instance.viewer.onPose((pose) => poses.push(pose));
|
||||
await instance.viewer.mount(host);
|
||||
await instance.viewer.open('fresh');
|
||||
assert.equal(poses.at(-1).capturedAt, Date.UTC(2025, 5, 1));
|
||||
assert.deepEqual(
|
||||
fake.lookups.filter((request) => request.pano),
|
||||
[],
|
||||
'no getPanorama({pano}) for a panorama the lookup already described',
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user