From 9020eaca48491b59a49fc9f5348764757b863eaf Mon Sep 17 00:00:00 2001 From: MK Date: Mon, 7 Sep 2026 09:33:14 +0800 Subject: [PATCH] test: reproduce SSR failure with the original dependencies --- .github/workflows/ci.yml | 39 +++++++++++++++++++++++++ tests/ssr.test.ts | 62 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 tests/ssr.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d7146ba --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + ssr: + name: SSR and build (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - uses: voidzero-dev/setup-vp@49c3e4e92c52e7f8392712a9267bbe71c5ab30e5 # v1.19.0 + with: + version-file: package.json + node-version: "24.14.1" + run-install: false + - name: Install locked dependencies + run: vp install --frozen-lockfile + - name: Show tool versions + run: vp --version + - name: Test server-rendered routes + run: vp test run + - name: Build for production + run: vp build diff --git a/tests/ssr.test.ts b/tests/ssr.test.ts new file mode 100644 index 0000000..b7a6a35 --- /dev/null +++ b/tests/ssr.test.ts @@ -0,0 +1,62 @@ +import { execFileSync, spawn } from "node:child_process"; +import { setTimeout as delay } from "node:timers/promises"; +import { fileURLToPath } from "node:url"; +import { expect, test } from "vite-plus/test"; + +test("vp dev renders both routes on the server", async ({ onTestFinished }) => { + const cli = fileURLToPath(new URL("../node_modules/vite-plus/bin/vp", import.meta.url)); + const server = spawn(process.execPath, [cli, "dev", "--host", "127.0.0.1", "--port", "0"], { + cwd: fileURLToPath(new URL("..", import.meta.url)), + env: { ...process.env, NO_COLOR: "1", BROWSER: "none" }, + detached: process.platform !== "win32", + stdio: ["ignore", "pipe", "pipe"], + }); + let output = ""; + let spawnError: Error | undefined; + server.on("error", (error) => { + spawnError = error; + }); + server.stdout.on("data", (chunk) => { + output += chunk; + }); + server.stderr.on("data", (chunk) => { + output += chunk; + }); + + onTestFinished(() => { + // The CLI starts Vite in a child process. Stop the full process tree. + if (server.pid) { + if (process.platform === "win32") { + execFileSync("taskkill", ["/pid", String(server.pid), "/T", "/F"], { stdio: "ignore" }); + } else { + try { + process.kill(-server.pid, "SIGTERM"); + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== "ESRCH") throw error; + } + } + } + }); + + const deadline = Date.now() + 30_000; + let baseUrl: string | undefined; + while (Date.now() < deadline) { + if (spawnError) throw spawnError; + if (server.exitCode !== null) throw new Error(`Dev server exited:\n${output}`); + baseUrl = output.match(/http:\/\/127\.0\.0\.1:[1-9]\d*/)?.[0]; + if (baseUrl) break; + await delay(100); + } + if (!baseUrl) throw new Error(`Dev server did not start:\n${output}`); + + for (const [route, content] of [ + ["/", "Start simple, ship quickly."], + ["/about", "A small starter with room to grow."], + ]) { + const response = await fetch(`${baseUrl}${route}`, { signal: AbortSignal.timeout(20_000) }); + const html = await response.text(); + expect(response.status, `${route}: ${html}`).toBe(200); + expect(response.headers.get("content-type")).toContain("text/html"); + expect(html).toContain(content); + } +}, 90_000);