test(bundler): improve test for compile asset paths

- Skip test in debug builds (compile is unreliable with debug builds)
- Use fixed port instead of parsing output (simpler and more reliable)
- Use HTTP polling to wait for server ready
- Use await using for automatic process cleanup
- Add timeout to handle slow builds

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Bot
2026-01-19 23:57:42 +00:00
parent b033c3defe
commit aa2ff1bbf7

View File

@@ -1,13 +1,16 @@
import { expect, test } from "bun:test";
import fs from "fs";
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
import { bunEnv, bunExe, isDebug, tempDirWithFiles } from "harness";
import path from "path";
// https://github.com/oven-sh/bun/issues/24180
// When building fullstack apps with --compile, asset paths in HTML should be
// absolute (starting with `/`) not relative (starting with `./`). Relative paths
// cause 404 errors when navigating to routes other than `/`.
test("fullstack compile uses absolute asset paths in generated HTML", async () => {
test.skipIf(isDebug)("fullstack compile uses absolute asset paths in generated HTML", { timeout: 60_000 }, async () => {
// Use a fixed port for testing - the compiled app will use this
const port = 49842;
const dir = tempDirWithFiles("24180", {
"index.html": `
<!DOCTYPE html>
@@ -33,7 +36,7 @@ console.log("loaded");
import html from "./index.html";
export default {
port: 0,
port: ${port},
static: {
"/": html,
"/about": html,
@@ -66,69 +69,58 @@ export default {
expect(buildExitCode).toBe(0);
expect(fs.existsSync(outfile)).toBe(true);
// Run the compiled executable
const serverProc = Bun.spawn({
// Run the compiled executable with await using for automatic cleanup
await using serverProc = Bun.spawn({
cmd: [outfile],
cwd: dir,
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
stdout: "inherit",
stderr: "inherit",
});
// Wait for the server to start and capture the port
const reader = serverProc.stdout.getReader();
let output = "";
let port: number | null = null;
while (true) {
const { done, value } = await reader.read();
if (done) break;
output += new TextDecoder().decode(value);
const match = output.match(/localhost:(\d+)/);
if (match) {
port = parseInt(match[1], 10);
// Wait for server to be ready by polling the HTTP endpoint
let res: Response | undefined;
const deadline = Date.now() + 10_000;
while (Date.now() < deadline) {
try {
res = await fetch(`http://localhost:${port}/`);
break;
} catch {
await Bun.sleep(100);
}
}
expect(port).not.toBeNull();
try {
// Fetch the HTML from the root route
const res = await fetch(`http://localhost:${port}/`);
const html = await res.text();
// Check that the CSS and JS paths are absolute, not relative
// They should start with `/` not `./`
const cssMatch = html.match(/href="([^"]+\.css)"/);
const jsMatch = html.match(/src="([^"]+\.js)"/);
expect(cssMatch).not.toBeNull();
expect(jsMatch).not.toBeNull();
const cssPath = cssMatch![1];
const jsPath = jsMatch![1];
// Verify paths are absolute (start with /)
expect(cssPath.startsWith("/")).toBe(true);
expect(jsPath.startsWith("/")).toBe(true);
// Verify paths don't use relative notation
expect(cssPath.startsWith("./")).toBe(false);
expect(jsPath.startsWith("./")).toBe(false);
// Also verify the assets are actually accessible
const cssRes = await fetch(`http://localhost:${port}${cssPath}`);
expect(cssRes.status).toBe(200);
const cssContent = await cssRes.text();
expect(cssContent).toContain("background");
const jsRes = await fetch(`http://localhost:${port}${jsPath}`);
expect(jsRes.status).toBe(200);
const jsContent = await jsRes.text();
expect(jsContent).toContain("loaded");
} finally {
serverProc.kill();
await serverProc.exited;
if (!res) {
throw new Error("Server did not start within timeout");
}
const html = await res.text();
// Check that the CSS and JS paths are absolute, not relative
// They should start with `/` not `./`
const cssMatch = html.match(/href="([^"]+\.css)"/);
const jsMatch = html.match(/src="([^"]+\.js)"/);
expect(cssMatch).not.toBeNull();
expect(jsMatch).not.toBeNull();
const cssPath = cssMatch![1];
const jsPath = jsMatch![1];
// Verify paths are absolute (start with /)
expect(cssPath.startsWith("/")).toBe(true);
expect(jsPath.startsWith("/")).toBe(true);
// Verify paths don't use relative notation
expect(cssPath.startsWith("./")).toBe(false);
expect(jsPath.startsWith("./")).toBe(false);
// Also verify the assets are actually accessible
const cssRes = await fetch(`http://localhost:${port}${cssPath}`);
expect(cssRes.status).toBe(200);
const cssContent = await cssRes.text();
expect(cssContent).toContain("background");
const jsRes = await fetch(`http://localhost:${port}${jsPath}`);
expect(jsRes.status).toBe(200);
const jsContent = await jsRes.text();
expect(jsContent).toContain("loaded");
});