-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Expand file tree
/
Copy pathvitest.config.common.ts
More file actions
185 lines (164 loc) · 8.55 KB
/
vitest.config.common.ts
File metadata and controls
185 lines (164 loc) · 8.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import {resolve} from 'node:path';
import {existsSync} from 'node:fs';
import {writeFile} from 'node:fs/promises';
import serveStatic from 'serve-static';
import {tilesets, staticFolders} from './test/integration/lib/middlewares.js';
import {getAllStyleFixturePaths, generateFixtureJson} from './test/integration/lib/generate-fixture-json.js';
import {getHTML, getDiagnosticsHTML} from './test/util/html_generator';
import type {DiagnosticInfo} from './test/util/html_generator';
import type {Plugin} from 'vite';
// On CI, test assets are immutable for the duration of the run. Telling the
// browser to cache them aggressively eliminates repeated conditional-request
// round-trips for tiles, glyphs, sprites, and models shared across tests.
const isCI = process.env.CI === 'true';
const staticCacheOptions = isCI ?
{maxAge: '1h', immutable: true, etag: false, lastModified: false} :
{};
function getShardedTests(suiteDir: string): string[] {
const testFiles = getAllStyleFixturePaths(suiteDir);
const shardId = parseInt(process.env.POOL_SHARD_ID || '0');
const totalShards = parseInt(process.env.POOL_SHARDS || '1');
// Interleave by index rather than slicing contiguously. Render tests are
// sorted alphabetically, so heavy tests cluster by feature name and a
// contiguous slice produces very unbalanced shards.
return testFiles.filter((_, i) => i % totalShards === shardId);
}
export function integrationTests({suiteDirs, includeImages}: {suiteDirs: string[], includeImages?: boolean}): Plugin {
if (!suiteDirs || suiteDirs.length === 0) {
throw new Error('No suite directories specified');
}
const virtualModuleId = 'virtual:integration-tests';
const resolvedVirtualModuleId = `\0${virtualModuleId}`;
const allRenderTests = {};
for (const dir of suiteDirs) {
const testFiles = getShardedTests(dir);
const dirTests = generateFixtureJson(dir, testFiles, includeImages);
// Merge test cases, checking for conflicts
for (const [testName, testData] of Object.entries(dirTests)) {
if (allRenderTests[testName]) {
throw new Error(
`Test name conflict: "${testName}" exists in multiple directories: ` +
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
`"${allRenderTests[testName].path}" and "${testData.path}"`
);
}
allRenderTests[testName] = testData;
}
}
return {
name: 'integration-tests',
resolveId(id) {
if (id === virtualModuleId) {
return resolvedVirtualModuleId;
}
},
load(id) {
if (id === resolvedVirtualModuleId) {
return `export const integrationTests = ${JSON.stringify(allRenderTests)};`;
}
}
};
}
function detectConfigFileFromArgv(): string | undefined {
const argv = process.argv;
const idx = argv.indexOf('--config');
if (idx >= 0 && idx + 1 < argv.length) return argv[idx + 1];
const eq = argv.find((a) => a.startsWith('--config='));
if (eq) return eq.slice('--config='.length);
return undefined;
}
function detectReproduceCommand(): string | undefined {
// argv[0] is node, argv[1] is the vitest entry; skip them for a clean npx invocation.
const rest = process.argv.slice(2);
if (rest.length) return `npx vitest ${rest.map((a) => (/\s/.test(a) ? JSON.stringify(a) : a)).join(' ')}`;
const configFile = detectConfigFileFromArgv();
if (configFile) return `npx vitest run --config ${configFile}`;
return undefined;
}
export function setupIntegrationTestsMiddlewares({reportPath, suiteName}: {
reportPath: string;
suiteName?: string;
}): Plugin {
return {
name: 'setup-integration-tests-middlewares',
configureServer(server) {
const reportFragmentsMap = new Map<number, string>();
let browserDiagnostics: Partial<DiagnosticInfo> = {};
staticFolders.forEach((folder) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
server.middlewares.use(`/${folder}`, serveStatic(resolve(__dirname, `test/integration/${folder}`), staticCacheOptions));
const internalPath = resolve(__dirname, `internal/test/integration/${folder}`);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
if (existsSync(internalPath)) server.middlewares.use(`/${folder}`, serveStatic(internalPath, staticCacheOptions));
});
server.middlewares.use('/report-html/send-fragment', (req, res) => {
let body = '';
req.on('data', (data) => {
body += data;
});
return req.on('end', () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const {id, data} = JSON.parse(body);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
reportFragmentsMap.set(id, Buffer.from(data, 'base64').toString());
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify({status: 'ok'}));
});
});
server.middlewares.use('/report-html/send-diagnostics', (req, res) => {
let body = '';
req.on('data', (data) => { body += data; });
return req.on('end', () => {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
browserDiagnostics = JSON.parse(body);
} catch (err) {
console.error('Error parsing browser diagnostics:', err);
}
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify({status: 'ok'}));
});
});
server.middlewares.use('/report-html/flush', (req, res) => {
const statsContent = reportFragmentsMap.get(0);
const testsContent = Array.from(reportFragmentsMap.entries()).sort((a, b) => a[0] - b[0]).map((r) => r[1]).slice(1).join('');
reportFragmentsMap.clear();
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify({status: 'ok'}));
const shardId = process.env.POOL_SHARD_ID;
const totalShards = process.env.POOL_SHARDS;
const diagnostics: DiagnosticInfo = {
platform: 'Mapbox GL JS (Web)',
generatedAt: new Date().toISOString(),
testSuite: suiteName,
configFile: detectConfigFileFromArgv(),
reproduceCommand: detectReproduceCommand(),
spriteFormat: process.env.SPRITE_FORMAT,
nodeVersion: process.version,
shard: shardId && totalShards ? `${Number(shardId) + 1} / ${totalShards}` : undefined,
...browserDiagnostics,
};
const diagnosticsContent = getDiagnosticsHTML(diagnostics);
writeFile(reportPath, getHTML(statsContent, testsContent, diagnosticsContent)).catch((err) => {
console.error('Error writing report file:', err);
});
});
// eslint-disable-next-line @typescript-eslint/no-misused-promises
server.middlewares.use('/tilesets', tilesets);
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
server.middlewares.use('/mapbox-gl-styles', serveStatic(resolve(__dirname, 'node_modules/mapbox-gl-styles'), staticCacheOptions));
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
server.middlewares.use('/mvt-fixtures', serveStatic(resolve(__dirname, 'node_modules/@mapbox/mvt-fixtures'), staticCacheOptions));
}
};
}
// Serve pre-built dist bundles as-is, bypassing Vite's transformMiddleware
export function serveDistPlugin(): Plugin {
return {
name: 'serve-dist',
configureServer(server) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
server.middlewares.use('/dist', serveStatic(resolve(__dirname, 'dist')));
},
};
}