Skip to content

Commit 3c1a991

Browse files
committed
Refactoring github issue number rules directory structure to simplify naming, and adding test coverage.
1 parent efc79e1 commit 3c1a991

File tree

12 files changed

+272
-156
lines changed

12 files changed

+272
-156
lines changed

packages/commitlint-plugin-github-rules/src/index.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
import { commitlintGitHubConstants } from 'commitlint-github-utils';
1+
import utils from 'commitlint-github-utils';
22
import { CommitlintPluginGitHub } from '../@types';
3-
import githubIssueNumberMissingRuleResolver from './rules/githubIssueNumberMissingRuleResolver';
4-
import githubIssueNumberFormatRuleResolver from './rules/githubIssueNumberFormatRuleResolver';
3+
4+
import githubIssueNumberMissingRuleResolver from './rules/githubIssueNumbers/isMissing';
5+
import githubIssueNumberFormatRuleResolver from './rules/githubIssueNumbers/isCorrectFormat';
6+
import githubIssueNumberDuplicateRuleResolver from './rules/githubIssueNumbers/isDuplicate';
7+
8+
const commitlintGitHubRules = utils.commitlintGitHubConstants.GITHUB_RULES;
59

610
export const commitlintPluginGitHub: CommitlintPluginGitHub = {
711
rules: {
8-
[commitlintGitHubConstants.GITHUB_RULES.issueNumberMissing]: githubIssueNumberMissingRuleResolver,
9-
[commitlintGitHubConstants.GITHUB_RULES.issueNumberFormat]: githubIssueNumberFormatRuleResolver,
12+
[commitlintGitHubRules.issueNumberMissing]: githubIssueNumberMissingRuleResolver,
13+
[commitlintGitHubRules.issueNumberFormat]: githubIssueNumberFormatRuleResolver,
14+
[commitlintGitHubRules.issueNumberDuplicate]: githubIssueNumberDuplicateRuleResolver,
1015
},
1116
};
1217

packages/commitlint-plugin-github-rules/src/rules/githubIssueNumberMissingRuleResolver.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.

packages/commitlint-plugin-github-rules/src/rules/githubIssueNumberFormatRuleResolver.ts renamed to packages/commitlint-plugin-github-rules/src/rules/githubIssueNumbers/isCorrectFormat.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
import { parseCommitMessage } from 'commitlint-github-utils';
2-
import { RuleResolver } from '../../@types';
1+
import utils from 'commitlint-github-utils';
2+
import { RuleResolver } from '../../../@types';
33

