Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
f649413
Support Vitest v4
penalosa Dec 18, 2025
3afc4f6
fix build
penalosa Dec 18, 2025
964012e
fix build
penalosa Dec 18, 2025
e1f2a8b
fix build
penalosa Dec 18, 2025
cfc5f70
fix snapshot
penalosa Dec 18, 2025
704466b
fix fixture tests
penalosa Dec 18, 2025
99a88c9
env & exports
penalosa Dec 19, 2025
3e60beb
better module runner
penalosa Dec 19, 2025
9dec2c5
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Dec 19, 2025
3037a20
fix tests after merge
penalosa Dec 19, 2025
c6af16d
green
penalosa Dec 19, 2025
64982d4
run with vitest3
penalosa Dec 19, 2025
c4ec1a8
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Jan 19, 2026
adc5e54
address some comments
penalosa Jan 19, 2026
1149696
fix tests
penalosa Jan 19, 2026
1625d85
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Jan 19, 2026
e80bd62
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Jan 19, 2026
9f80b75
lockfile
penalosa Jan 19, 2026
d317d15
Merge branch 'main' into penalosa/vitest-v4-support
penalosa Jan 20, 2026
20eb0a6
fix snapshot
penalosa Jan 20, 2026
2f511be
lock down versions
penalosa Jan 20, 2026
7143d5d
fix?
penalosa Jan 20, 2026
de5f57f
Add nunjucks to module-resolution fixture
penalosa Jan 22, 2026
7870094
Add comment re hardcoded event names
penalosa Jan 22, 2026
3f6fef8
delete temp dirs
penalosa Jan 22, 2026
131244a
address more comments
penalosa Jan 22, 2026
1c67bfe
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Jan 22, 2026
858ed07
fix tests after merge
penalosa Jan 22, 2026
79a1932
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Jan 22, 2026
d46a09b
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Feb 18, 2026
1f1072a
[vitest-pool-workers] Fix vitest 4.1.0-beta.4 compatibility
penalosa Feb 18, 2026
a925f45
[vitest-pool-workers] Fix Windows CI failures
penalosa Feb 18, 2026
36bdd7b
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Feb 18, 2026
522b117
[vitest-pool-workers] Fix lint errors and dep validation
penalosa Feb 18, 2026
6fdaa56
[create-cloudflare] Fix vite type conflict with vitest 3
penalosa Feb 18, 2026
177e94c
[vitest-pool-workers] Add @types/node to fixture tsconfig
penalosa Feb 18, 2026
2e76e3c
[miniflare] Normalize persist paths to forward slashes for Windows
penalosa Feb 18, 2026
a63af5a
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Feb 18, 2026
ccaf85f
[edge-preview-authenticated-proxy] Migrate to vitest-pool-workers v4 API
penalosa Feb 18, 2026
ebb56c4
[vitest-pool-workers] Exclude SQLite DO fixture on Windows (workerd bug)
penalosa Feb 19, 2026
b5561cf
[vitest-pool-workers] Make runner DO ephemeral
penalosa Feb 19, 2026
5764732
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Feb 19, 2026
6324643
[vitest-pool-workers] Revert ephemeral runner DO
penalosa Feb 19, 2026
8791f8b
[vitest-pool-workers] Use in-memory DO storage on Windows
penalosa Feb 19, 2026
4bb7043
[vitest-pool-workers] Revert in-memory DOs, re-add Windows exclusion
penalosa Feb 19, 2026
a8a1a8c
[vitest-pool-workers] Make runner DO ephemeral to avoid Windows SQLit…
penalosa Feb 19, 2026
8ca9dad
Update packages/wrangler/src/codemod.ts
penalosa Feb 19, 2026
d3ef674
[vitest-pool-workers] Fix review findings: bugs, error handling, and …
penalosa Feb 19, 2026
c4e2a7c
[vitest-pool-workers] Update Vitest source links to v4.0.18
penalosa Feb 23, 2026
ef2ba23
[vitest-pool-workers] Fix review findings: bugs, tests, and nits
penalosa Feb 23, 2026
ac94357
[vitest-pool-workers] Fix leaked FSWatcher handles in Pages ASSETS bi…
penalosa Feb 23, 2026
989f559
[vitest-pool-workers] Add regression tests for bare specifier resolut…
penalosa Feb 23, 2026
8003a66
[vitest-pool-workers] Fix review findings in Pages watcher cleanup
penalosa Feb 23, 2026
fe26fd9
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Feb 23, 2026
31a64e5
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Feb 24, 2026
3c40844
Merge remote-tracking branch 'origin/main' into penalosa/vitest-v4-su…
penalosa Feb 26, 2026
017ad0c
Fix lockfile after merge
penalosa Feb 26, 2026
755f295
[vitest-pool-workers] Remove redundant polyfills, use workerd compat …
penalosa Feb 27, 2026
6cce159
[edge-preview-authenticated-proxy] Use return await for async sub-han…
penalosa Feb 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/many-fishes-raise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@cloudflare/vitest-pool-workers": minor
---

