1 | import path from 'path';
|
2 | import {fix, git} from '@commitlint/test';
|
3 | import test from 'ava';
|
4 | import execa from 'execa';
|
5 | import {merge} from 'lodash';
|
6 | import * as sander from 'sander';
|
7 | import stream from 'string-to-stream';
|
8 |
|
9 | const bin = path.join(__dirname, './cli.js');
|
10 |
|
11 | const cli = (args, options) => {
|
12 | return (input = '') => {
|
13 | const c = execa(bin, args, {
|
14 | capture: ['stdout'],
|
15 | cwd: options.cwd,
|
16 | env: options.env
|
17 | });
|
18 | stream(input).pipe(c.stdin);
|
19 | return c.catch(err => err);
|
20 | };
|
21 | };
|
22 |
|
23 | test('should throw when called without [input]', async t => {
|
24 | const cwd = await git.bootstrap('fixtures/default');
|
25 | const actual = await cli([], {cwd})();
|
26 | t.is(actual.code, 1);
|
27 | });
|
28 |
|
29 | test('should reprint input from stdin', async t => {
|
30 | const cwd = await git.bootstrap('fixtures/default');
|
31 | const actual = await cli([], {cwd})('foo: bar');
|
32 | t.true(actual.stdout.includes('foo: bar'));
|
33 | });
|
34 |
|
35 | test('should produce no success output with --quiet flag', async t => {
|
36 | const cwd = await git.bootstrap('fixtures/default');
|
37 | const actual = await cli(['--quiet'], {cwd})('foo: bar');
|
38 | t.is(actual.stdout, '');
|
39 | t.is(actual.stderr, '');
|
40 | });
|
41 |
|
42 | test('should produce no success output with -q flag', async t => {
|
43 | const cwd = await git.bootstrap('fixtures/default');
|
44 | const actual = await cli(['-q'], {cwd})('foo: bar');
|
45 | t.is(actual.stdout, '');
|
46 | t.is(actual.stderr, '');
|
47 | });
|
48 |
|
49 | test('should fail for input from stdin without rules', async t => {
|
50 | const cwd = await git.bootstrap('fixtures/empty');
|
51 | const actual = await cli([], {cwd})('foo: bar');
|
52 | t.is(actual.code, 1);
|
53 | });
|
54 |
|
55 | test('should succeed for input from stdin with rules', async t => {
|
56 | const cwd = await git.bootstrap('fixtures/default');
|
57 | const actual = await cli([], {cwd})('type: bar');
|
58 | t.is(actual.code, 0);
|
59 | });
|
60 |
|
61 | test('should fail for input from stdin with rule from rc', async t => {
|
62 | const cwd = await git.bootstrap('fixtures/simple');
|
63 | const actual = await cli([], {cwd})('foo: bar');
|
64 | t.true(actual.stdout.includes('type must not be one of [foo]'));
|
65 | t.is(actual.code, 1);
|
66 | });
|
67 |
|
68 | test('should work with --config option', async t => {
|
69 | const file = 'config/commitlint.config.js';
|
70 | const cwd = await git.bootstrap('fixtures/specify-config-file');
|
71 | const actual = await cli(['--config', file], {cwd})('foo: bar');
|
72 | t.true(actual.stdout.includes('type must not be one of [foo]'));
|
73 | t.is(actual.code, 1);
|
74 | });
|
75 |
|
76 | test('should fail for input from stdin with rule from js', async t => {
|
77 | const cwd = await git.bootstrap('fixtures/extends-root');
|
78 | const actual = await cli(['--extends', './extended'], {cwd})('foo: bar');
|
79 | t.true(actual.stdout.includes('type must not be one of [foo]'));
|
80 | t.is(actual.code, 1);
|
81 | });
|
82 |
|
83 | test('should produce no error output with --quiet flag', async t => {
|
84 | const cwd = await git.bootstrap('fixtures/simple');
|
85 | const actual = await cli(['--quiet'], {cwd})('foo: bar');
|
86 | t.is(actual.stdout, '');
|
87 | t.is(actual.stderr, '');
|
88 | t.is(actual.code, 1);
|
89 | });
|
90 |
|
91 | test('should produce no error output with -q flag', async t => {
|
92 | const cwd = await git.bootstrap('fixtures/simple');
|
93 | const actual = await cli(['-q'], {cwd})('foo: bar');
|
94 | t.is(actual.stdout, '');
|
95 | t.is(actual.stderr, '');
|
96 | t.is(actual.code, 1);
|
97 | });
|
98 |
|
99 | test('should work with husky commitmsg hook and git commit', async () => {
|
100 | const cwd = await git.bootstrap('fixtures/husky/integration');
|
101 | await writePkg({scripts: {commitmsg: `'${bin}' -e`}}, {cwd});
|
102 |
|
103 | await execa('npm', ['install'], {cwd});
|
104 | await execa('git', ['add', 'package.json'], {cwd});
|
105 | await execa('git', ['commit', '-m', '"test: this should work"'], {cwd});
|
106 | });
|
107 |
|
108 | test('should work with husky commitmsg hook in sub packages', async () => {
|
109 | const upper = await git.bootstrap('fixtures/husky');
|
110 | const cwd = path.join(upper, 'integration');
|
111 | await writePkg({scripts: {commitmsg: `'${bin}' -e`}}, {cwd: upper});
|
112 |
|
113 | await execa('npm', ['install'], {cwd});
|
114 | await execa('git', ['add', 'package.json'], {cwd});
|
115 | await execa('git', ['commit', '-m', '"test: this should work"'], {cwd});
|
116 | });
|
117 |
|
118 | test('should work with husky via commitlint -e $GIT_PARAMS', async () => {
|
119 | const cwd = await git.bootstrap('fixtures/husky/integration');
|
120 | await writePkg({scripts: {commitmsg: `'${bin}' -e $GIT_PARAMS`}}, {cwd});
|
121 |
|
122 | await execa('npm', ['install'], {cwd});
|
123 | await execa('git', ['add', 'package.json'], {cwd});
|
124 | await execa('git', ['commit', '-m', '"test: this should work"'], {cwd});
|
125 | });
|
126 |
|
127 | test('should work with husky via commitlint -e %GIT_PARAMS%', async () => {
|
128 | const cwd = await git.bootstrap('fixtures/husky/integration');
|
129 | await writePkg({scripts: {commitmsg: `'${bin}' -e %GIT_PARAMS%`}}, {cwd});
|
130 |
|
131 | await execa('npm', ['install'], {cwd});
|
132 | await execa('git', ['add', 'package.json'], {cwd});
|
133 | await execa('git', ['commit', '-m', '"test: this should work"'], {cwd});
|
134 | });
|
135 |
|
136 | test('should work with husky via commitlint -e $HUSKY_GIT_PARAMS', async () => {
|
137 | const cwd = await git.bootstrap('fixtures/husky/integration');
|
138 | await writePkg(
|
139 | {scripts: {commitmsg: `'${bin}' -e $HUSKY_GIT_PARAMS`}},
|
140 | {cwd}
|
141 | );
|
142 |
|
143 | await execa('npm', ['install'], {cwd});
|
144 | await execa('git', ['add', 'package.json'], {cwd});
|
145 | await execa('git', ['commit', '-m', '"test: this should work"'], {cwd});
|
146 | });
|
147 |
|
148 | test('should work with husky via commitlint -e %HUSKY_GIT_PARAMS%', async () => {
|
149 | const cwd = await git.bootstrap('fixtures/husky/integration');
|
150 | await writePkg(
|
151 | {scripts: {commitmsg: `'${bin}' -e %HUSKY_GIT_PARAMS%`}},
|
152 | {cwd}
|
153 | );
|
154 |
|
155 | await execa('npm', ['install'], {cwd});
|
156 | await execa('git', ['add', 'package.json'], {cwd});
|
157 | await execa('git', ['commit', '-m', '"test: this should work"'], {cwd});
|
158 | });
|
159 |
|
160 | test('should allow reading of environment variables for edit file, succeeding if valid', async t => {
|
161 | const cwd = await git.bootstrap('fixtures/simple');
|
162 | await sander.writeFile(cwd, 'commit-msg-file', 'foo');
|
163 | const actual = await cli(['--env', 'variable'], {
|
164 | cwd,
|
165 | env: {variable: 'commit-msg-file'}
|
166 | })();
|
167 | t.is(actual.code, 0);
|
168 | });
|
169 |
|
170 | test('should allow reading of environment variables for edit file, failing if invalid', async t => {
|
171 | const cwd = await git.bootstrap('fixtures/simple');
|
172 | await sander.writeFile(
|
173 | cwd,
|
174 | 'commit-msg-file',
|
175 | 'foo: bar\n\nFoo bar bizz buzz.\n\nCloses #123.'
|
176 | );
|
177 | const actual = await cli(['--env', 'variable'], {
|
178 | cwd,
|
179 | env: {variable: 'commit-msg-file'}
|
180 | })();
|
181 | t.is(actual.code, 1);
|
182 | });
|
183 |
|
184 | test('should pick up parser preset and fail accordingly', async t => {
|
185 | const cwd = await git.bootstrap('fixtures/parser-preset');
|
186 | const actual = await cli(['--parser-preset', './parser-preset'], {cwd})(
|
187 | 'type(scope): subject'
|
188 | );
|
189 | t.is(actual.code, 1);
|
190 | t.true(actual.stdout.includes('may not be empty'));
|
191 | });
|
192 |
|
193 | test('should pick up parser preset and succeed accordingly', async t => {
|
194 | const cwd = await git.bootstrap('fixtures/parser-preset');
|
195 | const actual = await cli(['--parser-preset', './parser-preset'], {cwd})(
|
196 | '----type(scope): subject'
|
197 | );
|
198 | t.is(actual.code, 0);
|
199 | });
|
200 |
|
201 | test('should pick up config from outside git repo and fail accordingly', async t => {
|
202 | const outer = await fix.bootstrap('fixtures/outer-scope');
|
203 | const cwd = await git.init(path.join(outer, 'inner-scope'));
|
204 |
|
205 | const actual = await cli([], {cwd})('inner: bar');
|
206 | t.is(actual.code, 1);
|
207 | });
|
208 |
|
209 | test('should pick up config from outside git repo and succeed accordingly', async t => {
|
210 | const outer = await fix.bootstrap('fixtures/outer-scope');
|
211 | const cwd = await git.init(path.join(outer, 'inner-scope'));
|
212 |
|
213 | const actual = await cli([], {cwd})('outer: bar');
|
214 | t.is(actual.code, 0);
|
215 | });
|
216 |
|
217 | test('should pick up config from inside git repo with precedence and succeed accordingly', async t => {
|
218 | const outer = await fix.bootstrap('fixtures/inner-scope');
|
219 | const cwd = await git.init(path.join(outer, 'inner-scope'));
|
220 |
|
221 | const actual = await cli([], {cwd})('inner: bar');
|
222 | t.is(actual.code, 0);
|
223 | });
|
224 |
|
225 | test('should pick up config from inside git repo with precedence and fail accordingly', async t => {
|
226 | const outer = await fix.bootstrap('fixtures/inner-scope');
|
227 | const cwd = await git.init(path.join(outer, 'inner-scope'));
|
228 |
|
229 | const actual = await cli([], {cwd})('outer: bar');
|
230 | t.is(actual.code, 1);
|
231 | });
|
232 |
|
233 | test('should handle --amend with signoff', async () => {
|
234 | const cwd = await git.bootstrap('fixtures/signoff');
|
235 | await writePkg({scripts: {commitmsg: `'${bin}' -e`}}, {cwd});
|
236 |
|
237 | await execa('npm', ['install'], {cwd});
|
238 | await execa('git', ['add', 'package.json'], {cwd});
|
239 | await execa(
|
240 | 'git',
|
241 | ['commit', '-m', '"test: this should work"', '--signoff'],
|
242 | {cwd}
|
243 | );
|
244 | await execa('git', ['commit', '--amend', '--no-edit'], {cwd});
|
245 | });
|
246 |
|
247 | test('should handle linting with issue prefixes', async t => {
|
248 | const cwd = await git.bootstrap('fixtures/issue-prefixes');
|
249 | const actual = await cli([], {cwd})('foobar REF-1');
|
250 | t.is(actual.code, 0);
|
251 | });
|
252 |
|
253 | test('should print full commit message when input from stdin fails', async t => {
|
254 | const cwd = await git.bootstrap('fixtures/simple');
|
255 | const input = 'foo: bar\n\nFoo bar bizz buzz.\n\nCloses #123.';
|
256 | const actual = await cli([], {cwd})(input);
|
257 |
|
258 | t.true(actual.stdout.includes(input));
|
259 | t.is(actual.code, 1);
|
260 | });
|
261 |
|
262 | test('should not print full commit message when input succeeds', async t => {
|
263 | const cwd = await git.bootstrap('fixtures/default');
|
264 | const message = 'type: bar\n\nFoo bar bizz buzz.\n\nCloses #123.';
|
265 | const actual = await cli([], {cwd})(message);
|
266 |
|
267 | t.false(actual.stdout.includes(message));
|
268 | t.true(actual.stdout.includes(message.split('\n')[0]));
|
269 | t.is(actual.code, 0);
|
270 | });
|
271 |
|
272 | test('should fail for invalid formatters from configuration', async t => {
|
273 | const cwd = await git.bootstrap('fixtures/custom-formatter');
|
274 | const actual = await cli([], {cwd})('foo: bar');
|
275 | t.true(
|
276 | actual.stderr.includes(
|
277 | `Using format custom-formatter, but cannot find the module`
|
278 | )
|
279 | );
|
280 | t.is(actual.stdout, '');
|
281 | t.is(actual.code, 1);
|
282 | });
|
283 |
|
284 | test('should fail for invalid formatters from flags', async t => {
|
285 | const cwd = await git.bootstrap('fixtures/custom-formatter');
|
286 | const actual = await cli(['--format', 'through-flag'], {cwd})('foo: bar');
|
287 | t.true(
|
288 | actual.stderr.includes(
|
289 | `Using format through-flag, but cannot find the module`
|
290 | )
|
291 | );
|
292 | t.is(actual.stdout, '');
|
293 | t.is(actual.code, 1);
|
294 | });
|
295 |
|
296 | test('should work with absolute formatter path', async t => {
|
297 | const formatterPath = path.resolve(
|
298 | __dirname,
|
299 | '../fixtures/custom-formatter/formatters/custom.js'
|
300 | );
|
301 | const cwd = await git.bootstrap('fixtures/custom-formatter');
|
302 | const actual = await cli(['--format', formatterPath], {cwd})(
|
303 | 'test: this should work'
|
304 | );
|
305 |
|
306 | t.true(actual.stdout.includes('custom-formatter-ok'));
|
307 | t.is(actual.code, 0);
|
308 | });
|
309 |
|
310 | test('should work with relative formatter path', async t => {
|
311 | const cwd = path.resolve(
|
312 | await git.bootstrap('fixtures/custom-formatter'),
|
313 | './formatters'
|
314 | );
|
315 | const actual = await cli(['--format', './custom.js'], {cwd})(
|
316 | 'test: this should work'
|
317 | );
|
318 |
|
319 | t.true(actual.stdout.includes('custom-formatter-ok'));
|
320 | t.is(actual.code, 0);
|
321 | });
|
322 |
|
323 | async function writePkg(payload, options) {
|
324 | const pkgPath = path.join(options.cwd, 'package.json');
|
325 | const pkg = JSON.parse(await sander.readFile(pkgPath));
|
326 | const result = merge(pkg, payload);
|
327 | await sander.writeFile(pkgPath, JSON.stringify(result, null, ' '));
|
328 | }
|