import { describe, it, expect } from "vitest"; import { parseCommitMessage } from "@/lib/git/conventional"; describe("parseCommitMessage", () => { it("pulls out type, scope and subject", () => { expect(parseCommitMessage("feat(stock): inline qty editing")).toEqual({ type: "feat", scope: "stock", subject: "inline qty editing", breaking: false, }); }); it("handles a missing scope", () => { expect(parseCommitMessage("fix: correct the total")).toEqual({ type: "fix", scope: "", subject: "correct the total", breaking: false, }); }); it("flags a breaking change with and without a scope", () => { expect(parseCommitMessage("feat!: drop v1 API").breaking).toBe(true); expect(parseCommitMessage("feat(api)!: drop v1").breaking).toBe(true); expect(parseCommitMessage("feat(api)!: drop v1").scope).toBe("api"); }); it("reads only the first line", () => { const message = "feat(customers): lift the toggle\n\nA long body that\nmentions fix(other): things\n"; expect(parseCommitMessage(message)).toEqual({ type: "feat", scope: "customers", subject: "lift the toggle", breaking: false, }); }); it("lowercases the type and the scope", () => { const parsed = parseCommitMessage("Feat(Stock): thing"); expect(parsed.type).toBe("feat"); expect(parsed.scope).toBe("stock"); }); it("classifies an unparseable message as other, keeping the subject", () => { expect(parseCommitMessage("just some words")).toEqual({ type: "other", scope: "", subject: "just some words", breaking: false, }); }); it("does not accept a space before the parenthesis", () => { // "feat (stock): x" is not a conventional commit. Accepting it would make // the scope breakdown depend on invisible whitespace. expect(parseCommitMessage("feat (stock): x").type).toBe("other"); }); it("accepts any single lowercase word as a type, but not a phrase", () => { // "note" is not a conventional type, but the parser cannot tell an unknown // type from prose shaped like one, and it must not police a whitelist — // see the note after Step 4. expect(parseCommitMessage("note: this is not conventional").type).toBe("note"); // A space before the colon means it is a sentence, not a prefix. expect(parseCommitMessage("TODO tomorrow: finish").type).toBe("other"); }); it("rejects a type containing non-letters", () => { expect(parseCommitMessage("feat2(x): thing").type).toBe("other"); }); it("trims and caps an over-long subject", () => { const parsed = parseCommitMessage(`feat(x): ${"s".repeat(400)}`); expect(parsed.subject).toHaveLength(200); }); it("returns other for an empty message", () => { expect(parseCommitMessage("").type).toBe("other"); expect(parseCommitMessage(" \n ").subject).toBe(""); }); it("handles a scope containing a hyphen or slash", () => { expect(parseCommitMessage("fix(delivery-notes): x").scope).toBe("delivery-notes"); expect(parseCommitMessage("fix(api/v1): x").scope).toBe("api/v1"); }); });