Support Vitest 4 in `@cloudflare/vitest-pool-workers`.

This a breaking change to the `@cloudflare/vitest-pool-workers` integration in order to support Vitest v4. Along with supporting Vitest v4 (and dropping support for Vitest v2 and v3), we've made a number of changes that may require changes to your tests. Our aim has been to improve stability & the foundations of `@cloudflare/vitest-pool-workers` as we move towards a v1 release of the package.

We've made a codemod to make the migration easier: `wrangler codemod vitest-pool-v3-to-v4`, which will make the required changes to your config file.

- **`isolatedStorage` & `singleWorker`:** These have been removed in favour of a simpler isolation model that more closely matches Vitest. Storage isolation is now on a per test file basis, and you can make your test files share the same storage by using the Vitest flags `--max-workers=1 --no-isolate`
- **`import { env, SELF } from "cloudflare:test"`:** These have been removed in favour of `import { env, exports } from "cloudflare:workers"`. `exports.default.fetch()` has the same behaviour as `SELF.fetch()`, except that it doesn't expose Assets. To test your assets, use the `env.ASSETS` bindings or write an integration test using [`startDevWorker()` (recommended)](https://developers.cloudflare.com/workers/testing/unstable_startworker/)
- **`import { fetchMock } from "cloudflare:test"`:** This has been removed. Instead, [mock `globalThis.fetch`](https://github.com/cloudflare/workers-sdk/blob/main/fixtures/vitest-pool-workers-examples/request-mocking/test/imperative.test.ts) or use ecosystem libraries like [MSW (recommended)](https://mswjs.io/).
5 changes: 5 additions & 0 deletions .changeset/wrangler-codemod-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": minor
---

Add `wrangler codemod vitest-pool-v3-to-v4` command to help migrate `@cloudflare/vitest-pool-workers` config files from v3 to v4.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ describe("getPlatformProxy - env", () => {
vi.spyOn(console, "log").mockImplementation(() => {});
vi.spyOn(console, "error").mockImplementation(() => {});
warn = vi.spyOn(console, "warn").mockImplementation(() => {});
warn.mockReset();
});

describe("var bindings", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
declare module "cloudflare:test" {
interface ProvidedEnv extends Env {}
declare namespace Cloudflare {
interface Env {}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config";
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";

export default defineConfig({
plugins: [
cloudflareTest({
remoteBindings: false,
wrangler: { configPath: "./wrangler.jsonc" },
}),
],

export default defineWorkersProject({
test: {
globalSetup: ["./global-setup.ts"],
poolOptions: {
workers: {
singleWorker: true,
remoteBindings: false,
wrangler: { configPath: "./wrangler.jsonc" },
},
},
},
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
declare module "cloudflare:test" {
// Controls the type of `import("cloudflare:test").env`
interface ProvidedEnv {
declare namespace Cloudflare {
interface Env {
WORKER: Fetcher;
}
}
Original file line number Diff line number Diff line change
@@ -1,42 +1,43 @@
import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config";
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";

export default defineWorkersProject({
test: {
globalSetup: ["./global-setup.ts"],
poolOptions: {
workers: {
singleWorker: true,
miniflare: {
// Configuration for the test runner Worker
compatibilityDate: "2024-01-01",
compatibilityFlags: [
// This illustrates a Worker that in production only wants v1 of Node.js compatibility.
// The Vitest pool integration will need to remove this flag since the `MockAgent` requires v2.
"no_nodejs_compat_v2",
"nodejs_compat",
// Required to use `WORKER.scheduled()`. This is an experimental
// compatibility flag, and cannot be enabled in production.
"service_binding_extra_handlers",
],
serviceBindings: {
WORKER: "worker-under-test",
},

workers: [
// Configuration for the "auxiliary" Worker under test.
// Unfortunately, auxiliary Workers cannot load their configuration
// from `wrangler.toml` files, and must be configured with Miniflare
// `WorkerOptions`.
{
name: "worker-under-test",
modules: true,
scriptPath: "./dist/index.js", // Built by `global-setup.ts`
compatibilityDate: "2024-01-01",
compatibilityFlags: ["nodejs_compat"],
},
],
export default defineConfig({
plugins: [
cloudflareTest({
miniflare: {
// Configuration for the test runner Worker
compatibilityDate: "2024-01-01",
compatibilityFlags: [
// This illustrates a Worker that in production only wants v1 of Node.js compatibility.
// The Vitest pool integration will need to remove this flag since the `MockAgent` requires v2.
"no_nodejs_compat_v2",
"nodejs_compat",
// Required to use `WORKER.scheduled()`. This is an experimental
// compatibility flag, and cannot be enabled in production.
"service_binding_extra_handlers",
],
serviceBindings: {
WORKER: "worker-under-test",
},

workers: [
// Configuration for the "auxiliary" Worker under test.
// Unfortunately, auxiliary Workers cannot load their configuration
// from `wrangler.toml` files, and must be configured with Miniflare
// `WorkerOptions`.
{
name: "worker-under-test",
modules: true,
scriptPath: "./dist/index.js", // Built by `global-setup.ts`
compatibilityDate: "2024-01-01",
compatibilityFlags: ["nodejs_compat"],
},
],
},
},
}),
],

test: {
globalSetup: ["./global-setup.ts"],
},
});
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config";
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";

export default defineWorkersProject({
test: {
poolOptions: {
workers: {
singleWorker: true,
miniflare: {
// Required to use `SELF.scheduled()`. This is an experimental
// compatibility flag, and cannot be enabled in production.
compatibilityFlags: ["service_binding_extra_handlers"],
},
wrangler: {
configPath: "./wrangler.jsonc",
},
export default defineConfig({
plugins: [
cloudflareTest({
miniflare: {
// Required to use `SELF.scheduled()`. This is an experimental
// compatibility flag, and cannot be enabled in production.
compatibilityFlags: ["service_binding_extra_handlers"],
},
},
},
wrangler: {
configPath: "./wrangler.jsonc",
},
}),
],

test: {},
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
declare module "cloudflare:test" {
// Controls the type of `import("cloudflare:test").env`
interface ProvidedEnv extends Env {
declare namespace Cloudflare {
interface Env {
MY_CONTAINER: DurableObjectNamespace<MyContainer>;
}
}
Comment on lines +1 to 5
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand this file was here to provide the Env type for cloudflare:test. Is it still needed with the new setup? I'd expect users to already have their env defined alongside their worker source code.

If it's no longer necessary, it might be worth cleaning this up, especially in vitest-pool-workers-example since we reference that in the docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was, yes. However, it now provided the Env type for cloudflare:workers, and so it's still needed. Users running wrangler types would have this automatically generated.

Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config";
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";

export default defineWorkersProject({
test: {
poolOptions: {
workers: {
singleWorker: true,
wrangler: { configPath: "./wrangler.jsonc" },
},
},
},
export default defineConfig({
plugins: [
cloudflareTest({
wrangler: { configPath: "./wrangler.jsonc" },
}),
],

test: {},
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ declare namespace Cloudflare {
}
interface Env {
NAME: string;
AUXILIARY_WORKER: Fetcher;
}
}
interface Env extends Cloudflare.Env {}
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { env } from "cloudflare:test";
import { it } from "vitest";

declare module "cloudflare:test" {
// Controls the type of `import("cloudflare:test").env`
interface ProvidedEnv {
AUXILIARY_WORKER: Fetcher;
}
}

it("uses the correct context exports on the auxiliary worker", async ({
expect,
}) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@ it("can use context exports on the SELF worker", async ({ expect }) => {
);
});

it("can use context exports (parameterized with props) on the SELF worker", async ({
it("can use context exports (parameterized with props) on the exports.default worker", async ({
expect,
}) => {
const response = await SELF.fetch("http://example.com/props");
const response = await exports.default.fetch("http://example.com/props");
expect(await response.text()).toBe(
"👋 Hello MainWorker from Main NamedEntryPoint!\nAdditional props!!"
);
});

it("will warn on missing context exports on the SELF worker", async ({
it("will warn on missing context exports on the exports.default worker", async ({
expect,
}) => {
const warnSpy = vi.spyOn(console, "warn");
const response = await SELF.fetch("http://example.com/invalid-export");
const response = await exports.default.fetch(
"http://example.com/invalid-export"
);
expect(await response.text()).toMatchInlineSnapshot(`"👋 undefined"`);
expect(warnSpy).toHaveBeenCalledWith(
"Attempted to access 'ctx.exports.InvalidExport', which was not defined for the main 'SELF' Worker.\n" +
Expand All @@ -31,13 +33,15 @@ it("will warn on missing context exports on the SELF worker", async ({
);
});

it("will warn on implicit re-exports that will exist in production but cannot not be guessed on the SELF worker", async ({
it("will warn on implicit re-exports that will exist in production but cannot not be guessed on the exports.default worker", async ({
expect,
}) => {
// In this test, we are trying to access an entry-point that is wildcard (*) re-exported from a virtual module.
// This virtual module is understood by Vitest and TypeScript but not the lightweight esbuild that we use to guess exports.
const warnSpy = vi.spyOn(console, "warn");
const response = await SELF.fetch("http://example.com/virtual-implicit");
const response = await exports.default.fetch(
"http://example.com/virtual-implicit"
);
expect(await response.text()).toMatchInlineSnapshot(`"👋 undefined"`);
expect(warnSpy).toHaveBeenCalledWith(
"Attempted to access 'ctx.exports.ReexportedVirtualEntryPoint', which was not defined for the main 'SELF' Worker.\n" +
Expand All @@ -46,31 +50,33 @@ it("will warn on implicit re-exports that will exist in production but cannot no
);
});

it("will still guess re-exports on the SELF worker that cannot be fully analyzed by esbuild", async ({
it("will still guess re-exports on the exports.default worker that cannot be fully analyzed by esbuild", async ({
expect,
}) => {
// In this test, we are trying to access an entry-point that is explicitly re-exported from a virtual module.
// Although esbuild cannot really analyze what is being re-exported, it can at least see that something is being re-exported with that name.
const warnSpy = vi.spyOn(console, "warn");
const response = await SELF.fetch("http://example.com/virtual-explicit");
const response = await exports.default.fetch(
"http://example.com/virtual-explicit"
);
expect(await response.text()).toBe(
"👋 Hello MainWorker from ExplicitVirtualEntryPoint!"
);
});

it("can access configured virtual entry points on the SELF worker that cannot be fully analyzed by esbuild", async ({
it("can access configured virtual entry points on the exports.default worker that cannot be fully analyzed by esbuild", async ({
expect,
}) => {
// In this test, we are trying to access an entry-point that is explicitly re-exported from a virtual module.
// Although esbuild cannot really analyze what is being re-exported, it can at least see that something is being re-exported with that name.
const warnSpy = vi.spyOn(console, "warn");
const response = await SELF.fetch("http://example.com/virtual-configured");
const response = await exports.default.fetch(
"http://example.com/virtual-configured"
);
expect(await response.text()).toBe(
"👋 Hello MainWorker from ConfiguredVirtualEntryPoint!"
);
});

it("can access imported context exports for SELF worker", async ({
it("can access imported context exports for exports.default worker", async ({
expect,
}) => {
const msg = await exports.NamedEntryPoint.greet();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config";
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";
import { defineConfig } from "vitest/config";

// Configuration for the "auxiliary" Worker under test.
// Unfortunately, auxiliary Workers cannot load their configuration
Expand All @@ -15,21 +16,22 @@ export const auxiliaryWorker = {
},
};

export default defineWorkersProject({
export default defineConfig({
plugins: [
cloudflareTest({
wrangler: { configPath: "./src/wrangler.jsonc" },
miniflare: {
workers: [auxiliaryWorker],
},
additionalExports: {
// This entrypoint is wildcard re-exported from a virtual module so we cannot automatically infer it.
ConfiguredVirtualEntryPoint: "WorkerEntrypoint",
},
}),
],

test: {
globalSetup: ["./global-setup.ts"],
poolOptions: {
workers: {
wrangler: { configPath: "./src/wrangler.jsonc" },
miniflare: {
workers: [auxiliaryWorker],
},
additionalExports: {
// This entrypoint is wildcard re-exported from a virtual module so we cannot automatically infer it.
ConfiguredVirtualEntryPoint: "WorkerEntrypoint",
},
},
},
alias: {
// This alias is used to simulate a virtual module that Vitest and TypeScript can understand,
// but esbuild (used by the vitest-pool-workers to guess exports) cannot.
Expand Down

This file was deleted.

Loading
Loading