diff --git a/scripts/agents/includes/__tests__/labeler-utils.test.js b/scripts/agents/includes/__tests__/labeler-utils.test.js new file mode 100644 index 00000000..1c811bee --- /dev/null +++ b/scripts/agents/includes/__tests__/labeler-utils.test.js @@ -0,0 +1,97 @@ +const { + matchesBranchPattern, + matchesFilePatterns, + determineLabelsFromRules, +} = require("../labeler-utils.js"); + +describe("labeler-utils", () => { + describe("matchesBranchPattern", () => { + test("matches regex and glob patterns", () => { + expect(matchesBranchPattern("feat/new-flow", ["^feat/.*"])).toBe(true); + expect(matchesBranchPattern("docs/readme", ["docs/*"])).toBe(true); + expect(matchesBranchPattern("fix/bug", ["^feat/.*", "docs/*"])).toBe( + false, + ); + }); + + test("returns false for invalid regex patterns", () => { + expect(matchesBranchPattern("feat/new-flow", ["^(feat"])).toBe(false); + }); + }); + + describe("matchesFilePatterns", () => { + const changedFiles = [ + ".github/workflows/labeling.yml", + "docs/ISSUE_LABELS.md", + "scripts/agents/labeling.agent.js", + ]; + + test("supports any-glob-to-any-file", () => { + const config = { + "any-glob-to-any-file": [".github/workflows/**", "src/**"], + }; + expect(matchesFilePatterns(changedFiles, config)).toBe(true); + }); + + test("supports all-globs-to-all-files", () => { + const config = { + "all-globs-to-all-files": [".github/workflows/**", "docs/**"], + }; + expect(matchesFilePatterns(changedFiles, config)).toBe(true); + }); + + test("supports any-glob-to-all-files", () => { + const markdownFiles = ["docs/ISSUE_LABELS.md", "docs/ISSUE_TYPES.md"]; + const config = { + "any-glob-to-all-files": ["docs/**/*.md", "scripts/**/*.js"], + }; + expect(matchesFilePatterns(markdownFiles, config)).toBe(true); + }); + }); + + describe("determineLabelsFromRules", () => { + test("applies branch and file labels exactly once", () => { + const context = { + payload: { + pull_request: { + number: 427, + head: { ref: "feat/label-hardening" }, + }, + }, + }; + + const labelerRules = { + "type:feature": { + "head-branch": ["^feat/.*"], + }, + "area:ci": { + "changed-files": { + "any-glob-to-any-file": [".github/workflows/**"], + }, + }, + "area:labels": { + "head-branch": ["^feat/.*"], + "changed-files": { + "any-glob-to-any-file": ["scripts/agents/**"], + }, + }, + }; + + const changedFiles = [ + ".github/workflows/labeling.yml", + "scripts/agents/labeling.agent.js", + ]; + + const labels = determineLabelsFromRules( + context, + labelerRules, + changedFiles, + ); + + expect(labels).toContain("type:feature"); + expect(labels).toContain("area:ci"); + expect(labels).toContain("area:labels"); + expect(labels.filter((label) => label === "area:labels")).toHaveLength(1); + }); + }); +}); diff --git a/scripts/agents/includes/labeler-utils.js b/scripts/agents/includes/labeler-utils.js index 5db88da4..3a8c72b2 100644 --- a/scripts/agents/includes/labeler-utils.js +++ b/scripts/agents/includes/labeler-utils.js @@ -14,10 +14,13 @@ import fs from "fs"; import yaml from "js-yaml"; -import core from "@actions/core"; +import * as core from "@actions/core"; import minimatchPackage from "minimatch"; -const { minimatch } = minimatchPackage; +const minimatch = + typeof minimatchPackage === "function" + ? minimatchPackage + : minimatchPackage.minimatch; /** * Loads labeler rules from YAML configuration file