diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts index 63b4016..b44a433 100644 --- a/__tests__/config.test.ts +++ b/__tests__/config.test.ts @@ -1,139 +1,213 @@ -import {afterEach, beforeEach, describe, expect, test, vi} from "vitest" +import {afterEach, describe, expect, test, vi} from "vitest" +import {resolve} from "node:path" -const mockConfig = { - pullRequestNumber: 123, +vi.mock("@actions/core", () => ({ + getInput: vi.fn().mockReturnValue(""), + getBooleanInput: vi.fn().mockReturnValue(false), + getMultilineInput: vi.fn().mockReturnValue([]), + setFailed: vi.fn(), +})) + +const mockContext = vi.hoisted(() => ({ repo: {owner: "marocchino", repo: "sticky-pull-request-comment"}, - header: "", - append: false, - recreate: false, - deleteOldComment: false, - hideOldComment: false, - hideAndRecreate: false, - hideClassify: "OUTDATED", - hideDetails: false, - githubToken: "some-token", - ignoreEmpty: false, - skipUnchanged: false, - getBody: vi.fn().mockResolvedValue(""), -} + payload: {} as Record, +})) -vi.mock("../src/config", () => mockConfig) +vi.mock("@actions/github", () => ({ + context: mockContext, +})) -beforeEach(() => { - mockConfig.pullRequestNumber = 123 - mockConfig.repo = {owner: "marocchino", repo: "sticky-pull-request-comment"} - mockConfig.header = "" - mockConfig.append = false - mockConfig.recreate = false - mockConfig.deleteOldComment = false - mockConfig.hideOldComment = false - mockConfig.hideAndRecreate = false - mockConfig.hideClassify = "OUTDATED" - mockConfig.hideDetails = false - mockConfig.githubToken = "some-token" - mockConfig.ignoreEmpty = false - mockConfig.skipUnchanged = false - mockConfig.getBody.mockResolvedValue("") -}) +const mockGlobCreate = vi.hoisted(() => vi.fn()) + +vi.mock("@actions/glob", () => ({ + create: mockGlobCreate, +})) afterEach(() => { - vi.resetModules() + mockContext.payload = {} + mockContext.repo = {owner: "marocchino", repo: "sticky-pull-request-comment"} + mockGlobCreate.mockReset() }) -test("repo", async () => { - mockConfig.repo = {owner: "jin", repo: "other"} +async function loadConfig( + setup?: (mocks: {core: typeof import("@actions/core")}) => void, +) { + vi.resetModules() + const core = await import("@actions/core") + // vi.resetModules clears the config module cache but not mock instances, + // so reset core back to default values before each test. + vi.mocked(core.getInput).mockReturnValue("") + vi.mocked(core.getBooleanInput).mockReturnValue(false) + vi.mocked(core.getMultilineInput).mockReturnValue([]) + setup?.({core}) const config = await import("../src/config") - expect(config.repo).toEqual({owner: "jin", repo: "other"}) + return {config, core} +} + +describe("pullRequestNumber", () => { + test("number_force takes highest priority", async () => { + mockContext.payload = {pull_request: {number: 789}} + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getInput).mockImplementation(name => { + if (name === "number_force") return "456" + if (name === "number") return "123" + return "" + }) + }) + expect(config.pullRequestNumber).toBe(456) + }) + + test("falls back to context.payload.pull_request.number", async () => { + mockContext.payload = {pull_request: {number: 789}} + const {config} = await loadConfig() + expect(config.pullRequestNumber).toBe(789) + }) + + test("falls back to number input", async () => { + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getInput).mockImplementation(name => (name === "number" ? "123" : "")) + }) + expect(config.pullRequestNumber).toBe(123) + }) +}) + +describe("repo", () => { + test("defaults to context.repo", async () => { + const {config} = await loadConfig() + expect(config.repo).toEqual({owner: "marocchino", repo: "sticky-pull-request-comment"}) + }) + + test("uses owner and repo inputs when provided", async () => { + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getInput).mockImplementation(name => { + if (name === "owner") return "jin" + if (name === "repo") return "other" + return "" + }) + }) + expect(config.repo).toEqual({owner: "jin", repo: "other"}) + }) }) test("header", async () => { - mockConfig.header = "header" - const config = await import("../src/config") - expect(config.header).toBe("header") + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getInput).mockImplementation(name => (name === "header" ? "my-header" : "")) + }) + expect(config.header).toBe("my-header") }) test("append", async () => { - mockConfig.append = true - const config = await import("../src/config") + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getBooleanInput).mockImplementation(name => name === "append") + }) expect(config.append).toBe(true) }) test("recreate", async () => { - mockConfig.recreate = true - const config = await import("../src/config") + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getBooleanInput).mockImplementation(name => name === "recreate") + }) expect(config.recreate).toBe(true) }) -test("delete", async () => { - mockConfig.deleteOldComment = true - const config = await import("../src/config") +test("deleteOldComment", async () => { + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getBooleanInput).mockImplementation(name => name === "delete") + }) expect(config.deleteOldComment).toBe(true) }) test("hideOldComment", async () => { - mockConfig.hideOldComment = true - const config = await import("../src/config") + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getBooleanInput).mockImplementation(name => name === "hide") + }) expect(config.hideOldComment).toBe(true) }) test("hideAndRecreate", async () => { - mockConfig.hideAndRecreate = true - const config = await import("../src/config") + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getBooleanInput).mockImplementation(name => name === "hide_and_recreate") + }) expect(config.hideAndRecreate).toBe(true) }) test("hideClassify", async () => { - mockConfig.hideClassify = "OFF_TOPIC" - const config = await import("../src/config") + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getInput).mockImplementation(name => (name === "hide_classify" ? "OFF_TOPIC" : "")) + }) expect(config.hideClassify).toBe("OFF_TOPIC") }) test("hideDetails", async () => { - mockConfig.hideDetails = true - const config = await import("../src/config") + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getBooleanInput).mockImplementation(name => name === "hide_details") + }) expect(config.hideDetails).toBe(true) }) -describe("path", () => { - test("when exists return content of a file", async () => { - mockConfig.getBody.mockResolvedValue("hi there\n") - const config = await import("../src/config") - expect(await config.getBody()).toEqual("hi there\n") +test("ignoreEmpty", async () => { + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getBooleanInput).mockImplementation(name => name === "ignore_empty") }) - - test("glob match files", async () => { - mockConfig.getBody.mockResolvedValue("hi there\n\nhey there\n") - const config = await import("../src/config") - expect(await config.getBody()).toEqual("hi there\n\nhey there\n") - }) - - test("when not exists return null string", async () => { - mockConfig.getBody.mockResolvedValue("") - const config = await import("../src/config") - expect(await config.getBody()).toEqual("") - }) -}) - -test("message", async () => { - mockConfig.getBody.mockResolvedValue("hello there") - const config = await import("../src/config") - expect(await config.getBody()).toEqual("hello there") -}) - -test("ignore_empty", async () => { - mockConfig.ignoreEmpty = true - const config = await import("../src/config") expect(config.ignoreEmpty).toBe(true) }) -test("skip_unchanged", async () => { - mockConfig.skipUnchanged = true - const config = await import("../src/config") +test("skipUnchanged", async () => { + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getBooleanInput).mockImplementation(name => name === "skip_unchanged") + }) expect(config.skipUnchanged).toBe(true) }) -test("number_force", async () => { - mockConfig.pullRequestNumber = 456 - const config = await import("../src/config") - expect(config.pullRequestNumber).toBe(456) +test("githubToken", async () => { + const {config} = await loadConfig(({core}) => { + vi.mocked(core.getInput).mockImplementation(name => (name === "GITHUB_TOKEN" ? "my-token" : "")) + }) + expect(config.githubToken).toBe("my-token") +}) + +describe("getBody", () => { + test("returns message when no path is provided", async () => { + const {config, core} = await loadConfig() + vi.mocked(core.getInput).mockImplementation(name => (name === "message" ? "hello there" : "")) + expect(await config.getBody()).toBe("hello there") + }) + + test("returns file content when path exists", async () => { + const {config, core} = await loadConfig() + vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/result"]) + mockGlobCreate.mockResolvedValue({ + glob: vi.fn().mockResolvedValue([resolve("__tests__/assets/result")]), + }) + expect(await config.getBody()).toBe("hi there\n") + }) + + test("glob matches multiple files", async () => { + const {config, core} = await loadConfig() + vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/*"]) + mockGlobCreate.mockResolvedValue({ + glob: vi + .fn() + .mockResolvedValue([ + resolve("__tests__/assets/result"), + resolve("__tests__/assets/result2"), + ]), + }) + expect(await config.getBody()).toBe("hi there\n\nhey there\n") + }) + + test("returns empty string when path matches no files", async () => { + const {config, core} = await loadConfig() + vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/not_exists"]) + mockGlobCreate.mockResolvedValue({glob: vi.fn().mockResolvedValue([])}) + expect(await config.getBody()).toBe("") + }) + + test("returns empty string and calls setFailed when glob throws", async () => { + const {config, core} = await loadConfig() + vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/result"]) + mockGlobCreate.mockRejectedValue(new Error("glob error")) + expect(await config.getBody()).toBe("") + expect(core.setFailed).toHaveBeenCalledWith("glob error") + }) })