44
const githubIssueNumberFormatRuleResolver: RuleResolver<void> = parsed => {
55
const rawCommitMessage = parsed.raw;
66
if (!rawCommitMessage) return [false, 'Commit message should not be empty'];
77

8-
const commitMessage = parseCommitMessage(rawCommitMessage);
8+
const commitMessage = utils.parseCommitMessage(rawCommitMessage);
9+
10+
// We short circuit and return true for WIP commits since we don't valdiate those
11+
if (commitMessage.isWip) {
12+
return [true];
13+
}
914

1015
const issueNumbersValid = commitMessage.rawIssueNumbers == null || commitMessage.issueNumbers.length > 0;
1116

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import utils from 'commitlint-github-utils';
2+
import { RuleResolver } from '../../../@types';
3+
4+
const isNoDuplicates = (issueNumbers: number[]): boolean => new Set(issueNumbers).size === issueNumbers.length;
5+
6+
const githubIssueNumberDuplicateRuleResolver: RuleResolver<void> = parsed => {
7+
const rawCommitMessage = parsed.raw;
8+
if (!rawCommitMessage) return [false, 'Commit message should not be empty'];
9+
10+
const commitMessage = utils.parseCommitMessage(rawCommitMessage);
11+
12+
// We short circuit and return true for WIP commits since we don't valdiate those
13+
if (commitMessage.isWip) {
14+
return [true];
15+
}
16+
17+
const issueNumbersValid = commitMessage.rawIssueNumbers == null || isNoDuplicates(commitMessage.issueNumbers);
18+
19+
return [issueNumbersValid, 'the commit message duplicate issue numbers referenced in the commit message prefix.'];
20+
};
21+
22+
export default githubIssueNumberDuplicateRuleResolver;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { When } from 'commitlint-github-utils/@types';
2+
import utils from 'commitlint-github-utils';
3+
import { RuleResolver } from '../../../@types';
4+
5+
const githubIssueNumberMissingRuleResolver: RuleResolver<boolean> = (parsed, when) => {
6+
const rawCommitMessage = parsed.raw;
7+
if (!rawCommitMessage) return [false, 'Commit message should not be empty'];
8+
9+
const commitMessage = utils.parseCommitMessage(rawCommitMessage);
10+
11+
// We short circuit and return true for WIP commits since we don't valdiate those
12+
if (commitMessage.isWip) {
13+
return [true];
14+
}
15+
16+
const issueNumbersFound = commitMessage.rawIssueNumbers != null;
17+
18+
// First check for the standard case where no issue numbers are found and it is required to never be missing
19+
if (!issueNumbersFound && when === When.NEVER) {
20+
return [
21+
false,
22+
'the commit message must start with the associated GitHub issue number in parentheses followed by space. For example: (#42) My git commit message.',
23+
];
24+
}
25+
26+
// Next check the odd case of when issue numbers are found but it was required to always be missing
27+
if (issueNumbersFound && when === When.ALWAYS) {
28+
return [false, 'the commit message must NOT start with the associated GitHub issue number in parentheses.'];
29+
}
30+
31+
return [true];
32+
};
33+
34+
export default githubIssueNumberMissingRuleResolver;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { When } from 'commitlint-github-utils/@types';
2+
3+
import { RuleResolverResult } from '../../../../@types';
4+
import githubIssueNumberFormatRuleResolver from '../isCorrectFormat';
5+
import runRule from '../../utils/tests/utils';
6+
7+
const parse = (when: When, rawCommitMessage: string): RuleResolverResult =>
8+
runRule(githubIssueNumberFormatRuleResolver, [when], rawCommitMessage);
9+
10+
describe('githubIssueNumberFormatRuleResolver', () => {
11+
it('should always return an error response if an empty commit message is provided', () => {
12+
expect(parse(When.ALWAYS, '')[0]).toEqual(false);
13+
expect(parse(When.NEVER, '')[0]).toEqual(false);
14+
});
15+
16+
it('should return a success response if one issue number is specified', () => {
17+
expect(parse(When.ALWAYS, '(#1) my commit message')[0]).toEqual(true);
18+
expect(parse(When.ALWAYS, '(#1) chore: my commit message')[0]).toEqual(true);
19+
});
20+
21+
it('should return a success response if multiple issue numbers are specified', () => {
22+
expect(parse(When.ALWAYS, '(#1, #2) my commit message')[0]).toEqual(true);
23+
expect(parse(When.ALWAYS, '(#1,#2) my commit message')[0]).toEqual(true);
24+
25+
expect(parse(When.ALWAYS, '(#1, #2) chore: my commit message')[0]).toEqual(true);
26+
expect(parse(When.ALWAYS, '(#1,#2) chore: my commit message')[0]).toEqual(true);
27+
});
28+
29+
it('should return a success response if no issue numbers are specified (as nothing to validate)', () => {
30+
expect(parse(When.ALWAYS, 'my commit message')[0]).toEqual(true);
31+
expect(parse(When.ALWAYS, 'chore: my commit message')[0]).toEqual(true);
32+
});
33+
34+
it('should return an error response if not in correct format', () => {
35+
expect(parse(When.ALWAYS, '(1) my commit message')[0]).toEqual(false);
36+
expect(parse(When.ALWAYS, '(1) chore: my commit message')[0]).toEqual(false);
37+
38+
expect(parse(When.ALWAYS, '(bob) my commit message')[0]).toEqual(false);
39+
expect(parse(When.ALWAYS, '(bob) chore: my commit message')[0]).toEqual(false);
40+
41+
expect(parse(When.ALWAYS, '(#bob) my commit message')[0]).toEqual(false);
42+
expect(parse(When.ALWAYS, '(#bob) chore: my commit message')[0]).toEqual(false);
43+
44+
expect(parse(When.ALWAYS, '(#1, #2bob) my commit message')[0]).toEqual(false);
45+
expect(parse(When.ALWAYS, '(#1, #2bob) chore: my commit message')[0]).toEqual(false);
46+
47+
expect(parse(When.ALWAYS, '(#1,#2bob) my commit message')[0]).toEqual(false);
48+
expect(parse(When.ALWAYS, '(#1,#2bob) chore: my commit message')[0]).toEqual(false);
49+
50+
expect(parse(When.ALWAYS, '(#1, #2 bob) my commit message')[0]).toEqual(false);
51+
expect(parse(When.ALWAYS, '(#1, #2 bob) chore: my commit message')[0]).toEqual(false);
52+
});
53+
54+
it('should always return a success response for any WIP commit as rules are disabled for WIPs', () => {
55+
expect(parse(When.ALWAYS, '(#1) WIP: my commit message')[0]).toEqual(true);
56+
expect(parse(When.ALWAYS, '(#1, #2) WIP: my commit message')[0]).toEqual(true);
57+
expect(parse(When.ALWAYS, '(#1,#2) WIP: my commit message')[0]).toEqual(true);
58+
59+
expect(parse(When.ALWAYS, 'WIP')[0]).toEqual(true);
60+
expect(parse(When.ALWAYS, 'WIP2')[0]).toEqual(true);
61+
expect(parse(When.ALWAYS, 'WIP: my commit message')[0]).toEqual(true);
62+
expect(parse(When.ALWAYS, 'WIP2: My commit message')[0]).toEqual(true);
63+
expect(parse(When.ALWAYS, 'WIP 2: My commit message')[0]).toEqual(true);
64+
expect(parse(When.ALWAYS, 'WIP2 - My commit message')[0]).toEqual(true);
65+
expect(parse(When.ALWAYS, 'WIP 2 - My commit message')[0]).toEqual(true);
66+
});
67+
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { When } from 'commitlint-github-utils/@types';
2+
3+
import { RuleResolverResult } from '../../../../@types';
4+
import githubIssueNumberDuplicateRuleResolver from '../isDuplicate';
5+
import runRule from '../../utils/tests/utils';
6+
7+
const parse = (when: When, rawCommitMessage: string): RuleResolverResult =>
8+
runRule(githubIssueNumberDuplicateRuleResolver, [when], rawCommitMessage);
9+
10+
describe('githubIssueNumberDuplicateRuleResolver', () => {
11+
it('should always return an error response if an empty commit message is provided', () => {
12+
expect(parse(When.ALWAYS, '')[0]).toEqual(false);
13+
expect(parse(When.NEVER, '')[0]).toEqual(false);
14+
});
15+
16+
it('should return a success response if there are no duplicates', () => {
17+
expect(parse(When.NEVER, '(#1) my commit message')[0]).toEqual(true);
18+
expect(parse(When.NEVER, '(#1) chore: my commit message')[0]).toEqual(true);
19+
20+
expect(parse(When.NEVER, '(#1, #2) my commit message')[0]).toEqual(true);
21+
expect(parse(When.NEVER, '(#1,#2) my commit message')[0]).toEqual(true);
22+
23+
expect(parse(When.NEVER, '(#123, #23) chore: my commit message')[0]).toEqual(true);
24+
expect(parse(When.NEVER, '(#123,#23) chore: my commit message')[0]).toEqual(true);
25+
});
26+
27+
it('should return a success response if there are no issue numbers', () => {
28+
expect(parse(When.NEVER, 'my commit message')[0]).toEqual(true);
29+
expect(parse(When.NEVER, '() chore: my commit message')[0]).toEqual(true);
30+
});
31+
32+
it('should return an error response if there is at least one duplicate', () => {
33+
expect(parse(When.NEVER, '(#123, #2, #123) my commit message')[0]).toEqual(false);
34+
expect(parse(When.NEVER, '(#1, #1) chore: my commit message')[0]).toEqual(false);
35+
36+
expect(parse(When.NEVER, '(#133, #133) my commit message')[0]).toEqual(false);
37+
expect(parse(When.NEVER, '(#122,#122) my commit message')[0]).toEqual(false);
38+
39+
expect(parse(When.NEVER, '(#123, #123) chore: my commit message')[0]).toEqual(false);
40+
expect(parse(When.NEVER, '(#123,#123) chore: my commit message')[0]).toEqual(false);
41+
});
42+
43+
it('should always return a success response for any WIP commit as rules are disabled for WIPs', () => {
44+
expect(parse(When.NEVER, '(#1) WIP: my commit message')[0]).toEqual(true);
45+
expect(parse(When.NEVER, '(#1, #2) WIP: my commit message')[0]).toEqual(true);
46+
expect(parse(When.NEVER, '(#1,#2) WIP: my commit message')[0]).toEqual(true);
47+
48+
expect(parse(When.NEVER, 'WIP')[0]).toEqual(true);
49+
expect(parse(When.NEVER, 'WIP2')[0]).toEqual(true);
50+
expect(parse(When.NEVER, 'WIP: my commit message')[0]).toEqual(true);
51+
expect(parse(When.NEVER, 'WIP2: My commit message')[0]).toEqual(true);
52+
expect(parse(When.NEVER, 'WIP 2: My commit message')[0]).toEqual(true);
53+
expect(parse(When.NEVER, 'WIP2 - My commit message')[0]).toEqual(true);
54+
expect(parse(When.NEVER, 'WIP 2 - My commit message')[0]).toEqual(true);
55+
});
56+
});
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { When } from 'commitlint-github-utils/@types';
2+
3+
import { RuleResolverResult } from '../../../../@types';
4+
import githubIssueNumberMissingRuleResolver from '../isMissing';
5+
import runRule from '../../utils/tests/utils';
6+
7+
const parse = (when: When, rawCommitMessage: string): RuleResolverResult =>
8+
runRule(githubIssueNumberMissingRuleResolver, [when], rawCommitMessage);
9+
10+
describe('githubIssueNumberMissingRuleResolver', () => {
11+
it('should always return an error response if an empty commit message is provided', () => {
12+
expect(parse(When.ALWAYS, '')[0]).toEqual(false);
13+
expect(parse(When.NEVER, '')[0]).toEqual(false);
14+
});
15+
16+
it('should return a success response if one issue number is specified', () => {
17+
expect(parse(When.NEVER, '(#1) my commit message')[0]).toEqual(true);
18+
expect(parse(When.NEVER, '(#1) chore: my commit message')[0]).toEqual(true);
19+
});
20+
21+
it('should return a success response if multiple issue numbers are specified', () => {
22+
expect(parse(When.NEVER, '(#1, #2) my commit message')[0]).toEqual(true);
23+
expect(parse(When.NEVER, '(#1,#2) my commit message')[0]).toEqual(true);
24+
25+
expect(parse(When.NEVER, '(#1, #2) chore: my commit message')[0]).toEqual(true);
26+
expect(parse(When.NEVER, '(#1,#2) chore: my commit message')[0]).toEqual(true);
27+
});
28+
29+
it('should return a success response even if no separator before commit message', () => {
30+
expect(parse(When.NEVER, '(#1)my commit message')[0]).toEqual(true);
31+
});
32+
33+
it('should return an error response for a non-WIP commit if no issue numbers are specified', () => {
34+
expect(parse(When.NEVER, 'my commit message')[0]).toEqual(false);
35+
});
36+
37+
// Tests for the odd case of specifying ALWAYS to require issue numbers to be missing
38+
// Note the parser doesn't support this flow since it's not an initial requirement, but the rule resolver under test does
39+
40+
it('should return a success response for non-WIP commit if no issue numbers are specified and always required to be missing', () => {
41+
expect(parse(When.ALWAYS, 'my commit message')[0]).toEqual(true);
42+
});
43+
44+
it('should return an error response for non-WIP commit if issue numbers are specified and always required to be missing', () => {
45+
expect(parse(When.ALWAYS, '(#1) my commit message')[0]).toEqual(false);
46+
});
47+
48+
it('should always return a success response for any WIP commit as rules are disabled for WIPs', () => {
49+
expect(parse(When.NEVER, '(#1) WIP: my commit message')[0]).toEqual(true);
50+
expect(parse(When.NEVER, '(#1, #2) WIP: my commit message')[0]).toEqual(true);
51+
expect(parse(When.NEVER, '(#1,#2) WIP: my commit message')[0]).toEqual(true);
52+
53+
expect(parse(When.NEVER, 'WIP')[0]).toEqual(true);
54+
expect(parse(When.NEVER, 'WIP2')[0]).toEqual(true);
55+
expect(parse(When.NEVER, 'WIP: my commit message')[0]).toEqual(true);
56+
expect(parse(When.NEVER, 'WIP2: My commit message')[0]).toEqual(true);
57+
expect(parse(When.NEVER, 'WIP 2: My commit message')[0]).toEqual(true);
58+
expect(parse(When.NEVER, 'WIP2 - My commit message')[0]).toEqual(true);
59+
expect(parse(When.NEVER, 'WIP 2 - My commit message')[0]).toEqual(true);
60+
});
61+
});

packages/commitlint-plugin-github-rules/src/rules/tests/githubIssueNumberFormatRuleResolver.test.ts

Lines changed: 0 additions & 57 deletions
This file was deleted.

0 commit comments

Comments
 (0)