Skip to content

Commit

Permalink
feat: add function rule plugin sources and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jdbruijn committed Sep 6, 2020
1 parent 1f38e86 commit 1a8db80
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 4 deletions.
3 changes: 1 addition & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "commitlint-plugin-function-rules",
"version": "1.0.0",
"description": "Commitlint plugin to define rules as functions.",
"private": true,
"main": "dist/index.js",
"types": "dist/index.d.js",
"files": [
"dist/**/!(*.test).{js,d.ts}"
],
"scripts": {
"lint": "run-p format:check lint-es",
"lint:fix": "run-s format lint-es:fix",
Expand Down Expand Up @@ -34,10 +38,16 @@
"url": "https://github.com/vidavidorra/commitlint-plugin-function-rules/issues"
},
"homepage": "https://github.com/vidavidorra/commitlint-plugin-function-rules#readme",
"dependencies": {},
"peerDependencies": {
"@commitlint/lint": "9.1.2"
},
"dependencies": {
"@commitlint/types": "9.1.2"
},
"devDependencies": {
"@commitlint/cli": "9.1.2",
"@commitlint/config-conventional": "9.1.2",
"@commitlint/rules": "9.1.2",
"@jest/globals": "26.4.2",
"@semantic-release/changelog": "5.0.1",
"@semantic-release/git": "9.0.0",
Expand Down
95 changes: 95 additions & 0 deletions src/function-rule.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { Commit, RuleConfigCondition, RuleOutcome } from '@commitlint/types';
import { beforeEach, describe, expect, it, jest } from '@jest/globals';
import functionRule, { FunctionRule } from './function-rule';

describe('functionRule', (): void => {
const commit: Commit = {
type: 'chore',
scope: 'scope',
subject: 'test',
merge: null,
header: 'chore(scope): test',
body: null,
footer: null,
notes: [],
references: [],
mentions: [],
revert: null,
raw: 'chore(scope): test\n',
};
const when: RuleConfigCondition = 'always';
/**
* To pass this to an function, that is obviously not expecting a mock, a type
* assertion is needed. For this, the as-syntax is needed when a mock is
* passed as function argument.
*/
const ruleImplementation = jest.fn();

beforeEach(() => {
ruleImplementation.mockReset();
});

it('calls implementation function', () => {
functionRule(commit, when, ruleImplementation as FunctionRule);

expect(ruleImplementation).toHaveBeenCalledTimes(1);
});

it('passes arguments to implementation function', () => {
functionRule(commit, when, ruleImplementation as FunctionRule);

expect(ruleImplementation).toHaveBeenCalledWith(commit, when);
});

it("defaults 'when' argument to 'always'", () => {
functionRule(commit, undefined, ruleImplementation as FunctionRule);

expect(ruleImplementation).toHaveBeenCalledWith(commit, 'always');
});

it('returns value from sync implementation function', () => {
const returnValue: RuleOutcome = [
true,
'Message from sync implementation function.',
];
ruleImplementation.mockImplementation(() => returnValue);
const value = functionRule(
commit,
when,
ruleImplementation as FunctionRule,
);

expect(ruleImplementation).toHaveBeenCalledTimes(1);
expect(value).toEqual(returnValue);
});

it('returns value from async implementation function', async () => {
const returnValue: RuleOutcome = [
true,
'Message from async implementation function.',
];
ruleImplementation.mockImplementation(() => Promise.resolve(returnValue));
const value = await functionRule(
commit,
when,
ruleImplementation as FunctionRule,
);

expect(ruleImplementation).toHaveBeenCalledTimes(1);
expect(value).toEqual(returnValue);
});

it("throws an error when 'value' is 'undefined'", () => {
expect(() => {
functionRule(commit, when, undefined);
}).toThrow();
});

it("throws an error when 'value' is 'not an function'", () => {
expect(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error: TS2345: Argument of type ... is not assignable to parameter of type ...
functionRule(commit, when, 'not a function!');
}).toThrow();
});
});
26 changes: 26 additions & 0 deletions src/function-rule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
Commit,
Rule,
RuleConfigCondition,
RuleOutcome,
} from '@commitlint/types';

type FunctionRule = (
parsed: Commit,
when: RuleConfigCondition,
) => RuleOutcome | Promise<RuleOutcome>;

