Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 215 additions & 1 deletion src/__tests__/unit/helper.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
import { describe, it, expect, jest, beforeEach } from "@jest/globals";
import { createHelpers } from "../../helper.js";
import {
createHelpers,
parsePackageJSON,
parseRequirementsTxt,
mergeReusedCode,
} from "../../helper.js";
import {
GOV_DEPENDENCIES,
USWDS,
USWDS_COMPILE,
CMS_DESIGN_SYSTEM,
CMS_DS_HEALTHCARE_GOV,
} from "../../gov-dependencies.js";
import { createMockDeps, createMockOctokit } from "../fixtures/mock-deps.js";
import { Dependencies } from "../../types/Dependencies.js";

// returns a readFile mock that serves content by filepath and rejects otherwise
function readFileFrom(files: Record<string, string>) {
return jest.fn<any>((filepath: string) =>
filepath in files
? Promise.resolve(files[filepath])
: Promise.reject(new Error("ENOENT")),
);
}

describe("createHelpers - calculateMetaData", () => {
let deps: Dependencies;

Expand Down Expand Up @@ -203,4 +224,197 @@ describe("createHelpers - validateOnly", () => {
expect(deps.setFailed).not.toHaveBeenCalled();
expect(deps.log.info).toHaveBeenCalledWith("code.json is valid!");
});
});

describe("parsePackageJSON", () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verbose testing 💯

it("collects dependencies and devDependencies", () => {
const content = JSON.stringify({
dependencies: { uswds: "^3.0.0", react: "^18.0.0" },
devDependencies: { jest: "^29.0.0" },
});
expect(parsePackageJSON(content)).toEqual(["uswds", "react", "jest"]);
});

it("handles missing dependency sections", () => {
expect(parsePackageJSON(JSON.stringify({ name: "x" }))).toEqual([]);
});

it("returns empty array for invalid JSON", () => {
expect(parsePackageJSON("not json {{{")).toEqual([]);
});
});

describe("parseRequirementsTxt", () => {
it("strips version specifiers, extras, markers and comments", () => {
const content = [
"uswds==3.0.0",
"requests>=2.0 # http client",
"django[argon2]~=4.2",
'pytz; python_version < "3.9"',
"# a comment line",
"",
"-r other-requirements.txt",
"--hash=sha256:abc",
].join("\n");

expect(parseRequirementsTxt(content)).toEqual([
"uswds",
"requests",
"django",
"pytz",
]);
});
});

describe("createHelpers - detectReusedCode", () => {
it("matches a known gov dependency from package.json", async () => {
const deps = createMockDeps({
readFile: readFileFrom({
"/github/workspace/package.json": JSON.stringify({
dependencies: { "@uswds/uswds": "^3.0.0", react: "^18.0.0" },
}),
}),
});

expect(await createHelpers(deps).detectReusedCode()).toEqual([USWDS]);
});

it("matches a known gov dependency from requirements.txt", async () => {
const deps = createMockDeps({
readFile: readFileFrom({
"/github/workspace/requirements.txt": "uswds==3.0.0\nrequests==2.0",
}),
});

expect(await createHelpers(deps).detectReusedCode()).toEqual([USWDS]);
});

it("matches multiple distinct gov dependencies in one manifest", async () => {
const deps = createMockDeps({
readFile: readFileFrom({
"/github/workspace/package.json": JSON.stringify({
dependencies: {
"@uswds/uswds": "^3.0.0",
"@cmsgov/design-system": "^14.0.0",
react: "^18.0.0",
},
}),
}),
});

expect(await createHelpers(deps).detectReusedCode()).toEqual([
USWDS,
CMS_DESIGN_SYSTEM,
]);
});

it("lists @uswds/compile as a separate entry from @uswds/uswds", async () => {
const deps = createMockDeps({
readFile: readFileFrom({
"/github/workspace/package.json": JSON.stringify({
dependencies: { "@uswds/uswds": "^3.0.0" },
devDependencies: { "@uswds/compile": "^1.0.0" },
}),
}),
});

expect(await createHelpers(deps).detectReusedCode()).toEqual([
USWDS,
USWDS_COMPILE,
]);
});

it("lists each CMS dependency individually", async () => {
const deps = createMockDeps({
readFile: readFileFrom({
"/github/workspace/package.json": JSON.stringify({
dependencies: { "@cmsgov/design-system": "^14.0.0" },
devDependencies: { "@cmsgov/ds-healthcare-gov": "^18.0.0" },
}),
}),
});

expect(await createHelpers(deps).detectReusedCode()).toEqual([
CMS_DESIGN_SYSTEM,
CMS_DS_HEALTHCARE_GOV,
]);
});

it("dedupes the same dependency found across both manifests", async () => {
const deps = createMockDeps({
readFile: readFileFrom({
"/github/workspace/package.json": JSON.stringify({
dependencies: { "@uswds/uswds": "^3.0.0" },
}),
"/github/workspace/requirements.txt": "uswds==3.0.0",
}),
});

expect(await createHelpers(deps).detectReusedCode()).toEqual([USWDS]);
});

it("returns empty when no manifests are present", async () => {
const deps = createMockDeps();
expect(await createHelpers(deps).detectReusedCode()).toEqual([]);
});

it("returns empty when no known gov dependencies are found", async () => {
const deps = createMockDeps({
readFile: readFileFrom({
"/github/workspace/package.json": JSON.stringify({
dependencies: { react: "^18.0.0" },
}),
}),
});

expect(await createHelpers(deps).detectReusedCode()).toEqual([]);
});

it("ignores dependency names that collide with Object prototype members", async () => {
const deps = createMockDeps({
readFile: readFileFrom({
"/github/workspace/package.json": JSON.stringify({
dependencies: { constructor: "1.0.0", valueOf: "1.0.0" },
}),
}),
});

expect(await createHelpers(deps).detectReusedCode()).toEqual([]);
});
});

