Skip to content

fix(e2e): use taskkill to kill processes on windows - #1760

Merged
yamachi4416 merged 1 commit into
mainfrom
fix/windows-dev-teardown
Aug 1, 2026
Merged

fix(e2e): use taskkill to kill processes on windows#1760
yamachi4416 merged 1 commit into
mainfrom
fix/windows-dev-teardown

Conversation

@danielroe

Copy link
Copy Markdown
Member

🔗 Linked issue

📚 Description

this fixes a couple of issues when tearing down a fixture on windows:

  1. properly kill the whole task tree

  2. timeout the teardown so it doesn't fail the test suite

@danielroe
danielroe requested a review from yamachi4416 August 1, 2026 14:06
@pkg-pr-new

pkg-pr-new Bot commented Aug 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: bdb32fd

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The server shutdown path now uses Windows taskkill /T /F to terminate the process tree and waits for exit. Other platforms retain direct process termination. End-to-end teardown now uses a shared deadline for Nuxt closing, browser closing, and teardown hooks. Timeout handling logs the timeout, suppresses later errors, and rethrows earlier errors.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary Windows process-tree termination fix and matches the main changeset.
Description check ✅ Passed The description accurately covers both Windows process-tree termination and teardown timeout changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/windows-dev-teardown

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/e2e/server.ts`:
- Around line 1-3: Update the taskkill path used by stopServer to avoid
unbounded xSync blocking: pass a short tinyexec timeout or replace it with
asynchronous execution plus an explicit timeout, and treat timeout failures as
non-fatal so stopServer can continue to ctx.nuxt.close(), browser cleanup, and
teardown hooks.

In `@src/e2e/setup/index.ts`:
- Around line 51-68: Update the teardown flow around `deadline`, `remaining`,
and `withTimeout` so the shared `ctx.options.teardownTimeout` is a strict single
deadline: return 0 when expired, route `stopServer()` through the same timeout
limiter, and make `withTimeout` accept a lazy operation so no close or teardown
action starts after the budget is exhausted. Preserve the existing teardown
order and warning behavior.
🪄 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 Plus

Run ID: b9462957-72db-4482-9144-50f859d9b5f3

📥 Commits

Reviewing files that changed from the base of the PR and between 42f1f3b and bdb32fd.

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

Comment thread src/e2e/server.ts
Comment on lines +1 to +3
import { x, xSync } from 'tinyexec'
import { getRandomPort, waitForPort } from 'get-port-please'
import { isWindows } from 'std-env'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For tinyexec 1.2.4, how can xorxSyncexecutetaskkillwith a timeout, and doesxSyncforwardnodeOptions.timeoutto Node.jschild_process.spawnSync?

💡 Result:

In tinyexec 1.2.4, you can execute taskkill with a timeout using xSync by passing a timeout property (in milliseconds) within the options object [1]. Example: xSync('taskkill', ['/F', '/IM', 'process.exe'], { timeout: 5000 }); Regarding your second question, yes, xSync forwards nodeOptions to the underlying Node.js process execution function. While the synchronous API does not support features like AbortSignal, persist, or piping, other options including timeout and nodeOptions are supported and function in the same way as they do in the asynchronous API [1]. Because xSync ultimately utilizes Node.js's synchronous child process methods (such as spawnSync), the timeout and any provided nodeOptions are passed through to the underlying Node.js implementation [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate server.ts =="
fd -a 'server\.ts$' . || true

echo "== package tinyexec versions =="
if [ -f package.json ]; then
  node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json','utf8'));
console.log('package.json:', JSON.stringify({
  dependencies: pkg.dependencies,
  devDependencies: pkg.devDependencies
}, null, 2));
JS
fi
if [ -f pnpm-lock.yaml ]; then
  rg -n "tinyexec|get-port-please|std-env" pnpm-lock.yaml | head -80 || true
elif [ -f package-lock.json ]; then
  rg -n "tinyexec|get-port-please|std-env" package-lock.json | head -80 || true
elif [ -f yarn.lock ]; then
  rg -n "tinyexec|get-port-please|std-env" yarn.lock | head -80 || true
fi

echo "== inspect src/e2e/server.ts relevant lines =="
wc -l src/e2e/server.ts
sed -n '1,240p' src/e2e/server.ts

echo "== inspect setup teardown references =="
sed -n '1,120p' src/e2e/setup/index.ts

Repository: nuxt/test-utils

Length of output: 14905


🌐 Web query:

tinyexec 1.2.4 README timeout option taskkill timeout nodeOptions.timeout xSync

💡 Result:

In tinyexec 1.2.4, the timeout option is used to specify a duration in milliseconds after which a process will be forcibly killed [1][2]. This functionality is available for both the asynchronous x and synchronous xSync APIs [1][2]. Regarding your specific keywords: timeout: This option is a property within the options object passed to x or xSync [1][3]. When set, the library ensures the process is terminated after the specified time [1][3]. Under the hood, this integrates with Node.js child process mechanisms to manage the timeout [4]. taskkill: The library does not explicitly mention taskkill in its primary documentation [1][5][2]. It manages process termination via Node.js's built-in child process signals (defaulting to SIGTERM) when the timeout is reached or the kill method is called [1][5]. nodeOptions.timeout: While nodeOptions allows you to pass custom options to the underlying Node.js spawn function, the library's official documentation defines a top-level timeout option [1][6]. Users are encouraged to use the explicit timeout option provided by tinyexec for consistent behavior [1][2]. xSync: The xSync function supports the same core options as the asynchronous x function, including timeout, throwOnError, and nodeOptions [2][3]. Although xSync blocks the event loop and does not support features like signal, persist, or piping, the timeout mechanism remains functional for synchronous execution [1][2].

Citations:


Bound the taskkill invocation to a teardown timeout.

xSync blocks the event loop until taskkill exits, so the stopServer() Promise.race cannot start until the call returns. If taskkill stalls, teardown cannot continue to ctx.nuxt.close(), browser close, or teardown hooks in src/e2e/setup/index.ts. Pass a small tinyexec timeout, or run the command asynchronously with an explicit timeout; treat a timeout as a non-fatal teardown result.

[st稳定性_and_availability]

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/e2e/server.ts` around lines 1 - 3, Update the taskkill path used by
stopServer to avoid unbounded xSync blocking: pass a short tinyexec timeout or
replace it with asynchronous execution plus an explicit timeout, and treat
timeout failures as non-fatal so stopServer can continue to ctx.nuxt.close(),
browser cleanup, and teardown hooks.