const functionRule: Rule<FunctionRule> = (
parsed: Commit,
when: RuleConfigCondition = 'always',
value: FunctionRule | undefined,
) => {
if (typeof value === 'function') {
return value(parsed, when);
}

throw new Error('Not a valid function!');
};

export default functionRule;
export { FunctionRule };
14 changes: 14 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as plugin from './index';
import { describe, expect, it } from '@jest/globals';
import rules from './rules';

describe('index', () => {
it(`exports a CommonJS module with 'rules' object`, () => {
expect(Object.keys(plugin)).toEqual(['rules']);
expect(typeof plugin.rules).toEqual('object');
});

it('exports rules', () => {
expect(plugin).toMatchObject({ rules });
});
});
13 changes: 13 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Plugin from './plugin';
import rules from './rules';

const plugin: Plugin = {
rules,
};

/**
* Export single object, according to the CommonJS model. The CommonJS module is
* explicitly used here as that's the kind of module commitlint requires for
* plugins.
*/
export = plugin;
13 changes: 13 additions & 0 deletions src/plugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { describe, expect, it } from '@jest/globals';
import Plugin from './plugin';

describe('Plugin', (): void => {
it("defines 'rules' as an object", () => {
const plugin: Plugin = {
rules: {},
};

expect(Object.keys(plugin)).toEqual(['rules']);
expect(typeof plugin.rules).toEqual('object');
});
});
7 changes: 7 additions & 0 deletions src/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Rules } from './rules';

interface Plugin {
rules: Rules;
}

export default Plugin;
31 changes: 31 additions & 0 deletions src/rules.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { describe, expect, it } from '@jest/globals';
import commitlintRules from '@commitlint/rules';
import functionRule from './function-rule';
import rules from './rules';

describe('rules', (): void => {
const prefix = 'function-rules/';
const names = Object.keys(rules);
const commitlintRuleNames = Object.keys(commitlintRules).sort();

it('exports the same rules as commitlint', () => {
const strippedNames = names.map((e) => {
return e.replace(new RegExp(`^${prefix}`), '');
});

expect(strippedNames).toEqual(commitlintRuleNames);
});

it(`are exported with with '${prefix}' as prefix`, () => {
const everyNameHasPrefix = names.every((e) => e.startsWith(prefix));

expect(everyNameHasPrefix).toBe(true);
});

it(`have 'functionRule' as value`, () => {
const values = Object.values(rules);
const everyValueIsFunctionRule = values.every((e) => e === functionRule);

expect(everyValueIsFunctionRule).toBe(true);
});
});
42 changes: 42 additions & 0 deletions src/rules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import functionRule, { FunctionRule } from './function-rule';
import { Rule } from '@commitlint/types';

type Rules = Record<string, Rule<FunctionRule>>;

const rules: Rules = {
'function-rules/body-case': functionRule,
'function-rules/body-empty': functionRule,
'function-rules/body-leading-blank': functionRule,
'function-rules/body-max-length': functionRule,
'function-rules/body-max-line-length': functionRule,
'function-rules/body-min-length': functionRule,
'function-rules/footer-empty': functionRule,
'function-rules/footer-leading-blank': functionRule,
'function-rules/footer-max-length': functionRule,
'function-rules/footer-max-line-length': functionRule,
'function-rules/footer-min-length': functionRule,
'function-rules/header-case': functionRule,
'function-rules/header-full-stop': functionRule,
'function-rules/header-max-length': functionRule,
'function-rules/header-min-length': functionRule,
'function-rules/references-empty': functionRule,
'function-rules/scope-case': functionRule,
'function-rules/scope-empty': functionRule,
'function-rules/scope-enum': functionRule,
'function-rules/scope-max-length': functionRule,
'function-rules/scope-min-length': functionRule,
'function-rules/signed-off-by': functionRule,
'function-rules/subject-case': functionRule,
'function-rules/subject-empty': functionRule,
'function-rules/subject-full-stop': functionRule,
'function-rules/subject-max-length': functionRule,
'function-rules/subject-min-length': functionRule,
'function-rules/type-case': functionRule,
'function-rules/type-empty': functionRule,
'function-rules/type-enum': functionRule,
'function-rules/type-max-length': functionRule,
'function-rules/type-min-length': functionRule,
};

export default rules;
export { Rules };

0 comments on commit 1a8db80

Please sign in to comment.