diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index b775b51f6..e6d59b17d 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,8 +1,11 @@ name: PR Labeler on: - pull_request: + pull_request_target: jobs: label: + permissions: + contents: read + pull-requests: write runs-on: ubuntu-latest steps: - uses: actions/labeler@v5 diff --git a/e2e/dashboard-widgets.spec.js b/e2e/dashboard-widgets.spec.js index 6904ca6e9..f18d1f535 100644 --- a/e2e/dashboard-widgets.spec.js +++ b/e2e/dashboard-widgets.spec.js @@ -214,7 +214,7 @@ test("goal form posts a new goal", async ({ page }) => { await page.getByLabel("Goal title").fill("Ship one PR"); await page.getByLabel("Target").fill("1"); await page.getByLabel("Unit").selectOption("prs"); - await page.getByRole("button", { name: "Add goal" }).click(); + await page.getByRole("button", { name: "Create goal" }).click(); await expect.poll(() => goalPosts, { timeout: 15000 }).toHaveLength(1); expect(goalPosts[0]).toMatchObject({ diff --git a/test/jira-utils.test.ts b/test/jira-utils.test.ts index 79ff9ba9c..211714854 100644 --- a/test/jira-utils.test.ts +++ b/test/jira-utils.test.ts @@ -22,6 +22,16 @@ describe("categorizeStatus", () => { const issue = { statusCategory: "unknown" } as JiraIssue; expect(categorizeStatus(issue)).toBe("To Do"); }); + + it('is case-sensitive for statusCategory (returns "To Do" for "DONE" or "Done")', () => { + expect(categorizeStatus({ statusCategory: "DONE" } as JiraIssue)).toBe("To Do"); + expect(categorizeStatus({ statusCategory: "Done" } as JiraIssue)).toBe("To Do"); + }); + + it('returns "To Do" for statusCategory with trailing/leading spaces', () => { + expect(categorizeStatus({ statusCategory: "done " } as JiraIssue)).toBe("To Do"); + expect(categorizeStatus({ statusCategory: " done" } as JiraIssue)).toBe("To Do"); + }); }); describe("calculateMetrics", () => { @@ -100,4 +110,28 @@ describe("calculateMetrics", () => { expect(result.inProgress).toBe(1); expect(result.done).toBe(1); }); + + it("calculates avgTimeToClose correctly for exact same resolved and created time", () => { + const issues: JiraIssue[] = [ + { key: "PROJ-1", summary: "", status: "", statusCategory: "done", created: "2026-01-01T12:00:00.000Z", updated: "2026-01-01T12:00:00.000Z", resolved: "2026-01-01T12:00:00.000Z", assignee: null, priority: "" }, + ]; + const result = calculateMetrics(issues); + expect(result.avgTimeToClose).toBe(0); + }); + + it("handles negative time differences (resolved before created)", () => { + const issues: JiraIssue[] = [ + { key: "PROJ-1", summary: "", status: "", statusCategory: "done", created: "2026-01-02T12:00:00.000Z", updated: "2026-01-02T12:00:00.000Z", resolved: "2026-01-01T12:00:00.000Z", assignee: null, priority: "" }, + ]; + const result = calculateMetrics(issues); + expect(result.avgTimeToClose).toBe(-24); + }); + + it("handles invalid date strings by producing NaN in calculations", () => { + const issues: JiraIssue[] = [ + { key: "PROJ-1", summary: "", status: "", statusCategory: "done", created: "not-a-date", updated: "not-a-date", resolved: "2026-01-01", assignee: null, priority: "" }, + ]; + const result = calculateMetrics(issues); + expect(result.avgTimeToClose).toBeNaN(); + }); });