mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
d03c5aa045
* test(msw): introduce MSW infrastructure and migrate font-google-build
Replaces ad-hoc `globalThis.fetch` hijacking in tests with Mock Service
Worker. MSW intercepts both `globalThis.fetch` and `node:http`/`https`,
so it covers fetches issued by in-process Vite servers and fixture pages
without each test having to manage its own stub.
The setup file enables `onUnhandledRequest: 'error'` so any future test
that issues an unmocked external request fails loudly instead of
silently hitting the network. Loopback hostnames (localhost, 127.0.0.1,
::1) are bypassed because the integration project deliberately hits
in-process test servers via fetch.
What this PR contains:
- `tests/_msw/{handlers,server,setup}.ts` — shared infra
- `setupFiles` wired into both vitest projects (unit + integration)
- `msw` + `@mswjs/interceptors` added to the workspace catalog
- `msw` postinstall skipped via `allowBuilds` (it only copies a browser
service worker; we exclusively use `msw/node`)
- `tests/font-google-build.test.ts` migrated as the proof of pattern
Temporary handlers for `httpbin.org/uuid` and
`example.com/not-cacheable` keep the existing fixture pages
(`revalidate-tag-test`, `dynamic-error-fetch`) working under the new
guard. These are placeholders flagged with `TODO(msw-migration)` — a
follow-up will replace those live URLs with test-local endpoints.
This is PR 1 of 3:
- PR 2 will migrate `tests/fetch-cache.test.ts`
- PR 3 will migrate `tests/font-google.test.ts` and the font block in
`tests/app-router.test.ts`
* test(msw): restrict MSW to the unit project + use passthrough for loopback
Two fixes to the initial MSW wiring that surfaced when CI ran the full
suite:
1. The integration project is now excluded from MSW. Integration tests
spin up in-process HTTP servers and fixture dev servers and exercise
them via `fetch("http://127.0.0.1:<port>/...")`. Even with a loopback
`passthrough()` handler installed, the @mswjs/interceptors layer
interferes with that traffic in subtle ways:
- `tests/prerender.test.ts > errors without writing .rsc when the
middleware short-circuit fallback RSC request fails` timed out at
30s — the 5xx response body never reached the caller.
- `tests/ecosystem.test.ts > nuqs` hung in `beforeAll` waiting for
its fixture process to come up, because the readiness fetch
stalled inside the interceptor.
Integration tests already talk to real local servers, not the
internet, so the unhandled-request guard buys little there. The
value of MSW for this repo is mocking external HTTP for unit tests
of fetch wrappers (font-google, font-google-build, fetch-cache),
which is preserved.
2. The `onUnhandledRequest` callback in `setup.ts` that tried to
silently let loopback through wasn't sufficient — MSW's interceptor
still ran for those requests and could stall them. Replaced with a
default `http.all(LOOPBACK_URL_PATTERN, () => passthrough())`
handler in `handlers.ts` and a plain `onUnhandledRequest: "error"`
in `setup.ts`. Unit tests like the middleware-rewrite proxy in
`tests/shims.test.ts` and the JSX-in-JS suite need this to keep
their in-process HTTP servers reachable.
* test(msw): address BigBonk review notes
- Update PR description in the commit body to drop the stale mention of
TODO(msw-migration) handlers — they were removed when the integration
project was excluded from MSW; the fixture pages run there.
- Tighten the loopback IPv4 octet regex from `\d+` to `\d{1,3}` so it
doesn't match absurdly long octet strings. `URL.hostname` normalises
real addresses, so this is purely a readability fix.
* test(msw): address remaining BigBonk notes from the initial review
- Add `0.0.0.0` to the loopback passthrough pattern so test servers that
bind to the unspecified address keep working without per-test mocks.
- Add a `TODO(msw-3)` comment in `server.ts` flagging the
`SetupServerApi` deprecation in MSW 2.14.6 — the `FetchInterceptor`-only
construction will need to migrate to `defineNetwork`'s interceptor
config when we bump to MSW 3.
Both raised by BigBonk on the first review of this PR (the second review
focused on the stale description and the loose `\d+` octet bound, which
were addressed in the previous commit).
* test(msw): scope server.use() inside the try block in font-google-build
Minor structural fix from BigBonk's review: moves the `server.use()`
handler registration inside the `try` block so all setup and the
matching `fs.unlink` cleanup share the same try/finally scope.
No behaviour change — `server.use()` is synchronous and infallible, so
this is purely a structural tidy-up.
* test(msw): correct comment in font-google-build to match interceptor config
BigBonk caught that the comment described the default `setupServer()`
behaviour (which uses both `FetchInterceptor` and `ClientRequestInterceptor`),
but this project's `server.ts` deliberately constructs `SetupServerApi`
with only `FetchInterceptor`. Update the comment to reflect that — it
intercepts `globalThis.fetch`, not `node:http`/`node:https`.
No code change, comment only.
208 lines
7.8 KiB
TypeScript
208 lines
7.8 KiB
TypeScript
import path from "node:path";
|
|
import { defineConfig } from "vite-plus";
|
|
import { randomUUID } from "node:crypto";
|
|
|
|
const SHIMS_SRC = path.resolve(import.meta.dirname, "packages/vinext/src/shims");
|
|
const MSW_SETUP = path.resolve(import.meta.dirname, "tests/_msw/setup.ts");
|
|
|
|
export default defineConfig({
|
|
staged: {
|
|
"*": "vp check --fix",
|
|
},
|
|
fmt: {
|
|
printWidth: 100,
|
|
tabWidth: 2,
|
|
useTabs: false,
|
|
semi: true,
|
|
singleQuote: false,
|
|
trailingComma: "all",
|
|
ignorePatterns: ["tests/fixtures/ecosystem/**", "examples/**"],
|
|
},
|
|
lint: {
|
|
ignorePatterns: [
|
|
"fixtures/ecosystem/**",
|
|
"tests/fixtures/**",
|
|
"tests/fixtures/ecosystem/**",
|
|
"examples/**",
|
|
],
|
|
options: {
|
|
typeAware: true,
|
|
typeCheck: true,
|
|
denyWarnings: true,
|
|
},
|
|
plugins: ["typescript", "unicorn", "import", "react"],
|
|
jsPlugins: ["./oxlint-plugins/prefer-import-alias.js"],
|
|
rules: {
|
|
"@typescript-eslint/no-explicit-any": "error",
|
|
"typescript/consistent-type-definitions": ["error", "type"],
|
|
"@typescript-eslint/no-unsafe-function-type": "error",
|
|
"@typescript-eslint/no-unused-vars": "error",
|
|
"@typescript-eslint/no-redeclare": "error",
|
|
"@typescript-eslint/no-implied-eval": "error",
|
|
|
|
"unicorn/prefer-node-protocol": "error",
|
|
|
|
"import/first": "error",
|
|
"import/no-duplicates": "error",
|
|
|
|
"no-new-func": "error",
|
|
"no-implied-eval": "error",
|
|
"arrow-body-style": ["error", "as-needed"],
|
|
|
|
"react/exhaustive-deps": "error",
|
|
"react/no-array-index-key": "error",
|
|
"react/rules-of-hooks": "error",
|
|
"react/self-closing-comp": "error",
|
|
},
|
|
overrides: [
|
|
{
|
|
files: ["**.spec.ts", "**.test.ts"],
|
|
rules: {
|
|
"@typescript-eslint/no-explicit-any": "off",
|
|
"@typescript-eslint/no-unsafe-function-type": "off",
|
|
"typescript/no-misused-promises": "error",
|
|
"typescript/switch-exhaustiveness-check": "error",
|
|
"import/no-self-import": "error",
|
|
"unicorn/throw-new-error": "error",
|
|
"unicorn/error-message": "error",
|
|
},
|
|
},
|
|
{
|
|
files: ["packages/vinext/src/**/*.{ts,tsx}"],
|
|
rules: {
|
|
"typescript/no-misused-promises": "error",
|
|
"typescript/switch-exhaustiveness-check": "error",
|
|
"typescript/restrict-template-expressions": "error",
|
|
"import/no-self-import": "error",
|
|
"unicorn/throw-new-error": "error",
|
|
"unicorn/error-message": "error",
|
|
},
|
|
},
|
|
{
|
|
// Forces relative imports of own-package files inside vinext to use
|
|
// the tsconfig path alias (e.g. ../shims/X.js → vinext/shims/X).
|
|
// Originally added for #1001 — bare specifiers keep
|
|
// @vitejs/plugin-rsc's `packageSources` map populated, which avoids
|
|
// the broken absolute-fs-path proxy fallback.
|
|
files: ["packages/vinext/**"],
|
|
rules: {
|
|
"vinext-local/prefer-import-alias": "error",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
test: {
|
|
// GitHub Actions reporter adds inline failure annotations in PR diffs.
|
|
// Agent reporter suppresses passing test noise when running inside AI agents.
|
|
reporters: process.env.CI ? ["default", "github-actions"] : ["default", "agent"],
|
|
|
|
// Shared env for all projects.
|
|
env: {
|
|
// Mirrors the Vite `define` in index.ts that inlines a build-time UUID.
|
|
// Setting it here means tests exercise the same code path as production.
|
|
__VINEXT_DRAFT_SECRET: randomUUID(),
|
|
},
|
|
|
|
// Coverage activated only via --coverage flag (or CLI/config override).
|
|
// Istanbul provider is required because vinext source loads through Vite's
|
|
// module runner (helpers.ts:15 imports from packages/vinext/src directly,
|
|
// and integration tests start in-process Vite servers via createServer()).
|
|
// V8 coverage misses code under that runner.
|
|
coverage: {
|
|
provider: "istanbul",
|
|
include: ["packages/vinext/src/**/*.{ts,tsx}"],
|
|
exclude: ["**/*.d.ts", "**/*.test.ts", "**/*.spec.ts"],
|
|
reporter: ["text", "html", "json-summary", "lcov"],
|
|
reportsDirectory: "coverage",
|
|
clean: true,
|
|
skipFull: true,
|
|
// Always emit the report, even if some test files failed. A failing
|
|
// integration test (e.g. timing-sensitive teardown) shouldn't suppress
|
|
// coverage data we already collected.
|
|
reportOnFailure: true,
|
|
},
|
|
|
|
projects: [
|
|
{
|
|
resolve: {
|
|
alias: { "vinext/shims": SHIMS_SRC },
|
|
},
|
|
test: {
|
|
name: "unit",
|
|
setupFiles: [MSW_SETUP],
|
|
include: ["tests/**/*.test.ts"],
|
|
exclude: [
|
|
"tests/fixtures/**/node_modules/**",
|
|
// Integration tests: spin up Vite dev servers against shared fixture
|
|
// dirs. Must run serially to avoid Vite deps optimizer cache races
|
|
// (node_modules/.vite/*) that produce "outdated pre-bundle" 500s.
|
|
// When adding a test that calls startFixtureServer() or createServer(),
|
|
// move it here.
|
|
"tests/app-router.test.ts",
|
|
"tests/api-handler.test.ts",
|
|
"tests/cjs.test.ts",
|
|
"tests/ecosystem.test.ts",
|
|
"tests/entry-templates.test.ts",
|
|
"tests/features.test.ts",
|
|
"tests/image-optimization-parity.test.ts",
|
|
"tests/node-modules-css.test.ts",
|
|
"tests/pages-i18n-prod.test.ts",
|
|
"tests/pages-router-concurrency.test.ts",
|
|
"tests/pages-router.test.ts",
|
|
"tests/postcss-resolve.test.ts",
|
|
"tests/prerender.test.ts",
|
|
"tests/static-export.test.ts",
|
|
"tests/vite-hmr-websocket.test.ts",
|
|
"tests/nextjs-compat/**/*.test.ts",
|
|
// Flaky under parallelism due to 1 MiB buffer allocation pressure.
|
|
"tests/kv-cache-handler.test.ts",
|
|
],
|
|
},
|
|
},
|
|
{
|
|
resolve: {
|
|
alias: { "vinext/shims": SHIMS_SRC },
|
|
},
|
|
test: {
|
|
name: "integration",
|
|
// MSW is intentionally NOT installed in the integration project.
|
|
// Integration tests spin up in-process HTTP servers and fixture
|
|
// dev servers and exercise them via `fetch("http://127.0.0.1:<port>/...")`.
|
|
// The @mswjs/interceptors layer interferes with that loopback
|
|
// traffic in subtle ways even when handlers `passthrough()`
|
|
// (e.g. 5xx response bodies stall, fixture-startup readiness
|
|
// probes time out). MSW's value here is mocking external HTTP
|
|
// for unit tests of fetch wrappers — integration tests already
|
|
// talk to real local servers, not to the network, so the
|
|
// unhandled-request guard buys little. If a future integration
|
|
// test needs to mock an external fetch, wire MSW per-file with
|
|
// `setupServer` rather than reverting this exclusion.
|
|
include: [
|
|
"tests/app-router.test.ts",
|
|
"tests/api-handler.test.ts",
|
|
"tests/cjs.test.ts",
|
|
"tests/ecosystem.test.ts",
|
|
"tests/entry-templates.test.ts",
|
|
"tests/features.test.ts",
|
|
"tests/image-optimization-parity.test.ts",
|
|
"tests/kv-cache-handler.test.ts",
|
|
"tests/node-modules-css.test.ts",
|
|
"tests/pages-i18n-prod.test.ts",
|
|
"tests/pages-router-concurrency.test.ts",
|
|
"tests/pages-router.test.ts",
|
|
"tests/postcss-resolve.test.ts",
|
|
"tests/prerender.test.ts",
|
|
"tests/static-export.test.ts",
|
|
"tests/vite-hmr-websocket.test.ts",
|
|
"tests/nextjs-compat/**/*.test.ts",
|
|
],
|
|
testTimeout: 30000,
|
|
// Serial execution prevents Vite deps optimizer cache races when
|
|
// multiple test files share the same fixture directory.
|
|
fileParallelism: false,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|