Skip to content

Commit fb8f7c1

Browse files
nperez0111claude
andcommitted
fix: use dynamic port and proper process group cleanup in Next.js CI test
Replaces hardcoded port 3951 with get-port-please to avoid EADDRINUSE when previous runs leave orphaned processes. Spawns Next.js with detached: true so afterAll can kill the whole process group (SIGTERM then SIGKILL after 5s) instead of just the npx wrapper. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 80a75e9 commit fb8f7c1

File tree

3 files changed

+52
-14
lines changed

3 files changed

+52
-14
lines changed

pnpm-lock.yaml

Lines changed: 17 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,8 @@
3838
"extends": [
3939
"../.eslintrc.json"
4040
]
41+
},
42+
"dependencies": {
43+
"get-port-please": "3.2.0"
4144
}
4245
}

tests/src/unit/nextjs/serverUtil.test.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { execSync, spawn, ChildProcess } from "child_process";
2+
import { getPort } from "get-port-please";
23
import path from "path";
34
import { afterAll, beforeAll, describe, expect, it } from "vitest";
45

56
const TEST_APP_DIR = path.resolve(__dirname, "../../../nextjs-test-app");
6-
const PORT = 3951; // Unusual port to avoid conflicts
7-
const BASE_URL = `http://localhost:${PORT}`;
7+
let PORT: number;
8+
let BASE_URL: string;
89
const MODE = (process.env.NEXTJS_TEST_MODE || "dev") as "dev" | "build";
910

1011
let nextProcess: ChildProcess;
@@ -20,6 +21,9 @@ let serverErrors = "";
2021
*/
2122
describe(`server-util in Next.js App Router (#942) [${MODE}]`, () => {
2223
beforeAll(async () => {
24+
PORT = await getPort({ portRange: [3900, 4100] });
25+
BASE_URL = `http://localhost:${PORT}`;
26+
2327
// Pack and install @blocknote packages as tarballs
2428
execSync("bash setup.sh", {
2529
cwd: TEST_APP_DIR,
@@ -42,6 +46,7 @@ describe(`server-util in Next.js App Router (#942) [${MODE}]`, () => {
4246
{
4347
cwd: TEST_APP_DIR,
4448
stdio: ["ignore", "pipe", "pipe"],
49+
detached: true,
4550
},
4651
);
4752
} else {
@@ -53,6 +58,7 @@ describe(`server-util in Next.js App Router (#942) [${MODE}]`, () => {
5358
cwd: TEST_APP_DIR,
5459
stdio: ["ignore", "pipe", "pipe"],
5560
env: { ...process.env, NODE_ENV: "development" },
61+
detached: true,
5662
},
5763
);
5864
}
@@ -93,10 +99,30 @@ describe(`server-util in Next.js App Router (#942) [${MODE}]`, () => {
9399
});
94100
}, 180_000);
95101

96-
afterAll(() => {
97-
if (nextProcess) {
98-
nextProcess.kill("SIGTERM");
99-
}
102+
afterAll(async () => {
103+
if (nextProcess?.pid == null) return;
104+
105+
await new Promise<void>((resolve) => {
106+
nextProcess.once("exit", () => resolve());
107+
108+
try {
109+
// Kill the entire process group so Next.js children don't linger
110+
process.kill(-nextProcess.pid!, "SIGTERM");
111+
} catch {
112+
// Process may have already exited
113+
resolve();
114+
return;
115+
}
116+
117+
// Escalate to SIGKILL if still alive after 5s
118+
setTimeout(() => {
119+
try {
120+
process.kill(-nextProcess.pid!, "SIGKILL");
121+
} catch {
122+
// already gone
123+
}
124+
}, 5_000);
125+
});
100126
});
101127

102128
it("ServerBlockNoteEditor works in API route (mirrors ReactServer.test.tsx)", async () => {

0 commit comments

Comments
 (0)