Skip to content

test: share React Start basic mode e2e suite#7060

Merged
beaussan merged 11 commits intoTanStack:mainfrom
beaussan:test/shared-react-start-e2e-project
Mar 28, 2026
Merged

test: share React Start basic mode e2e suite#7060
beaussan merged 11 commits intoTanStack:mainfrom
beaussan:test/shared-react-start-e2e-project

Conversation

@beaussan
Copy link
Copy Markdown
Contributor

@beaussan beaussan commented Mar 27, 2026

Summary

  • split the React Start, Vue Start, and Solid Start basic e2e suites into shared test-suite projects plus focused SSR, SPA, prerender, and preview harnesses
  • replace the previous single-project multi-mode setup with mode-specific Nx targets so CI can schedule and cache them independently while still reusing the same shared Playwright coverage
  • keep the shared suites centralized so agents and Nx can reason about real project boundaries instead of symlinked or overloaded test layouts

Why

  • this change is primarily about CI speed and cache efficiency
  • before this split, the basic Start e2e coverage ran as a few large, mode-switching projects that were slower to execute and harder for Nx agents to distribute effectively
  • after splitting the suites into smaller mode-specific targets, Nx can parallelize and cache the work much more efficiently

Impact

  • end-to-end CI time dropped from about 16 minutes to about 9 minutes
  • agent utilization improved from about 35% to about 94%
  • the result is faster feedback, better cache reuse, and more efficient agent scheduling without reducing test coverage

Testing

  • CI=1 NX_DAEMON=false pnpm nx run-many --target=test:e2e --projects=tanstack-react-start-e2e-basic,tanstack-react-start-e2e-basic-spa,tanstack-react-start-e2e-basic-prerender,tanstack-react-start-e2e-basic-preview,tanstack-vue-start-e2e-basic,tanstack-vue-start-e2e-basic-spa,tanstack-vue-start-e2e-basic-prerender,tanstack-vue-start-e2e-basic-preview,tanstack-solid-start-e2e-basic,tanstack-solid-start-e2e-basic-spa,tanstack-solid-start-e2e-basic-prerender,tanstack-solid-start-e2e-basic-preview --outputStyle=stream --skipRemoteCache

Summary by CodeRabbit

  • New Features

    • Added new E2E suites for SPA, Prerender, and Preview across React, Solid, and Vue.
    • Introduced shared test-suite utilities for consistent server lifecycle and package-name based discovery.
  • Tests

    • New Playwright configurations with deterministic port handling, single-worker runs, global setup/teardown, and readiness polling to improve reliability.
    • Consolidated mode handling and simplified startup/build/test sequences.
  • Chores

    • Updated CI orchestration to include the new E2E variants and added ignore files for test artifacts.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 27, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e1e54eaf-143d-4c4f-b618-feba9cab15dd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Centralizes E2E test setup into shared basic-test-suite and adds per-framework E2E packages and Playwright configs (SPA/preview/prerender) for React, Solid, and Vue; migrates per-package lifecycle scripts to the shared suite and updates CI/nx entries.

Changes

Cohort / File(s) Summary
Shared test-suite
e2e/*-start/basic-test-suite/.gitignore, e2e/*-start/basic-test-suite/package.json, e2e/*-start/basic-test-suite/tsconfig.json, e2e/*-start/basic-test-suite/src/setup/*, e2e/*-start/basic-test-suite/src/utils/*
Added centralized test suite: global setup/teardown, waitForDummyServer, getPackageName, getBasicAppRoot, mode flags (isSpaMode/isPreview/isPrerender), tsconfig and package manifest.
React E2E packages
e2e/react-start/basic-{spa,prerender,preview}/.gitignore, e2e/react-start/basic-{spa,prerender,preview}/package.json, e2e/react-start/basic-{spa,prerender,preview}/playwright.config.ts, e2e/react-start/basic/package.json, e2e/react-start/basic/playwright.config.ts
Added React SPA/preview/prerender E2E packages and Playwright configs; moved lifecycle scripts to shared suite; simplified existing basic package scripts/config to use shared utilities and common port naming.
Solid E2E packages
e2e/solid-start/basic-{spa,prerender,preview}/.gitignore, e2e/solid-start/basic-{spa,prerender,preview}/package.json, e2e/solid-start/basic-{spa,prerender,preview}/playwright.config.ts, e2e/solid-start/basic/package.json, e2e/solid-start/basic/playwright.config.ts
Added Solid SPA/preview/prerender E2E packages and Playwright configs mirroring React changes; migrated lifecycle scripts to shared suite and simplified basic package/config.
Vue E2E packages
e2e/vue-start/basic-{spa,prerender,preview}/.gitignore, e2e/vue-start/basic-{spa,prerender,preview}/package.json, e2e/vue-start/basic-{spa,prerender,preview}/playwright.config.ts, e2e/vue-start/basic/package.json, e2e/vue-start/basic/playwright.config.ts
Added Vue SPA/preview/prerender E2E packages and Playwright configs with dynamic ports and shared-suite wiring; updated basic package scripts to use the suite.
Playwright configs / webServer changes
e2e/*-start/*/playwright.config.ts
New Playwright configs compute dynamic ports, set testDir to shared suite, use shared globalTeardown, and orchestrate chained pnpm commands (start dummy server → build → start) with environment variables for MODE and ports.
Test specs updates
e2e/*-start/basic-test-suite/src/*.spec.ts
Adjusted imports and path resolution: not-found.spec.ts import paths updated; redirect.spec.ts uses getPackageName(); prerender specs use getBasicAppRoot() for locating built dist.
Per-package start/stop scripts
e2e/*-start/*/package.json
Added npm scripts to start/stop dummy server via dynamic ESM imports into the shared suite and test:e2e scripts that set MODE and run Playwright; Nx test:e2e targets set parallelism: false.
Root tooling / CI
package.json, nx.json
Expanded CI test script to include new E2E projects; added nx.json top-level bust entry.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant PW as Playwright
    participant PN as pnpm
    participant DS as Dummy Server
    participant APP as App Server

    PW->>PN: start webServer (chained pnpm commands)
    PN->>DS: pnpm run test:e2e:startDummyServer
    DS-->>PN: dummy server ready (port file)
    PN->>PN: pnpm build (prerender/preview/spa)
    PN->>DS: pnpm run test:e2e:stopDummyServer
    DS-->>PN: dummy stopped
    PN->>APP: pnpm start (app server on PORT)
    APP-->>PN: app ready
    PW->>APP: run tests against baseURL
    PW->>PN: invoke globalTeardown -> pnpm run test:e2e:stopDummyServer
    PN->>DS: stop dummy server
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • schiller-manuel
  • birkskyum

