feat(lab): complete E30 evidence review gate
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readdir, readFile } from "node:fs/promises";
|
||||
import { extname, join } from "node:path";
|
||||
import { test } from "node:test";
|
||||
|
||||
const sourceRoot = new URL("../src/", import.meta.url);
|
||||
|
||||
async function sourceFiles(relativeDirectory) {
|
||||
const directory = new URL(`${relativeDirectory}/`, sourceRoot);
|
||||
const entries = await readdir(directory, {
|
||||
recursive: true,
|
||||
withFileTypes: true,
|
||||
});
|
||||
return entries
|
||||
.filter((entry) => entry.isFile() && [".ts", ".tsx"].includes(extname(entry.name)))
|
||||
.map((entry) => join(entry.parentPath, entry.name));
|
||||
}
|
||||
|
||||
async function read(relativePath) {
|
||||
return readFile(new URL(relativePath, sourceRoot), "utf8");
|
||||
}
|
||||
|
||||
test("dependency direction keeps core and reusable components below workspaces", async () => {
|
||||
const coreFiles = await sourceFiles("core");
|
||||
const componentFiles = await sourceFiles("components");
|
||||
|
||||
for (const file of coreFiles) {
|
||||
const source = await readFile(file, "utf8");
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
/from\s+["'][^"']*(?:workspaces|\/App)["']/,
|
||||
`${file} imports an application composition layer`,
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
/@nodedc\/ui-(?:react|dom)/,
|
||||
`${file} imports a visual adapter from the domain layer`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const file of componentFiles) {
|
||||
const source = await readFile(file, "utf8");
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
/from\s+["'][^"']*(?:workspaces|\/App)["']/,
|
||||
`${file} imports a workspace composition layer`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("shared visual primitives come only from the Design Guideline packages", async () => {
|
||||
const packageJson = JSON.parse(
|
||||
await readFile(new URL("../package.json", import.meta.url), "utf8"),
|
||||
);
|
||||
for (const packageName of [
|
||||
"@nodedc/tokens",
|
||||
"@nodedc/ui-core",
|
||||
"@nodedc/ui-react",
|
||||
]) {
|
||||
assert.equal(typeof packageJson.dependencies[packageName], "string");
|
||||
}
|
||||
|
||||
const files = await sourceFiles(".");
|
||||
for (const file of files) {
|
||||
const source = await readFile(file, "utf8");
|
||||
assert.doesNotMatch(source, /lucide-react/);
|
||||
assert.doesNotMatch(source, /NODEDC_DESIGN_GUIDELINE\/(?:apps|src)\//);
|
||||
}
|
||||
});
|
||||
|
||||
test("laboratory UI is a bounded feature slice, not a central workspace branch", async () => {
|
||||
const workspaceHub = await read("workspaces/Workspaces.tsx");
|
||||
const laboratory = await read(
|
||||
"workspaces/laboratory/LaboratoryArchiveWorkspace.tsx",
|
||||
);
|
||||
const workspaceCss = await read("styles/workspaces.css");
|
||||
const laboratoryCss = await read("styles/laboratory.css");
|
||||
const e30Review = await read("workspaces/E30ReviewWorkspace.tsx");
|
||||
const e30HumanReview = await read(
|
||||
"workspaces/E30HumanReviewPanel.tsx",
|
||||
);
|
||||
const e30HumanReviewCss = await read("styles/e30-human-review.css");
|
||||
|
||||
assert.doesNotMatch(
|
||||
workspaceHub,
|
||||
/fetchE(?:29|30)|LaboratoryWorkTemplate|function\s+E(?:29|30)LaboratoryResult/,
|
||||
);
|
||||
assert.match(
|
||||
workspaceHub,
|
||||
/<LaboratoryArchiveWorkspace[\s\S]*SpatialView=\{SpatialWorkspace\}/,
|
||||
);
|
||||
assert.match(laboratory, /export function LaboratoryArchiveWorkspace/);
|
||||
assert.match(laboratory, /type LaboratoryWorkspaceProps = WorkspaceRendererProps/);
|
||||
assert.doesNotMatch(workspaceCss, /\.(?:lab-|laboratory-|e30-)/);
|
||||
assert.match(laboratoryCss, /\.laboratory-work-template/);
|
||||
assert.match(laboratoryCss, /\.e30-review-workspace/);
|
||||
assert.doesNotMatch(laboratory, /E30HumanReviewPanel/);
|
||||
assert.match(e30Review, /<E30HumanReviewPanel/);
|
||||
assert.doesNotMatch(e30Review, /E30EngineeringAuditPanel/);
|
||||
assert.match(e30HumanReview, /from "@nodedc\/ui-react"/);
|
||||
assert.doesNotMatch(laboratoryCss, /\.e30-human-review/);
|
||||
assert.match(e30HumanReviewCss, /\.e30-human-review/);
|
||||
});
|
||||
|
||||
test("central composition files cannot silently become monoliths again", async () => {
|
||||
const ratchets = [
|
||||
["App.tsx", 1_250],
|
||||
["workspaces/Workspaces.tsx", 1_200],
|
||||
["workspaces/laboratory/LaboratoryArchiveWorkspace.tsx", 1_000],
|
||||
["styles/workspaces.css", 4_350],
|
||||
["styles/laboratory.css", 900],
|
||||
];
|
||||
|
||||
for (const [relativePath, maximumLines] of ratchets) {
|
||||
const lineCount = (await read(relativePath)).split("\n").length;
|
||||
assert.ok(
|
||||
lineCount <= maximumLines,
|
||||
`${relativePath} has ${lineCount} lines; split the feature instead of raising ${maximumLines}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user