diff --git a/.github/actions/time-to-first-review/index.js b/.github/actions/time-to-first-review/index.js index 04e5b91..a10716c 100644 --- a/.github/actions/time-to-first-review/index.js +++ b/.github/actions/time-to-first-review/index.js @@ -1,36 +1,12 @@ const core = require("@actions/core"); -const fetchPullRequests = require("./src/fetch-pull-requests"); -const getTimeToFirstReview = require("./src/get-time-to-first-review"); +const main = require("./src"); try { - // GitHub repository information const repo = core.getInput("repo"); const token = core.getInput("github_token"); - const maxPRs = core.getInput("num_prs") || 200; - - // Main function - const main = async () => { - const prs = await fetchPullRequests(repo, token, maxPRs); - - let timesToFirstReview = await Promise.all( - prs.map((pr) => getTimeToFirstReview(pr, repo, token)) - ); - - timesToFirstReview = timesToFirstReview.filter( - (time) => time !== undefined - ); - - if (timesToFirstReview.length > 0) { - const total = timesToFirstReview.reduce((sum, value) => sum + value, 0); - const average = total / timesToFirstReview.length; - console.log(`Average time to first review: ${average.toFixed(2)} hours`); - } else { - console.log(`No reviews found for the last ${maxPRs} PRs.`); - } - }; - - main(); + const maxPRs = core.getInput("num_prs") || 100; + main(repo, token, maxPRs); } catch (error) { core.setFailed(error.message); } diff --git a/.github/actions/time-to-first-review/src/__tests__/get-time-to-first-review.test.js b/.github/actions/time-to-first-review/src/__tests__/get-time-to-first-review.test.js new file mode 100644 index 0000000..79ef8b7 --- /dev/null +++ b/.github/actions/time-to-first-review/src/__tests__/get-time-to-first-review.test.js @@ -0,0 +1,67 @@ +// Mock the fetchReviews function +jest.mock("../fetch-reviews", () => jest.fn()); + +const getTimeToFirstReview = require("../get-time-to-first-review"); +const fetchReviews = require("../fetch-reviews"); + +const repo = "owner/repo"; +const gitHubToken = "my-token"; + +describe.only("getTimeToFirstReview", () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + + it("should return the time difference between the PR creation and the first review", async () => { + const pr = { number: 1, created_at: new Date("2024-01-01T00:00:00Z") }; + + const consoleLogSpy = jest.spyOn(console, "log").mockImplementation(); + + fetchReviews.mockResolvedValue([ + { submitted_at: new Date("2024-01-01T01:00:00Z") }, + { submitted_at: new Date("2024-01-01T02:00:00Z") }, + ]); + + const result = await getTimeToFirstReview(pr, repo, gitHubToken); + + expect(result).toBe(1); + expect(consoleLogSpy).toHaveBeenCalledTimes(1); + expect(consoleLogSpy).toHaveBeenCalledWith( + "PR #1 - Time to first review: 1 hours" + ); + }); + + it("should return 0 if there are no reviews", async () => { + const pr = { number: 1, created_at: new Date("2024-01-01T00:00:00Z") }; + + const consoleLogSpy = jest.spyOn(console, "log").mockImplementation(); + + fetchReviews.mockResolvedValue([]); + + const result = await getTimeToFirstReview(pr, repo, gitHubToken); + + expect(result).toBe(undefined); + expect(consoleLogSpy).toHaveBeenCalledTimes(1); + expect(consoleLogSpy).toHaveBeenCalledWith("PR #1 - No reviews found"); + }); + + it("should return 0 if the first review is created before the PR", async () => { + const pr = { number: 1, created_at: new Date("2024-01-01T00:00:00Z") }; + + const consoleLogSpy = jest.spyOn(console, "log").mockImplementation(); + + fetchReviews.mockResolvedValue([ + { submitted_at: new Date("2023-12-31T23:00:00Z") }, + { submitted_at: new Date("2024-01-01T01:00:00Z") }, + ]); + + const result = await getTimeToFirstReview(pr, repo, gitHubToken); + + expect(result).toBe(0); + expect(consoleLogSpy).toHaveBeenCalledTimes(1); + expect(consoleLogSpy).toHaveBeenCalledWith( + "PR #1 - Time to first review: 0 hours" + ); + }); +}); diff --git a/.github/actions/time-to-first-review/src/__tests__/index.test.js b/.github/actions/time-to-first-review/src/__tests__/index.test.js new file mode 100644 index 0000000..fb198d2 --- /dev/null +++ b/.github/actions/time-to-first-review/src/__tests__/index.test.js @@ -0,0 +1,42 @@ +jest.mock("../get-time-to-first-review"); +jest.mock("../fetch-pull-requests"); + +const getTimeToFirstReview = require("../get-time-to-first-review"); +const fetchPullRequests = require("../fetch-pull-requests"); + +const main = require(".."); + +describe("main", () => { + const repo = "owner/repo"; + const token = "token"; + const maxPRs = 10; + + beforeEach(() => { + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + + it("should call getTimeToFirstReview and fetchPullRequests with the correct arguments", async () => { + fetchPullRequests.mockResolvedValue([ + { + id: 1, + }, + { + id: 2, + }, + ]); + + getTimeToFirstReview.mockResolvedValue(5); + + const consoleLogSpy = jest.spyOn(console, "log").mockImplementation(); + + await main(repo, token, maxPRs); + + expect(fetchPullRequests).toHaveBeenCalledWith(repo, token, maxPRs); + expect(getTimeToFirstReview).toHaveBeenCalledTimes(2); + expect(consoleLogSpy).toHaveBeenCalledTimes(1); + expect(consoleLogSpy).toHaveBeenCalledWith( + "Average time to first review: 5.00 hours" + ); + }); +}); diff --git a/.github/actions/time-to-first-review/src/get-time-to-first-review.js b/.github/actions/time-to-first-review/src/get-time-to-first-review.js index fa03dc4..3ded7db 100644 --- a/.github/actions/time-to-first-review/src/get-time-to-first-review.js +++ b/.github/actions/time-to-first-review/src/get-time-to-first-review.js @@ -8,18 +8,17 @@ module.exports = async (pr, repo, gitHubToken) => { // Find the first review date const firstReviewDate = reviews .map((review) => review.submitted_at) - .filter((date) => date) // filter out null or undefined - .sort()[0]; // Get the earliest review date + .filter((date) => date) + .sort()[0]; if (!firstReviewDate) { console.log(`PR #${pr.number} - No reviews found`); return undefined; } - const timeToFirstReview = calculateTimeDifference( - prCreatedAt, - firstReviewDate - ); + let timeToFirstReview = calculateTimeDifference(prCreatedAt, firstReviewDate); + + timeToFirstReview = Math.max(timeToFirstReview, 0); console.log( `PR #${pr.number} - Time to first review: ${timeToFirstReview} hours` diff --git a/.github/actions/time-to-first-review/src/index.js b/.github/actions/time-to-first-review/src/index.js new file mode 100644 index 0000000..a35e672 --- /dev/null +++ b/.github/actions/time-to-first-review/src/index.js @@ -0,0 +1,20 @@ +const getTimeToFirstReview = require("./get-time-to-first-review"); +const fetchPullRequests = require("./fetch-pull-requests"); + +module.exports = async (repo, token, maxPRs) => { + const prs = await fetchPullRequests(repo, token, maxPRs); + + let timesToFirstReview = await Promise.all( + prs.map((pr) => getTimeToFirstReview(pr, repo, token)) + ); + + timesToFirstReview = timesToFirstReview.filter((time) => time !== undefined); + + if (timesToFirstReview.length > 0) { + const total = timesToFirstReview.reduce((sum, value) => sum + value, 0); + const average = total / timesToFirstReview.length; + console.log(`Average time to first review: ${average.toFixed(2)} hours`); + } else { + console.log(`No reviews found for the last ${maxPRs} PRs.`); + } +};