Skip to content

Commit 752340f

Browse files
JPeer264claude
andcommitted
test(cloudflare): Add Vite-build support to the integration-test runner
Teach the integration-test runner to support suites that opt into the Sentry Vite plugin. When a suite ships a `vite.config.*`, the runner runs `vite build` first (so the plugin's auto-instrumentation transform runs) and points `wrangler dev` at the generated `dist/<worker>/wrangler.json`, matched to the right source config via its `userConfigPath`. Suites without a Vite config keep running straight from source, unchanged. No suite uses this path yet; the auto-instrument feature commits that follow add their own `vite-autoinstrument/*` integration suites. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e5d9096 commit 752340f

3 files changed

Lines changed: 411 additions & 4 deletions

File tree

dev-packages/cloudflare-integration-tests/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@
2929
"openai": "5.18.1"
3030
},
3131
"devDependencies": {
32+
"@cloudflare/vite-plugin": "1.34.0",
3233
"@cloudflare/workers-types": "^4.20260426.0",
3334
"@sentry-internal/test-utils": "10.67.0",
3435
"@sentry/conventions": "0.16.0",
3536
"eslint-plugin-regexp": "^3.1.0",
3637
"prisma": "6.15.0",
38+
"vite": "7.3.2",
3739
"vitest": "^3.2.6",
3840
"wrangler": "4.86.0"
3941
},

dev-packages/cloudflare-integration-tests/runner.ts

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { Envelope, EnvelopeItemType } from '@sentry/core';
22
import { normalize } from '@sentry/core';
33
import { createBasicSentryServer } from '@sentry-internal/test-utils';
4-
import { spawn } from 'child_process';
5-
import { existsSync } from 'fs';
4+
import { spawn, spawnSync } from 'child_process';
5+
import { existsSync, readdirSync, readFileSync } from 'fs';
66
import { join } from 'path';
77
import { inspect } from 'util';
88
import { expect } from 'vitest';
@@ -18,6 +18,60 @@ export function cleanupChildProcesses(): void {
1818

1919
process.on('exit', cleanupChildProcesses);
2020

21+
/**
22+
* Resolve the wrangler config `wrangler dev` should serve for a worker.
23+
*
24+
* Most suites run straight from source (`wrangler dev --config <name>.jsonc`).
25+
* A suite that opts into the Sentry Vite plugin instead ships a `vite.config.*`
26+
* (and no top-level `main` in its wrangler config): for those we run `vite build`
27+
* first — so the plugin's build-time auto-instrumentation transform runs — and
28+
* point wrangler at the generated config under `dist/<worker>/wrangler.json`.
29+
*
30+
* `wranglerConfigName` selects which source config the Vite build corresponds to
31+
* (`wrangler.jsonc` for the main worker, `wrangler-sub-worker.jsonc` for a sub),
32+
* so a Vite suite's generated output is matched to the right worker.
33+
*/
34+
function resolveWorkerConfig(testPath: string, wranglerConfigName: string): string {
35+
const sourceConfig = join(testPath, wranglerConfigName);
36+
const viteConfig = ['vite.config.ts', 'vite.config.mts', 'vite.config.js', 'vite.config.mjs']
37+
.map(name => join(testPath, name))
38+
.find(existsSync);
39+
40+
// No Vite config → serve the source wrangler config unchanged (existing path).
41+
if (!viteConfig) {
42+
return sourceConfig;
43+
}
44+
45+
const result = spawnSync('vite', ['build'], { cwd: testPath, stdio: process.env.DEBUG ? 'inherit' : 'ignore' });
46+
if (result.status !== 0) {
47+
throw new Error(`vite build failed for ${testPath} (exit code ${result.status})`);
48+
}
49+
50+
// `@cloudflare/vite-plugin` emits one directory per worker under `dist/`, each
51+
// containing a resolved `wrangler.json`. Match the one whose original config is
52+
// this worker's source config so multi-worker suites map correctly.
53+
const distDir = join(testPath, 'dist');
54+
const builtConfig = readdirSync(distDir, { withFileTypes: true })
55+
.filter(entry => entry.isDirectory())
56+
.map(entry => join(distDir, entry.name, 'wrangler.json'))
57+
.find(configPath => existsSync(configPath) && builtFromSource(configPath, sourceConfig));
58+
59+
if (!builtConfig) {
60+
throw new Error(`Could not locate a Vite-built wrangler config for ${sourceConfig} under ${distDir}`);
61+
}
62+
return builtConfig;
63+
}
64+
65+
/** Whether a generated `wrangler.json` was built from the given source config. */
66+
function builtFromSource(builtConfigPath: string, sourceConfigPath: string): boolean {
67+
try {
68+
const built = JSON.parse(readFileSync(builtConfigPath, 'utf8')) as { userConfigPath?: string; configPath?: string };
69+
return built.userConfigPath === sourceConfigPath || built.configPath === sourceConfigPath;
70+
} catch {
71+
return false;
72+
}
73+
}
74+
2175
// Wrangler can report "Ready" before it can actually handle requests.
2276
// This retries fetch on connection errors and transient 500 responses to handle this race condition.
2377
// The budget (maxRetries * retryDelayMs) must cover the "ready-but-not-serving" window, which can be
@@ -299,7 +353,7 @@ export function createRunner(...paths: string[]) {
299353
[
300354
'dev',
301355
'--config',
302-
join(testPath, 'wrangler-sub-worker.jsonc'),
356+
resolveWorkerConfig(testPath, 'wrangler-sub-worker.jsonc'),
303357
'--show-interactive-dev-session',
304358
'false',
305359
'--var',
@@ -325,7 +379,7 @@ export function createRunner(...paths: string[]) {
325379
[
326380
'dev',
327381
'--config',
328-
join(testPath, 'wrangler.jsonc'),
382+
resolveWorkerConfig(testPath, 'wrangler.jsonc'),
329383
'--show-interactive-dev-session',
330384
'false',
331385
'--var',

0 commit comments

Comments
 (0)