Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [main]
pull_request:
branches: [main]
types: [opened, synchronize, reopened, labeled]
workflow_call:

jobs:
Expand All @@ -23,10 +24,13 @@ jobs:
- run: pnpm check

test:
runs-on: blacksmith-4vcpu-ubuntu-2404
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# Windows runs on main pushes and PRs labeled "test: windows"
os: '${{ (github.event_name == ''push'' || contains(github.event.pull_request.labels.*.name, ''test: windows'')) && fromJSON(''["blacksmith-4vcpu-ubuntu-2404","windows-latest"]'') || fromJSON(''["blacksmith-4vcpu-ubuntu-2404"]'') }}'
suite: [unit, core-markdown, metaframeworks, pages-react-vue, pages-svelte-solid]
include:
- suite: unit
script: test:unit
Expand All @@ -52,6 +56,19 @@ jobs:
- run: pnpm build

- run: pnpm exec playwright install --with-deps chromium
if: matrix.suite == 'core-markdown' || matrix.suite == 'metaframeworks' || startsWith(matrix.suite, 'pages-')
if: matrix.suite != 'unit' && !contains(matrix.os, 'windows')

- run: pnpm exec playwright install chromium
if: matrix.suite != 'unit' && contains(matrix.os, 'windows')

- run: pnpm ${{ matrix.script }}${{ matrix.retry && format(' --retry {0}', matrix.retry) || '' }}

done:
runs-on: ubuntu-latest
if: always()
needs:
- lint-and-check
- test
steps:
- run: exit 1
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
"test": "vitest --run"
},
"devDependencies": {
"@types/cross-spawn": "^6.0.6",
"@typescript/native-preview": "catalog:",
"cross-spawn": "^7.0.6",
"husky": "^9.1.7",
"lint-staged": "^16.4.0",
"oxfmt": "^0.42.0",
Expand Down
3 changes: 2 additions & 1 deletion packages/md/test/integration/markdown-blog.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it, beforeAll, afterAll } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { join } from "node:path";
import { chromium, type Browser, type Page } from "playwright";
import { getPort, waitForServer, killChild } from "../../../../test/integration-helpers";
Expand Down
3 changes: 2 additions & 1 deletion packages/react/test/integration/react-pages-client.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it, beforeAll, afterAll } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { join } from "node:path";
import { readFileSync, writeFileSync } from "node:fs";
import { chromium, type Browser, type Page } from "playwright";
Expand Down Expand Up @@ -784,7 +785,7 @@
});
}, 60_000);

describe("react-pages client (preview)", { retry: 2, timeout: 60_000 }, () => {

Check warning on line 788 in packages/react/test/integration/react-pages-client.test.ts

View workflow job for this annotation

GitHub Actions / lint-and-check

eslint-plugin-jest(valid-describe-callback)

Second argument must be a function
let child: ReturnType<typeof spawn>;
let stderr = "";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { join } from "node:path";
import { readFileSync, writeFileSync } from "node:fs";
import { chromium, type Browser, type Page } from "playwright";
Expand Down Expand Up @@ -719,7 +720,7 @@
});
}, 60_000);

describe("solid-pages client (preview)", { retry: 2, timeout: 60_000 }, () => {

Check warning on line 723 in packages/solid/test/integration/solid-pages-playground.test.ts

View workflow job for this annotation

GitHub Actions / lint-and-check

eslint-plugin-jest(valid-describe-callback)

Second argument must be a function
let child: ReturnType<typeof spawn>;
let stderr = "";

Expand Down
3 changes: 2 additions & 1 deletion packages/svelte/test/integration/svelte-pages-client.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it, beforeAll, afterAll } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { join } from "node:path";
import { readFileSync, writeFileSync } from "node:fs";
import { chromium, type Browser, type Page } from "playwright";
Expand Down Expand Up @@ -722,7 +723,7 @@
});
}, 60_000);