describe("mergeReusedCode", () => {
it("appends detected entries to existing ones", () => {
const existing = [{ name: "Other Gov Tool", URL: "https://example.gov" }];
expect(mergeReusedCode(existing, [USWDS])).toEqual([...existing, USWDS]);
});

it("does not duplicate an entry already present by URL", () => {
const existing = [{ name: "USWDS (manual)", URL: USWDS.URL }];
expect(mergeReusedCode(existing, [USWDS])).toEqual(existing);
});

it("does not duplicate an entry already present by name", () => {
const existing = [{ name: USWDS.name, URL: "https://old.example" }];
expect(mergeReusedCode(existing, [USWDS])).toEqual(existing);
});

it("returns existing unchanged when nothing is detected", () => {
const existing = [{ name: "Gov Tool", URL: "https://example.gov" }];
expect(mergeReusedCode(existing, [])).toEqual(existing);
});

it("tolerates a non-array existing value", () => {
expect(mergeReusedCode(undefined as any, [USWDS])).toEqual([USWDS]);
});
});

describe("GOV_DEPENDENCIES integrity", () => {
const entries = Object.entries(GOV_DEPENDENCIES);

it.each(entries)("%s has a lowercase key and a valid entry", (key, entry) => {
expect(key).toBe(key.toLowerCase());
expect(entry.name.trim()).not.toBe("");
expect(entry.URL).toMatch(/^https:\/\//);
});
});
59 changes: 59 additions & 0 deletions src/gov-dependencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
export interface ReusedCodeEntry {
name: string;
URL: string;
}

// each dependency gets its own entry with the repository URL where the source code is hosted
export const USWDS: ReusedCodeEntry = {
name: "U.S. Web Design System (USWDS)",
URL: "https://github.com/uswds/uswds",
};

export const USWDS_COMPILE: ReusedCodeEntry = {
name: "USWDS Compile",
URL: "https://github.com/uswds/uswds-compile",
};

export const CMS_DESIGN_SYSTEM: ReusedCodeEntry = {
name: "CMS Design System",
URL: "https://github.com/CMSgov/design-system",
};

export const CMS_DS_HEALTHCARE_GOV: ReusedCodeEntry = {
name: "CMS Design System - HealthCare.gov",
URL: "https://github.com/CMSgov/design-system/tree/main/packages/ds-healthcare-gov",
};

export const CMS_DS_MEDICARE_GOV: ReusedCodeEntry = {
name: "CMS Design System - Medicare.gov",
URL: "https://github.com/CMSgov/design-system/tree/main/packages/ds-medicare-gov",
};

export const CMS_DS_CMS_GOV: ReusedCodeEntry = {
name: "CMS Design System - CMS.gov",
URL: "https://github.com/CMSgov/design-system/tree/main/packages/ds-cms-gov",
};
Comment on lines +7 to +35

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Phase 1 this fits our needs as we have a short well-curated list! As our list of fed software dependencies grows (I can forsee this list going into the 100s), this approach of creating ReusedCodeEntry object for each dependency may not scale well (too much repetition, must know aliases, hard to sort through data) so flagging that maybe there is a different way to go about this for Phase 2/3?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! The per-entry approach won't scale well as the list grows.

This will change in Phase 2:

  • The dependency data moves into its own module, separate from the lookup logic.
  • Lookup falls back to the npm scope, so @uswds/* resolves off a single entry instead of listing every alias individually.

Thanks for flagging this!


// keys are lowercased package names; each maps to its own unique entry
export const GOV_DEPENDENCIES: Record<string, ReusedCodeEntry> = {
uswds: USWDS,
"@uswds/uswds": USWDS,
"@uswds/compile": USWDS_COMPILE,
"@cmsgov/design-system": CMS_DESIGN_SYSTEM,
"@cmsgov/ds-healthcare-gov": CMS_DS_HEALTHCARE_GOV,
"@cmsgov/ds-medicare-gov": CMS_DS_MEDICARE_GOV,
"@cmsgov/ds-cms-gov": CMS_DS_CMS_GOV,
Comment on lines +40 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, what I was looking for!

};

// npm names are lowercase and Python names are case-insensitive, so lowercasing suffices to match
export function normalizePackageName(name: string): string {
return name.trim().toLowerCase();
}

export function lookupGovDependency(name: string): ReusedCodeEntry | undefined {
// hasOwn guard so names like "constructor"/"__proto__" don't match inherited members
const key = normalizePackageName(name);
return Object.hasOwn(GOV_DEPENDENCIES, key)
? GOV_DEPENDENCIES[key]
: undefined;
}
Loading
Loading