Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

fix(e2e): wait for HTTP readiness before resolving startServer#1675

Merged
danielroe merged 4 commits intomainnuxt/test-utils:mainfrom
fix-startserver-flakenuxt/test-utils:fix-startserver-flakeCopy head branch name to clipboard
Apr 27, 2026
Merged

fix(e2e): wait for HTTP readiness before resolving startServer#1675
danielroe merged 4 commits intomainnuxt/test-utils:mainfrom
fix-startserver-flakenuxt/test-utils:fix-startserver-flakeCopy head branch name to clipboard

Conversation

@danielroe
Copy link
Copy Markdown
Member

this adds configurability for starting the server + avoids orphaned processes.

additionally, we don't just wait for port to become available, but perform a fetch request to make sure it's accepting connections

@danielroe danielroe requested a review from yamachi4416 April 26, 2026 22:35
@danielroe danielroe changed the title Fix startserver flake fix(e2e): wait for HTTP readiness before resolving startServer Apr 26, 2026
@danielroe danielroe changed the title fix(e2e): wait for HTTP readiness before resolving startServer fix(e2e): wait for HTTP readiness before resolving startServer Apr 26, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 26, 2026

Warning

Rate limit exceeded

@danielroe has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 41 minutes and 19 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 41 minutes and 19 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: da216cbb-f216-4378-ab12-3fdeca54b3ab

📥 Commits

Reviewing files that changed from the base of the PR and between 216abef and 9ba4d6d.

📒 Files selected for processing (2)
  • src/e2e/context.ts
  • src/e2e/server.ts
📝 Walkthrough

Walkthrough

The changes refactor server readiness detection in the e2e testing setup. The server.ts file replaces separate dev and built readiness checking logic with a unified waitForServer function that polls the app via HTTP requests instead of checking internal state. The new approach adds configurable timeout via a serverStartTimeout option and includes platform-specific defaults. The types.ts file adds the corresponding serverStartTimeout configuration field to the TestOptions interface.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly relates to the changeset, mentioning server startup configurability, prevention of orphaned processes, and HTTP fetch verification for connection acceptance.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title directly and clearly describes the main change: replacing port-availability polling with HTTP readiness checks before resolving startServer, which is the primary objective of this changeset.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-startserver-flake

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.

Copy link
Copy Markdown

@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: 1

🧹 Nitpick comments (3)
src/e2e/types.ts (1)

39-45: serverStartTimeout typed as required but not defaulted in createTestContext.

The field is declared as a non-optional number, yet createTestContext in src/e2e/context.ts does not assign a default for it (unlike setupTimeout/teardownTimeout). The consumer in server.ts papers over this with ctx.options.serverStartTimeout ?? (isWindows ? 120_000 : 60_000), which means the type is effectively a lie at runtime and the platform-specific default is duplicated/owned by server.ts rather than the central context bootstrap.

Either mark the field optional, or (preferred, for consistency with sibling timeouts) initialize the default in createTestContext and drop the fallback in server.ts:

♻️ Proposed default in createTestContext
     setupTimeout: isWindows ? 240_000 : 120_000,
     teardownTimeout: isWindows ? 60_000 : 30_000,
+    serverStartTimeout: isWindows ? 120_000 : 60_000,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/e2e/types.ts` around lines 39 - 45, The serverStartTimeout option is
declared required but not defaulted in createTestContext, causing server.ts to
duplicate the platform-specific fallback; update createTestContext to initialize
options.serverStartTimeout to the platform-default (use isWindows ? 120_000 :
60_000 or equivalent process.platform check) alongside
setupTimeout/teardownTimeout so the context always has a value, and then remove
the ad-hoc fallback in server.ts (references: serverStartTimeout,
createTestContext, server.ts).
src/e2e/server.ts (2)

71-78: Port-wait budget is taken from the same deadline but isn't bounded by it.

deadline is captured before waitForPort, but the port wait can independently consume up to portRetries × the get-port-please default delay before the fetch loop even starts. In the worst case that can chew through (or exceed) the entire serverStartTimeout, leaving the HTTP-readiness loop with zero or one iteration and producing a confusing "Timeout … waiting for … server to become ready" error when the real failure was the port never opening.

Consider either dropping the port-wait entirely (the fetch loop already retries and tolerates connection refusals via the catch), or capping its retries to a small fixed number and letting the fetch loop own the overall budget:

♻️ Suggested simplification
-  const portRetries = Math.max(1, Math.ceil(timeout / 1000))
-  await waitForPort(port, { retries: portRetries, host }).catch(() => {})
+  // brief opportunistic wait; the fetch loop below owns the real readiness budget
+  await waitForPort(port, { retries: 8, host }).catch(() => {})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/e2e/server.ts` around lines 71 - 78, The port-wait (await
waitForPort(...)) can consume the entire serverStartTimeout before the HTTP
readiness loop uses the shared deadline; change waitForServer so the port wait
does not own the full budget: either remove the await waitForPort call entirely
(the fetch loop already tolerates connection refusals) or cap its retries to a
small fixed bound (e.g. Math.min(3, Math.ceil(timeout/1000))) so it only does a
few quick attempts; keep the existing deadline variable (used by the
fetch/readiness loop) unchanged so the HTTP loop retains the overall timeout
budget.

