QA How-To
How to Fix "Cannot find module '@playwright/test'" in Playwright
Fix Cannot Find Module Playwright Test errors in local projects, monorepos, CI, and Docker with exact install commands, cause checks, and verification steps.
19 min read | 3,417 words
TL;DR
Install @playwright/test as a development dependency in the test package, then verify with node -p "require.resolve('@playwright/test')" and npx playwright test --list. If it fails only in CI or Docker, check dev-dependency installation, workspace location, and lockfile use before investigating browsers.
Key Takeaways
- Install @playwright/test in the package that owns the importing tests.
- Use require.resolve and Playwright test discovery to isolate module failures from browser failures.
- CI test jobs must install development dependencies from a synchronized lockfile.
- A Playwright Docker browser image still needs the project's npm packages installed.
- Editor-only TS2307 errors require a TypeScript project and workspace check.
- Match Docker image tags to the Playwright version recorded by the project.
To fix Cannot Find Module Playwright Test errors, first identify where resolution fails: your editor, the local test runner, a CI job, or a container. The error commonly appears just after adding import { test, expect } from '@playwright/test' to a new specification, or after a clean install in another environment.
Error: Cannot find module '@playwright/test'
The same dependency problem can appear in TypeScript as TS2307: Cannot find module '@playwright/test' or its corresponding type declarations. Follow the checks below from the directory that owns the tests. Each fix includes a command that proves whether that specific cause is gone.
TL;DR
From the package that contains your Playwright tests, install the test runner, confirm Node can resolve it, and ask Playwright to discover tests:
npm install -D @playwright/test
node -p "require.resolve('@playwright/test')"
npx playwright test --list
If node -p prints a path but the browser later fails to launch, the module is fixed. Install the required browser separately with npx playwright install, or on supported Linux environments use npx playwright install --with-deps. In CI, use npm ci with development dependencies present. In Docker, install the project's npm dependencies inside the image even when the base image already contains browsers.
What the Error Actually Means
@playwright/test is a package identifier. Node and TypeScript look for it through the package's dependency installation and module resolution rules. If the import cannot be resolved from the file doing the import, tests cannot even be collected. The runner has not reached browser startup, navigation, or assertions.
A stack trace from Node may include code: 'MODULE_NOT_FOUND' and a Require stack that identifies the file that requested the package. TypeScript may instead show TS2307 under the import in the editor. These diagnostics are related, but they are not identical: a TypeScript project can be configured incorrectly while the Playwright CLI still works, and a local editor can look healthy while an isolated CI installation lacks the package.
The quickest useful distinction is between package resolution and browser binaries. Run node -p "require.resolve('@playwright/test')" in the package directory. If that fails, solve installation or workspace scope. If it succeeds and a test reports that a browser executable does not exist, follow the Playwright browser executable fix. Reinstalling browsers cannot make a missing JavaScript package appear.
The package named playwright provides browser automation APIs; playwright-core is a lower-level library without downloaded browsers. For a test file importing test and expect from @playwright/test, declare @playwright/test in the test package. The Playwright test runner tutorial explains the runner features after installation.
Root-Cause Decision Table
| Symptom | Root cause | Fix |
|---|---|---|
npm ls @playwright/test shows (empty) and Node cannot resolve the import |
Test runner is absent from this package | Install @playwright/test as a development dependency |
| Package exists elsewhere in a monorepo, but the test workspace cannot resolve it | Dependency was added to the wrong workspace | Add it to the package that owns the tests |
| Local run works, CI says module is missing | CI omitted development dependencies or did not install this workspace | Use the correct lockfile, install dev dependencies, and run in the test workspace |
playwright or playwright-core exists, but the imported specifier is @playwright/test |
Package identity and import do not match | Add @playwright/test and import from that name |
| A teammate or clean checkout fails after a local install | Manifest and committed lockfile disagree, or installation was not reproduced | Update and commit both files, then verify with a clean npm ci |
CLI lists tests, but the editor reports TS2307 |
Editor chose another TypeScript project or stale language-service state | Include the tests in the intended tsconfig and reload TypeScript |
| Docker image has browsers, but the test import fails | Image lacks the project's npm dependencies | Copy package files and run npm ci inside the image |
Start with the first two rows. They account for most first-run failures. Use the later rows when the same import works in one environment and fails in another.
1. Fix Cannot Find Module Playwright Test When the Package Is Missing
Check the dependency declaration and installed tree from the directory containing package.json. A declaration in the manifest says what should be installed; npm ls says what is actually installed. These can disagree if installation stopped, someone copied only the source files, or node_modules was removed.
pwd
npm pkg get devDependencies.@playwright/test
npm ls @playwright/test --depth=0
node -p "require.resolve('@playwright/test')"
An (empty) tree or a resolution exception means this package cannot supply the import. Install the test runner locally:
npm install -D @playwright/test
node -p "require.resolve('@playwright/test')"
npm ls @playwright/test --depth=0
The first verification command must print a file path, and the second must show @playwright/test under the current project. npm install -D also updates package.json and package-lock.json; commit both for a reproducible checkout. Avoid a global install, which does not establish the project-local dependency that source imports resolve. If your repository uses pnpm or Yarn, use its own add command and keep that manager's lockfile authoritative.
You may then install a browser when you intend to execute browser tests:
npx playwright install chromium
npx playwright test --list
The list command collects tests without opening a browser. It is a useful boundary check: a successful list proves the runner can load the test files and config. A browser test may still fail later because of an application URL, unavailable browser, or assertion. For a fresh test project, the Playwright TypeScript framework guide covers the wider layout, fixtures, and config.
2. Fix Cannot Find Module Playwright Test in the Wrong Workspace
Monorepos have several package.json files. A dependency installed at the repository root is not necessarily available to a nested package under pnpm's strict workspace layout or an isolated CI job. Determine which package owns the importing test and which directory the failing command uses. Do not diagnose a workspace from a terminal opened in an unrelated package.
For a conventional npm workspace named by path packages/e2e, run the following from the repository root. The path is an example; replace it with the actual workspace path registered in your root package.json:
npm pkg get workspaces
npm install -D @playwright/test --workspace=packages/e2e
npm exec --workspace=packages/e2e -- playwright test --list
Then resolve the specifier as that workspace would. createRequire uses the workspace manifest as the lookup origin, which matters when a root-level resolution would give a misleading success:
node -e "const { createRequire } = require('node:module'); const r = createRequire(process.cwd() + '/packages/e2e/package.json'); console.log(r.resolve('@playwright/test'))"
A path proves the workspace can find the package. If this check fails, inspect the workspace path, package name, and manager-specific layout. For pnpm, enter the package directory and use pnpm add -D @playwright/test, followed by pnpm exec playwright test --list. For Yarn, use the workspace command supported by the repository's Yarn setup rather than mixing in npm. Keep one lockfile family and run the same manager locally and in CI.
Also check the location of playwright.config.ts. Playwright searches for config from the command's working directory unless you pass --config. Running at the root may load a different config or no tests at all. A successful package resolution plus No tests found points to discovery or working-directory settings, not an absent module.
3. Restore Development Dependencies in CI
A typical QA repository declares @playwright/test under devDependencies, which is appropriate because it runs during verification rather than application runtime. A CI command that installs only production dependencies can remove that package from disk. With npm, NODE_ENV=production or an explicit omit setting can make this happen even though the lockfile still records the dependency.
Print a few focused diagnostics in the failing job before changing code:
node --version
npm --version
npm config get omit
npm pkg get devDependencies.@playwright/test
npm ls @playwright/test --depth=0
If omit contains dev, install the locked development dependencies for the test job. A minimal shell sequence in a checkout that contains package-lock.json is:
npm ci --include=dev
node -p "require.resolve('@playwright/test')"
npx playwright install --with-deps
npx playwright test --list
npx playwright test
npm ci requires a lockfile and installs from it without updating the manifest. --include=dev makes the test dependency explicit even when the environment has production settings. On a hosted Linux runner, --with-deps installs browsers and supported OS packages; on another platform, follow Playwright's platform-specific browser installation instructions. The verified sequence is install package, resolve package, install browsers, discover tests, run tests.
If your CI matrix tests a subpackage, set its working directory or invoke the workspace command consistently for both install and execution. If the job builds a production artifact first, use separate dependency installations or stages rather than expecting the production-only artifact to contain a test runner. See GitHub Actions for Playwright for an end-to-end pipeline pattern. The CI troubleshooting interview guide also covers how to explain the installation boundary clearly.
4. Match the Import to the Installed Package
It is easy to see playwright in package.json and assume it satisfies an import from @playwright/test. Package names in imports are exact specifiers. Adding playwright-core, playwright, or a browser image does not itself declare the @playwright/test dependency for the package that owns the tests.
Inspect the current package and test file:
npm ls playwright playwright-core @playwright/test --depth=0
rg "@playwright/test|playwright" tests playwright.config.ts
If rg is unavailable, use your editor's search for those imports. For a Playwright Test suite, standardize on this form:
// tests/module-resolution.spec.ts
import { test, expect } from '@playwright/test';
test('runner loads the local package', () => {
expect(2 + 2).toBe(4);
});
Save that file, then install the matching package and collect only this test:
npm install -D @playwright/test
npx playwright test tests/module-resolution.spec.ts --list
npx playwright test tests/module-resolution.spec.ts
This test has no browser fixture, so its success demonstrates runner installation without depending on a browser download or application server. Once it passes, restore your real tests. If your project is deliberately using only the playwright library for a standalone Node script, retain its actual APIs and imports; do not rename the package just to silence an editor. Conversely, Playwright Test's test, expect, fixtures, and CLI belong in a suite with the test runner dependency.
A typo is another exact-match failure. Compare @playwright/test character by character, including the @ and slash. Do not create a local declare module '@playwright/test' shim to hide an unresolved package; that can silence TypeScript while Node still fails at runtime.
5. Reconcile the Manifest, Lockfile, and Clean Install
A local node_modules directory can mask a missing dependency declaration. You may have installed @playwright/test experimentally, copied modules from another checkout, or changed package.json without updating the lockfile. Another developer then runs a clean install and gets a different dependency tree. Resolve that disagreement at the package metadata level.
Start by reading the package state rather than deleting anything:
npm pkg get devDependencies.@playwright/test
npm ls @playwright/test --depth=0
git diff -- package.json package-lock.json
If the manifest lacks the runner, add it with npm install -D @playwright/test. If the manifest declares it but npm ci says the lockfile is out of sync, run npm install with the repository's npm configuration, review the resulting lockfile change, and commit it with the manifest. Do not hand-edit lockfile entries. Keep any repository .npmrc options used to create the lockfile available to CI, since a different install configuration can itself break npm ci.
Verify the result with the same clean-install command used by teammates and CI:
npm ci --include=dev
node -p "require.resolve('@playwright/test')"
npx playwright test --list
npm ci replaces the local installation, so run it only when you are ready for that reset. The successful resolution and test list are stronger evidence than a green editor underline. If a private registry or network failure interrupts the install, solve that install error first; a subsequent missing-module message is only the downstream symptom.
Record the resulting package.json and lockfile changes together in the commit. A lockfile from npm paired with a pnpm install, or multiple competing lockfiles, makes it harder to reproduce the exact tree. The practical goal is one documented installation path that works from a checkout with no node_modules.
6. Diagnose TypeScript and Editor-Only Resolution
Suppose node -p "require.resolve('@playwright/test')" prints a path and npx playwright test --list works, but VS Code still underlines the import with TS2307. The package is installed for the runner. The remaining question is which TypeScript project owns the test file, and whether the editor's language service has picked up the current dependency tree.
First, open the test from the same workspace folder you use in the terminal. In a monorepo, opening only a child folder may change which tsconfig.json TypeScript sees. Inspect the config that includes the test. If your application config includes only src, create or use a test config that includes tests and the Playwright config:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"noEmit": true,
"strict": true
},
"include": ["tests/**/*.ts", "playwright.config.ts"]
}
Save this as tsconfig.e2e.json at the test package root if a dedicated config fits the repository. The include paths are relative to this config file. If the package already uses moduleResolution: "bundler" with a bundler, do not switch modes merely because of this error; TypeScript's module mode should match the runtime and toolchain. The sample is for a Node-oriented test project.
With TypeScript installed locally, verify the project explicitly:
npx tsc --noEmit --project tsconfig.e2e.json
npx playwright test --list
If tsc fails for unrelated Node globals, add the repository's normal Node type dependency and types configuration rather than changing the Playwright import. If TypeScript succeeds but VS Code is stale, run the editor command TypeScript: Restart TS Server, then reopen the file. An editor restart can refresh diagnostics, but it cannot repair a missing node_modules package. The Node.js guide for testers gives useful background on package lookup and runtime boundaries.
7. Install the Project Dependency Inside Docker
The official Playwright Docker image supplies browsers and operating-system dependencies. It does not replace installing your application's npm packages. A container can therefore have Chromium available and still throw Cannot find module '@playwright/test' when it loads a test. Copy the manifest and lockfile into the image, install the dependency there, then copy the tests.
Before using this Dockerfile, replace <your-playwright-version> with the exact version installed by the project's lockfile. The Playwright image version and project version must match for browser discovery. Do not guess a tag or pin a number from an unrelated example.
FROM mcr.microsoft.com/playwright:v<your-playwright-version>-noble
WORKDIR /work
COPY package.json package-lock.json ./
RUN npm ci --include=dev
COPY . .
RUN node -p "require.resolve('@playwright/test')"
RUN npx playwright test --list
CMD ["npx", "playwright", "test"]
Keep node_modules out of the Docker build context with .dockerignore; otherwise COPY . . can overwrite the clean container installation with host-specific files. If the tests are in a workspace, copy all manifests and workspace directories needed by npm ci, then run the relevant workspace command. The short Dockerfile assumes one npm package and a committed package-lock.json.
After substituting the tag, verify the image and then run it:
docker build -t playwright-module-check .
docker run --rm playwright-module-check
A successful image build proves the import resolves and tests are discoverable inside the image. The run can still need network access to your application, environment variables, or mounted secrets. Those are separate test-environment requirements. The Docker for Playwright guide covers browser images, container networking, and CI use in more depth.
How to Verify the Fix
Use a short ladder of checks rather than jumping straight to the full suite. Run each command in the exact directory or workspace where the original failure occurred:
npm ls @playwright/test --depth=0
node -p "require.resolve('@playwright/test')"
npx playwright test --list
npx playwright test tests/module-resolution.spec.ts
The first line confirms npm sees a local dependency. The second confirms Node can find the specifier. The third confirms Playwright can load the config and collect tests. The fourth executes the small runner-only test created in section 4. If that file was not created, substitute an existing browser-independent Playwright Test file, or stop after --list and then run your normal suite.
These checks answer different questions. npm ls can show a dependency while TypeScript still chooses the wrong project. require.resolve can work in a root directory while a strict workspace package cannot resolve the same import. --list can pass while a later browser launch fails. Record which boundary failed before changing anything else.
When a test reaches browser startup, run npx playwright install for the browser your project uses. If you see a browser executable error, consult the linked browser fix rather than reinstalling @playwright/test repeatedly. When an application URL or authentication fails, the module problem has been solved; debug the test environment with the Playwright debugging interview scenarios as a diagnostic checklist.
For a CI-only failure, run the same four checks inside the failing job after its install step. For Docker, place resolution and --list checks in the image build temporarily or keep them as low-cost assertions. A local success alone does not prove an isolated runner receives the package.
Prevent It From Coming Back
Keep the test runner in the manifest of the package that owns the tests. Commit the package manager's lockfile with the manifest change. Use the same manager and install command in local documentation, CI, and Docker. A new checkout should reproduce the dependency tree without copied node_modules or a globally installed Playwright CLI.
Add a fast discovery gate before the expensive browser suite. npx playwright test --list catches unresolved imports and malformed config without opening a browser. If CI omits development dependencies for deployment, create a separate test installation with dev dependencies. Do not reuse a production-only dependency tree for a test job. In Docker, install packages after copying lockfiles and before copying frequently changing source files so layer caching remains useful.
Keep the import spelling uniform: import { test, expect } from '@playwright/test' in Playwright Test files. Review workspace declarations when moving tests between packages. A moved test can silently cross a package boundary even when its source code is unchanged. Check both the importing file's package and the command's working directory during code review.
Pin dependency versions through your lockfile and match the Playwright Docker image tag to the installed version. The exact version should come from the project, not a tutorial sample. When updating Playwright, update its browsers as directed by the official installation guide. This keeps package resolution, browser binaries, and container images aligned without turning every failure into an indiscriminate reinstall.
Interview Questions and Answers
Q: What does Cannot find module '@playwright/test' prove?
It proves the import cannot be resolved from the process or TypeScript project reporting the error. I first identify the importing file and its package directory, then check the manifest, installed tree, and require.resolve. It does not prove that a browser is missing.
Q: Why can CI fail when local tests pass?
Local node_modules may contain an undeclared package, while CI starts from a clean checkout. CI may also omit development dependencies or run in another workspace. I compare npm ci settings, working directories, and lockfiles before changing test code.
Q: Is installing playwright the same as installing @playwright/test?
No. The import specifier must match the dependency available to the test package. For a suite using the Playwright Test runner, I declare @playwright/test and import test and expect from it.
Q: How do you separate a package error from a browser error?
I run require.resolve('@playwright/test') and npx playwright test --list. If both pass, the JavaScript package and test discovery work. A later executable-not-found message points to browser installation or version alignment.
Q: Why might TypeScript show TS2307 while the CLI works?
The editor may attach the file to a different TypeScript project, exclude the tests from the intended config, or hold a stale language-service state. I validate the test tsconfig with tsc, check the workspace opened in the editor, and restart the TypeScript server only after package resolution passes.
Q: What should a Playwright Docker image still install?
It must install the project's npm dependencies from its manifest and lockfile. The browser image contains browser-related components, but it is not a substitute for the application's test runner dependency. I verify resolution during the image build and match the image tag to the lockfile's Playwright version.
Common Mistakes
- Running
npx playwright installas the first response to a missing@playwright/testimport. That command installs browsers, not the package declaration. - Installing the runner globally and assuming a local TypeScript import will resolve against global modules.
- Adding
@playwright/testat the monorepo root while tests execute in a workspace with its own dependency boundary. - Using production-only
npm ciin a CI job that runs dev-dependency test tools. - Updating
package.jsonwithout committing the lockfile that the clean build uses. - Mixing npm, pnpm, and Yarn commands in the same repair, leaving competing lockfiles.
- Hiding
TS2307with a custom ambient module declaration instead of fixing the package or tsconfig. - Copying host
node_modulesinto a Docker image after its clean installation. - Treating
No tests foundas proof that@playwright/testis absent. Test discovery and package resolution are separate checks. - Changing several installation variables at once, then losing track of which boundary actually failed.
Conclusion
Fix the missing import at the package boundary where it fails. Install @playwright/test in the test-owning package, preserve its lockfile, and verify with require.resolve and npx playwright test --list. If the same code fails only in CI or Docker, reproduce the installation and working directory inside that environment. Once discovery works, move to browser installation and application-level test failures as separate problems.
The official Playwright installation guide, CI guide, Docker guide, Node module documentation, and TypeScript module resolution reference provide the underlying commands and resolution rules.
Interview Questions and Answers
How would you diagnose a missing @playwright/test import?
I identify the failing process and importing file, then run `npm ls @playwright/test --depth=0` and `require.resolve` from that package. I inspect its manifest and lockfile if the installed tree is absent. Once resolution succeeds, I use `npx playwright test --list` to check collection separately.
What is the difference between MODULE_NOT_FOUND and a browser executable failure?
`MODULE_NOT_FOUND` occurs while loading JavaScript modules, before the test can run. An executable failure occurs after Playwright is loaded and attempts to launch a browser. I verify the package first, then install browsers only when the later failure is present.
How can a monorepo make a local dependency check misleading?
A command at the repository root may resolve a hoisted package while the actual test workspace has no declared dependency. Strict package managers and isolated CI installs expose that difference. I check resolution from the test workspace and add the runner to that workspace's manifest.
Why can a clean CI job fail while a developer laptop passes?
The laptop may retain an undeclared dependency in `node_modules`, whereas CI installs only what the lockfile and settings allow. Production-only installation can also omit dev dependencies. I compare the install command, working directory, and lockfile, then reproduce with a clean installation.
What does npx playwright test --list establish?
It shows that the Playwright CLI can load its configuration and collect test files without starting a browser. That is stronger than checking for a package directory alone. It does not prove browser binaries, network access, or application behavior are ready.
How would you fix an editor-only TS2307 error?
I first confirm Node resolution and CLI collection succeed. Then I inspect which tsconfig owns the test, validate it with `tsc`, and ensure the editor opened the correct workspace. I restart the TypeScript server only after configuration and installation are sound.
What must a Dockerfile do when using an official Playwright image?
It must install the project's npm dependencies, preferably from its committed lockfile, because the browser image is not the test package installation. I keep host `node_modules` out of the build context and check resolution during the build. I also match the image tag to the project Playwright version.
Frequently Asked Questions
How do I fix Cannot find module '@playwright/test'?
In the package containing the tests, run `npm install -D @playwright/test`. Verify the result with `node -p "require.resolve('@playwright/test')"` and `npx playwright test --list`. If resolution still fails, check the package directory and workspace installation.
Why does TypeScript say it cannot find @playwright/test when tests run?
The editor may be using a TypeScript project that excludes the test file or has stale language-service state. Check the test tsconfig and open workspace, run `npx tsc --noEmit --project tsconfig.e2e.json` if that config exists, then restart the TypeScript server.
Does npx playwright install fix a missing @playwright/test module?
No. That command installs browser binaries needed after the JavaScript package loads. Install `@playwright/test` in the project first; use browser installation only for an executable or browser-dependency error.
Why is @playwright/test missing only in GitHub Actions?
A clean CI job may omit development dependencies, use the wrong workspace, or install from a lockfile that does not reflect the manifest. Run `npm ci --include=dev` in the test package and check resolution in the same job before running tests.
Does the Playwright Docker image include @playwright/test?
The browser image supplies browser and operating-system components, but your Docker build still needs to install the project's npm dependencies. Copy the manifest and lockfile, run `npm ci --include=dev`, and check module resolution inside the image.
Should I install playwright or @playwright/test for test and expect imports?
Declare `@playwright/test` for a Playwright Test suite that imports `test` and `expect` from that specifier. The `playwright` library serves a different use case; an import resolves by its exact package name.
How can I verify the fix without opening a browser?
Run `node -p "require.resolve('@playwright/test')"` to check package resolution, then `npx playwright test --list` to collect tests. A small test with no browser fixture can verify runner execution without downloading browsers.
Related Guides
- How to Debug a failing test in VS Code in Playwright (2026)
- How to Fix Playwright "Element is not attached to the DOM"
- How to Fix Playwright locator resolved to hidden element
- How to Fix Playwright Tests That Pass Locally but Fail in CI
- How to Fix Playwright waiting for element to be visible enabled and stable
- How to Fix Selenium "cannot find Chrome binary"