diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts new file mode 100644 index 0000000..02d5ba4 --- /dev/null +++ b/__tests__/main.test.ts @@ -0,0 +1,265 @@ +import {beforeEach, describe, expect, test, vi} from "vitest" + +vi.mock("@actions/core", () => ({ + info: vi.fn(), + setFailed: vi.fn(), + setOutput: vi.fn(), +})) + +vi.mock("@actions/github", () => ({ + getOctokit: vi.fn().mockReturnValue({}), +})) + +vi.mock("../src/comment", () => ({ + commentsEqual: vi.fn(), + createComment: vi.fn(), + deleteComment: vi.fn(), + findPreviousComment: vi.fn(), + getBodyOf: vi.fn(), + minimizeComment: vi.fn(), + updateComment: vi.fn(), +})) + +const mockConfig = { + append: false, + deleteOldComment: false, + getBody: vi.fn(), + githubToken: "some-token", + header: "", + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + hideOldComment: false, + ignoreEmpty: false, + onlyCreateComment: false, + onlyUpdateComment: false, + pullRequestNumber: 123, + recreate: false, + repo: {owner: "marocchino", repo: "sticky-pull-request-comment"}, + skipUnchanged: false, +} + +vi.mock("../src/config", () => mockConfig) + +const flushPromises = () => new Promise(resolve => setTimeout(resolve, 0)) + +async function runMain( + setup?: (mocks: {comment: typeof import("../src/comment")}) => void, +) { + vi.resetModules() + const comment = await import("../src/comment") + vi.mocked(comment.findPreviousComment).mockResolvedValue(null) + vi.mocked(comment.createComment).mockResolvedValue({data: {id: 456}} as any) + vi.mocked(comment.deleteComment).mockResolvedValue(undefined) + vi.mocked(comment.updateComment).mockResolvedValue(undefined) + vi.mocked(comment.minimizeComment).mockResolvedValue(undefined) + vi.mocked(comment.commentsEqual).mockReturnValue(false) + vi.mocked(comment.getBodyOf).mockReturnValue(undefined) + setup?.({comment}) + await import("../src/main") + await flushPromises() + const core = await import("@actions/core") + return {comment, core} +} + +beforeEach(() => { + mockConfig.append = false + mockConfig.deleteOldComment = false + mockConfig.getBody = vi.fn().mockResolvedValue("test body") + mockConfig.githubToken = "some-token" + mockConfig.header = "" + mockConfig.hideAndRecreate = false + mockConfig.hideClassify = "OUTDATED" + mockConfig.hideDetails = false + mockConfig.hideOldComment = false + mockConfig.ignoreEmpty = false + mockConfig.onlyCreateComment = false + mockConfig.onlyUpdateComment = false + mockConfig.pullRequestNumber = 123 + mockConfig.recreate = false + mockConfig.repo = {owner: "marocchino", repo: "sticky-pull-request-comment"} + mockConfig.skipUnchanged = false +}) + +describe("run", () => { + test("skips step when pullRequestNumber is NaN", async () => { + mockConfig.pullRequestNumber = NaN + const {comment, core} = await runMain() + expect(core.info).toHaveBeenCalledWith("no pull request numbers given: skip step") + expect(comment.findPreviousComment).not.toHaveBeenCalled() + }) + + test("skips step when pullRequestNumber is less than 1", async () => { + mockConfig.pullRequestNumber = 0 + const {comment, core} = await runMain() + expect(core.info).toHaveBeenCalledWith("no pull request numbers given: skip step") + expect(comment.findPreviousComment).not.toHaveBeenCalled() + }) + + test("skips step when body is empty and ignoreEmpty is true", async () => { + mockConfig.getBody = vi.fn().mockResolvedValue("") + mockConfig.ignoreEmpty = true + const {comment, core} = await runMain() + expect(core.info).toHaveBeenCalledWith("no body given: skip step by ignoreEmpty") + expect(comment.findPreviousComment).not.toHaveBeenCalled() + }) + + test("fails when body is empty and no delete or hide flags are set", async () => { + mockConfig.getBody = vi.fn().mockResolvedValue("") + const {core} = await runMain() + expect(core.setFailed).toHaveBeenCalledWith("Either message or path input is required") + }) + + test("fails when deleteOldComment and recreate are both true", async () => { + mockConfig.deleteOldComment = true + mockConfig.recreate = true + const {core} = await runMain() + expect(core.setFailed).toHaveBeenCalledWith( + "delete and recreate cannot be both set to true", + ) + }) + + test("fails when onlyCreateComment and onlyUpdateComment are both true", async () => { + mockConfig.onlyCreateComment = true + mockConfig.onlyUpdateComment = true + const {core} = await runMain() + expect(core.setFailed).toHaveBeenCalledWith( + "only_create and only_update cannot be both set to true", + ) + }) + + test("fails when hideOldComment and hideAndRecreate are both true", async () => { + mockConfig.hideOldComment = true + mockConfig.hideAndRecreate = true + const {core} = await runMain() + expect(core.setFailed).toHaveBeenCalledWith( + "hide and hide_and_recreate cannot be both set to true", + ) + }) + + test("deletes previous comment when deleteOldComment is true and previous exists", async () => { + mockConfig.deleteOldComment = true + const previous = {id: "existing-id", body: "old body"} + const {comment, core} = await runMain(({comment}) => { + vi.mocked(comment.findPreviousComment).mockResolvedValue(previous as any) + }) + expect(core.setOutput).toHaveBeenCalledWith("previous_comment_id", "existing-id") + expect(comment.deleteComment).toHaveBeenCalledWith(expect.anything(), "existing-id") + expect(comment.createComment).not.toHaveBeenCalled() + }) + + test("skips delete when deleteOldComment is true but no previous comment exists", async () => { + mockConfig.deleteOldComment = true + const {comment, core} = await runMain() + expect(core.setOutput).toHaveBeenCalledWith("previous_comment_id", undefined) + expect(comment.deleteComment).not.toHaveBeenCalled() + }) + + test("skips creating comment when onlyUpdateComment is true and no previous exists", async () => { + mockConfig.onlyUpdateComment = true + const {comment} = await runMain() + expect(comment.createComment).not.toHaveBeenCalled() + }) + + test("creates comment when no previous comment exists", async () => { + const {comment, core} = await runMain() + expect(comment.createComment).toHaveBeenCalledWith( + expect.anything(), + {owner: "marocchino", repo: "sticky-pull-request-comment"}, + 123, + "test body", + "", + ) + expect(core.setOutput).toHaveBeenCalledWith("created_comment_id", 456) + }) + + test("skips update when onlyCreateComment is true and previous exists", async () => { + mockConfig.onlyCreateComment = true + const previous = {id: "existing-id", body: "old body"} + const {comment} = await runMain(({comment}) => { + vi.mocked(comment.findPreviousComment).mockResolvedValue(previous as any) + }) + expect(comment.updateComment).not.toHaveBeenCalled() + expect(comment.createComment).not.toHaveBeenCalled() + }) + + test("minimizes previous comment when hideOldComment is true", async () => { + mockConfig.hideOldComment = true + const previous = {id: "existing-id", body: "old body"} + const {comment} = await runMain(({comment}) => { + vi.mocked(comment.findPreviousComment).mockResolvedValue(previous as any) + }) + expect(comment.minimizeComment).toHaveBeenCalledWith( + expect.anything(), + "existing-id", + "OUTDATED", + ) + expect(comment.updateComment).not.toHaveBeenCalled() + }) + + test("skips update when skipUnchanged is true and body is unchanged", async () => { + mockConfig.skipUnchanged = true + const previous = {id: "existing-id", body: "old body"} + const {comment} = await runMain(({comment}) => { + vi.mocked(comment.findPreviousComment).mockResolvedValue(previous as any) + vi.mocked(comment.commentsEqual).mockReturnValue(true) + }) + expect(comment.updateComment).not.toHaveBeenCalled() + expect(comment.createComment).not.toHaveBeenCalled() + }) + + test("deletes and recreates comment when recreate is true", async () => { + mockConfig.recreate = true + const previous = {id: "existing-id", body: "old body"} + const {comment, core} = await runMain(({comment}) => { + vi.mocked(comment.findPreviousComment).mockResolvedValue(previous as any) + vi.mocked(comment.getBodyOf).mockReturnValue("previous body content") + }) + expect(comment.deleteComment).toHaveBeenCalledWith(expect.anything(), "existing-id") + expect(comment.createComment).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + expect.anything(), + "test body", + "", + "previous body content", + ) + expect(core.setOutput).toHaveBeenCalledWith("created_comment_id", 456) + }) + + test("minimizes and recreates comment when hideAndRecreate is true", async () => { + mockConfig.hideAndRecreate = true + const previous = {id: "existing-id", body: "old body"} + const {comment, core} = await runMain(({comment}) => { + vi.mocked(comment.findPreviousComment).mockResolvedValue(previous as any) + }) + expect(comment.minimizeComment).toHaveBeenCalledWith( + expect.anything(), + "existing-id", + "OUTDATED", + ) + expect(comment.createComment).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + expect.anything(), + "test body", + "", + ) + expect(core.setOutput).toHaveBeenCalledWith("created_comment_id", 456) + }) + + test("updates existing comment by default", async () => { + const previous = {id: "existing-id", body: "old body"} + const {comment} = await runMain(({comment}) => { + vi.mocked(comment.findPreviousComment).mockResolvedValue(previous as any) + vi.mocked(comment.getBodyOf).mockReturnValue("previous body content") + }) + expect(comment.updateComment).toHaveBeenCalledWith( + expect.anything(), + "existing-id", + "test body", + "", + "previous body content", + ) + }) +})