mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
### What?
Every `pnpm test-*` / `pnpm testonly-*` script in the root `package.json` is now a single direct invocation of a new `scripts/run-jest.sh` helper instead of chaining through several `pnpm run` + `cross-env` layers.
### Why?
Each hop through `pnpm run` / `cross-env` spins up its own Node process and takes a few hundred milliseconds before anything useful happens. For example, `pnpm test-dev-turbo` used to walk through five layers:
```
test-dev-turbo
-> pnpm run with-turbo pnpm test-dev-inner (cross-env IS_TURBOPACK_TEST=1)
-> pnpm test-dev-inner (cross-env NEXT_TEST_MODE=dev)
-> pnpm testheadless (cross-env HEADLESS=true)
-> pnpm testonly
-> jest --runInBand
```
#### Measured overhead (same machine, same stubbed jest invocation — `jest --version`)
| Chain | Layers | Median (ms) | Range (ms) |
|---|---|---|---|
| `jest --version` (baseline, direct) | 1 | 80 | 75–89 |
| `pnpm test-dev-turbo --version` (**new**) | `pnpm → bash → jest` | 448 | 435–493 |
| `pnpm test-dev-turbo --version` (**old**) | `pnpm → cross-env → pnpm → cross-env → pnpm → cross-env → pnpm → jest` | 2494 | 2462–2512 |
That's **~2 seconds per test invocation** shaved off, and ~85% reduction in wrapper overhead (from ~2414 ms to ~368 ms). The remaining ~368 ms is essentially just one `pnpm run` startup, which is unavoidable while we keep the `pnpm test-*` entry points.
Real-world impact: for a ~72 s e2e test run this is ~3%, but for fast unit-test files (often <5 s) this is closer to 30-40%, and it adds up quickly in CI jobs that shell out to these scripts in a loop.
### How?
- New `scripts/run-jest.sh` parses named flags and `exec`s jest directly:
- `--mode=<dev|start|deploy>` → sets `NEXT_TEST_MODE`
- `--bundler=<webpack|turbo|rspack>` → sets `IS_WEBPACK_TEST=1` / `IS_TURBOPACK_TEST=1` / `NEXT_RSPACK=1 NEXT_TEST_USE_RSPACK=1`
- `--experimental` → sets `__NEXT_CACHE_COMPONENTS=true __NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER=true`
- `--headless` → sets `HEADLESS=true`
- `--` terminates flag parsing; everything after is forwarded verbatim to jest so existing workflows like `pnpm test-dev-turbo path/to/foo.test.ts -t "pattern"` keep working.
- Every `test-*` / `testonly-*` entry in `package.json` (including `testheadless`) now reads like `scripts/run-jest.sh --mode=dev --bundler=turbo --headless --`. No more `pnpm run` / `cross-env` indirection at the top level.
- Removed nine unused internal intermediates (`test-dev-inner`, `test-dev-experimental-inner`, `test-start-inner`, `test-start-experimental-inner`, `test-deploy-inner`, `testonly-dev-inner`, `testonly-start-inner`, `testonly-deploy-inner`, `test-inner`). These were only called by other package.json scripts and not referenced anywhere else in the repo.
- Kept `testonly`, `testheadless`, and all public `test-*` / `testonly-*` names so `AGENTS.md`, `contributing/core/testing.md`, `.conductor/`, and `.agents/skills/` work unchanged.
- The `with-webpack`, `with-turbo`, `with-rspack`, and `with-experimental` prefix scripts remain for ad-hoc use; they are just no longer in the hot path of the test scripts.
- `scripts/run-jest.sh` invokes a bare `jest`, which resolves via `$PATH` (pnpm prepends `node_modules/.bin/` when running a package script). The helper is documented as only supported when invoked through a package runner.
### Verification
Ran a small e2e test through the new chain in both dev+turbopack and start+webpack modes. Output confirms env vars propagate correctly (`PASS Turbopack ...` / `PASS webpack ...`, plus `pnpm next --turbopack` vs `pnpm next start` in the logs):
```
pnpm test-dev-turbo test/e2e/app-dir/_allow-underscored-root-directory/...
-> PASS Turbopack ... 3 passed
pnpm test-start-webpack test/e2e/app-dir/_allow-underscored-root-directory/...
-> PASS webpack ... 3 passed
```
CI is unaffected: every workflow invokes `node run-tests.js ...` directly, never through these pnpm scripts.
<!-- NEXT_JS_LLM_PR -->
64 lines
1.5 KiB
Bash
Executable File
64 lines
1.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Set up environment variables for a Next.js jest test run and exec jest
|
|
# in a single hop, replacing this shell process.
|
|
#
|
|
# Usage:
|
|
# scripts/run-jest.sh \
|
|
# [--mode=<dev|start|deploy>] \
|
|
# [--bundler=<webpack|turbo|rspack>] \
|
|
# [--experimental] \
|
|
# [--headless] \
|
|
# -- [jest args...]
|
|
#
|
|
# All arguments after `--` are forwarded verbatim to jest.
|
|
|
|
set -eo pipefail
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--mode=dev|--mode=start|--mode=deploy)
|
|
export NEXT_TEST_MODE="${1#--mode=}"
|
|
;;
|
|
--mode=*)
|
|
echo "run-jest.sh: unknown mode: ${1#--mode=}" >&2
|
|
exit 1
|
|
;;
|
|
--bundler=webpack)
|
|
export IS_WEBPACK_TEST=1
|
|
;;
|
|
--bundler=turbo)
|
|
export IS_TURBOPACK_TEST=1
|
|
;;
|
|
--bundler=rspack)
|
|
export NEXT_RSPACK=1
|
|
export NEXT_TEST_USE_RSPACK=1
|
|
;;
|
|
--bundler=*)
|
|
echo "run-jest.sh: unknown bundler: ${1#--bundler=}" >&2
|
|
exit 1
|
|
;;
|
|
--experimental)
|
|
export __NEXT_CACHE_COMPONENTS=true
|
|
export __NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER=true
|
|
;;
|
|
--headless)
|
|
export HEADLESS=true
|
|
;;
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
*)
|
|
echo "run-jest.sh: unknown argument: $1" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# Resolves to `node_modules/.bin/jest` via `$PATH`. This relies on being
|
|
# invoked through pnpm (or another package runner), which prepends the
|
|
# workspace's `node_modules/.bin/` to `$PATH` before running the script.
|
|
exec jest --runInBand "$@"
|