73 lines
3.7 KiB
TypeScript
73 lines
3.7 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
import { messageFromError } from "../src/composables/useForm.ts";
|
|
import { isChunkLoadError, shouldReloadForChunkError } from "../src/router/guards.ts";
|
|
import { resolveDocumentTitle } from "../src/router/title.ts";
|
|
|
|
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
|
test("R6 route meta title and chunk recovery match Sub2API navigation pattern", () => {
|
|
assert.equal(resolveDocumentTitle("Gate"), "Gate - HWLAB Cloud Web");
|
|
assert.equal(resolveDocumentTitle(" "), "平台控制台 - HWLAB Cloud Web");
|
|
assert.equal(isChunkLoadError(new Error("Failed to fetch dynamically imported module")), true);
|
|
assert.equal(isChunkLoadError(new Error("Loading CSS chunk 42 failed")), true);
|
|
assert.equal(isChunkLoadError(new Error("Unable to preload CSS for /assets/MdtodoView.css")), true);
|
|
assert.equal(isChunkLoadError(new Error("ordinary route guard failure")), false);
|
|
|
|
const memory = new Map<string, string>();
|
|
const storage = {
|
|
getItem: (key: string) => memory.get(key) ?? null,
|
|
setItem: (key: string, value: string) => { memory.set(key, value); }
|
|
};
|
|
assert.equal(shouldReloadForChunkError(100_000, storage), true);
|
|
assert.equal(shouldReloadForChunkError(105_000, storage), false);
|
|
assert.equal(shouldReloadForChunkError(111_000, storage), true);
|
|
});
|
|
|
|
test("R6 useForm normalizes nested API errors for toast/error surfaces", () => {
|
|
assert.equal(messageFromError(new Error("plain failure")), "plain failure");
|
|
assert.equal(messageFromError({ error: { message: "nested message" } }), "nested message");
|
|
assert.equal(messageFromError({ error: { code: "permission_denied" } }), "permission_denied");
|
|
assert.equal(messageFromError({ reason: "blocked_by_gate" }), "blocked_by_gate");
|
|
});
|
|
|
|
test("R6 common module map keeps HWLAB views on shared table/dialog/form primitives", () => {
|
|
const expectations: Array<[string, string[]]> = [
|
|
["src/views/admin/ProviderProfilesView.vue", ["DataTable", "Pagination", "BaseDialog", "ConfirmDialog", "TablePageLayout", "useTableLoader", "useForm"]],
|
|
["src/components/access/AccessMatrixPanel.vue", ["DataTable", "TablePageLayout"]],
|
|
["src/views/GateView.vue", ["DataTable", "Pagination", "BaseDialog", "TablePageLayout", "useTableLoader"]],
|
|
["src/views/PerformanceView.vue", ["DataTable", "TablePageLayout", "useTableLoader"]],
|
|
["src/views/SkillsView.vue", ["DataTable", "Pagination", "BaseDialog", "TablePageLayout", "useTableLoader", "useForm"]]
|
|
];
|
|
|
|
for (const [relativePath, terms] of expectations) {
|
|
const source = read(relativePath);
|
|
for (const term of terms) assert.match(source, new RegExp(`\\b${term}\\b`, "u"), `${relativePath} should use ${term}`);
|
|
assert.doesNotMatch(source, /<table\s/u, `${relativePath} should not regress to local raw table markup`);
|
|
}
|
|
});
|
|
|
|
test("R6 Sub2API source comparison is recorded in parent/child issue scope", () => {
|
|
const issueBody = readIssueBodyFixture();
|
|
assert.match(issueBody, /Wei-Shaw\/sub2api@v0\.1\.136=a2f76e4/u);
|
|
assert.match(issueBody, /router\/index\.ts/u);
|
|
assert.match(issueBody, /components\/common/u);
|
|
assert.match(issueBody, /useTableLoader\.ts/u);
|
|
assert.match(issueBody, /useForm\.ts/u);
|
|
});
|
|
|
|
function read(relativePath: string): string {
|
|
return fs.readFileSync(path.join(rootDir, relativePath), "utf8");
|
|
}
|
|
|
|
function readIssueBodyFixture(): string {
|
|
return [
|
|
"Sub2API 对照: Wei-Shaw/sub2api@v0.1.136=a2f76e4",
|
|
"router/index.ts api/client.ts components/common/* composables/useTableLoader.ts useForm.ts admin/user CRUD views"
|
|
].join("\n");
|
|
}
|