Skip to content

Commit 60d2bc6

Browse files
edmundhungpenalosa
andauthoredMar 3, 2025··
feat(vitest-pool-workers): step-through debugging (#8199)
Co-authored-by: Somhairle MacLeòid <smacleod@cloudflare.com>
1 parent ff96a70 commit 60d2bc6

File tree

3 files changed

+174
-1
lines changed

3 files changed

+174
-1
lines changed
 

Diff for: ‎.changeset/small-news-kneel.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@cloudflare/vitest-pool-workers": patch
3+
---
4+
5+
Added step-through debugging support with Vitest.
6+
7+
To start debugging, run Vitest with the following command and attach a debugger to port 9229:
8+
9+
```sh
10+
vitest --inspect=9229 --no-file-parallelism
11+
```
12+
13+
For more details, check out our [Vitest Debugging guide](https://developers.cloudflare.com/workers/testing/vitest-integration/debugging).

Diff for: ‎packages/vitest-pool-workers/src/pool/index.ts

+48-1
Original file line numberDiff line numberDiff line change
@@ -556,13 +556,24 @@ function buildProjectMiniflareOptions(
556556
assert(runnerWorker.name !== undefined);
557557
assert(runnerWorker.name.startsWith(WORKER_NAME_PREFIX));
558558

559+
const inspectorPort = ctx.config.inspector.enabled
560+
? ctx.config.inspector.port ?? 9229
561+
: undefined;
562+
563+
if (inspectorPort !== undefined && !project.options.singleWorker) {
564+
log.warn(`Tests run in singleWorker mode when the inspector is open.`);
565+
566+
project.options.singleWorker = true;
567+
}
568+
559569
if (project.options.singleWorker || project.options.isolatedStorage) {
560570
// Single Worker, Isolated or Shared Storage
561571
// --> single instance with single runner worker
562572
// Multiple Workers, Isolated Storage:
563573
// --> multiple instances each with single runner worker
564574
return {
565575
...SHARED_MINIFLARE_OPTIONS,
576+
inspectorPort,
566577
unsafeModuleFallbackService: moduleFallbackService,
567578
workers: [runnerWorker, ABORT_ALL_WORKER, ...auxiliaryWorkers],
568579
};
@@ -610,7 +621,11 @@ async function getProjectMiniflare(
610621
if (project.mf === undefined) {
611622
// If `mf` is now `undefined`, create new instances
612623
if (singleInstance) {
613-
log.info(`Starting single runtime for ${project.relativePath}...`);
624+
log.info(
625+
`Starting single runtime for ${project.relativePath}` +
626+
`${mfOptions.inspectorPort !== undefined ? ` with inspector on port ${mfOptions.inspectorPort}` : ""}` +
627+
`...`
628+
);
614629
project.mf = new Miniflare(mfOptions);
615630
} else {
616631
log.info(`Starting isolated runtimes for ${project.relativePath}...`);
@@ -864,6 +879,31 @@ function assertCompatibleVitestVersion(ctx: Vitest) {
864879
log.warn(message);
865880
}
866881
}
882+
883+
let warnedUnsupportedInspectorOptions = false;
884+
885+
function validateInspectorConfig(config: SerializedConfig) {
886+
if (config.inspector.host) {
887+
throw new TypeError(
888+
"Customizing inspector host is not supported with vitest-pool-workers."
889+
);
890+
}
891+
892+
if (config.inspector.enabled && !warnedUnsupportedInspectorOptions) {
893+
if (config.inspectBrk) {
894+
log.warn(
895+
`The "--inspect-brk" flag is not supported. Use "--inspect" instead.`
896+
);
897+
} else if (config.inspector.waitForDebugger) {
898+
log.warn(
899+
`The "inspector.waitForDebugger" option is not supported. Insert a debugger statement if you need to pause execution.`
900+
);
901+
}
902+
903+
warnedUnsupportedInspectorOptions = true;
904+
}
905+
}
906+
867907
async function executeMethod(
868908
ctx: Vitest,
869909
specs: TestSpecification[],
@@ -932,6 +972,13 @@ async function executeMethod(
932972
timerMethod !== "setImmediate" && timerMethod !== "clearImmediate"
933973
);
934974

975+
validateInspectorConfig(config);
976+
977+
// We don't want it to call `node:inspector` inside Workerd
978+
config.inspector = {
979+
enabled: false,
980+
};
981+
935982
// We don't need all pool options from the config at runtime.
936983
// Additionally, users may set symbols in the config which aren't
937984
// serialisable. `getSerializableConfig()` may also return references to

Diff for: ‎packages/vitest-pool-workers/test/inspector.test.ts

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import dedent from "ts-dedent";
2+
import { test, waitFor } from "./helpers";
3+
4+
test("opens an inspector with the `--inspect` argument", async ({
5+
expect,
6+
seed,
7+
vitestDev,
8+
}) => {
9+
await seed({
10+
"vitest.config.mts": dedent`
11+
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
12+
export default defineWorkersConfig({
13+
test: {
14+
poolOptions: {
15+
workers: {
16+
main: "./index.ts",
17+
singleWorker: true,
18+
miniflare: {
19+
compatibilityDate: "2024-01-01",
20+
compatibilityFlags: ["nodejs_compat"],
21+
},
22+
},
23+
},
24+
}
25+
});
26+
`,
27+
"index.ts": dedent`
28+
export default {
29+
async fetch(request, env, ctx) {
30+
return new Response("hello world");
31+
}
32+
}
33+
`,
34+
"index.test.ts": dedent`
35+
import { env, createExecutionContext, waitOnExecutionContext } from "cloudflare:test";
36+
import { it, expect } from "vitest";
37+
import worker from "./index";
38+
it("sends request", async () => {
39+
const request = new Request("https://example.com");
40+
const ctx = createExecutionContext();
41+
const response = await worker.fetch(request, env, ctx);
42+
await waitOnExecutionContext(ctx);
43+
expect(await response.text()).toBe("hello world");
44+
});
45+
`,
46+
});
47+
const result = vitestDev({
48+
flags: ["--inspect", "--no-file-parallelism"],
49+
});
50+
51+
await waitFor(() => {
52+
expect(result.stdout).toMatch("inspector on port 9229");
53+
});
54+
});
55+
56+
test("customize inspector config", async ({ expect, seed, vitestDev }) => {
57+
await seed({
58+
"vitest.config.mts": dedent`
59+
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
60+
export default defineWorkersConfig({
61+
test: {
62+
inspector: {
63+
// Test if this overrides the inspector port
64+
port: 3456,
65+
},
66+
poolOptions: {
67+
workers: {
68+
main: "./index.ts",
69+
// Test if we warn and override the singleWorker option when the inspector is open
70+
singleWorker: false,
71+
miniflare: {
72+
compatibilityDate: "2024-01-01",
73+
compatibilityFlags: ["nodejs_compat"],
74+
},
75+
},
76+
},
77+
}
78+
});
79+
`,
80+
"index.ts": dedent`
81+
export default {
82+
async fetch(request, env, ctx) {
83+
return new Response("hello world");
84+
}
85+
}
86+
`,
87+
"index.test.ts": dedent`
88+
import { env, createExecutionContext, waitOnExecutionContext } from "cloudflare:test";
89+
import { it, expect } from "vitest";
90+
import worker from "./index";
91+
it("sends request", async () => {
92+
const request = new Request("https://example.com");
93+
const ctx = createExecutionContext();
94+
const response = await worker.fetch(request, env, ctx);
95+
await waitOnExecutionContext(ctx);
96+
expect(await response.text()).toBe("hello world");
97+
});
98+
`,
99+
});
100+
const result = vitestDev({
101+
// Test if we warn and ignore the `waitForDebugger` option
102+
flags: ["--inspect-brk", "--no-file-parallelism"],
103+
});
104+
105+
await waitFor(() => {
106+
expect(result.stdout).toMatch(
107+
"Tests run in singleWorker mode when the inspector is open."
108+
);
109+
expect(result.stdout).toMatch(`The "--inspect-brk" flag is not supported.`);
110+
expect(result.stdout).toMatch("Starting single runtime");
111+
expect(result.stdout).toMatch("inspector on port 3456");
112+
});
113+
});

0 commit comments

Comments
 (0)
Please sign in to comment.