Support Vitest 4 in @cloudflare/vitest-pool-workers#11632
Support Vitest 4 in @cloudflare/vitest-pool-workers#11632
@cloudflare/vitest-pool-workers#11632Conversation
🦋 Changeset detectedLatest commit: 6cce159 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
107ac9f to
e018115
Compare
create-cloudflare
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-pool-workers
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
| workerdBuiltinModules.has(specifier)) | ||
| ) { | ||
| return this.socket.send( | ||
| // Tell Vitest to treat this module as "external" and load it using a workerd module import |
There was a problem hiding this comment.
This will then end up at our module fallback server
| * hooks can be used to seed data. If this is disabled, all tests will share | ||
| * the same storage. | ||
| */ | ||
| isolatedStorage: z.boolean().default(true), |
There was a problem hiding this comment.
No more isolated storage
There was a problem hiding this comment.
The runtime limit was recently raised to 32MB, so we no longer need to chunk
There was a problem hiding this comment.
We no longer have a custom test runner
e28ec0a to
d45260d
Compare
|
Claude encountered an error —— View job Reviewing Changesets
|
d45260d to
60332b7
Compare
b4ce401 to
964012e
Compare
| d && | ||
| typeof d === "object" && | ||
| "m" in d && | ||
| "a" in d && | ||
| "i" in d && | ||
| Array.isArray(d.a) && | ||
| d.m === "fetch" |
packages/vitest-pool-workers/src/pool/cloudflare-pool-worker.ts
Outdated
Show resolved
Hide resolved
| "@vitest/runner": "4.0.16", | ||
| "@vitest/snapshot": "4.0.16", | ||
| "vitest": "4.0.16" |
There was a problem hiding this comment.
Do we need a peerdep on a pinned version? Seems a bit strict!
There was a problem hiding this comment.
4.0.16 is currently the only the version that is guaranteed to work. Previously we've updated the peer dep as we validate it on newer versions, but maybe that's a bit overkill
| const fromVitest = | ||
| /\/node_modules\/(\.store\/)?vitest/.test(callerFileName ?? "") || | ||
| /\/packages\/vitest\/dist/.test(callerFileName ?? ""); |
There was a problem hiding this comment.
The monkey-patching in this function seems really fragile. Is this unavoidable?
There was a problem hiding this comment.
Unfortunately we do need to do it, in order to prevent Vitest doing IO at the top level & in order to register setTimeouts with waitUntil(). Despite how fragile it looks, it's been fairly okay across 3 major versions of Vitest.
There was a problem hiding this comment.
That said, I've just had a chat with the runtime team, and I think we can get some changes landed in Q1 that will remove the need for this.
There was a problem hiding this comment.
Tracked by https://jira.cfdata.org/browse/EW-9937
| }; | ||
| // Required by vitest/worker | ||
| // @ts-expect-error We don't actually implement `process.memoryUsage()` | ||
| process.memoryUsage = () => ({}); |
There was a problem hiding this comment.
So it's fine with the stub implementation? Could we upstream a fix to remove the requirement?
There was a problem hiding this comment.
It seems fine, yeah. If possible, I'd like to get this in as-is and then work on upstreaming, since there are a few different things it would be good to get changed.
|
|
||
| // Vitest needs this | ||
| // @ts-expect-error Apparently this is read-only | ||
| process.versions = { node: "20.0.0" }; |
There was a problem hiding this comment.
Could we upstream a fix to not require it?
…pport # Conflicts: # pnpm-lock.yaml
- Use workspace:* for @cloudflare/vitest-pool-workers (v4 compatible) - Convert vitest config from defineWorkersConfig + poolOptions to cloudflareTest plugin - Update test tsconfig types for v4 (@cloudflare/workers-types + /types export)
workerd's Windows SQLite code path in sqlite.c++:511 uses kj::Path::toString() which produces Unix-style forward-slash paths (e.g. D:/a/_temp/...), but passes nullptr VFS to sqlite3_open_v2() which uses the win32 VFS expecting native paths. This causes SQLITE_CANTOPEN. The fix is in workerd (toString -> toNativeString), skip the test until then.
The __VITEST_POOL_WORKERS_RUNNER_DURABLE_OBJECT__ doesn't need persistent state — it's just a singleton host for running tests. Making it ephemeral avoids disk-backed SQLite, which hits a workerd bug on Windows (sqlite.c++ uses Unix-style paths with the win32 VFS, causing SQLITE_CANTOPEN for any project with DOs).
…pport # Conflicts: # pnpm-lock.yaml
Ephemeral DOs don't support getByName() which the pool needs to connect to the runner singleton via getDurableObjectNamespace(). The runner DO uses KV-backed storage (not SQLite) so it isn't affected by the Windows SQLite path bug anyway.
workerd's Windows SQLite VFS uses kj::Path::toString() which produces Unix-style forward-slash paths, causing SQLITE_CANTOPEN with the win32 VFS (cloudflare/workerd#6110). Instead of excluding SQLite DO fixtures on Windows, use unsafeEphemeralDurableObjects to switch all DOs to in-memory storage. This avoids disk SQLite paths entirely while keeping DOs fully functional (including SQLite-backed ones). Cross-restart persistence isn't needed for tests.
unsafeEphemeralDurableObjects doesn't work: inMemory storage doesn't support enableSql (SQLite-backed DOs) and listDurableObjectIds reads from disk. There's no miniflare-level workaround for the workerd Windows SQLite path bug — disk-backed SQLite DOs require localDisk storage. Re-add the durable-objects fixture exclusion on Windows until workerd#6110 ships.
ebf3249 to
35ac491
Compare
35ac491 to
a8a1a8c
Compare
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…dead code - Remove duplicate tailStream in WORKER_ENTRYPOINT_KEYS - Fix cycle detection in getCjsNamedExports (seen.add wrong variable) - Fix isEnabled() to match assertIsEnabled() semantics (disable flag precedence) - Fix missing space in user-facing error message - Fix SELF proxy crash on non-function properties - Fix structuredSerializableStringify mutating caller's object - Improve connectToMiniflareSocket error on WebSocket failure - Add no-op methods to BroadcastChannel stub - Fix off() to map Vitest event names to WebSocket events - Fix garbled getSerializedOptions error message - Remove TODO(now), commented-out code, stale comments - Remove redundant ?? true after Zod default - Add cross-referencing comments for shared magic strings
- Fix prototype nesting in createProxyPrototypeClass (re-wraps on every construction) - Fix off() unable to remove wrapped message/error listeners - Close WebSocket explicitly in stop() - Guard actionResults map against fetch failure leaks - Avoid mutating caller objects in structuredSerializableStringify and send() - Fix chunking test to use meaningful 1.2 MiB payload - Fix console test no-op assertion and remove debug log - Remove duplicate isolation test, clean up stale config options - Add wrangler changeset for new codemod command - Fix pnpm path in setTimeout caller detection regex - Remove unused warnSpy variables in context-exports fixture - Use semver check instead of startsWith for version guard
…nding (#7717) generateAssetsFetch() in wrangler creates a chokidar watcher for _headers and _redirects files that was never closed. This kept the Node.js event loop alive, causing Vitest's "close timed out" warning on every test run. Wire an AbortSignal through generateAssetsFetch() so the watcher can be closed on demand. In vitest-pool-workers, track AbortControllers in a module-level registry and abort them all in CloudflarePoolWorker.stop().
- Reference-count active workers so watchers only close when the last worker stops, not when the first one does - Move AbortController registration after the wrangler import so a failed import does not leak an orphaned controller - Handle already-aborted signals in generateAssetsFetch - Catch errors from watcher.close() to avoid unhandled rejections
…pport # Conflicts: # pnpm-lock.yaml
…pport # Conflicts: # pnpm-lock.yaml
| // Stub polyfill for node:v8 — workerd doesn't natively support this module. | ||
| // Vitest 4.1.0-beta.4 imports node:v8 to check `v8.startupSnapshot.isBuildingSnapshot()` | ||
| // which should always return false in workerd. |
There was a problem hiding this comment.
Workerd does now support this natively: https://github.com/cloudflare/workerd/blob/main/src/workerd/io/compatibility-date.capnp#L1086C15-L1092
There was a problem hiding this comment.
…flags Replace hand-written polyfills with workerd's native implementations: - node:v8 stub → nodejs_v8_module compat flag - process.memoryUsage/EventEmitter prototype → nodejs_process_v2 compat flag - globalThis.Buffer → already provided by nodejs_compat_v2 - globalThis.process, process.argv, process.versions.node → already provided by nodejs_compat_v2/process_v2 Fix unhandled rejection in runDurableObjectAlarm (return → return await) to prevent intermediate rejected promise with process_v2's proper unhandledrejection tracking. Fix request-mocking fixture: replace HttpResponse.error() with HTTP 400 to avoid MSW-internal TypeError escaping as unhandled rejection.
…dlers Add `await` to `return` statements in handleRequest() that call async sub-handlers (handleTokenExchange, updatePreviewToken, handleRawHttp). Without `await`, when these functions throw synchronously (e.g. via assertValidURL), the intermediate rejected promise is reported as an unhandled rejection before the outer try/catch can handle it. This is a workerd process_v2 behaviour difference vs Node.js — to be reported upstream.

Fixes #11064
Fixes #10260 (tests added to verify the mentioned libraries can be imported)
Fixes #9957 (
providedata moved from HTTP headers to WebSocket messages, removing the ~8 KB size limit)Fixes #8369 (
defineWorkersConfigis removed)Fixes #7795 (everything is now encapsulated in a plugin, and so this type of configuration problem is impossible)
Fixes #7717 (leaked FSWatcher from Pages ASSETS binding is now closed via AbortSignal on pool worker stop)
Fixes #7339 (we no longer recommend importing
envfromcloudflare:test. Instead, importenvfromcloudflare:workersand usewithEnvto override the value: https://developers.cloudflare.com/workers/runtime-apis/bindings/#overriding-env-values)Fixes #7324 (
nodejs_compat_v2and Node module flags are now forcibly enabled during tests)Fixes #7288 (new
WorkersSnapshotEnvironmentproxies snapshot file operations over a service binding)Fixes #6465 (
workerdresolve condition is explicitly included,nodecondition is removed)Fixes #6214 (bare Node.js specifiers like
"url"are resolved by prependingnode:in the module fallback)Fixes #5592 (SSR optimizer replaced with
server.deps.inline = true, so Stripe resolves to its worker build)Fixes #5396 (module graph is now populated via standard Vitest
PoolWorkerRPC — the UI should work)This a breaking change to the
@cloudflare/vitest-pool-workersintegration 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-workersas 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-isolateimport { env, SELF } from "cloudflare:test": These have been deprecated in favour ofimport { env, exports } from "cloudflare:workers".exports.default.fetch()has the same behaviour asSELF.fetch(), except that it doesn't expose Assets. To test your assets, use theenv.ASSETSbindings or write an integration test usingstartDevWorker()(recommended)import { fetchMock } from "cloudflare:test": This has been removed. Instead, mockglobalThis.fetchor use ecosystem libraries like MSW (recommended).A picture of a cute animal (not mandatory, but encouraged)
