Files
HiveGrid-Wireframes/hivegrid-cloud/scripts/verify-db-legacy-markup.test.mjs
T

137 lines
4.5 KiB
JavaScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import assert from "node:assert/strict";
import { afterEach, describe, it } from "vitest";
import {
formatDbLegacyMarkupResult,
listDbLegacyFiles,
verifyDbLegacyMarkup,
} from "./verify-db-legacy-markup.mjs";
const fixtures = [];
function writeFile(rootDir, relativePath, content) {
const absolutePath = path.join(rootDir, relativePath);
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
fs.writeFileSync(absolutePath, content);
}
function createFixture(files = {}) {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "db-legacy-markup-"));
fixtures.push(rootDir);
writeFile(
rootDir,
"apps/web/app/utils/db/users.db.server.ts",
files["apps/web/app/utils/db/users.db.server.ts"] ?? `type Payload = Promise<{ id: string; ok: boolean }>;
export async function listUsers(): Payload {
return { id: "user_1", ok: true };
}
`,
);
for (const [relativePath, content] of Object.entries(files)) {
writeFile(rootDir, relativePath, content);
}
return rootDir;
}
afterEach(() => {
while (fixtures.length > 0) {
fs.rmSync(fixtures.pop(), { recursive: true, force: true });
}
});
describe("verify-db-legacy-markup", () => {
it("lists DB legacy TypeScript files recursively", () => {
const rootDir = createFixture({
"apps/web/app/utils/db/entities/rows.db.server.ts": "export const rows = [];\n",
"apps/web/app/utils/db/entities/notes.md": "ignored\n",
});
assert.deepEqual(listDbLegacyFiles(rootDir), [
"apps/web/app/utils/db/entities/rows.db.server.ts",
"apps/web/app/utils/db/users.db.server.ts",
]);
});
it("passes clean DB files and generic object type annotations", () => {
const rootDir = createFixture({
"apps/web/app/utils/db/users.db.server.ts": `type Payload = Promise<{ id: string; ok: boolean }>;
type Selection<T extends { id: string }> = Pick<T, "id">;
type Lookup = Record<string, Array<Partial<{ value: number }>>>;
export async function listUsers<T extends { id: string }>(rows: T[]): Promise<Selection<T>[]> {
return rows.map((row) => ({ id: row.id }));
}
export function compare(left: number, right: number) {
return left < right && right > 0;
}
`,
});
const result = verifyDbLegacyMarkup(rootDir);
assert.deepEqual(result.failures, []);
assert.equal(result.filesScanned, 1);
assert.match(formatDbLegacyMarkupResult(result), /PASS DB legacy markup verification/);
});
it("fails when DB files import React or React subpaths", () => {
const rootDir = createFixture({
"apps/web/app/utils/db/users.db.server.ts": `import type { ReactNode } from "react";
import { cache } from "react/cache";
export type UserNode = ReactNode;
export const loader = cache(() => null);
`,
});
const result = verifyDbLegacyMarkup(rootDir);
assert.match(result.failures.join("\n"), /app\/utils\/db\/users\.db\.server\.ts: forbidden React import/);
assert.match(result.failures.join("\n"), /app\/utils\/db\/users\.db\.server\.ts: forbidden ReactNode type/);
});
it("fails when a TSX file enters the DB legacy boundary", () => {
const rootDir = createFixture({
"apps/web/app/utils/db/render.db.server.tsx": "export function Render() { return <div />; }\n",
});
const result = verifyDbLegacyMarkup(rootDir);
assert.match(result.failures.join("\n"), /app\/utils\/db\/render\.db\.server\.tsx: forbidden \.tsx file/);
});
it("fails on dangerouslySetInnerHTML markers without treating HTML strings as markup", () => {
const rootDir = createFixture({
"apps/web/app/utils/db/users.db.server.ts": "export const html = { dangerouslySetInnerHTML: { __html: '<b>x</b>' } };\n",
});
const result = verifyDbLegacyMarkup(rootDir);
assert.match(result.failures.join("\n"), /forbidden dangerouslySetInnerHTML marker/);
assert.doesNotMatch(result.failures.join("\n"), /forbidden JSX-like markup tag/);
});
it("fails on JSX type annotations and JSX-like markup in TypeScript files", () => {
const rootDir = createFixture({
"apps/web/app/utils/db/users.db.server.ts": `export type View = JSX.Element;
export type Fragment = JSXFragment;
export const element = <span data-id={row.id} />;
`,
});
const result = verifyDbLegacyMarkup(rootDir);
assert.match(result.failures.join("\n"), /forbidden JSX\.Element type/);
assert.match(result.failures.join("\n"), /forbidden JSXFragment type/);
assert.match(result.failures.join("\n"), /forbidden JSX-like markup tag/);
});
});