0f0effb3c3
Reduce the root TanStack route to orchestration and shell state. Move backend-to-view mapping and binary invocation formatting into console-model, presentation views into components/views, and modal workflows into components/dialogs. Update the rendered artifact test to assert behavior at the new module boundaries without changing the generated UI or routes.
128 lines
4.2 KiB
JavaScript
128 lines
4.2 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { spawn } from "node:child_process";
|
|
import { access, readFile } from "node:fs/promises";
|
|
import { createServer } from "node:net";
|
|
import { fileURLToPath } from "node:url";
|
|
import test, { after, before } from "node:test";
|
|
|
|
let serverProcess;
|
|
let serverOrigin;
|
|
|
|
async function reservePort() {
|
|
const server = createServer();
|
|
await new Promise((resolve, reject) => {
|
|
server.once("error", reject);
|
|
server.listen(0, "127.0.0.1", resolve);
|
|
});
|
|
const address = server.address();
|
|
assert(address && typeof address === "object");
|
|
await new Promise((resolve, reject) => {
|
|
server.close((error) => (error ? reject(error) : resolve()));
|
|
});
|
|
return address.port;
|
|
}
|
|
|
|
before(async () => {
|
|
const port = await reservePort();
|
|
serverOrigin = `http://127.0.0.1:${port}`;
|
|
serverProcess = spawn(
|
|
process.execPath,
|
|
[
|
|
fileURLToPath(new URL("../node_modules/srvx/bin/srvx.mjs", import.meta.url)),
|
|
"serve",
|
|
"--prod",
|
|
"--entry",
|
|
"dist/server/server.js",
|
|
"--static",
|
|
"../client",
|
|
],
|
|
{
|
|
env: {
|
|
...process.env,
|
|
HOST: "127.0.0.1",
|
|
PORT: String(port),
|
|
},
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
},
|
|
);
|
|
|
|
for (let attempt = 0; attempt < 50; attempt += 1) {
|
|
if (serverProcess.exitCode !== null) {
|
|
throw new Error(`Node server exited with code ${serverProcess.exitCode}`);
|
|
}
|
|
try {
|
|
const response = await fetch(serverOrigin);
|
|
if (response.ok) return;
|
|
} catch {
|
|
// The server is still starting.
|
|
}
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
}
|
|
throw new Error("Node server did not become ready");
|
|
});
|
|
|
|
after(() => {
|
|
serverProcess?.kill("SIGTERM");
|
|
});
|
|
|
|
test("server-renders the Wasm management console", async () => {
|
|
const response = await fetch(serverOrigin, {
|
|
headers: { accept: "text/html" },
|
|
});
|
|
assert.equal(response.status, 200);
|
|
assert.match(response.headers.get("content-type") ?? "", /^text\/html\b/i);
|
|
|
|
const html = await response.text();
|
|
assert.match(html, /<title>Wasmeld Console<\/title>/i);
|
|
assert.match(html, /运行概览/);
|
|
assert.match(html, /WIT 包/);
|
|
assert.match(html, /连接中/);
|
|
assert.match(html, /0 个对外服务/);
|
|
assert.match(html, /Component Model/);
|
|
assert.doesNotMatch(html, /本地演示|codex-preview|vinext|next\.js/i);
|
|
});
|
|
|
|
test("uses TanStack Start routing and produces Node artifacts", async () => {
|
|
const [
|
|
rootRoute,
|
|
indexRoute,
|
|
views,
|
|
dialogs,
|
|
consoleModel,
|
|
apiClient,
|
|
router,
|
|
packageJson,
|
|
viteConfig,
|
|
] = await Promise.all([
|
|
readFile(new URL("../src/routes/__root.tsx", import.meta.url), "utf8"),
|
|
readFile(new URL("../src/routes/index.tsx", import.meta.url), "utf8"),
|
|
readFile(new URL("../src/components/views.tsx", import.meta.url), "utf8"),
|
|
readFile(new URL("../src/components/dialogs.tsx", import.meta.url), "utf8"),
|
|
readFile(new URL("../src/console-model.ts", import.meta.url), "utf8"),
|
|
readFile(new URL("../src/api.ts", import.meta.url), "utf8"),
|
|
readFile(new URL("../src/router.tsx", import.meta.url), "utf8"),
|
|
readFile(new URL("../package.json", import.meta.url), "utf8"),
|
|
readFile(new URL("../vite.config.ts", import.meta.url), "utf8"),
|
|
]);
|
|
|
|
assert.match(rootRoute, /createRootRoute/);
|
|
assert.match(rootRoute, /<HeadContent \/>/);
|
|
assert.match(rootRoute, /<Scripts \/>/);
|
|
assert.match(indexRoute, /createFileRoute\("\/"\)/);
|
|
assert.match(dialogs, /切换对外版本/);
|
|
assert.match(views, /Host 能力/);
|
|
assert.match(views, /service\.capabilities/);
|
|
assert.match(consoleModel, /TextDecoder\("utf-8", \{ fatal: true \}\)/);
|
|
assert.match(dialogs, /outputFormat\.automatic/);
|
|
assert.match(apiClient, /\/api\/v1\/deployments/);
|
|
assert.match(apiClient, /capabilities: BackendCapability\[\]/);
|
|
assert.match(router, /createRouter/);
|
|
assert.match(packageJson, /"@tanstack\/react-start"/);
|
|
assert.match(packageJson, /"srvx"/);
|
|
assert.doesNotMatch(packageJson, /"next"|"vinext"|"drizzle-orm"/);
|
|
assert.match(viteConfig, /tanstackStart\(\{/);
|
|
|
|
await access(new URL("../dist/server/server.js", import.meta.url));
|
|
await access(new URL("../dist/client/og.png", import.meta.url));
|
|
});
|