test.rules/rules/eslint/strings-diagram-identifiers.eslint.rule.test.ts
test.rules/rules/eslint/strings-diagram-identifiers.eslint.rule.test.ts is a file in Codebase Testing. 71 lines of code and 0 definitions.
import { DIAGRAM_HEADERS, DIAGRAM_KEYWORDS } from "@ssot/govlab/shared/manifests/diagram.manifest.ts";
import { describe, expect, it } from "vitest";
import { isDiagramSource, reservedNodeIdsOf } from "@ssot/govlab/shared/analyzers/diagram.analyzer.ts";
import { Linter } from "eslint";
import stringsDiagramIdentifiers from "@ssot/govlab/rules/eslint/strings-diagram-identifiers.eslint.rule.ts";
const linter = new Linter();
const config: Linter.Config = {
files: ["**/*.ts"],
languageOptions: { ecmaVersion: 2025, sourceType: "module" },
plugins: { local: { rules: { "strings-diagram-identifiers": stringsDiagramIdentifiers } } },
rules: { "local/strings-diagram-identifiers": "error" },
};
const STRINGS_FILE = "src/page.strings.ts";
const OTHER_FILE = "src/element.factory.ts";
const NL = String.fromCodePoint(10);
const lint = function lint(code: string, filename: string): Linter.LintMessage[] {
return linter.verify(code, config, { filename });
};
const diagram = function diagram(...lines: readonly string[]): string {
return ["flowchart TB", ...lines.map((line) => ` ${line}`)].join(NL);
};
const stringsModule = function stringsModule(source: string): string {
return `const D = ${JSON.stringify(source)};${NL}export const S = { caption: "c", kind: "mermaid", text: D };`;
};
describe("reservedNodeIdsOf", () => {
it("reports a keyword declared as a node or used as an edge end", () => {
const found = reservedNodeIdsOf(
diagram('a["A"]', 'class["One class"]', "a --> class", "end --- a", "a -->|label| style"),
);
expect(found.map((hit) => `${hit.line}:${hit.word}`)).toStrictEqual(["3:class", "4:class", "5:end", "6:style"]);
});
it("accepts the keywords in their own positions and inside labels, brackets and quotes", () => {
const found = reservedNodeIdsOf(
diagram(
'subgraph core["The end of the class"]',
'subgraph graph["A group may carry the word"]',
"direction TB",
'inner["default"]',
"end",
"a -- the default --> b",
"a -. by class .-> b",
"class a b important",
"style a fill:#fff",
),
);
expect(found).toStrictEqual([]);
});
it("recognizes a diagram by its header and holds the keyword set in data", () => {
expect(isDiagramSource("flowchart LR")).toBe(true);
expect(isDiagramSource("Plain prose")).toBe(false);
expect(DIAGRAM_HEADERS.has("graph")).toBe(true);
expect(DIAGRAM_KEYWORDS.has("class")).toBe(true);
});
});
describe("strings-diagram-identifiers", () => {
it("reports a reserved node identifier in a diagram block of a strings module, through the named const", () => {
const msgs = lint(stringsModule(diagram('class["One class"]', "a --> class")), STRINGS_FILE);
expect(msgs).toHaveLength(2);
expect(msgs.every((msg) => msg.messageId === "reservedNodeId")).toBe(true);
});
it("accepts a diagram whose node identifiers avoid the keywords", () => {
const clean = stringsModule(diagram('decay["One class"]', "a --> decay"));
expect(lint(clean, STRINGS_FILE)).toHaveLength(0);
});
it("stays silent outside a strings module and outside a diagram block", () => {
const reserved = stringsModule(diagram('class["One class"]'));
const outsideBlock = JSON.stringify(diagram('class["x"]'));
expect(lint(reserved, OTHER_FILE)).toHaveLength(0);
expect(lint(`export const X = ${outsideBlock};`, STRINGS_FILE)).toHaveLength(0);
});
});