Ctrl K
ComunidadGuías…

test/scripts/unit-tests-report.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { expect } from "@std/expect";
import { describe, it as test } from "@std/testing/bdd";
import {
  buildReport,
  countLines,
  DEFAULT_LIMIT,
  DEFAULT_OPTIONS,
  isSourcePath,
  isTestableSource,
  isTestPath,
  mirrorPrefix,
  owningSourcePrefix,
  suggestedTarget,
  toJsonReport,
} from "#scripts/unit-tests-report-lib.ts";
import { options, src, tst } from "./unit-tests-report-fixtures.ts";

describe("countLines", () => {
  test("counts non-blank lines only", () => {
    // Multi-character lines distinguish a real newline split from a per-char one.
    expect(countLines("ab\n\n  \ncd\n")).toBe(2);
    expect(countLines("")).toBe(0);
  });
});

describe("isSourcePath", () => {
  test("accepts .ts and .tsx but not declaration or other files", () => {
    expect(isSourcePath("src/a.ts")).toBe(true);
    expect(isSourcePath("src/a.tsx")).toBe(true);
    expect(isSourcePath("src/a.d.ts")).toBe(false);
    expect(isSourcePath("src/a.json")).toBe(false);
  });
});

describe("isTestPath", () => {
  test("accepts only .test.ts and .test.tsx files", () => {
    expect(isTestPath("test/a.test.ts")).toBe(true);
    expect(isTestPath("test/a.test.tsx")).toBe(true);
    expect(isTestPath("test/a.ts")).toBe(false);
    expect(isTestPath("test/helpers.ts")).toBe(false);
  });
});

describe("isTestableSource", () => {
  test("skips declaration files and exempt prefixes, keeps the rest", () => {
    expect(isTestableSource("src/shared/a.ts", options)).toBe(true);
    expect(isTestableSource("src/static.d.ts", options)).toBe(false);
    expect(isTestableSource("src/locales/en/index.ts", options)).toBe(false);
  });
});

describe("mirrorPrefix", () => {
  test("swaps the src root for the test root and drops the extension", () => {
    expect(mirrorPrefix("src/shared/accounting/store.ts", options)).toBe(
      "test/shared/accounting/store",
    );
    expect(mirrorPrefix("src/ui/templates/nav.tsx", options)).toBe(
      "test/ui/templates/nav",
    );
  });
});

describe("owningSourcePrefix", () => {
  test("matches a test's own direct mirror", () => {
    const prefixes = new Set(["test/shared/store"]);
    expect(owningSourcePrefix("test/shared/store.test.ts", prefixes)).toBe(
      "test/shared/store",
    );
    expect(owningSourcePrefix("test/shared/store.test.tsx", prefixes)).toBe(
      "test/shared/store",
    );
  });

  test("falls back to the nearest ancestor directory (directory suite)", () => {
    const prefixes = new Set(["test/shared/store"]);
    expect(
      owningSourcePrefix("test/shared/store/reads.test.ts", prefixes),
    ).toBe("test/shared/store");
  });

  test("prefers a child's own mirror over its parent directory", () => {
    const prefixes = new Set(["test/db/attendees", "test/db/attendees/kind"]);
    expect(owningSourcePrefix("test/db/attendees/kind.test.ts", prefixes)).toBe(
      "test/db/attendees/kind",
    );
  });

  test("does not match a sibling whose name merely starts with the prefix", () => {
    const prefixes = new Set(["test/db/attendees"]);
    // "attendees-notes" starts with "attendees" but is not inside it.
    expect(
      owningSourcePrefix("test/db/attendees-notes.test.ts", prefixes),
    ).toBeNull();
  });

  test("returns null when no source owns the test", () => {
    expect(
      owningSourcePrefix("test/shared/ghost.test.ts", new Set()),
    ).toBeNull();
  });
});

