test.rules/codemods/analyzers/field.analyzer.test.ts
test.rules/codemods/analyzers/field.analyzer.test.ts is a file in Codebase Testing. 69 lines of code and 0 definitions.
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { mkdtempSync, rmSync } from "node:fs";
import { writeCanonicalJson, writeCanonicalText } from "@govlab/canonical-write";
import type { FieldScope } from "@ssot/govlab/types/field.types.ts";
import { join } from "node:path";
import { scopeReach } from "@ssot/govlab/codemods/analyzers/field.analyzer.ts";
import { tmpdir } from "node:os";
const TYPES = [
"export interface Inner { deep: number; skipped: number }",
"export interface Carried { kept: string; dropped: string }",
"export interface Root { read: string; unread: string; nested: Inner[]; spread: Carried }",
"export interface View { kept: string }",
].join("\n");
const TYPES_FILE = "types.generated.ts";
const READER_FILE = "reader.generated.ts";
const CONFIG_FILE = "tsconfig.generated.json";
const READER = [
'import type { Root, View } from "./types.generated";',
"export const use = (root: Root): readonly unknown[] => [",
" root.read,",
" root.nested.map(({ deep }) => deep),",
" ((): View => ({ ...root.spread }))(),",
"];",
].join("\n");
let root = "";
const scope = function scope(roots: FieldScope["roots"]): FieldScope {
return {
label: "probe",
readerRoots: [join(root, READER_FILE)],
roots,
tsconfig: join(root, CONFIG_FILE),
typeRoots: [join(root, TYPES_FILE)],
};
};
beforeEach(async () => {
root = mkdtempSync(join(tmpdir(), "field-"));
await writeCanonicalText(join(root, TYPES_FILE), TYPES);
await writeCanonicalText(join(root, READER_FILE), READER);
await writeCanonicalJson(join(root, CONFIG_FILE), {
compilerOptions: { module: "esnext", noEmit: true, strict: true, target: "es2022" },
});
});
afterEach(() => {
rmSync(root, { force: true, recursive: true });
});
describe("scopeReach", () => {
it("collects every field reachable from the root and marks the ones a reader reads", () => {
const reach = scopeReach(scope([{ file: join(root, TYPES_FILE), name: "Root" }]));
const unread = reach.fields.map((field) => field.key).filter((key) => !reach.read.has(key));
expect(reach.fields.map((field) => field.key).sort()).toStrictEqual([
"Carried.dropped",
"Carried.kept",
"Inner.deep",
"Inner.skipped",
"Root.nested",
"Root.read",
"Root.spread",
"Root.unread",
]);
expect(unread.sort()).toStrictEqual(["Carried.dropped", "Inner.skipped", "Root.unread"]);
expect(reach.missingRoots).toStrictEqual([]);
});
it("names a root the file does not declare, and collects no field from it", () => {
const gone = { file: join(root, TYPES_FILE), name: "Gone" };
const reach = scopeReach(scope([gone]));
expect(reach.missingRoots).toStrictEqual([gone]);
expect(reach.fields).toStrictEqual([]);
});
});