describe("svelte-pages client (preview)", { retry: 2, timeout: 60_000 }, () => {

Check warning on line 726 in packages/svelte/test/integration/svelte-pages-client.test.ts

View workflow job for this annotation

GitHub Actions / lint-and-check

eslint-plugin-jest(valid-describe-callback)

Second argument must be a function
let child: ReturnType<typeof spawn>;
let stderr = "";

Expand Down
3 changes: 2 additions & 1 deletion packages/void/test/integration/astro.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { existsSync, readFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { getPort, waitForServer, killChild } from "../../../../test/integration-helpers";
Expand Down
7 changes: 4 additions & 3 deletions packages/void/test/integration/kitchen-sink-build.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { beforeAll, describe, expect, it } from "vitest";
import { spawnSync } from "node:child_process";
import spawn from "cross-spawn";
import { existsSync, readFileSync, readdirSync, rmSync } from "node:fs";
import { join } from "node:path";
import { toSlash } from "../../src/shared/utils";

const ROOT = join(import.meta.dirname, "..", "..", "..", "..");
const PLAYGROUND = "playground/kitchen-sink";
Expand All @@ -15,14 +16,14 @@
rmSync(join(pg, "dist"), { recursive: true, force: true });
rmSync(join(pg, ".void"), { recursive: true, force: true });

const result = spawnSync("pnpm", ["--dir", PLAYGROUND, "build"], {
const result = spawn.sync("pnpm", ["--dir", PLAYGROUND, "build"], {
cwd: ROOT,
encoding: "utf8",
maxBuffer: 10 * 1024 * 1024,
});

buildOutput = `${result.stdout ?? ""}${result.stderr ?? ""}`;
expect(result.status).toBe(0);

Check warning on line 26 in packages/void/test/integration/kitchen-sink-build.test.ts

View workflow job for this annotation

GitHub Actions / lint-and-check

eslint-plugin-jest(no-standalone-expect)

`expect` must be inside of a test block.
}, 120_000);

it("emits the SSR worker entry", () => {
Expand Down Expand Up @@ -51,7 +52,7 @@
function collectFiles(dir: string): string[] {
const files: string[] = [];
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const entryPath = join(dir, entry.name);
const entryPath = toSlash(join(dir, entry.name));
if (entry.isDirectory()) {
files.push(...collectFiles(entryPath));
} else {
Expand Down
3 changes: 2 additions & 1 deletion packages/void/test/integration/kitchen-sink.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it, beforeAll, afterAll } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { join } from "node:path";
import { rmSync } from "node:fs";
import { chromium, type Browser, type Page } from "playwright";
Expand Down
2 changes: 1 addition & 1 deletion packages/void/test/integration/named-layouts.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it, beforeAll, afterAll } from "vitest";
import { spawn } from "node:child_process";
import spawn from "cross-spawn";
import { join } from "node:path";
import { getPort, waitForServer, killChild } from "../../../../test/integration-helpers";

Expand Down
5 changes: 3 additions & 2 deletions packages/void/test/integration/node-target.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { existsSync, readFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { getPort, waitForServer, killChild } from "../../../../test/integration-helpers";

const ROOT = new URL("../../../..", import.meta.url).pathname;
const ROOT = join(import.meta.dirname, "..", "..", "..", "..");
let DEV_PORT: number;
let DEV_BASE: string;
let PROD_PORT: number;
Expand Down
3 changes: 2 additions & 1 deletion packages/void/test/integration/nuxt.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { existsSync, readFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { getPort, waitForServer, killChild } from "../../../../test/integration-helpers";
Expand Down
2 changes: 1 addition & 1 deletion packages/void/test/integration/pg-test.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { readFileSync, writeFileSync, unlinkSync } from "node:fs";
import { spawn } from "node:child_process";
import spawn from "cross-spawn";
import { join } from "node:path";
import { once } from "node:events";
import { PGlite } from "@electric-sql/pglite";
Expand Down
3 changes: 2 additions & 1 deletion packages/void/test/integration/react-router.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { existsSync, readFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { getPort, waitForServer, killChild } from "../../../../test/integration-helpers";
Expand Down Expand Up @@ -42,7 +43,7 @@
});
});

describe("playground/react-router dev", { retry: 2, timeout: 60_000 }, () => {

Check warning on line 46 in packages/void/test/integration/react-router.test.ts

View workflow job for this annotation

GitHub Actions / lint-and-check

eslint-plugin-jest(valid-describe-callback)

Second argument must be a function
let child: ReturnType<typeof spawn>;
let stderr = "";

Expand Down
3 changes: 2 additions & 1 deletion packages/void/test/integration/ssr-playground.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { existsSync, readFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { getPort, waitForServer, killChild } from "../../../../test/integration-helpers";
Expand Down
3 changes: 2 additions & 1 deletion packages/void/test/integration/sveltekit.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { getPort, waitForServer, killChild } from "../../../../test/integration-helpers";
Expand Down
3 changes: 2 additions & 1 deletion packages/void/test/integration/tanstack-start.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { existsSync, readFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { getPort, waitForServer, killChild } from "../../../../test/integration-helpers";
Expand Down
3 changes: 2 additions & 1 deletion packages/void/test/integration/vinext-app.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { existsSync, readFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { getPort, waitForServer, killChild } from "../../../../test/integration-helpers";
Expand Down
3 changes: 2 additions & 1 deletion packages/void/test/integration/vinext-pages.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { existsSync, readFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { getPort, waitForServer, killChild } from "../../../../test/integration-helpers";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { spawn } from "node:child_process";
import spawn from "cross-spawn";
import { join } from "node:path";
import { chromium, type Browser, type Page } from "playwright";
import { getPort, killChild, waitForServer } from "../../../../test/integration-helpers";
Expand Down
6 changes: 5 additions & 1 deletion packages/vue/test/integration/vue-pages-client.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it, beforeAll, afterAll } from "vitest";
import { execSync, spawn } from "node:child_process";
import { execSync } from "node:child_process";
import spawn from "cross-spawn";
import { join } from "node:path";
import { readFileSync, writeFileSync } from "node:fs";
import { chromium, type Browser, type Page } from "playwright";
Expand Down Expand Up @@ -734,7 +735,7 @@
if (hmrDelivered) {
// Verify it was a hot update, not a full page reload
const markerSurvived = await page.evaluate(() => (window as any).__hmr_marker === true);
expect(markerSurvived).toBe(true);

Check warning on line 738 in packages/vue/test/integration/vue-pages-client.test.ts

View workflow job for this annotation

GitHub Actions / lint-and-check

eslint-plugin-jest(no-conditional-expect)

Unexpected conditional expect
}
} finally {
await page.context().close();
Expand Down Expand Up @@ -764,6 +765,9 @@
() => document.querySelector('[data-testid="count"]')?.textContent === "Count: 1",
);

// Wait a bit for reactive state to stabilize before HMR (Windows can be slower)
await new Promise((r) => setTimeout(r, 200));

// Set a window marker to detect full page reload
await page.evaluate(() => {
(window as any).__hmr_marker = true;
Expand Down Expand Up @@ -831,7 +835,7 @@
});
}, 60_000);

describe("vue-pages client (preview)", { retry: 2, timeout: 60_000 }, () => {

Check warning on line 838 in packages/vue/test/integration/vue-pages-client.test.ts

View workflow job for this annotation

GitHub Actions / lint-and-check

eslint-plugin-jest(valid-describe-callback)

Second argument must be a function
let child: ReturnType<typeof spawn>;
let stderr = "";

Expand Down
13 changes: 13 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions test/integration-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createServer, type Server } from "node:net";
import { once } from "node:events";
import type { spawn } from "node:child_process";
import { execSync, type spawn } from "node:child_process";

/**
* Ports held by getPort() until waitForServer() releases them.
Expand Down Expand Up @@ -63,7 +63,18 @@ export async function waitForServer(

export async function killChild(child: ReturnType<typeof spawn>): Promise<void> {
if (child.exitCode === null && child.signalCode === null) {
child.kill("SIGTERM");
if (process.platform === "win32" && child.pid) {
// On Windows, SIGTERM doesn't propagate to child processes.
// Use taskkill /T to kill the entire process tree (e.g. workerd.exe).
// See: https://github.com/voidzero-dev/void/actions/runs/23939152784/job/69826006981?pr=63
try {
execSync(`taskkill /pid ${child.pid} /T /F`, { stdio: "ignore" });
} catch {
// Process may have already exited
}
} else {
child.kill("SIGTERM");
}
await Promise.race([once(child, "exit"), new Promise((resolve) => setTimeout(resolve, 5_000))]);
if (child.exitCode === null && child.signalCode === null) {
child.kill("SIGKILL");
Expand Down
Loading