# test.rules/codemods/analyzers/vocabulary.analyzer.test.ts

> 64 lines of code and 0 definitions.

Tree: Codebase Testing
Language: typescript
Canonical: https://banes-lab.com/anatomy/testing#file-testing-test-rules-codemods-analyzers-vocabulary-analyzer-test-ts
Source text: https://banes-lab.com/source/testing/test.rules/codemods/analyzers/vocabulary.analyzer.test.ts.txt

Listed in [test.rules/codemods/analyzers](https://banes-lab.com/api/source/testing/test.rules/codemods/analyzers.md), after [test.rules/codemods/analyzers/specifier.target.analyzer.test.ts](https://banes-lab.com/source/testing/test.rules/codemods/analyzers/specifier.target.analyzer.test.ts.md) and before [test.rules/codemods/analyzers/word.analyzer.test.ts](https://banes-lab.com/source/testing/test.rules/codemods/analyzers/word.analyzer.test.ts.md).

## Contained in

- [test.rules/codemods/analyzers](https://banes-lab.com/anatomy/testing/folder-testing-test-rules-codemods-analyzers.md)

## Source

````typescript
import {
    collectVocabularyFindings,
    vocabularyFindingsIn,
} from "@ssot/govlab/codemods/analyzers/vocabulary.analyzer.ts";
import { describe, expect, it } from "vitest";
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
import { join } from "node:path";
import { pathExclusion } from "@govlab/quality/config";
import { tmpdir } from "node:os";
import { writeVerbatim } from "@govlab/canonical-write";

const renamed = function renamed(fileName: string, content: string, wholeIds = false): string[] {
    return vocabularyFindingsIn(fileName, content, wholeIds).map((finding) => `${finding.from}>${finding.to}`);
};

describe("vocabularyFindingsIn", () => {
    it("renames a retired id in a reference, an anchor, a catalog path and a collection name", () => {
        expect(renamed("probe.ts", 'const a = "arch:x"; const b = "#lex-y"; const c = "/records/algo/z.md";')).toEqual([
            "arch>architecture",
            "lex>lexicon",
            "algo>algorithms",
        ]);
        expect(renamed("probe.ts", 'const d = "The reason collection holds the loop.";')).toEqual(["reason>reasoning"]);
    });

    it("renames a renamed word in prose and keeps its case, only where the literal is prose", () => {
        expect(renamed("probe.ts", 'const a = "Each face has a Markdown twin."; const b = "face";')).toEqual([
            "face>collection",
            "twin>alternate",
        ]);
    });

    it("renames a literal that is exactly an id only where whole ids are declared, and never a field name", () => {
        const code = 'const a = { face: "arch" }; const b = x["arch"]; const c = "arch" in y;';
        expect(renamed("probe.ts", code)).toEqual([]);
        expect(renamed("probe.ts", code, true)).toEqual(["arch>architecture"]);
    });

    it("leaves module specifiers, JSON keys and the words of a hyphenated identifier as written", () => {
        expect(renamed("probe.ts", 'import x from "./arch:y";')).toEqual([]);
        expect(renamed("probe.json", '{ "arch:x": "lex:y" }')).toEqual(["lex>lexicon"]);
        expect(renamed("probe.ts", 'const a = "the font-face rule and a face_value";')).toEqual([]);
    });

    it("reads shapes everywhere in Markdown and words only outside code", () => {
        const content = "the `face` field and `arch:x`\n```\nface\n```\na face";
        expect(renamed("probe.md", content)).toEqual(["arch>architecture", "face>collection"]);
    });
});

describe("collectVocabularyFindings", () => {
    it("walks the roots, skips excluded folders, skipped paths and generated files, and marks whole-id roots", () => {
        const root = mkdtempSync(join(tmpdir(), "vocabulary-"));
        mkdirSync(join(root, "excluded"));
        mkdirSync(join(root, "ids"));
        writeVerbatim(join(root, "a.ts"), 'const a = "arch";\n');
        writeVerbatim(join(root, "ids", "b.ts"), 'const b = "arch";\n');
        writeVerbatim(join(root, "c.generated.ts"), 'const c = "arch:x";\n');
        writeVerbatim(join(root, "excluded", "d.ts"), 'const d = "arch:x";\n');
        writeVerbatim(join(root, "e.ts"), 'const e = "arch:x";\n');
        const found = collectVocabularyFindings({
            extensions: [".ts"],
            roots: [root],
            skipped: pathExclusion(root, ["excluded", "*.generated.*"]),
            skippedPaths: [join(root, "e.ts")],
            wholeIdRoots: [join(root, "ids")],
        }).map((finding) => finding.file.slice(finding.file.lastIndexOf("/") + 1));
        rmSync(root, { force: true, recursive: true });
        expect(found).toEqual(["b.ts"]);
    });
});
````