Poem

🐰
I hopped to stitch the tests and ports,
Shared setup hums in tidy sorts.
Servers start, builds march in line,
Playwright sips the baseURL fine.
A carrot patch — small, yet bright!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'test: share React Start basic mode e2e suite' clearly and accurately summarizes the main change: moving from a single multi-mode test suite to a shared test-suite project structure across React Start, Vue Start, and Solid Start e2e tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud bot commented Mar 27, 2026

View your CI Pipeline Execution ↗ for commit 95ae7e1

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 1m 55s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 2s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-28 10:29:11 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 27, 2026

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/@tanstack/arktype-adapter@7060

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/@tanstack/eslint-plugin-router@7060

@tanstack/history

npm i https://pkg.pr.new/@tanstack/history@7060

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/@tanstack/nitro-v2-vite-plugin@7060

@tanstack/react-router

npm i https://pkg.pr.new/@tanstack/react-router@7060

@tanstack/react-router-devtools

npm i https://pkg.pr.new/@tanstack/react-router-devtools@7060

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/@tanstack/react-router-ssr-query@7060

@tanstack/react-start

npm i https://pkg.pr.new/@tanstack/react-start@7060

@tanstack/react-start-client

npm i https://pkg.pr.new/@tanstack/react-start-client@7060

@tanstack/react-start-server

npm i https://pkg.pr.new/@tanstack/react-start-server@7060

@tanstack/router-cli

npm i https://pkg.pr.new/@tanstack/router-cli@7060

@tanstack/router-core

npm i https://pkg.pr.new/@tanstack/router-core@7060

@tanstack/router-devtools

npm i https://pkg.pr.new/@tanstack/router-devtools@7060

@tanstack/router-devtools-core

npm i https://pkg.pr.new/@tanstack/router-devtools-core@7060

@tanstack/router-generator

npm i https://pkg.pr.new/@tanstack/router-generator@7060

@tanstack/router-plugin

npm i https://pkg.pr.new/@tanstack/router-plugin@7060

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/@tanstack/router-ssr-query-core@7060

@tanstack/router-utils

npm i https://pkg.pr.new/@tanstack/router-utils@7060

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/@tanstack/router-vite-plugin@7060

@tanstack/solid-router

npm i https://pkg.pr.new/@tanstack/solid-router@7060

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/@tanstack/solid-router-devtools@7060

@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/@tanstack/solid-router-ssr-query@7060

@tanstack/solid-start

npm i https://pkg.pr.new/@tanstack/solid-start@7060

@tanstack/solid-start-client

npm i https://pkg.pr.new/@tanstack/solid-start-client@7060

@tanstack/solid-start-server

npm i https://pkg.pr.new/@tanstack/solid-start-server@7060

@tanstack/start-client-core

npm i https://pkg.pr.new/@tanstack/start-client-core@7060

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/@tanstack/start-fn-stubs@7060

@tanstack/start-plugin-core

npm i https://pkg.pr.new/@tanstack/start-plugin-core@7060

@tanstack/start-server-core

npm i https://pkg.pr.new/@tanstack/start-server-core@7060

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/@tanstack/start-static-server-functions@7060

@tanstack/start-storage-context

npm i https://pkg.pr.new/@tanstack/start-storage-context@7060

@tanstack/valibot-adapter

npm i https://pkg.pr.new/@tanstack/valibot-adapter@7060

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/@tanstack/virtual-file-routes@7060

@tanstack/vue-router

npm i https://pkg.pr.new/@tanstack/vue-router@7060

@tanstack/vue-router-devtools

npm i https://pkg.pr.new/@tanstack/vue-router-devtools@7060

@tanstack/vue-router-ssr-query

npm i https://pkg.pr.new/@tanstack/vue-router-ssr-query@7060

@tanstack/vue-start

npm i https://pkg.pr.new/@tanstack/vue-start@7060

@tanstack/vue-start-client

npm i https://pkg.pr.new/@tanstack/vue-start-client@7060

@tanstack/vue-start-server

npm i https://pkg.pr.new/@tanstack/vue-start-server@7060

@tanstack/zod-adapter

npm i https://pkg.pr.new/@tanstack/zod-adapter@7060

