Skip to content

Commit 602ff02

Browse files
dcojgithub-actions[bot]
authored andcommitted
Test esm loading to ensure modules load as expected
GitOrigin-RevId: ab8d2ea08e9ceab8af72cd51bec1f735a5fcbdb3
1 parent ae2a3ab commit 602ff02

14 files changed

Lines changed: 265 additions & 3 deletions

build/generate-access-token-script.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fs from 'fs';
22

3-
const script = fs.readFileSync(new URL('../debug/access_token.js', import.meta.url), 'utf-8')
3+
const script = fs.readFileSync(new URL('../test/util/access_token.js', import.meta.url), 'utf-8')
44
.replace('process.env.MapboxAccessToken',
55
JSON.stringify(process.env.MapboxAccessToken))
66
.replace('process.env.MAPBOX_ACCESS_TOKEN',
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {test, expect, waitFor} from '../../util/vitest';
2+
import {HD} from '../../../modules/hd_main_esm';
3+
import {makeMap, waitForLoaded} from './helpers';
4+
5+
test('building layer triggers HD loading', async () => {
6+
const geojson = {type: 'FeatureCollection', features: [{
7+
type: 'Feature', properties: {},
8+
geometry: {type: 'Polygon', coordinates: [[[0, 0], [0, 0.01], [0.01, 0.01], [0.01, 0], [0, 0]]]},
9+
}]};
10+
const map = makeMap(
11+
[{id: 'bldg', type: 'building', source: 'geo', layout: {'building-base': 0, 'building-height': 10}, paint: {}}],
12+
{geo: {type: 'geojson', data: geojson}},
13+
);
14+
try {
15+
await waitFor(map, 'idle');
16+
await waitForLoaded(HD);
17+
expect(HD.loaded).toBe(true);
18+
} finally {
19+
map.remove();
20+
}
21+
});

test/unit/esm_loading/helpers.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import {vi} from '../../util/vitest';
2+
import {Map} from '../../../src/index.esm';
3+
4+
import type {LayerSpecification} from '../../../src/style-spec/types';
5+
6+
export function makeContainer() {
7+
const container = document.createElement('div');
8+
Object.defineProperty(container, 'getBoundingClientRect',
9+
{value: () => ({height: 200, width: 200}), configurable: true});
10+
return container;
11+
}
12+
13+
export function makeMap(layers: LayerSpecification[] = [], sources = {}) {
14+
vi.spyOn(Map.prototype, '_detectMissingCSS').mockImplementation(() => {});
15+
const map = new Map({
16+
testMode: true,
17+
container: makeContainer(),
18+
zoom: 1,
19+
center: [0, 0],
20+
interactive: false,
21+
attributionControl: false,
22+
performanceMetricsCollection: false,
23+
precompilePrograms: false,
24+
style: {version: 8, sources, layers},
25+
});
26+
map._authenticate = () => {};
27+
return map;
28+
}
29+
30+
export async function waitForLoaded(mod: {loaded?: boolean}) {
31+
await vi.waitUntil(() => mod.loaded === true, {timeout: 3000, interval: 20});
32+
}
33+
34+
export async function settle() {
35+
await new Promise<void>(resolve => { setTimeout(resolve, 150); });
36+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {test, expect, waitFor} from '../../util/vitest';
2+
import {HD} from '../../../modules/hd_main_esm';
3+
import {makeMap, waitForLoaded} from './helpers';
4+
5+
test('line with hd-road-markup elevation triggers HD loading', async () => {
6+
const geojson = {type: 'FeatureCollection', features: [{
7+
type: 'Feature', properties: {},
8+
geometry: {type: 'LineString', coordinates: [[0, 0], [0.01, 0.01]]},
9+
}]};
10+
const map = makeMap(
11+
[{id: 'road', type: 'line', source: 'geo', layout: {'line-elevation-reference': 'hd-road-markup'}, paint: {}}],
12+
{geo: {type: 'geojson', data: geojson}},
13+
);
14+
try {
15+
await waitFor(map, 'idle');
16+
await waitForLoaded(HD);
17+
expect(HD.loaded).toBe(true);
18+
} finally {
19+
map.remove();
20+
}
21+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {test, expect, waitFor} from '../../util/vitest';
2+
import {HD} from '../../../modules/hd_main_esm';
3+
import {makeMap, settle} from './helpers';
4+
5+
test('line without elevation reference does not load HD', async () => {
6+
const geojson = {type: 'FeatureCollection', features: [{
7+
type: 'Feature', properties: {},
8+
geometry: {type: 'LineString', coordinates: [[0, 0], [0.01, 0.01]]},
9+
}]};
10+
const map = makeMap(
11+
[{id: 'road', type: 'line', source: 'geo', paint: {}}],
12+
{geo: {type: 'geojson', data: geojson}},
13+
);
14+
try {
15+
await waitFor(map, 'idle');
16+
await settle();
17+
expect(HD.loaded).toBeUndefined();
18+
} finally {
19+
map.remove();
20+
}
21+
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Hits the real Mapbox Standard style to verify it loads Standard (which includes
2+
// model layers) but does not trigger HD. Guards against HD being accidentally
3+
// pulled in by Standard. Uses the localhost-scoped CI token; the test will fail
4+
// if the token is invalid or the API is unreachable.
5+
import {test, expect, vi, waitFor} from '../../util/vitest';
6+
import {Map, setAccessToken} from '../../../src/index.esm';
7+
import {LOCALHOST_CI_TOKEN} from '../../util/access_token.js';
8+
import {HD} from '../../../modules/hd_main_esm';
9+
import {Standard} from '../../../modules/standard_main_esm';
10+
import {makeContainer, settle, waitForLoaded} from './helpers';
11+
12+
test('mapbox standard style loads Standard but not HD', {timeout: 30000}, async () => {
13+
setAccessToken(LOCALHOST_CI_TOKEN);
14+
vi.spyOn(Map.prototype, '_detectMissingCSS').mockImplementation(() => {});
15+
const map = new Map({
16+
testMode: true,
17+
container: makeContainer(),
18+
zoom: 1,
19+
center: [0, 0],
20+
interactive: false,
21+
attributionControl: false,
22+
performanceMetricsCollection: false,
23+
precompilePrograms: false,
24+
style: 'mapbox://styles/mapbox/standard',
25+
});
26+
try {
27+
await waitFor(map, 'idle');
28+
await waitForLoaded(Standard);
29+
await settle();
30+
expect(Standard.loaded).toBe(true);
31+
expect(HD.loaded).toBeUndefined();
32+
} finally {
33+
map.remove();
34+
}
35+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Hits the real Mapbox Streets v12 style to verify it loads neither HD nor Standard.
2+
// Guards against regressions where a layer type added to Streets v12 accidentally
3+
// triggers a module load. Uses the localhost-scoped CI token; the test will fail
4+
// if the token is invalid or the API is unreachable.
5+
import {test, expect, vi, waitFor} from '../../util/vitest';
6+
import {Map, setAccessToken} from '../../../src/index.esm';
7+
import {LOCALHOST_CI_TOKEN} from '../../util/access_token.js';
8+
import {HD} from '../../../modules/hd_main_esm';
9+
import {Standard} from '../../../modules/standard_main_esm';
10+
import {makeContainer, settle} from './helpers';
11+
12+
test('mapbox streets-v12 style loads neither HD nor Standard', {timeout: 30000}, async () => {
13+
setAccessToken(LOCALHOST_CI_TOKEN);
14+
vi.spyOn(Map.prototype, '_detectMissingCSS').mockImplementation(() => {});
15+
const map = new Map({
16+
testMode: true,
17+
container: makeContainer(),
18+
zoom: 1,
19+
center: [0, 0],
20+
interactive: false,
21+
attributionControl: false,
22+
performanceMetricsCollection: false,
23+
precompilePrograms: false,
24+
style: 'mapbox://styles/mapbox/streets-v12',
25+
});
26+
try {
27+
await waitFor(map, 'idle');
28+
await settle();
29+
expect(HD.loaded).toBeUndefined();
30+
expect(Standard.loaded).toBeUndefined();
31+
} finally {
32+
map.remove();
33+
}
34+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Precipitation triggers HD via a separate code path in painter.ts (line ~1169)
2+
// that is independent of mayUse and renderLayer. This test exercises that path.
3+
import {test, expect, waitFor} from '../../util/vitest';
4+
import {HD} from '../../../modules/hd_main_esm';
5+
import {makeMap, waitForLoaded} from './helpers';
6+
7+
test('rain precipitation triggers HD loading via painter path', async () => {
8+
const map = makeMap();
9+
try {
10+
await waitFor(map, 'idle');
11+
map.painter._debugParams.forceEnablePrecipitation = true;
12+
map.triggerRepaint();
13+
await waitForLoaded(HD);
14+
expect(HD.loaded).toBe(true);
15+
} finally {
16+
map.remove();
17+
}
18+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// This is the primary regression test for the raster-particle bug: RasterParticleStyleLayer
2+
// was missing mayUse('HD'), so setupHD() was never triggered from renderLayer.
3+
// raster-particle bypasses the coords guard in renderLayer, so renderLayer fires even with
4+
// no tile data. A non-empty tiles URL is still required to pass style validation; tile loads
5+
// are mocked to 404 so no real network traffic occurs.
6+
import {test, expect} from '../../util/vitest';
7+
import {mockFetch} from '../../util/network';
8+
import {HD} from '../../../modules/hd_main_esm';
9+
import {Standard} from '../../../modules/standard_main_esm';
10+
import {makeMap, waitForLoaded} from './helpers';
11+
12+
test('raster-particle layer triggers HD loading', async () => {
13+
mockFetch({'.*': () => Promise.resolve(new Response(null, {status: 404}))});
14+
const map = makeMap(
15+
[{id: 'rp', type: 'raster-particle', source: 'wind', 'source-layer': 'u_component_of_wind', paint: {}}],
16+
{wind: {type: 'raster-array', tiles: ['https://example.com/{z}/{x}/{y}.mrt'], tileSize: 512}},
17+
);
18+
try {
19+
// Don't wait for idle (depends on tile load completion); just wait for
20+
// setupHD() to fire, which happens on the first render frame.
21+
await waitForLoaded(HD);
22+
expect(HD.loaded).toBe(true);
23+
expect(Standard.loaded).toBeUndefined();
24+
} finally {
25+
map.remove();
26+
}
27+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {test, expect, waitFor} from '../../util/vitest';
2+
import {HD} from '../../../modules/hd_main_esm';
3+
import {makeMap, settle} from './helpers';
4+
5+
test('standard-style-like layers (fill/line/symbol/fill-extrusion) do not load HD', async () => {
6+
const geojson = {type: 'FeatureCollection', features: [{
7+
type: 'Feature', properties: {height: 20},
8+
geometry: {type: 'Polygon', coordinates: [[[0, 0], [0, 0.01], [0.01, 0.01], [0.01, 0], [0, 0]]]},
9+
}]};
10+
const map = makeMap(
11+
[
12+
{id: 'fill', type: 'fill', source: 'geo', paint: {}},
13+
{id: 'line', type: 'line', source: 'geo', paint: {}},
14+
{id: 'symbol', type: 'symbol', source: 'geo', layout: {}},
15+
{id: 'extrude', type: 'fill-extrusion', source: 'geo', paint: {'fill-extrusion-height': ['get', 'height']}},
16+
],
17+
{geo: {type: 'geojson', data: geojson}},
18+
);
19+
try {
20+
await waitFor(map, 'idle');
21+
await settle();
22+
expect(HD.loaded).toBeUndefined();
23+
} finally {
24+
map.remove();
25+
}
26+
});

0 commit comments

Comments
 (0)