106 lines
4.5 KiB
TypeScript
106 lines
4.5 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
import { assertCloudWebDistFresh, buildCloudWebDist, cloudWebDistAliasFiles, inspectCloudWebDistFreshness } from "./dist-contract.ts";
|
|
|
|
const DIST_CONTRACT_TEST_TIMEOUT_MS = 60_000;
|
|
|
|
test("Cloud Web dist build emits Vue assets and route aliases", { timeout: DIST_CONTRACT_TEST_TIMEOUT_MS }, async () => {
|
|
const rootDir = makeCloudWebFixture();
|
|
try {
|
|
const distDir = await buildCloudWebDist(rootDir);
|
|
assert.equal(distDir, path.join(rootDir, "dist"));
|
|
|
|
const html = read(path.join(distDir, "index.html"));
|
|
assert.match(html, /<div id="app"><\/div>/u);
|
|
assert.match(html, /src="\/app\.js"/u);
|
|
assert.match(html, /href="\/assets\/index\.css"/u);
|
|
assert.match(html, /href="\/assets\/favicon\.svg"/u);
|
|
assert.match(read(path.join(distDir, "app.js")), /fixture-app/u);
|
|
assert.match(read(path.join(distDir, "assets/index.css")), /fixture-shell/u);
|
|
assert.equal(read(path.join(distDir, "help.md")), read(path.join(rootDir, "help.md")));
|
|
|
|
for (const relativePath of cloudWebDistAliasFiles) {
|
|
assert.equal(read(path.join(distDir, relativePath)), html, `alias asset ${relativePath}`);
|
|
}
|
|
|
|
const freshness = await assertCloudWebDistFresh(rootDir);
|
|
assert.equal(freshness.status, "pass");
|
|
assert.deepEqual(freshness.mismatches, []);
|
|
} finally {
|
|
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("Cloud Web dist freshness reports stale generated app asset", { timeout: DIST_CONTRACT_TEST_TIMEOUT_MS }, async () => {
|
|
const rootDir = makeCloudWebFixture();
|
|
try {
|
|
await buildCloudWebDist(rootDir);
|
|
fs.writeFileSync(path.join(rootDir, "src/App.vue"), "<template><main>changed-app</main></template>\n");
|
|
|
|
const freshness = await inspectCloudWebDistFreshness(rootDir);
|
|
assert.equal(freshness.status, "blocked");
|
|
assert.ok(freshness.mismatches.includes("app.js"));
|
|
assert.equal(freshness.files.find((file) => file.path === "app.js")?.status, "mismatch");
|
|
} finally {
|
|
fs.rmSync(rootDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
function makeCloudWebFixture(): string {
|
|
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "hwlab-cloud-web-dist-"));
|
|
linkPackageNodeModules(rootDir);
|
|
write(rootDir, "package.json", JSON.stringify({ type: "module", dependencies: { "@vitejs/plugin-vue": "^5.2.3", vite: "5.4.10", vue: "^3.4.0" } }, null, 2));
|
|
write(rootDir, "index.html", [
|
|
"<!doctype html>",
|
|
"<html lang=\"zh-CN\">",
|
|
" <head>",
|
|
" <meta charset=\"UTF-8\" />",
|
|
" <link rel=\"icon\" href=\"/favicon.svg\" type=\"image/svg+xml\" />",
|
|
" <title>fixture</title>",
|
|
" </head>",
|
|
" <body><div id=\"app\"></div><script type=\"module\" src=\"/src/main.ts\"></script></body>",
|
|
"</html>",
|
|
""
|
|
].join("\n"));
|
|
write(rootDir, "vite.config.ts", [
|
|
"import vue from '@vitejs/plugin-vue';",
|
|
"export default {",
|
|
" plugins: [vue()],",
|
|
" build: {",
|
|
" outDir: 'dist',",
|
|
" emptyOutDir: true,",
|
|
" rollupOptions: { output: { entryFileNames: 'app.js', chunkFileNames: 'assets/[name].js', assetFileNames: 'assets/[name][extname]' } }",
|
|
" }",
|
|
"};",
|
|
""
|
|
].join("\n"));
|
|
write(rootDir, "src/main.ts", "import { createApp } from 'vue';\nimport App from './App.vue';\nimport './style.css';\ncreateApp(App).mount('#app');\n");
|
|
write(rootDir, "src/App.vue", "<template><main class=\"fixture-shell\">fixture-app</main></template>\n");
|
|
write(rootDir, "src/style.css", ".fixture-shell { color: #0f766e; }\n");
|
|
write(rootDir, "favicon.svg", "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1 1\"><rect width=\"1\" height=\"1\"/></svg>\n");
|
|
write(rootDir, "help.md", "# fixture help\n");
|
|
return rootDir;
|
|
}
|
|
|
|
function linkPackageNodeModules(rootDir: string): void {
|
|
const packageNodeModules = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "node_modules");
|
|
const fixtureNodeModules = path.join(rootDir, "node_modules");
|
|
if (!fs.existsSync(packageNodeModules) || fs.existsSync(fixtureNodeModules)) return;
|
|
fs.symlinkSync(packageNodeModules, fixtureNodeModules, "dir");
|
|
}
|
|
|
|
function write(rootDir: string, relativePath: string, text: string): void {
|
|
const filePath = path.join(rootDir, relativePath);
|
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
fs.writeFileSync(filePath, text);
|
|
}
|
|
|
|
function read(filePath: string): string {
|
|
return fs.readFileSync(filePath, "utf8");
|
|
}
|