diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ece157d..96e3058 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,9 @@ jobs: - name: Build run: pnpm build + - name: Unit tests + run: pnpm test:unit + - name: Integration tests run: pnpm test:integration env: diff --git a/package.json b/package.json index 7259a83..c2be87e 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "format": "oxfmt --check", "format:fix": "oxfmt", "lint": "oxlint", + "test:unit": "vitest", "test:integration": "vitest -c vitest.integration.config.ts", "version-packages": "changeset version && pnpm format:fix", "release": "pnpm build && changeset publish" diff --git a/src/core.ts b/src/core.ts index f669cde..1af5491 100644 --- a/src/core.ts +++ b/src/core.ts @@ -1,5 +1,4 @@ import type { GetRepositoryMetadataQuery } from "./github/graphql/generated/operations.js"; -import type { CommitMessage } from "./github/graphql/generated/types.ts"; import { createCommitOnBranchQuery, getRepositoryMetadata, @@ -9,6 +8,7 @@ import type { CommitFilesResult, GitBase, } from "./interface.ts"; +import { normalizeCommitMessage } from "./utils.ts"; const getBaseRef = (base: GitBase): string => { if ("branch" in base) { @@ -62,14 +62,6 @@ const createCommit = async ({ refId: string; baseOid: string; }) => { - const normalizedMessage: CommitMessage = - typeof message === "string" - ? { - headline: message.split("\n")[0]?.trim() ?? "", - body: message.split("\n").slice(1).join("\n").trim(), - } - : message; - // we have to stick to GraphQL here as with REST, each file change would become a separate API call return createCommitOnBranchQuery(octokit, { input: { @@ -77,7 +69,7 @@ const createCommit = async ({ id: refId, }, expectedHeadOid: baseOid, - message: normalizedMessage, + message: normalizeCommitMessage(message), fileChanges, }, }); diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..71bf7f6 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,22 @@ +import type { CommitMessage } from "./github/graphql/generated/types.ts"; + +export function normalizeCommitMessage( + message: string | CommitMessage, +): CommitMessage { + if (typeof message === "object") { + return { + headline: message.headline.trim(), + body: message.body?.trim(), + }; + } + + if (!message.includes("\n")) { + return { headline: message.trim() }; + } + + const [headline, ...bodyLines] = message.split("\n"); + return { + headline: headline.trim(), + body: bodyLines.join("\n").trim(), + }; +} diff --git a/tests/integration/node.test.ts b/tests/integration/node.test.ts index 1d0aacc..dedd5c7 100644 --- a/tests/integration/node.test.ts +++ b/tests/integration/node.test.ts @@ -2,7 +2,6 @@ import { promises as fs } from "fs"; import { getOctokit } from "@actions/github"; import git from "isomorphic-git"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; -import type { CommitMessage } from "../../src/github/graphql/generated/types.ts"; import { createRefMutation, getRefTreeQuery, @@ -497,97 +496,6 @@ describe("node", () => { }); }); }); - - describe("commit message is correctly handled", () => { - const testCommitMessage = async ({ - branch, - input, - expected, - }: { - branch: string; - input: string | CommitMessage; - expected: string; - }) => { - await commitFilesFromBuffers({ - octokit, - ...REPO, - branch, - base: { - commit: testTargetCommit, - }, - ...BASIC_FILE_CONTENTS, - message: input, - }); - - await waitForGitHubToBeReady(); - - const ref = ( - await getRefTreeQuery(octokit, { - ...REPO, - ref: `refs/heads/${branch}`, - path: "package.json", - }) - ).repository?.ref?.target; - - if (!ref || !("message" in ref)) { - throw new Error("Unexpected result"); - } - - expect(ref.message).toEqual(expected); - }; - - it("single string", async () => { - const branch = `${TEST_BRANCH_PREFIX}-commit-message-single`; - branches.push(branch); - - await testCommitMessage({ - branch, - input: "This is a basic commit message", - expected: "This is a basic commit message", - }); - }); - - it("multi-line string", async () => { - const branch = `${TEST_BRANCH_PREFIX}-commit-message-multi`; - branches.push(branch); - - await testCommitMessage({ - branch, - input: - "This is a basic commit message\nwith a second line\n\nand some more lines!", - expected: - "This is a basic commit message\n\nwith a second line\n\nand some more lines!", - }); - }); - - it("headline only", async () => { - const branch = `${TEST_BRANCH_PREFIX}-commit-message-h-only`; - branches.push(branch); - - await testCommitMessage({ - branch, - input: { - headline: "This is a basic commit message!!", - }, - expected: "This is a basic commit message!!", - }); - }); - - it("headline & body", async () => { - const branch = `${TEST_BRANCH_PREFIX}-commit-message-h-and-b`; - branches.push(branch); - - await testCommitMessage({ - branch, - input: { - headline: "This is a basic commit message!!", - body: "This is the body of the commit message\nit has more text", - }, - expected: - "This is a basic commit message!!\n\nThis is the body of the commit message\nit has more text", - }); - }); - }); }); afterAll(async () => { diff --git a/tests/utils.test.ts b/tests/utils.test.ts new file mode 100644 index 0000000..166748d --- /dev/null +++ b/tests/utils.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from "vitest"; +import { normalizeCommitMessage } from "../src/utils.ts"; + +describe("normalizeCommitMessage", () => { + it("handles single line messages", () => { + const message = "This is a commit message"; + const result = normalizeCommitMessage(message); + expect(result).toEqual({ headline: "This is a commit message" }); + }); + + it("handles multi line messages", () => { + const message = + "This is a commit message\nwith a second line\nand a third line"; + const result = normalizeCommitMessage(message); + expect(result).toEqual({ + headline: "This is a commit message", + body: "with a second line\nand a third line", + }); + }); + + it("trims whitespace from headline and body", () => { + const message = " This is a commit message \n with a second line "; + const result = normalizeCommitMessage(message); + expect(result).toEqual({ + headline: "This is a commit message", + body: "with a second line", + }); + }); + + it("handles object messages", () => { + const message = { + headline: " This is a commit message ", + body: " with a second line ", + }; + const result = normalizeCommitMessage(message); + expect(result).toEqual({ + headline: "This is a commit message", + body: "with a second line", + }); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..4903f03 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + experimental: { preParse: true }, + clearMocks: true, + include: ["tests/*.test.ts"], + }, +}); diff --git a/vitest.integration.config.ts b/vitest.integration.config.ts index 22dd3d3..2e28e6f 100644 --- a/vitest.integration.config.ts +++ b/vitest.integration.config.ts @@ -16,7 +16,8 @@ process.env.ROOT_TEMP_DIRECTORY ??= path.join( export default defineConfig({ test: { - environment: "node", + experimental: { preParse: true }, + clearMocks: true, globalSetup: ["./tests/integration/globalSetup.ts"], include: ["tests/integration/**/*.test.ts"], },