commit: 95ae7e1

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/react-start/basic-prerender/playwright.config.ts`:
- Line 10: EXTERNAL_PORT is being allocated in this workspace with
getDummyServerPort but the dummy server is started/stopped in ../basic, causing
a port mismatch; update the prerender dummy-server lifecycle so the same
workspace manages both port allocation and server scripts: keep using
EXTERNAL_PORT from getDummyServerPort and change any start/stop invocations that
use "pnpm --dir ../basic ..." (or equivalent commands referenced in the
Playwright config) to run against this workspace (e.g., remove or replace the
../basic dir flag so the scripts resolve package/port-*.txt here), and ensure
VITE_EXTERNAL_PORT is set to EXTERNAL_PORT when launching the dummy server and
when running the prerender build.

In `@e2e/react-start/basic-test-suite/package.json`:
- Line 8: Replace the internal dependency version for
"@tanstack/router-e2e-utils" in package.json from the incorrect protocol
"workspace:^" to the repository-approved "workspace:*"; update the string value
for the dependency entry (the "@tanstack/router-e2e-utils" key) and scan other
internal dependencies in the same file to ensure they also use "workspace:*" to
follow the workspace protocol policy.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 82274e2f-5513-4711-952d-363089c318d4

📥 Commits

Reviewing files that changed from the base of the PR and between 21bd992 and 348dee4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (37)
  • e2e/react-start/basic-prerender/.gitignore
  • e2e/react-start/basic-prerender/package.json
  • e2e/react-start/basic-prerender/playwright.config.ts
  • e2e/react-start/basic-preview/.gitignore
  • e2e/react-start/basic-preview/package.json
  • e2e/react-start/basic-preview/playwright.config.ts
  • e2e/react-start/basic-spa/.gitignore
  • e2e/react-start/basic-spa/package.json
  • e2e/react-start/basic-spa/playwright.config.ts
  • e2e/react-start/basic-test-suite/.gitignore
  • e2e/react-start/basic-test-suite/package.json
  • e2e/react-start/basic-test-suite/src/client-only.spec.ts
  • e2e/react-start/basic-test-suite/src/navigation.spec.ts
  • e2e/react-start/basic-test-suite/src/not-found.spec.ts
  • e2e/react-start/basic-test-suite/src/open-redirect-prevention.spec.ts
  • e2e/react-start/basic-test-suite/src/prerendering.spec.ts
  • e2e/react-start/basic-test-suite/src/raw-stream.spec.ts
  • e2e/react-start/basic-test-suite/src/redirect.spec.ts
  • e2e/react-start/basic-test-suite/src/root-scripts.spec.ts
  • e2e/react-start/basic-test-suite/src/script-duplication.spec.ts
  • e2e/react-start/basic-test-suite/src/search-params.spec.ts
  • e2e/react-start/basic-test-suite/src/setup/global.setup.ts
  • e2e/react-start/basic-test-suite/src/setup/global.teardown.ts
  • e2e/react-start/basic-test-suite/src/setup/waitForDummyServer.ts
  • e2e/react-start/basic-test-suite/src/special-characters.spec.ts
  • e2e/react-start/basic-test-suite/src/streaming.spec.ts
  • e2e/react-start/basic-test-suite/src/type-only-reexport.spec.ts
  • e2e/react-start/basic-test-suite/src/utils/getPackageName.ts
  • e2e/react-start/basic-test-suite/src/utils/isPrerender.ts
  • e2e/react-start/basic-test-suite/src/utils/isPreview.ts
  • e2e/react-start/basic-test-suite/src/utils/isSpaMode.ts
  • e2e/react-start/basic-test-suite/tsconfig.json
  • e2e/react-start/basic/package.json
  • e2e/react-start/basic/playwright.config.ts
  • e2e/react-start/basic/tests/setup/global.setup.ts
  • e2e/react-start/basic/tests/setup/global.teardown.ts
  • package.json
💤 Files with no reviewable changes (2)
  • e2e/react-start/basic/tests/setup/global.teardown.ts
  • e2e/react-start/basic/tests/setup/global.setup.ts


const PORT = await getTestServerPort(packageJson.name)
const START_PORT = await getTestServerPort(`${packageJson.name}_start`)
const EXTERNAL_PORT = await getDummyServerPort(packageJson.name)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Keep the prerender dummy-server lifecycle in this workspace.

Line 10 allocates EXTERNAL_PORT from tanstack-react-start-e2e-basic-prerender, but Line 27 starts/stops the dummy server via pnpm --dir ../basic .... Those scripts resolve their package/port-*.txt state from ../basic, so the dummy server can bind a different port than the one injected as VITE_EXTERNAL_PORT. The prerender build then talks to a port that no dummy server is serving.

🐛 Suggested fix
   webServer: {
     command:
-      'pnpm --dir ../basic run test:e2e:startDummyServer && pnpm --dir ../basic build:prerender && pnpm --dir ../basic run test:e2e:stopDummyServer && pnpm --dir ../basic start',
+      'node -e \'import("../basic-test-suite/src/setup/global.setup.ts").then((m) => m.default())\' & node -e \'import("../basic-test-suite/src/setup/waitForDummyServer.ts").then((m) => m.default())\' && pnpm --dir ../basic build:prerender && node -e \'import("../basic-test-suite/src/setup/global.teardown.ts").then((m) => m.default())\' && pnpm --dir ../basic start',

Also applies to: 25-37

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/react-start/basic-prerender/playwright.config.ts` at line 10,
EXTERNAL_PORT is being allocated in this workspace with getDummyServerPort but
the dummy server is started/stopped in ../basic, causing a port mismatch; update
the prerender dummy-server lifecycle so the same workspace manages both port
allocation and server scripts: keep using EXTERNAL_PORT from getDummyServerPort
and change any start/stop invocations that use "pnpm --dir ../basic ..." (or
equivalent commands referenced in the Playwright config) to run against this
workspace (e.g., remove or replace the ../basic dir flag so the scripts resolve
package/port-*.txt here), and ensure VITE_EXTERNAL_PORT is set to EXTERNAL_PORT
when launching the dummy server and when running the prerender build.

@beaussan beaussan marked this pull request as draft March 27, 2026 07:56
@beaussan beaussan marked this pull request as ready for review March 27, 2026 12:44
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
e2e/solid-start/basic-test-suite/src/setup/global.teardown.ts (1)

4-8: Teardown silently swallows errors.

The empty catch {} prevents teardown failures from masking test results, which is reasonable. However, for debugging purposes, consider logging the error at a low level.

