Files
NODEDC_MISSION_CORE/apps/control-station/test/applicationArchitecture.test.mjs
T

128 lines
4.6 KiB
JavaScript

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 laboratoryReportingCss = await read("styles/laboratory-reporting.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.match(laboratoryReportingCss, /\.laboratory-summary__brief/);
assert.match(laboratoryReportingCss, /\.laboratory-result-conclusion/);
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],
["core/laboratory/advancedResults.ts", 1_000],
["core/laboratory/e40ProductGate.ts", 500],
["styles/workspaces.css", 4_350],
["styles/laboratory.css", 900],
["styles/laboratory-reporting.css", 100],
];
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}`,
);
}
});