86-100: Remove unnecessary res.text() call in non-dev mode.

The await res.text() is always invoked even when not needed. In non-dev mode with a successful response, the body is never checked. This unnecessarily consumes the response stream; a large streaming response could keep the socket alive longer than needed. Gate the .text() call on dev:

♻️ Minor refactor
       const res = await globalFetch(joinURL(ctx.url!, baseURL), { signal: AbortSignal.timeout(10_000) })
-      const body = await res.text()
       // any response means the server is accepting connections.
       // the dev server (`nuxi _dev`) is the one exception: it answers with
       // 503 or a 200 HTML placeholder containing `__NUXT_LOADING__` while the
       // underlying dev server is still starting up.
       if (dev && res.status === 503) {
+        await res.body?.cancel()
         lastError = new Error(`Server responded with ${res.status} ${res.statusText}`)
       }
-      else if (dev && body.includes('__NUXT_LOADING__')) {
+      else if (dev && (await res.text()).includes('__NUXT_LOADING__')) {
         lastError = new Error('Dev server is still starting up')
       }
       else {
+        await res.body?.cancel()
         return
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/e2e/server.ts` around lines 86 - 100, The code always calls await
res.text() even when not in dev mode, which consumes the response stream
unnecessarily; modify the logic in the server readiness check (the block using
globalFetch/joinURL and variables res, body, dev, lastError) so that res.text()
is only awaited when dev is true and you need to inspect the body for
'__NUXT_LOADING__' (i.e., only compute body when dev is true), and in the
non-dev successful case return immediately without reading the response body to
avoid holding the response stream open.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/e2e/server.ts`:
- Around line 108-111: stopServer currently calls ctx.serverProcess.kill()
synchronously and returns immediately, so the timeout branch (where you call
await stopServer()) does not wait for the child to actually exit; change
stopServer to return a Promise that waits for the child process to emit an
exit/close event (listen on ctx.serverProcess.once('exit'/'close') and resolve)
and implement a short grace timeout after the initial kill that, if exceeded,
escalates to a forceful kill (SIGKILL or equivalent) then waits for the exit;
ensure any listeners are cleaned up and that stopServer rejects or resolves only
once the process has actually terminated.

---

Nitpick comments:
In `@src/e2e/server.ts`:
- Around line 71-78: The port-wait (await waitForPort(...)) can consume the
entire serverStartTimeout before the HTTP readiness loop uses the shared
deadline; change waitForServer so the port wait does not own the full budget:
either remove the await waitForPort call entirely (the fetch loop already
tolerates connection refusals) or cap its retries to a small fixed bound (e.g.
Math.min(3, Math.ceil(timeout/1000))) so it only does a few quick attempts; keep
the existing deadline variable (used by the fetch/readiness loop) unchanged so
the HTTP loop retains the overall timeout budget.
- Around line 86-100: The code always calls await res.text() even when not in
dev mode, which consumes the response stream unnecessarily; modify the logic in
the server readiness check (the block using globalFetch/joinURL and variables
res, body, dev, lastError) so that res.text() is only awaited when dev is true
and you need to inspect the body for '__NUXT_LOADING__' (i.e., only compute body
when dev is true), and in the non-dev successful case return immediately without
reading the response body to avoid holding the response stream open.

In `@src/e2e/types.ts`:
- Around line 39-45: The serverStartTimeout option is declared required but not
defaulted in createTestContext, causing server.ts to duplicate the
platform-specific fallback; update createTestContext to initialize
options.serverStartTimeout to the platform-default (use isWindows ? 120_000 :
60_000 or equivalent process.platform check) alongside
setupTimeout/teardownTimeout so the context always has a value, and then remove
the ad-hoc fallback in server.ts (references: serverStartTimeout,
createTestContext, server.ts).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6e2e6f25-df84-46e6-a5c7-ce6e272829e4

📥 Commits

Reviewing files that changed from the base of the PR and between d318d64 and 216abef.

📒 Files selected for processing (2)
  • src/e2e/server.ts
  • src/e2e/types.ts

Comment thread src/e2e/server.ts Outdated
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 26, 2026

Open in StackBlitz

npm i https://pkg.pr.new/@nuxt/test-utils@1675
npm i https://pkg.pr.new/vitest-environment-nuxt@1675

commit: 9ba4d6d

@danielroe danielroe merged commit 0aa5c23 into main Apr 27, 2026
10 checks passed
@danielroe danielroe deleted the fix-startserver-flake branch April 27, 2026 11:40
@github-actions github-actions Bot mentioned this pull request Apr 27, 2026
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.

2 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.