💡 Optional: Add debug logging for teardown failures
 export default async function teardown() {
   try {
     await e2eStopDummyServer(getPackageName())
-  } catch {}
+  } catch (error) {
+    // Swallow error to avoid masking test failures
+    // Log for debugging if CI debug mode is enabled
+    if (process.env.DEBUG) {
+      console.debug('Teardown: Failed to stop dummy server:', error)
+    }
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/solid-start/basic-test-suite/src/setup/global.teardown.ts` around lines 4
- 8, The teardown function currently swallows errors with an empty catch which
hides useful debug info; update the catch in the teardown() function to capture
the caught error (e.g., catch (err)) and log it at a low level using
console.debug (or the project's test logger) with context including
getPackageName() and e2eStopDummyServer so teardown failures are visible for
debugging without failing tests.
e2e/vue-start/basic-spa/package.json (1)

12-15: Use workspace:* for the internal utils package.

Line 13 is the only internal dependency here using workspace:^, so it drifts from the repo's package.json convention.

♻️ Suggested change
-    "@tanstack/router-e2e-utils": "workspace:^",
+    "@tanstack/router-e2e-utils": "workspace:*",
Based on learnings, Use workspace protocol for internal dependencies (workspace:*).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/vue-start/basic-spa/package.json` around lines 12 - 15, Replace the
inconsistent internal dependency version for "@tanstack/router-e2e-utils" which
currently uses "workspace:^" by changing it to use the workspace protocol
"workspace:*" so it matches the repo convention; update the package entry for
"@tanstack/router-e2e-utils" in package.json to use "workspace:*" alongside the
other workspace dependencies.
e2e/solid-start/basic-spa/playwright.config.ts (1)

1-48: Consider a tiny shared config factory for these mode harnesses.

This file repeats the same port calculation, testDir, teardown, project, and env boilerplate as the sibling split configs, with only MODE and webServer.command changing. Centralizing that would make future harness tweaks less drift-prone.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/solid-start/basic-spa/playwright.config.ts` around lines 1 - 48, Extract
the repeated setup into a small shared factory function (e.g., createE2EConfig)
that computes PORT, START_PORT, EXTERNAL_PORT and baseURL once and returns the
object passed to defineConfig; update this file to call that factory with the
differing pieces (MODE value and webServer.command) instead of duplicating
testDir, globalTeardown, projects, env boilerplate and port calculations; ensure
the factory exposes parameters for mode and command and still sets the env keys
(MODE, VITE_NODE_ENV, VITE_EXTERNAL_PORT, VITE_SERVER_PORT, START_PORT, PORT)
and returns a value compatible with defineConfig so you can replace the current
default export with defineConfig(createE2EConfig({ mode: 'spa', command: 'pnpm
run test:e2e:startDummyServer && pnpm --dir ../basic build:spa && pnpm --dir
../basic start' })).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/react-start/basic-prerender/playwright.config.ts`:
- Line 18: The teardown is reading package name at runtime via getPackageName()
which uses process.cwd(), causing a mismatch with the static packageJson.name
used when starting the dummy server; change the config so the teardown receives
the correct package name from the config rather than reading process.cwd():
update globalTeardown usage in playwright.config.ts (symbol: globalTeardown) to
pass the expected package name (packageJson.name from the basic-prerender
package) or modify getPackageName() calls in the teardown implementation
(symbol: getPackageName) to accept and use a baseDir argument and call it with
the basic-prerender directory so the teardown stops the server created with that
package name.

In `@e2e/solid-start/basic-prerender/playwright.config.ts`:
- Around line 24-29: The Playwright config currently allows skipping
webServer.command via reuseExistingServer (variable reuseExistingServer) which
can bypass starting the dummy server included in webServer.command; change
reuseExistingServer to false (replace reuseExistingServer: !process.env.CI with
reuseExistingServer: false) so the command always runs, or alternatively remove
dummy-server startup from webServer.command and instead start the dummy server
unconditionally in a test lifecycle hook (e.g., a global setup/beforeAll) so
tests always have the external fixture available; reference webServer.command,
reuseExistingServer, and baseURL when making the change.

In `@e2e/solid-start/basic-test-suite/src/setup/waitForDummyServer.ts`:
- Around line 11-20: The loop in waitForDummyServer can hang because each fetch
can block past the overall deadline; update the probe to use an AbortSignal
timeout per fetch (using AbortSignal.timeout()) so each attempt respects the
remaining time budget: compute remaining = deadline - Date.now(), if remaining
<= 0 throw/return failure, then call fetch(`http://localhost:${port}/`, {
signal: AbortSignal.timeout(remaining) }) and handle the abort/timeout error
appropriately before awaiting the retry sleep; reference the waitForDummyServer
loop and the variables port, deadline, and retryIntervalMs when making the
change.

In `@e2e/vue-start/basic-spa/package.json`:
- Around line 7-8: The npm scripts test:e2e:startDummyServer and
test:e2e:stopDummyServer invoke TypeScript files directly via node -e
'import(...)' which will fail at runtime; update these scripts to either (a) use
a TS runtime loader (e.g., prefix the node commands to run via tsx or ts-node)
or (b) set NODE_OPTIONS=--experimental-strip-types before the node -e invocation
so Node 24.8.0 can strip types, ensuring the imports of
../basic-test-suite/src/setup/global.setup.ts, waitForDummyServer.ts and
global.teardown.ts execute correctly.

In `@e2e/vue-start/basic/playwright.config.ts`:
- Around line 19-28: The webServer.command currently includes starting the dummy
server (`pnpm run test:e2e:startDummyServer`) which is skipped when Playwright
detects an existing server due to reuseExistingServer; move dummy-server startup
into an always-run Playwright setup hook instead: create or update globalSetup
(e.g., function named globalSetup) to invoke `pnpm run
test:e2e:startDummyServer` before tests, remove the dummy-server part from
webServer.command (keep only the app server start using ssrModeCommand and
baseURL), and ensure globalSetup is referenced in playwright.config.ts so the
dummy server is reliably started on every run regardless of
webServer.reuseExistingServer.

---

Nitpick comments:
In `@e2e/solid-start/basic-spa/playwright.config.ts`:
- Around line 1-48: Extract the repeated setup into a small shared factory
function (e.g., createE2EConfig) that computes PORT, START_PORT, EXTERNAL_PORT
and baseURL once and returns the object passed to defineConfig; update this file
to call that factory with the differing pieces (MODE value and
webServer.command) instead of duplicating testDir, globalTeardown, projects, env
boilerplate and port calculations; ensure the factory exposes parameters for
mode and command and still sets the env keys (MODE, VITE_NODE_ENV,
VITE_EXTERNAL_PORT, VITE_SERVER_PORT, START_PORT, PORT) and returns a value
compatible with defineConfig so you can replace the current default export with
defineConfig(createE2EConfig({ mode: 'spa', command: 'pnpm run
test:e2e:startDummyServer && pnpm --dir ../basic build:spa && pnpm --dir
../basic start' })).

In `@e2e/solid-start/basic-test-suite/src/setup/global.teardown.ts`:
- Around line 4-8: The teardown function currently swallows errors with an empty
catch which hides useful debug info; update the catch in the teardown() function
to capture the caught error (e.g., catch (err)) and log it at a low level using
console.debug (or the project's test logger) with context including
getPackageName() and e2eStopDummyServer so teardown failures are visible for
debugging without failing tests.

In `@e2e/vue-start/basic-spa/package.json`:
- Around line 12-15: Replace the inconsistent internal dependency version for
"@tanstack/router-e2e-utils" which currently uses "workspace:^" by changing it
to use the workspace protocol "workspace:*" so it matches the repo convention;
update the package entry for "@tanstack/router-e2e-utils" in package.json to use
"workspace:*" alongside the other workspace dependencies.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 621ff90d-7266-459d-b911-36a30b6e72e0

📥 Commits

Reviewing files that changed from the base of the PR and between 348dee4 and d9e233f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (78)
  • e2e/react-start/basic-prerender/package.json
  • e2e/react-start/basic-prerender/playwright.config.ts
  • e2e/react-start/basic-preview/package.json
  • e2e/react-start/basic-preview/playwright.config.ts
  • e2e/react-start/basic-spa/package.json
  • e2e/react-start/basic-spa/playwright.config.ts
  • e2e/react-start/basic-test-suite/src/prerendering.spec.ts
  • e2e/react-start/basic-test-suite/src/setup/global.teardown.ts
  • e2e/react-start/basic-test-suite/src/utils/getBasicAppRoot.ts
  • e2e/react-start/basic/package.json
  • e2e/react-start/basic/playwright.config.ts
  • e2e/solid-start/basic-prerender/.gitignore
  • e2e/solid-start/basic-prerender/package.json
  • e2e/solid-start/basic-prerender/playwright.config.ts
  • e2e/solid-start/basic-preview/.gitignore
  • e2e/solid-start/basic-preview/package.json
  • e2e/solid-start/basic-preview/playwright.config.ts
  • e2e/solid-start/basic-spa/.gitignore
  • e2e/solid-start/basic-spa/package.json
  • e2e/solid-start/basic-spa/playwright.config.ts
  • e2e/solid-start/basic-test-suite/.gitignore
  • e2e/solid-start/basic-test-suite/package.json
  • e2e/solid-start/basic-test-suite/src/navigation.spec.ts
  • e2e/solid-start/basic-test-suite/src/not-found.spec.ts
  • e2e/solid-start/basic-test-suite/src/prerendering.spec.ts
  • e2e/solid-start/basic-test-suite/src/redirect.spec.ts
  • e2e/solid-start/basic-test-suite/src/script-duplication.spec.ts
  • e2e/solid-start/basic-test-suite/src/search-params.spec.ts
  • e2e/solid-start/basic-test-suite/src/setup/global.setup.ts
  • e2e/solid-start/basic-test-suite/src/setup/global.teardown.ts
  • e2e/solid-start/basic-test-suite/src/setup/waitForDummyServer.ts
  • e2e/solid-start/basic-test-suite/src/special-characters.spec.ts
  • e2e/solid-start/basic-test-suite/src/streaming.spec.ts
  • e2e/solid-start/basic-test-suite/src/transition.spec.ts
  • e2e/solid-start/basic-test-suite/src/utils/getBasicAppRoot.ts
  • e2e/solid-start/basic-test-suite/src/utils/getPackageName.ts
  • e2e/solid-start/basic-test-suite/src/utils/isPrerender.ts
  • e2e/solid-start/basic-test-suite/src/utils/isPreview.ts
  • e2e/solid-start/basic-test-suite/src/utils/isSpaMode.ts
  • e2e/solid-start/basic-test-suite/tsconfig.json
  • e2e/solid-start/basic/package.json
  • e2e/solid-start/basic/playwright.config.ts
  • e2e/solid-start/basic/tests/setup/global.setup.ts
  • e2e/solid-start/basic/tests/setup/global.teardown.ts
  • e2e/vue-start/basic-prerender/.gitignore
  • e2e/vue-start/basic-prerender/package.json
  • e2e/vue-start/basic-prerender/playwright.config.ts
  • e2e/vue-start/basic-preview/.gitignore
  • e2e/vue-start/basic-preview/package.json
  • e2e/vue-start/basic-preview/playwright.config.ts
  • e2e/vue-start/basic-spa/.gitignore
  • e2e/vue-start/basic-spa/package.json
  • e2e/vue-start/basic-spa/playwright.config.ts
  • e2e/vue-start/basic-test-suite/.gitignore
  • e2e/vue-start/basic-test-suite/package.json
  • e2e/vue-start/basic-test-suite/src/navigation.spec.ts
  • e2e/vue-start/basic-test-suite/src/not-found.spec.ts
  • e2e/vue-start/basic-test-suite/src/prerendering.spec.ts
  • e2e/vue-start/basic-test-suite/src/redirect.spec.ts
  • e2e/vue-start/basic-test-suite/src/script-duplication.spec.ts
  • e2e/vue-start/basic-test-suite/src/search-params.spec.ts
  • e2e/vue-start/basic-test-suite/src/setup/global.setup.ts
  • e2e/vue-start/basic-test-suite/src/setup/global.teardown.ts
  • e2e/vue-start/basic-test-suite/src/setup/waitForDummyServer.ts
  • e2e/vue-start/basic-test-suite/src/special-characters.spec.ts
  • e2e/vue-start/basic-test-suite/src/streaming.spec.ts
  • e2e/vue-start/basic-test-suite/src/utils/getBasicAppRoot.ts
  • e2e/vue-start/basic-test-suite/src/utils/getPackageName.ts
  • e2e/vue-start/basic-test-suite/src/utils/isPrerender.ts
  • e2e/vue-start/basic-test-suite/src/utils/isPreview.ts
  • e2e/vue-start/basic-test-suite/src/utils/isSpaMode.ts
  • e2e/vue-start/basic-test-suite/tsconfig.json
  • e2e/vue-start/basic/package.json
  • e2e/vue-start/basic/playwright.config.ts
  • e2e/vue-start/basic/tests/setup/global.setup.ts
  • e2e/vue-start/basic/tests/setup/global.teardown.ts
  • nx.json
  • package.json
💤 Files with no reviewable changes (4)
  • e2e/solid-start/basic/tests/setup/global.teardown.ts
  • e2e/solid-start/basic/tests/setup/global.setup.ts
  • e2e/vue-start/basic/tests/setup/global.setup.ts
  • e2e/vue-start/basic/tests/setup/global.teardown.ts
✅ Files skipped from review due to trivial changes (21)
  • e2e/vue-start/basic-prerender/.gitignore
  • e2e/vue-start/basic-spa/.gitignore
  • e2e/solid-start/basic-prerender/.gitignore
  • e2e/vue-start/basic-preview/.gitignore
  • e2e/vue-start/basic-test-suite/.gitignore
  • e2e/solid-start/basic-preview/.gitignore
  • e2e/solid-start/basic-test-suite/src/utils/isSpaMode.ts
  • e2e/react-start/basic-test-suite/src/prerendering.spec.ts
  • e2e/solid-start/basic-test-suite/.gitignore
  • e2e/solid-start/basic-spa/.gitignore
  • nx.json
  • e2e/vue-start/basic-test-suite/package.json
  • e2e/solid-start/basic-test-suite/tsconfig.json
  • e2e/solid-start/basic-spa/package.json
  • e2e/vue-start/basic-preview/package.json
  • e2e/react-start/basic-test-suite/src/setup/global.teardown.ts
  • e2e/vue-start/basic-test-suite/tsconfig.json
  • e2e/solid-start/basic-preview/package.json
  • e2e/solid-start/basic-test-suite/package.json
  • e2e/react-start/basic-spa/package.json
  • e2e/react-start/basic-prerender/package.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • e2e/react-start/basic/playwright.config.ts
  • e2e/react-start/basic/package.json
  • package.json
  • e2e/react-start/basic-preview/package.json

workers: 1,
reporter: [['line']],

globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check getPackageName implementation to verify it resolves correctly

# Find and display getPackageName implementation
fd -t f 'getPackageName.ts' e2e/react-start/basic-test-suite --exec cat {}

echo "---"
# Check if it reads from cwd or has hardcoded values
rg -n 'getPackageName|packageJson|process.cwd' e2e/react-start/basic-test-suite/src/utils/

Repository: TanStack/router

Length of output: 1276


🏁 Script executed:

# Check the full playwright.config.ts to see how packageJson is loaded
cat -n e2e/react-start/basic-prerender/playwright.config.ts | head -25

Repository: TanStack/router

Length of output: 912


🏁 Script executed:

# Check the package.json in basic-prerender to see its name
cat e2e/react-start/basic-prerender/package.json | jq '.name'

Repository: TanStack/router

Length of output: 101


🏁 Script executed:

# Check the package.json at the repo root to compare names
cat package.json | jq '.name'

Repository: TanStack/router

Length of output: 65


🏁 Script executed:

# Check global.teardown.ts to see exactly how it uses getPackageName
cat -n e2e/react-start/basic-test-suite/src/setup/global.teardown.ts

Repository: TanStack/router

Length of output: 352


Fix package name mismatch between server startup and teardown.

The dummy server port is allocated using packageJson.name (static value "tanstack-react-start-e2e-basic-prerender" from basic-prerender's package.json), but globalTeardown calls getPackageName() which reads process.cwd()/package.json at runtime. When tests run from the repository root, process.cwd() resolves to the repo root's package.json (name: "root"), causing the teardown to attempt stopping a server under the wrong package name. The dummy server will not be properly stopped.

Either pass the package name to the teardown function, or ensure getPackageName() is called with the correct context directory.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/react-start/basic-prerender/playwright.config.ts` at line 18, The
teardown is reading package name at runtime via getPackageName() which uses
process.cwd(), causing a mismatch with the static packageJson.name used when
starting the dummy server; change the config so the teardown receives the
correct package name from the config rather than reading process.cwd(): update
globalTeardown usage in playwright.config.ts (symbol: globalTeardown) to pass
the expected package name (packageJson.name from the basic-prerender package) or
modify getPackageName() calls in the teardown implementation (symbol:
getPackageName) to accept and use a baseDir argument and call it with the
basic-prerender directory so the teardown stops the server created with that
package name.

Comment on lines +24 to +29
webServer: {
command:
'pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build:prerender && pnpm --dir ../basic start',
url: baseURL,
reuseExistingServer: !process.env.CI,
stdout: 'pipe',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does Playwright skip running webServer.commandwhenwebServer.reuseExistingServeris true andwebServer.url is already available?

💡 Result:

No, Playwright does not skip running webServer.command when webServer.reuseExistingServer is true and webServer.url is already available. It first checks if a server is available at the specified webServer.url (or port). If the server is detected as running and ready (via socket connection or HTTP check returning appropriate status), it reuses it without starting the command. If not available, it runs the webServer.command to start a new server.

Citations:


🏁 Script executed:

# Check if the file exists and read the playwright config
find . -path "*e2e/solid-start/basic-prerender/playwright.config.ts" -type f

Repository: TanStack/router

Length of output: 113


🏁 Script executed:

# Also search for other playwright configs in the e2e directory to compare patterns
find . -path "*e2e/*/playwright.config.ts" -type f | head -20

Repository: TanStack/router

Length of output: 1315


🏁 Script executed:

# Once we find the file, let's read the relevant lines
cat -n e2e/solid-start/basic-prerender/playwright.config.ts | head -40

Repository: TanStack/router

Length of output: 1452


🏁 Script executed:

# Let's check a few other playwright configs to see if they have similar patterns
for file in ./e2e/react-start/basic-auth/playwright.config.ts ./e2e/react-start/basic-cloudflare/playwright.config.ts ./e2e/react-router/basic/playwright.config.ts; do
  echo "=== $file ==="
  cat -n "$file" | grep -A 10 "webServer:" | head -15
done

Repository: TanStack/router

Length of output: 1337


🏁 Script executed:

# Search for how VITE_EXTERNAL_PORT (dummy server) is actually used in tests
rg "VITE_EXTERNAL_PORT|EXTERNAL_PORT" e2e/solid-start/basic-prerender --type ts --type tsx -A 2 -B 2

Repository: TanStack/router

Length of output: 86


🏁 Script executed:

# Look for references to the dummy server in the solid-start basic-prerender test files
find e2e/solid-start/basic-prerender -name "*.ts" -o -name "*.tsx" | xargs grep -l "dummy" 2>/dev/null

Repository: TanStack/router

Length of output: 43


🏁 Script executed:

# Try the search differently - just look for references to EXTERNAL_PORT or dummy
rg "EXTERNAL_PORT|getDummyServerPort" e2e/solid-start/basic-prerender --max-count 20

Repository: TanStack/router

Length of output: 353


🏁 Script executed:

# Check the test suite that's being used
ls -la e2e/solid-start/basic-prerender/

Repository: TanStack/router

Length of output: 367


🏁 Script executed:

# Look at the package.json to understand what test:e2e:startDummyServer does
cat e2e/solid-start/basic-prerender/package.json | grep -A 5 -B 5 "startDummyServer\|test:e2e"

Repository: TanStack/router

Length of output: 960


🏁 Script executed:

# Compare with react-router basic which also uses EXTERNAL_PORT
cat -n e2e/react-router/basic/playwright.config.ts | head -35

Repository: TanStack/router

Length of output: 1282


🏁 Script executed:

# Look at the test suite being used to see if it uses the dummy server
find e2e/solid-start/basic-prerender -type f \( -name "*.ts" -o -name "*.js" \) | head -20

Repository: TanStack/router

Length of output: 111


🏁 Script executed:

# Check the global setup files to understand dummy server usage
cat e2e/solid-start/basic-prerender/../basic-test-suite/src/setup/global.setup.ts 2>/dev/null | head -50

Repository: TanStack/router

Length of output: 272


🏁 Script executed:

# Look for any test that might use external port or dummy server functionality
rg "external\|dummy\|EXTERNAL" e2e/solid-start/basic-prerender/.. --type ts --max-count 30

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

# Check if there are other similar configs with startDummyServer in the command
rg "startDummyServer" e2e --type ts

Repository: TanStack/router

Length of output: 1880


🏁 Script executed:

# Check if the solid-start basic-prerender has a globalSetup or globalTeardown configuration
cat -n e2e/solid-start/basic-prerender/playwright.config.ts | grep -i "global\|setup\|teardown"

Repository: TanStack/router

Length of output: 135


🏁 Script executed:

# Check the global teardown referenced
cat e2e/solid-start/basic-prerender/../basic-test-suite/src/setup/global.teardown.ts 2>/dev/null

Repository: TanStack/router

Length of output: 296


🏁 Script executed:

# Look at what the test:e2e:startDummyServer script does vs what global.setup does
cat e2e/solid-start/basic-prerender/../basic-test-suite/src/setup/waitForDummyServer.ts 2>/dev/null

Repository: TanStack/router

Length of output: 716


🏁 Script executed:

# Check if there's also a globalSetup in the solid-start basic-prerender config
grep -r "globalSetup\|globalTeardown" e2e/solid-start/basic-prerender/

Repository: TanStack/router

Length of output: 181


🏁 Script executed:

# Let's verify that tests actually use the dummy server
rg "VITE_EXTERNAL_PORT\|http.*external\|fetch.*dummy" e2e/solid-start/basic-prerender/../basic-test-suite/src --type ts --max-count 10

Repository: TanStack/router

Length of output: 41


🏁 Script executed:

# Also check what the dummy server is used for
rg "VITE_EXTERNAL_PORT" e2e/solid-start/basic-prerender/../basic --type ts --type tsx --max-count 10

Repository: TanStack/router

Length of output: 86


🏁 Script executed:

# Simpler search for external port usage
find e2e/solid-start/basic-prerender/../basic -type f -name "*.ts" -o -name "*.tsx" | xargs grep -l "VITE_EXTERNAL_PORT" 2>/dev/null | head -10

Repository: TanStack/router

Length of output: 319


🏁 Script executed:

# Check the test suite structure
ls -la e2e/solid-start/basic-prerender/../basic-test-suite/src/

Repository: TanStack/router

Length of output: 909


🏁 Script executed:

# Look for test files that might reference dummy or external
find e2e/solid-start/basic-prerender/../basic-test-suite -type f -name "*.ts" | head -20

Repository: TanStack/router

Length of output: 1381


🏁 Script executed:

# Check one of the test files to see if it uses the dummy server
cat e2e/solid-start/basic-prerender/../basic-test-suite/src/search-params.spec.ts | head -80

Repository: TanStack/router

Length of output: 2841


🏁 Script executed:

# Check the API files to see what they fetch from
cat e2e/solid-start/basic-prerender/../basic/src/routes/api/users.ts

Repository: TanStack/router

Length of output: 2048


reuseExistingServer can skip required dummy-server setup.

Line 28 lets Playwright bypass the entire webServer.command when baseURL is already up. In this harness that command also starts the dummy server, so local reruns can reuse the app server while silently skipping the external fixture server. The tests depend on the dummy server for API calls (e.g., in /api/users.ts the queryURL is set to the external port when in test mode), so this causes failures on reruns with an existing server. Either disable reuse here or move dummy-server startup into a hook that always runs.

💡 Minimal fix
   webServer: {
     command:
       'pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build:prerender && pnpm --dir ../basic start',
     url: baseURL,
-    reuseExistingServer: !process.env.CI,
+    reuseExistingServer: false,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
webServer: {
command:
'pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build:prerender && pnpm --dir ../basic start',
url: baseURL,
reuseExistingServer: !process.env.CI,
stdout: 'pipe',
webServer: {
command:
'pnpm run test:e2e:startDummyServer && pnpm --dir ../basic build:prerender && pnpm --dir ../basic start',
url: baseURL,
reuseExistingServer: false,
stdout: 'pipe',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/solid-start/basic-prerender/playwright.config.ts` around lines 24 - 29,
The Playwright config currently allows skipping webServer.command via
reuseExistingServer (variable reuseExistingServer) which can bypass starting the
dummy server included in webServer.command; change reuseExistingServer to false
(replace reuseExistingServer: !process.env.CI with reuseExistingServer: false)
so the command always runs, or alternatively remove dummy-server startup from
webServer.command and instead start the dummy server unconditionally in a test
lifecycle hook (e.g., a global setup/beforeAll) so tests always have the
external fixture available; reference webServer.command, reuseExistingServer,
and baseURL when making the change.

Comment on lines +11 to +20
while (Date.now() < deadline) {
try {
const response = await fetch(`http://localhost:${port}/`)

if (response.ok) {
return
}
} catch {}

await new Promise((resolve) => setTimeout(resolve, retryIntervalMs))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n e2e/solid-start/basic-test-suite/src/setup/waitForDummyServer.ts

Repository: TanStack/router

Length of output: 884


🏁 Script executed:

find . -name "package.json" -path "*/e2e/solid-start*" | head -5 | xargs cat

Repository: TanStack/router

Length of output: 5358


🏁 Script executed:

cat package.json | grep -A 5 -B 5 "engines\|node"

Repository: TanStack/router

Length of output: 3255


Enforce the deadline on each probe.

Line 13 can still block past the 10s budget because the loop only re-checks the deadline after fetch settles. If the port accepts the connection but never responds, this helper can hang instead of failing fast. AbortSignal.timeout() is supported in your Node.js version (22+).

💡 Suggested change
-      const response = await fetch(`http://localhost:${port}/`)
+      const response = await fetch(`http://localhost:${port}/`, {
+        signal: AbortSignal.timeout(
+          Math.max(1, Math.min(retryIntervalMs, deadline - Date.now())),
+        ),
+      })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
while (Date.now() < deadline) {
try {
const response = await fetch(`http://localhost:${port}/`)
if (response.ok) {
return
}
} catch {}
await new Promise((resolve) => setTimeout(resolve, retryIntervalMs))
while (Date.now() < deadline) {
try {
const response = await fetch(`http://localhost:${port}/`, {
signal: AbortSignal.timeout(
Math.max(1, Math.min(retryIntervalMs, deadline - Date.now())),
),
})
if (response.ok) {
return
}
} catch {}
await new Promise((resolve) => setTimeout(resolve, retryIntervalMs))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/solid-start/basic-test-suite/src/setup/waitForDummyServer.ts` around
lines 11 - 20, The loop in waitForDummyServer can hang because each fetch can
block past the overall deadline; update the probe to use an AbortSignal timeout
per fetch (using AbortSignal.timeout()) so each attempt respects the remaining
time budget: compute remaining = deadline - Date.now(), if remaining <= 0
throw/return failure, then call fetch(`http://localhost:${port}/`, { signal:
AbortSignal.timeout(remaining) }) and handle the abort/timeout error
appropriately before awaiting the retry sleep; reference the waitForDummyServer
loop and the variables port, deadline, and retryIntervalMs when making the
change.

Comment on lines +7 to +8
"test:e2e:startDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.setup.ts\").then(m => m.default())' & node -e 'import(\"../basic-test-suite/src/setup/waitForDummyServer.ts\").then(m => m.default())'",
"test:e2e:stopDummyServer": "node -e 'import(\"../basic-test-suite/src/setup/global.teardown.ts\").then(m => m.default())'",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Declared Node versions =='
fd -HI 'package\.json|\.nvmrc|\.node-version|\.tool-versions' | while read -r file; do
  echo "-- $file"
  case "$file" in
    *.json)
      rg -n '"engines"\s*:|"node"\s*:|"packageManager"\s*:' "$file" || true
      ;;
    *)
      sed -n '1,40p' "$file"
      ;;
  esac
done

echo
echo '== CI Node setup =='
rg -n 'setup-node|node-version' .github/workflows || true

echo
echo '== Existing raw node -> .ts imports in package scripts =='
rg -n 'node -e .+import\(".*\.ts"\)' --glob '**/package.json'

echo
echo '== Existing TS runtime flags/loaders =='
rg -n '(experimental-strip-types|tsx|ts-node|NODE_OPTIONS)' --glob '!**/node_modules/**'

Repository: TanStack/router

Length of output: 50371


🏁 Script executed:

cat e2e/vue-start/basic-spa/package.json

Repository: TanStack/router

Length of output: 944


Add TypeScript loader or --experimental-strip-types flag to enable raw node -e 'import(...ts)' execution.

Lines 7–8 use node -e 'import("../basic-test-suite/src/setup/*.ts")' to dynamically load TypeScript files. This pattern has no precedent elsewhere in the codebase and will fail at runtime without a TypeScript loader (tsx, ts-node) or Node's --experimental-strip-types flag. The repo pins Node 24.8.0, which supports --experimental-strip-types, but no TS runtime configuration is currently in place. Either wrap the command with a loader or prefix with NODE_OPTIONS=--experimental-strip-types.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/vue-start/basic-spa/package.json` around lines 7 - 8, The npm scripts
test:e2e:startDummyServer and test:e2e:stopDummyServer invoke TypeScript files
directly via node -e 'import(...)' which will fail at runtime; update these
scripts to either (a) use a TS runtime loader (e.g., prefix the node commands to
run via tsx or ts-node) or (b) set NODE_OPTIONS=--experimental-strip-types
before the node -e invocation so Node 24.8.0 can strip types, ensuring the
imports of ../basic-test-suite/src/setup/global.setup.ts, waitForDummyServer.ts
and global.teardown.ts execute correctly.

Comment on lines +19 to 28
globalTeardown: '../basic-test-suite/src/setup/global.teardown.ts',

use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL,
},

webServer: {
command: getCommand(),
command: `pnpm run test:e2e:startDummyServer && ${ssrModeCommand}`,
url: baseURL,
reuseExistingServer: !process.env.CI,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Playwright, when reuseExistingServeris true andwebServer.urlis already responding, does Playwright skip runningwebServer.command?

💡 Result:

No, Playwright does not skip running webServer.command when reuseExistingServer is true and webServer.url is already responding. Instead, it re-uses the existing server on the port or URL when available. If no server is running, it will run the command to start a new server.

Citations:


Move dummy-server boot out of webServer.command.

When reuseExistingServer: !process.env.CI is enabled, Playwright skips running webServer.command if the server at baseURL is already responding. This means local test reruns will not run pnpm run test:e2e:startDummyServer, potentially leaving the dummy server in an inconsistent state. Use an always-run setup hook (e.g., globalSetup) to ensure the dummy server is reliably started on every test run, independent of the app server lifecycle.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/vue-start/basic/playwright.config.ts` around lines 19 - 28, The
webServer.command currently includes starting the dummy server (`pnpm run
test:e2e:startDummyServer`) which is skipped when Playwright detects an existing
server due to reuseExistingServer; move dummy-server startup into an always-run
Playwright setup hook instead: create or update globalSetup (e.g., function
named globalSetup) to invoke `pnpm run test:e2e:startDummyServer` before tests,
remove the dummy-server part from webServer.command (keep only the app server
start using ssrModeCommand and baseURL), and ensure globalSetup is referenced in
playwright.config.ts so the dummy server is reliably started on every run
regardless of webServer.reuseExistingServer.

nx-cloud[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

@nx-cloud nx-cloud bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.

Nx Cloud has identified a possible root cause for your failed CI:

We classified this failure as environment_state because the failing task (tanstack-router-e2e-react-scroll-restoration-sandbox-vite:test:e2e) is unrelated to this PR's changes, which only reorganize React/Vue/Solid Start e2e suites. The error is a scroll position timing timeout in CI that would not be caused or fixed by removing this PR's changes.

No code changes were suggested for this issue.

You can trigger a rerun by pushing an empty commit:

git commit --allow-empty -m "chore: trigger rerun"
git push

Nx Cloud View detailed reasoning on Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@beaussan beaussan merged commit a556afa into TanStack:main Mar 28, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant