Fix TransformStream is not defined in Jest
Separate an old Node limitation from a missing Jest global, then verify the stream reads, writes and closes without replacing the Worker runtime.
TL;DR#
ReferenceError: TransformStream is not defined means the name is missing where the code executes. If it works in your Worker or Node shell but fails in Jest, check the test environment before installing a polyfill. For a Jest jsdom unit test on a supported Node runtime, expose Node’s native constructor from node:stream/web through setupFiles. Keep that shim in test setup; use a Workers runtime test for Cloudflare-specific behaviour.
ReferenceError: TransformStream is not definedThe original Stack Overflow question describes a Cloudflare Worker that inlines Google Fonts successfully, then fails in a Jest test. Its accepted answer reflects the Node implementation available when it was written. Today, Node documents the constructor globally since Node 18, as well as an explicit module export. Copying the old “Node does not implement it” explanation into a current project skips the environment that actually failed.
Why it happens#
The relevant boundary is the global scope in which the module runs. A successful shell probe does not establish that a Jest environment exposes the same names. Jest’s environment documentation distinguishes its Node and jsdom environments; the selected environment is part of the test configuration.
This distinction was reproduced on 3 November 2026 in an isolated node:24-slim container, running Node 24.22.2, Jest 30.2.0 and jest-environment-jsdom 30.2.0. The host reported that the constructor was a function. Nevertheless, importing the fixture in jsdom failed before any assertion ran. Adding a setup file to that same environment made the test pass. Changing only the environment to Node, without that setup file, also passed as a separate control.
That result proves a sandbox mismatch for this fixture. It does not prove every occurrence has the same cause. An old runtime, a different test environment or code that deliberately replaces a global requires its own diagnosis. Capture the first failing stack frame and identify whether it belongs to your application, a dependency or the runner before changing production code.
Fix#
1. Compare the host with the failing environment#
Run this in the same shell or CI job that starts the tests:
node -p "JSON.stringify({node:process.version,TransformStream:typeof globalThis.TransformStream})"Then inspect the test configuration and any per-file environment selection. A browser component test may need jsdom for DOM behaviour, while a stream-processing unit test may need only Node. Do not switch the whole suite to Node if its assertions require browser objects.
If the host itself lacks the constructor, inspect its version against the Node Web Streams API. Align the job with the project’s supported runtime. A shell on your workstation and a container running the tests can use different executables; changing the former alone cannot repair the latter.
2. Supply the native constructor before imports#
For a jsdom suite that must remain in that environment, create a CommonJS setup file named jest.setup.cjs:
const { TransformStream } = require('node:stream/web');
if (typeof globalThis.TransformStream === 'undefined') {
globalThis.TransformStream = TransformStream;
}This uses the implementation provided by the Node host, rather than installing a second stream library. The conditional also preserves an implementation already present in another environment. Add other missing APIs only when the next diagnostic establishes that they are needed; this particular fixture required only the transform constructor.
Register the file in the existing Jest configuration. For the small fixture below, the complete jest.config.cjs is:
module.exports = {
testEnvironment: 'jsdom',
transform: {},
setupFiles: ['<rootDir>/jest.setup.cjs'],
};In an established application, merge the setup entry into its configuration and preserve its existing compiler settings. The empty transform map above belongs to this plain CommonJS fixture; it is not a replacement for a TypeScript or JSX compiler.
The setupFiles contract runs these modules before test code, inside each test file’s environment. That ordering matters when an imported dependency reads the constructor immediately. Putting the assignment inside a test callback happens after top-level imports, which is too late for that dependency. The setup file needs no Jest mocking API. If a separate native ESM test complains that jest itself is not defined, that is the mocking-object import problem described on that page.
3. Prove a stream can actually finish#
Checking that the global is a function is useful, but a round trip proves more. Save this complete module as round-trip.cjs:
const StreamConstructor = TransformStream;
module.exports = async function roundTrip(input) {
const stream = new StreamConstructor({
transform(chunk, controller) {
controller.enqueue(chunk.toUpperCase());
},
});
const reader = stream.readable.getReader();
const writer = stream.writable.getWriter();
const reading = (async () => {
let output = '';
for (;;) {
const { done, value } = await reader.read();
if (done) return output;
output += value;
}
})();
await writer.write(input);
await writer.close();
return reading;
};The constructor is captured during import so this also tests the setup ordering. Reading begins before the writes are awaited, and closing the writer lets the reader finish. The fixture checks native Node stream behaviour; it does not simulate a Worker request or make a network call.
Save its test as round-trip.test.cjs:
const roundTrip = require('./round-trip.cjs');
test('transforms and closes the stream', async () => {
expect(await roundTrip('fonts')).toBe('FONTS');
});If Jest now stops with a module-resolution error before importing that file, investigate its resolver separately. The Jest absolute-import diagnosis explains a src-prefix mismatch; changing a stream global cannot correct a path that points at the wrong directory.
Verify the fix#
Run the focused test through the local Jest installation:
node node_modules/jest/bin/jest.js round-trip.test.cjs --config jest.config.cjs --runInBandIn the reproduction, the unchanged test first failed in jsdom without setup, with no assertions executed. Adding the setup entry retained jsdom and allowed the real stream assertion to pass. The Node-environment control succeeded without the shim, confirming that the host could already provide the API.
Apply the same verification to the original failing module, rather than accepting only this fixture. Read the complete output and await the asynchronous operation the test is meant to check. A constructor existing is not proof that response rewriting, body consumption or an unrelated application assertion works.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
Cloudflare HTML rewriting needs its own runtime test#
The source question concerns a Worker, so a passing Node unit test is only part of the answer. Cloudflare’s TransformStream reference describes its current implementation as an identity stream and documents differences from the Streams Standard. The uppercase transformer above demonstrates Node’s constructor; it should not be treated as proof of equivalent behaviour in every Workers compatibility configuration.
Cloudflare separately documents HTMLRewriter for HTML transformations. Replacing your implementation with that API is an application change, not a fix that automatically makes an ordinary Jest sandbox understand Workers APIs. For platform behaviour, follow the Workers Vitest integration, which runs tests locally inside the Workers runtime and supplies access to its APIs and bindings. That integration was not run in this reproduction.
Keep the Node import in Node test infrastructure. Moving it into browser application code introduces a different dependency boundary; the node: URI bundling error shows that failure for node:buffer. Resolve the missing global where the failing code executes, then verify the real operation under the runtime whose behaviour the test promises to cover.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix Jest Cannot find module on absolute path imports
Why an import that already matches moduleDirectories still throws Cannot find module: a failing test, then a passing one, for two different fixes.
Cannot use 'import.meta' outside a module: Jest + Vite fix
Three fixes for import.meta.env in Jest tests of a Vite app, each reproduced, plus the secret-leak trap in the popular process.env answer.
Fix: exports is not defined in ES module scope
How Node decides CommonJS vs ES module, and three remedies for aligning source syntax, compiler output and the runtime.