Comment thread src/e2e/setup/index.ts
Comment on lines +51 to +68
// Every step is bounded against a single shared budget so that a shutdown
// that never settles degrades to a warning instead of failing the run.
const deadline = Date.now() + ctx.options.teardownTimeout
const remaining = () => Math.max(1_000, deadline - Date.now())

if (ctx.serverProcess) {
setTestContext(ctx)
await stopServer()
setTestContext(undefined)
}
if (ctx.nuxt && ctx.nuxt.options.dev) {
await ctx.nuxt.close()
await withTimeout('closing the Nuxt instance', remaining(), ctx.nuxt.close())
}
if (ctx.browser) {
await ctx.browser.close()
await withTimeout('closing the browser', remaining(), ctx.browser.close())
}
// clear side effects
await Promise.all(!ctx.teardown ? [] : ctx.teardown.map(fn => fn()))
await withTimeout('running teardown hooks', remaining(), Promise.all(!ctx.teardown ? [] : ctx.teardown.map(fn => fn())))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Enforce teardownTimeout as one deadline.

remaining() returns 1,000ms after deadline. Each later teardown step can therefore wait another second. stopServer() is also awaited outside withTimeout, even though the deadline starts before it.

The total afterAll duration can exceed ctx.options.teardownTimeout by the server shutdown time and by one second per later step. Use 0 as the expired budget, and apply the same limiter to server shutdown. Make withTimeout accept a lazy operation so an expired deadline does not start another close operation.

Proposed direction
-function withTimeout<T>(label: string, ms: number, promise: Promise<T>): Promise<T | undefined> {
+function withTimeout<T>(label: string, ms: number, operation: () => Promise<T>): Promise<T | undefined> {
+  if (ms <= 0) {
+    console.warn(`[`@nuxt/test-utils`] Timed out ${label} during teardown. Continuing; some processes or file handles may still be held.`)
+    return Promise.resolve(undefined)
+  }
   // ...
-  const guarded = promise.catch(/* ... */)
+  const guarded = Promise.resolve().then(operation).catch(/* ... */)
 }
 
-const remaining = () => Math.max(1_000, deadline - Date.now())
+const remaining = () => Math.max(0, deadline - Date.now())
 
-await stopServer()
+await withTimeout('stopping the test server', remaining(), () => stopServer())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/e2e/setup/index.ts` around lines 51 - 68, Update the teardown flow around
`deadline`, `remaining`, and `withTimeout` so the shared
`ctx.options.teardownTimeout` is a strict single deadline: return 0 when
expired, route `stopServer()` through the same timeout limiter, and make
`withTimeout` accept a lazy operation so no close or teardown action starts
after the budget is exhausted. Preserve the existing teardown order and warning
behavior.

@yamachi4416
yamachi4416 merged commit d32cec1 into main Aug 1, 2026
14 checks passed
@yamachi4416
yamachi4416 deleted the fix/windows-dev-teardown branch August 1, 2026 14:38
@github-actions github-actions Bot mentioned this pull request Jul 31, 2026
@github-actions github-actions Bot mentioned this pull request Aug 3, 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