test.rules/shared/selectors/literal.selector.test.ts
test.rules/shared/selectors/literal.selector.test.ts is a file in Codebase Testing. 158 lines of code and 0 definitions.
import {
CODE_KINDS,
CODE_SLOTS,
DIAGRAM_KINDS,
boundCopyOf,
boundInitOf,
codeLiteralsOf,
copyTextOf,
diagramLiteralsOf,
templateShapeOf,
} from "@ssot/govlab/shared/selectors/literal.selector.ts";
import { Linter, type Rule } from "eslint";
import { asNode, literalString, nodeAt, walk } from "@ssot/govlab/shared/selectors/syntax.selector.ts";
import { describe, expect, it } from "vitest";
import type { AstNode } from "@ssot/govlab/types/syntax.types.ts";
import { listener } from "@ssot/govlab/shared/factories/listener.factory.ts";
const linter = new Linter();
const programOf = function programOf(code: string): AstNode {
let program: unknown = null;
const capture = {
create() {
return listener({
program(_view, node) {
program = node;
},
});
},
meta: { messages: {}, schema: [], type: "problem" as const },
};
linter.verify(
code,
[
{
files: ["**/*.ts"],
languageOptions: { ecmaVersion: 2025 as const, sourceType: "module" as const },
plugins: { t: { rules: { r: capture } } },
rules: { "t/r": "error" as const },
},
],
"probe.ts",
);
const node = asNode(program);
if (node === null) {
throw new Error("the capture rule did not observe a Program node");
}
return node;
};
const SOURCE = [
`const SAMPLE = "held sample";`,
`const PROSE = "held prose";`,
`export const A = { code: SAMPLE, kind: "code" };`,
`export const B = { code: "inline sample", kind: "code" };`,
`export const C = { text: PROSE, title: "a title" };`,
`export const D = { grammar: "<rule> ::= <term>", title: "a rule" };`,
`const FIGURE = "[a]-->[b]";`,
`export const E = { kind: "diagram", text: FIGURE };`,
`export const F = { kind: "paragraph", text: "plain prose" };`,
].join("\n");
describe("codeLiteralsOf", () => {
const program = programOf(SOURCE);
const consumed = codeLiteralsOf(program);
const consumedTexts = [...consumed].map((node) => literalString(node) ?? "").toSorted();
it("collects a literal consumed inline by a code slot and one reaching it through a module-local const", () => {
expect(consumedTexts).toStrictEqual(["<rule> ::= <term>", "[a]-->[b]", "held sample", "inline sample"]);
expect(CODE_SLOTS.has("grammar")).toBe(true);
expect(CODE_KINDS.has("diagram")).toBe(true);
});
it("leaves every other literal of the module as prose", () => {
const prose: string[] = [];
walk(program, (node) => {
const text = literalString(node);
if (text !== null && !consumed.has(node)) {
prose.push(text);
}
});
expect(prose).toContain("held prose");
expect(prose).toContain("a title");
expect(prose).toContain("plain prose");
expect(prose).not.toContain("held sample");
expect(prose).not.toContain("[a]-->[b]");
});
});
describe("copyTextOf", () => {
it("reads static text whole, a worded template with its holes marked, and no text from a template of values", () => {
const texts: string[] = [];
const code = ["const a = `Links to ", "{x}.`; const b = `", "{x}/", "{y}.md`; const c = `Plain`;"].join("$");
walk(programOf(code), (node) => {
const text = node.type === "TemplateLiteral" ? copyTextOf(node) : null;
if (text !== null) {
texts.push(text);
}
});
expect(texts.toSorted()).toStrictEqual(["Links to ….", "Plain"]);
});
});
describe("templateShapeOf", () => {
it("joins a template's text parts with a mark for each hole, and gives nothing for another node", () => {
const shapes: string[] = [];
const code = ["const a = `https://h:", "{p}/x/", "{t}`; const b = 3;"].join("$");
walk(programOf(code), (node) => {
const shape = templateShapeOf(node);
if (shape !== null) {
shapes.push(shape);
}
});
expect(shapes).toStrictEqual(["https://h:…/x/…"]);
});
});
describe("boundInitOf and boundCopyOf", () => {
it("finds what a name is bound to in the nearest scope that declares it, and nothing for a parameter", () => {
const found: (string | null)[] = [];
const copies: (string | null)[] = [];
const capture = {
create(context: Rule.RuleContext) {
return listener({
returnStatement(view, node) {
const scope = context.sourceCode.getScope(node);
for (const name of ["title", "outer", "id", "missing"]) {
found.push(literalString(boundInitOf(scope, name)));
}
const copyOf = boundCopyOf(scope);
const copy = copyOf(nodeAt(view, "argument"));
copies.push(literalString(copy));
},
});
},
meta: { messages: {}, schema: [], type: "problem" as const },
};
linter.verify(
[
'const outer = "Outer text";',
'const title = "Shadowed";',
'const f = (id) => { const title = "Inner text"; return id; };',
'const g = () => { const title = "Bound copy"; return title; };',
].join("\n"),
[
{
files: ["**/*.ts"],
languageOptions: { ecmaVersion: 2025 as const, sourceType: "module" as const },
plugins: { t: { rules: { r: capture } } },
rules: { "t/r": "error" as const },
},
],
"probe.ts",
);
expect(found).toStrictEqual(["Inner text", "Outer text", null, null, "Bound copy", "Outer text", null, null]);
expect(copies).toStrictEqual([null, "Bound copy"]);
expect(boundInitOf(null, "title")).toBeNull();
});
});
describe("diagramLiteralsOf", () => {
it("collects only the literals a diagram block consumes, inline or through a module-local const", () => {
const diagrams = [...diagramLiteralsOf(programOf(SOURCE))].map((node) => literalString(node) ?? "");
expect(diagrams).toStrictEqual(["[a]-->[b]"]);
expect(DIAGRAM_KINDS.has("mermaid")).toBe(true);
expect(DIAGRAM_KINDS.has("code")).toBe(false);
});
});