Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
272 changes: 271 additions & 1 deletion @commitlint/cz-commitlint/src/services/getRuleQuestionConfig.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, test, expect } from "vitest";
import { RuleConfigSeverity } from "@commitlint/types";
import { describe, expect, test } from "vitest";

import { setPromptConfig } from "../store/prompts.js";
import { setRules } from "../store/rules.js";
Expand Down Expand Up @@ -253,6 +253,276 @@ describe("enum list", () => {
expect(enumList?.[1]).toBe("lint");
expect(enumList?.[2]).toBe("cli");
});

test("should format enum items with emojis and descriptions, ensuring consistent spacing", () => {
const ENUM_RULE_LIST = ["feat", "fix", "chore"];
setRules({
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
} as any);

setPromptConfig({
questions: {
type: {
enum: {
feat: {
description: "Features",
emoji: "✨",
},
fix: {
description: "Bug fixes",
emoji: "🐛",
},
chore: {
description: "Chore",
emoji: "♻️",
},
},
},
},
});

const enumList = getRuleQuestionConfig("type")?.enumList;
expect(enumList).toEqual([
{
name: "✨ feat: Features",
value: "feat",
short: "feat",
},
{
name: "🐛 fix: Bug fixes",
value: "fix",
short: "fix",
},
{
name: "♻️ chore: Chore",
value: "chore",
short: "chore",
},
]);
});

test("should handle custom alignment for emoji and description with extra space", () => {
const ENUM_RULE_LIST = ["feat", "fix", "chore"];
setRules({
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
} as any);

setPromptConfig({
questions: {
type: {
enum: {
feat: {
description: "Features",
emoji: "✨ ",
},
fix: {
description: "Bug fixes",
emoji: "🐛 ",
},
chore: {
description: "Chore",
emoji: "♻️ ",
},
},
},
},
});

const enumList = getRuleQuestionConfig("type")?.enumList;
expect(enumList).toEqual([
{
name: "✨ feat: Features",
value: "feat",
short: "feat",
},
{
name: "🐛 fix: Bug fixes",
value: "fix",
short: "fix",
},
{
name: "♻️ chore: Chore",
value: "chore",
short: "chore",
},
]);
});

test("should handle inconsistent emoji usage by using 4 blank spaces as a prefix", () => {
const ENUM_RULE_LIST = ["feat", "fix", "chore"];
setRules({
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
} as any);

setPromptConfig({
questions: {
type: {
enum: {
feat: {
description: "Features",
emoji: "✨",
},
fix: {
description: "Bug fixes",
},
chore: {
description: "Chore",
},
},
},
},
});

const enumList = getRuleQuestionConfig("type")?.enumList;
expect(enumList).toEqual([
{
name: "✨ feat: Features",
value: "feat",
short: "feat",
},
{
name: " fix: Bug fixes",
value: "fix",
short: "fix",
},
{
name: " chore: Chore",
value: "chore",
short: "chore",
},
]);
});

test("should handle no enums having emojis correctly", () => {
const ENUM_RULE_LIST = ["feat", "fix", "chore"];
setRules({
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
} as any);

setPromptConfig({
questions: {
type: {
enum: {
feat: {
description: "Features",
},
fix: {
description: "Bug fixes",
},
chore: {
description: "Chore",
},
},
},
},
});

const enumList = getRuleQuestionConfig("type")?.enumList;
expect(enumList).toEqual([
{
name: "feat: Features",
value: "feat",
short: "feat",
},
{
name: "fix: Bug fixes",
value: "fix",
short: "fix",
},
{
name: "chore: Chore",
value: "chore",
short: "chore",
},
]);
});

test("should include the emoji in the value when `emojiInHeader` is true", () => {
const ENUM_RULE_LIST = ["feat", "fix"];
setRules({
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
} as any);

setPromptConfig({
questions: {
type: {
emojiInHeader: true,
enum: {
feat: {
description: "Features",
emoji: "✨",
},
fix: {
description: "Bug fixes",
emoji: "🐛",
},
},
},
},
});

const enumList = getRuleQuestionConfig("type")?.enumList;
expect(enumList).toEqual([
{
name: "✨ feat: Features",
value: "✨ feat",
short: "feat",
},
{
name: "🐛 fix: Bug fixes",
value: "🐛 fix",
short: "fix",
},
]);
});

test("should trim empty spaces from emoji in the answer", () => {
const ENUM_RULE_LIST = ["feat", "fix", "chore"];
setRules({
"type-enum": [RuleConfigSeverity.Error, "always", ENUM_RULE_LIST],
} as any);

setPromptConfig({
questions: {
type: {
emojiInHeader: true,
enum: {
feat: {
description: "Features",
emoji: "✨ ",
},
fix: {
description: "Bug fixes",
emoji: "🐛 ",
},
chore: {
description: "Chore",
emoji: "♻️ ",
},
},
},
},
});

const enumList = getRuleQuestionConfig("type")?.enumList;
expect(enumList).toEqual([
{
name: "✨ feat: Features",
value: "✨ feat",
short: "feat",
},
{
name: "🐛 fix: Bug fixes",
value: "🐛 fix",
short: "fix",
},
{
name: "♻️ chore: Chore",
value: "♻️ chore",
short: "chore",
},
]);
});
});

test("should return correct question config", () => {
Expand Down
30 changes: 24 additions & 6 deletions @commitlint/cz-commitlint/src/services/getRuleQuestionConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,41 @@ export default function (rulePrefix: RuleField): QuestionConfig | null {

if (enumRuleList) {
const enumDescriptions = questionSettings?.["enum"];
const emojiInHeader = questionSettings?.emojiInHeader;

if (enumDescriptions) {
const enumNames = Object.keys(enumDescriptions);
const longest = Math.max(
...enumRuleList.map((enumName) => enumName.length),
);
// TODO emoji + title
const firstHasEmoji =
(enumDescriptions[enumNames[0]]?.emoji?.length ?? 0) > 0;
const hasConsistentEmojiUsage = !enumRuleList.some(
(enumName) =>
(enumDescriptions[enumName]?.emoji?.length ?? 0) > 0 !==
firstHasEmoji,
);
enumList = enumRuleList
.sort((a, b) => enumNames.indexOf(a) - enumNames.indexOf(b))
.map((enumName) => {
const enumDescription = enumDescriptions[enumName]?.description;
if (enumDescription) {
return {
name: `${enumName}:`.padEnd(longest + 4) + enumDescription,
value: enumName,
short: enumName,
};
const emoji = enumDescriptions[enumName]?.emoji;

const emojiPrefix = emoji
? `${emoji} `
: hasConsistentEmojiUsage
? ""
: " ";

const paddedName = `${enumName}:`.padEnd(longest + 4);

const name = `${emojiPrefix}${paddedName}${enumDescription}`;

const value =
emojiInHeader && emoji ? `${emoji.trim()} ${enumName}` : enumName;

return { name, value, short: enumName };
} else {
return enumName;
}
Expand Down
1 change: 1 addition & 0 deletions @commitlint/types/src/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type PromptConfig = {
emoji?: string;
};
};
emojiInHeader?: boolean;
}
>
>;
Expand Down
Binary file modified docs/public/assets/cz-commitlint.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/assets/vs-code-commit-msg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/assets/vs-code-emoji.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading