# test.rules/codemods/selectors/program.selector.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-selectors-program-selector-test-ts
Source text: https://banes-lab.com/source/testing/test.rules/codemods/selectors/program.selector.test.ts.txt

Listed in [test.rules/codemods/selectors](https://banes-lab.com/api/source/testing/test.rules/codemods/selectors.md), after [test.rules/codemods/selectors/heritage.selector.test.ts](https://banes-lab.com/source/testing/test.rules/codemods/selectors/heritage.selector.test.ts.md).

## Contained in

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

## Source

```typescript
import {
    CODEMOD_TSCONFIGS,
    inRepo,
    lineOf,
    programFor,
    relPath,
    repoSourceFiles,
    toPosix,
} from "@ssot/govlab/codemods/selectors/program.selector.ts";
import { absolutePath, relativePath } from "@ssot/paths";
import { describe, expect, it } from "vitest";
import { join, sep } from "node:path";
import { existsSync } from "node:fs";
import ts from "typescript";

const MEMBER = absolutePath("app.member").split(sep).join("/");

describe("toPosix", () => {
    it("normalizes a platform path to forward slashes", () => {
        expect(toPosix(join("core", "registries", "probe.ts"))).toBe(["core", "registries", "probe.ts"].join("/"));
    });

    it("leaves an already-posix path untouched", () => {
        const value = ["core", "registries", "probe.ts"].join("/");
        expect(toPosix(value)).toBe(value);
    });
});

describe("relPath", () => {
    it("expresses a file inside the workspace relative to it", () => {
        expect(relPath(`${MEMBER}/index.ts`)).toBe(`${relativePath("app.member")}/index.ts`);
    });
});

describe("inRepo", () => {
    it("accepts a file inside the workspace", () => {
        expect(inRepo(`${MEMBER}/index.ts`)).toBe(true);
    });

    it("refuses an installed dependency, which the codemods never rewrite", () => {
        expect(inRepo(`${MEMBER}/node_modules/pkg/index.ts`)).toBe(false);
    });

    it("refuses a path outside the workspace", () => {
        expect(inRepo(["", "elsewhere", "index.ts"].join("/"))).toBe(false);
    });
});

describe("CODEMOD_TSCONFIGS", () => {
    it("covers one program per workspace member, since a member absent here is silently never scanned", () => {
        expect(CODEMOD_TSCONFIGS.length).toBeGreaterThan(0);
        expect(CODEMOD_TSCONFIGS).toContain(`${relativePath("app.member")}/tsconfig.json`);
        expect(CODEMOD_TSCONFIGS).toContain(`${relativePath("codebase.testing")}/tsconfig.json`);
        expect(CODEMOD_TSCONFIGS.every((entry) => entry.endsWith("/tsconfig.json"))).toBe(true);
        expect(CODEMOD_TSCONFIGS.every((entry) => existsSync(entry))).toBe(true);
    });
});

describe("programFor and repoSourceFiles", () => {
    it("builds a program whose repo sources exclude declarations and dependencies", () => {
        const program = programFor(`${relativePath("app.member")}/tsconfig.json`);
        const sources = repoSourceFiles(program);
        expect(sources.every((file) => !file.isDeclarationFile)).toBe(true);
        expect(sources.every((file) => inRepo(file.fileName))).toBe(true);
    });
});

describe("lineOf", () => {
    it("reports a one-based line for a node", () => {
        const source = ts.createSourceFile("probe.ts", "const a = 1;\nconst b = 2;", ts.ScriptTarget.Latest, true);
        const [, second] = source.statements;
        expect(second === undefined ? 0 : lineOf(source, second)).toBe(2);
    });
});
```