describe("buildReport", () => {
  test("pairs sources with mirrored tests and sums their lines", () => {
    const report = buildReport(
      [src("src/shared/a.ts", 100)],
      [tst("test/shared/a.test.ts", 40)],
      options,
    );
    expect(report.totalSources).toBe(1);
    expect(report.testedCount).toBe(1);
    expect(report.untested).toEqual([]);
    expect(report.ranked[0]).toMatchObject({
      path: "src/shared/a.ts",
      ratio: 2.5,
      tested: true,
      testFiles: ["test/shared/a.test.ts"],
      testLines: 40,
    });
  });

  test("gives a source with a single one-line test a finite ratio", () => {
    const report = buildReport(
      [src("src/a.ts", 7)],
      [tst("test/a.test.ts", 1)],
      options,
    );
    expect(report.ranked[0]).toMatchObject({ ratio: 7, tested: true });
  });

  test("groups several test files under a source's directory", () => {
    const report = buildReport(
      [src("src/shared/a.ts", 90)],
      [
        tst("test/shared/a/one.test.ts", 20),
        tst("test/shared/a/two.test.ts", 10),
      ],
      options,
    );
    expect(report.ranked[0]).toMatchObject({
      ratio: 3,
      testFiles: ["test/shared/a/one.test.ts", "test/shared/a/two.test.ts"],
      testLines: 30,
    });
  });

  test("does not credit a child's mirror test to its parent source", () => {
    const report = buildReport(
      [src("src/db/attendees.ts", 100), src("src/db/attendees/kind.ts", 20)],
      [tst("test/db/attendees/kind.test.ts", 10)],
      options,
    );
    expect(report.untested.map((s) => s.path)).toEqual(["src/db/attendees.ts"]);
    expect(report.ranked.map((s) => s.path)).toEqual([
      "src/db/attendees/kind.ts",
    ]);
    expect(report.orphanTests).toEqual([]);
  });

  test("marks a source with no mirror as untested with an infinite ratio", () => {
    const report = buildReport([src("src/shared/a.ts", 50)], [], options);
    expect(report.testedCount).toBe(0);
    expect(report.untested[0]).toMatchObject({
      ratio: Number.POSITIVE_INFINITY,
      tested: false,
      testLines: 0,
    });
    expect(report.ranked).toEqual([]);
  });

  test("drops exempt sources before counting", () => {
    const report = buildReport(
      [src("src/locales/en/index.ts", 10), src("src/static.d.ts", 3)],
      [],
      options,
    );
    expect(report.totalSources).toBe(0);
  });

  test("orders untested biggest-first and ranked thinnest-first", () => {
    const report = buildReport(
      [
        src("src/small.ts", 10),
        src("src/big.ts", 200),
        src("src/thin.ts", 100),
        src("src/thick.ts", 100),
      ],
      [tst("test/thin.test.ts", 5), tst("test/thick.test.ts", 90)],
      options,
    );
    expect(report.untested.map((s) => s.path)).toEqual([
      "src/big.ts",
      "src/small.ts",
    ]);
    expect(report.ranked.map((s) => s.path)).toEqual([
      "src/thin.ts",
      "src/thick.ts",
    ]);
  });

  test("breaks an untested tie on file path, and a ratio tie on size then path", () => {
    const report = buildReport(
      [
        // Same size, both untested -> tie broken by path (b before z).
        src("src/z.ts", 100),
        src("src/b.ts", 100),
        // Same ratio (4.0); bigger source ranks first.
        src("src/big-ratio.ts", 200),
        src("src/small-ratio.ts", 20),
        // Identical ratio (2.0) and size -> tie broken by path (m before n).
        src("src/n.ts", 40),
        src("src/m.ts", 40),
      ],
      [
        tst("test/big-ratio.test.ts", 50),
        tst("test/small-ratio.test.ts", 5),
        tst("test/n.test.ts", 20),
        tst("test/m.test.ts", 20),
      ],
      options,
    );
    expect(report.untested.map((s) => s.path)).toEqual([
      "src/b.ts",
      "src/z.ts",
    ]);
    expect(report.ranked.map((s) => s.path)).toEqual([
      "src/big-ratio.ts",
      "src/small-ratio.ts",
      "src/m.ts",
      "src/n.ts",
    ]);
  });

  test("reports test files that mirror no source, sorted, minus exempt trees", () => {
    const report = buildReport(
      [src("src/shared/a.ts", 10)],
      [
        tst("test/shared/a.test.ts", 5),
        // Two orphans, given out of order, so the sort is exercised.
        tst("test/shared/zeta.test.ts", 5),
        tst("test/shared/ghost.test.ts", 5),
        tst("test/e2e/flow.test.ts", 5),
        tst("test/setup.ts", 5),
      ],
      options,
    );
    expect(report.orphanTests).toEqual([
      "test/shared/ghost.test.ts",
      "test/shared/zeta.test.ts",
    ]);
  });
});

describe("suggestedTarget", () => {
  test("prefers the largest untested source", () => {
    const report = buildReport(
      [src("src/big.ts", 200), src("src/tested.ts", 100)],
      [tst("test/tested.test.ts", 10)],
      options,
    );
    expect(suggestedTarget(report)?.path).toBe("src/big.ts");
  });

  test("falls back to the thinnest-tested source when none are untested", () => {
    const report = buildReport(
      [src("src/thin.ts", 100), src("src/thick.ts", 100)],
      [tst("test/thin.test.ts", 5), tst("test/thick.test.ts", 90)],
      options,
    );
    expect(suggestedTarget(report)?.path).toBe("src/thin.ts");
  });

  test("returns null when there are no testable sources", () => {
    expect(suggestedTarget(buildReport([], [], options))).toBeNull();
  });
});

describe("toJsonReport", () => {
  test("keeps finite ratios and turns infinite ones into null", () => {
    const json = toJsonReport(
      buildReport(
        [src("src/thin.ts", 100), src("src/untested.ts", 50)],
        [tst("test/thin.test.ts", 5)],
        options,
      ),
    );
    expect(json.ranked[0]).toMatchObject({ path: "src/thin.ts", ratio: 20 });
    expect(json.untested[0]).toMatchObject({
      path: "src/untested.ts",
      ratio: null,
    });
    expect(json.totalSources).toBe(2);
  });
});

describe("DEFAULT_OPTIONS and DEFAULT_LIMIT", () => {
  test("target the real roots, exempt lists, and default row cap", () => {
    expect(DEFAULT_LIMIT).toBe(25);
    expect(DEFAULT_OPTIONS.srcRoot).toBe("src");
    expect(DEFAULT_OPTIONS.testRoot).toBe("test");
    expect(DEFAULT_OPTIONS.exemptSourcePrefixes).toEqual(["src/locales/"]);
    expect(DEFAULT_OPTIONS.exemptTestPrefixes).toEqual([
      "test/e2e/",
      "test/integration/",
      "test/scripts/",
      "test/test-utils/",
      "test/setup.ts",
    ]);
  });
});