fix(web): recover transient map SDK failures
This commit is contained in:
@@ -1,5 +1,18 @@
|
||||
import { expect, test } from 'vitest';
|
||||
import { buildAMapMarkerURL, gcj02ToWgs84, isValidAMapCoordinate, wgs84ToGcj02 } from './amap';
|
||||
import { afterEach, expect, test, vi } from 'vitest';
|
||||
import {
|
||||
buildAMapMarkerURL,
|
||||
gcj02ToWgs84,
|
||||
isValidAMapCoordinate,
|
||||
loadAMap,
|
||||
type AMapLike,
|
||||
wgs84ToGcj02
|
||||
} from './amap';
|
||||
|
||||
afterEach(() => {
|
||||
delete window.__LINGNIU_APP_CONFIG__;
|
||||
delete window.AMapLoader;
|
||||
document.querySelector('script[src="https://webapi.amap.com/loader.js"]')?.remove();
|
||||
});
|
||||
|
||||
test('accepts coordinates inside the China vehicle service range', () => {
|
||||
expect(isValidAMapCoordinate(116.397128, 39.916527)).toBe(true);
|
||||
@@ -30,3 +43,31 @@ test('keeps coordinates outside China unchanged and converts AMap marker links a
|
||||
expect(position[0]).toBeCloseTo(116.40337, 5);
|
||||
expect(position[1]).toBeCloseTo(39.91793, 5);
|
||||
});
|
||||
|
||||
test('retries the SDK load after a transient AMap loader rejection', async () => {
|
||||
const amap = {} as AMapLike;
|
||||
const load = vi.fn()
|
||||
.mockRejectedValueOnce(new Error('temporary network failure'))
|
||||
.mockResolvedValueOnce(amap);
|
||||
window.__LINGNIU_APP_CONFIG__ = { amapWebJsKey: 'amap-web-key' };
|
||||
window.AMapLoader = { load };
|
||||
|
||||
await expect(loadAMap(['AMap.Geocoder'])).rejects.toThrow('temporary network failure');
|
||||
await expect(loadAMap(['AMap.Geocoder'])).resolves.toBe(amap);
|
||||
expect(load).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test('removes a failed loader script so a later request can recover', async () => {
|
||||
const amap = {} as AMapLike;
|
||||
window.__LINGNIU_APP_CONFIG__ = { amapWebJsKey: 'amap-web-key' };
|
||||
|
||||
const firstAttempt = loadAMap(['AMap.ToolBar']);
|
||||
const failedScript = document.querySelector<HTMLScriptElement>('script[src="https://webapi.amap.com/loader.js"]');
|
||||
expect(failedScript).not.toBeNull();
|
||||
failedScript?.dispatchEvent(new Event('error'));
|
||||
await expect(firstAttempt).rejects.toThrow('高德地图 Loader 加载失败');
|
||||
expect(document.querySelector('script[src="https://webapi.amap.com/loader.js"]')).toBeNull();
|
||||
|
||||
window.AMapLoader = { load: vi.fn().mockResolvedValue(amap) };
|
||||
await expect(loadAMap(['AMap.ToolBar'])).resolves.toBe(amap);
|
||||
});
|
||||
|
||||
@@ -104,16 +104,31 @@ function loadScript(src: string) {
|
||||
}
|
||||
const existing = document.querySelector<HTMLScriptElement>(`script[src="${src}"]`);
|
||||
if (existing) {
|
||||
if (existing.dataset.loadState === 'loaded') {
|
||||
existing.remove();
|
||||
loadScript(src).then(resolve, reject);
|
||||
return;
|
||||
}
|
||||
existing.addEventListener('load', () => resolve(), { once: true });
|
||||
existing.addEventListener('error', () => reject(new Error('高德地图 Loader 加载失败')), { once: true });
|
||||
existing.addEventListener('error', () => {
|
||||
existing.remove();
|
||||
reject(new Error('高德地图 Loader 加载失败'));
|
||||
}, { once: true });
|
||||
if (window.AMapLoader) resolve();
|
||||
return;
|
||||
}
|
||||
const script = document.createElement('script');
|
||||
script.src = src;
|
||||
script.async = true;
|
||||
script.onload = () => resolve();
|
||||
script.onerror = () => reject(new Error('高德地图 Loader 加载失败'));
|
||||
script.dataset.loadState = 'loading';
|
||||
script.onload = () => {
|
||||
script.dataset.loadState = 'loaded';
|
||||
resolve();
|
||||
};
|
||||
script.onerror = () => {
|
||||
script.remove();
|
||||
reject(new Error('高德地图 Loader 加载失败'));
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
@@ -221,16 +236,21 @@ export function loadAMap(plugins: AMapPlugin[] = ['AMap.Scale']) {
|
||||
} else if (config.securityJsCode) {
|
||||
window._AMapSecurityConfig = { securityJsCode: config.securityJsCode };
|
||||
}
|
||||
const promise = loadScript('https://webapi.amap.com/loader.js').then(() => {
|
||||
if (!window.AMapLoader) {
|
||||
throw new Error('高德地图 Loader 不可用');
|
||||
}
|
||||
return window.AMapLoader.load({
|
||||
key: config.webJsKey,
|
||||
version: '2.0',
|
||||
plugins
|
||||
}) as Promise<AMapLike>;
|
||||
});
|
||||
const promise = loadScript('https://webapi.amap.com/loader.js')
|
||||
.then(() => {
|
||||
if (!window.AMapLoader) {
|
||||
throw new Error('高德地图 Loader 不可用');
|
||||
}
|
||||
return window.AMapLoader.load({
|
||||
key: config.webJsKey,
|
||||
version: '2.0',
|
||||
plugins
|
||||
}) as Promise<AMapLike>;
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
amapLoaderPromises.delete(key);
|
||||
throw error;
|
||||
});
|
||||
amapLoaderPromises.set(key, promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user