Skip to content

Commit 4c7207e

Browse files
authored
feat(valid-title): support mustMatch & mustNotMatch options (#608)
Closes #233
1 parent 94fa724 commit 4c7207e

File tree

3 files changed

+362
-8
lines changed

3 files changed

+362
-8
lines changed

docs/rules/valid-title.md

+38-2
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,11 @@ describe('foo', () => {
152152
## Options
153153

154154
```ts
155-
interface {
155+
interface Options {
156156
ignoreTypeOfDescribeName?: boolean;
157157
disallowedWords?: string[];
158+
mustNotMatch?: Partial<Record<'describe' | 'test' | 'it', string>> | string;
159+
mustMatch?: Partial<Record<'describe' | 'test' | 'it', string>> | string;
158160
}
159161
```
160162

@@ -172,7 +174,7 @@ Default: `[]`
172174
A string array of words that are not allowed to be used in test titles. Matching
173175
is not case-sensitive, and looks for complete words:
174176

175-
Examples of **incorrect** code using `disallowedWords`:
177+
Examples of **incorrect** code when using `disallowedWords`:
176178

177179
```js
178180
// with disallowedWords: ['correct', 'all', 'every', 'properly']
@@ -190,3 +192,37 @@ it('correctly sets the value', () => {});
190192
test('that everything is as it should be', () => {});
191193
describe('the proper way to handle things', () => {});
192194
```
195+
196+
#### `mustMatch` & `mustNotMatch`
197+
198+
Defaults: `{}`
199+
200+
Allows enforcing that titles must match or must not match a given Regular
201+
Expression. An object can be provided to apply different Regular Expressions to
202+
specific Jest test function groups (`describe`, `test`, and `it`).
203+
204+
Examples of **incorrect** code when using `mustMatch`:
205+
206+
```js
207+
// with mustMatch: '$that'
208+
describe('the correct way to do things', () => {});
209+
fit('this there!', () => {});
210+
211+
// with mustMatch: { test: '$that' }
212+
describe('the tests that will be run', () => {});
213+
test('the stuff works', () => {});
214+
xtest('errors that are thrown have messages', () => {});
215+
```
216+
217+
Examples of **correct** code when using `mustMatch`:
218+
219+
```js
220+
// with mustMatch: '$that'
221+
describe('that thing that needs to be done', () => {});
222+
fit('that this there!', () => {});
223+
224+
// with mustMatch: { test: '$that' }
225+
describe('the tests that will be run', () => {});
226+
test('that the stuff works', () => {});
227+
xtest('that errors that thrown have messages', () => {});
228+
```

src/rules/__tests__/valid-title.test.ts

+217
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { TSESLint } from '@typescript-eslint/experimental-utils';
2+
import dedent from 'dedent';
23
import resolveFrom from 'resolve-from';
34
import rule from '../valid-title';
45

@@ -100,6 +101,222 @@ ruleTester.run('disallowedWords option', rule, {
100101
],
101102
});
102103

104+
ruleTester.run('mustMatch & mustNotMatch options', rule, {
105+
valid: [
106+
'describe("the correct way to properly handle all the things", () => {});',
107+
'test("that all is as it should be", () => {});',
108+
{
109+
code: 'it("correctly sets the value", () => {});',
110+
options: [{ mustMatch: undefined }],
111+
},
112+
{
113+
code: 'it("correctly sets the value", () => {});',
114+
options: [{ mustMatch: / /u.source }],
115+
},
116+
{
117+
code: 'it("correctly sets the value #unit", () => {});',
118+
options: [{ mustMatch: /#(?:unit|integration|e2e)/u.source }],
119+
},
120+
{
121+
code: 'it("correctly sets the value", () => {});',
122+
options: [{ mustMatch: /^[^#]+$|(?:#(?:unit|e2e))/u.source }],
123+
},
124+
{
125+
code: 'it("correctly sets the value", () => {});',
126+
options: [{ mustMatch: { test: /#(?:unit|integration|e2e)/u.source } }],
127+
},
128+
{
129+
code: dedent`
130+
describe('things to test', () => {
131+
describe('unit tests #unit', () => {
132+
it('is true', () => {
133+
expect(true).toBe(true);
134+
});
135+
});
136+
137+
describe('e2e tests #e2e', () => {
138+
it('is another test #jest4life', () => {});
139+
});
140+
});
141+
`,
142+
options: [{ mustMatch: { test: /^[^#]+$|(?:#(?:unit|e2e))/u.source } }],
143+
},
144+
],
145+
invalid: [
146+
{
147+
code: dedent`
148+
describe('things to test', () => {
149+
describe('unit tests #unit', () => {
150+
it('is true', () => {
151+
expect(true).toBe(true);
152+
});
153+
});
154+
155+
describe('e2e tests #e4e', () => {
156+
it('is another test #e2e #jest4life', () => {});
157+
});
158+
});
159+
`,
160+
options: [
161+
{
162+
mustNotMatch: /(?:#(?!unit|e2e))\w+/u.source,
163+
mustMatch: /^[^#]+$|(?:#(?:unit|e2e))/u.source,
164+
},
165+
],
166+
errors: [
167+
{
168+
messageId: 'mustNotMatch',
169+
data: {
170+
jestFunctionName: 'describe',
171+
pattern: /(?:#(?!unit|e2e))\w+/u,
172+
},
173+
column: 12,
174+
line: 8,
175+
},
176+
{
177+
messageId: 'mustNotMatch',
178+
data: {
179+
jestFunctionName: 'it',
180+
pattern: /(?:#(?!unit|e2e))\w+/u,
181+
},
182+
column: 8,
183+
line: 9,
184+
},
185+
],
186+
},
187+
{
188+
code: dedent`
189+
describe('things to test', () => {
190+
describe('unit tests #unit', () => {
191+
it('is true', () => {
192+
expect(true).toBe(true);
193+
});
194+
});
195+
196+
describe('e2e tests #e4e', () => {
197+
it('is another test #e2e #jest4life', () => {});
198+
});
199+
});
200+
`,
201+
options: [
202+
{
203+
mustNotMatch: { describe: /(?:#(?!unit|e2e))\w+/u.source },
204+
mustMatch: { describe: /^[^#]+$|(?:#(?:unit|e2e))/u.source },
205+
},
206+
],
207+
errors: [
208+
{
209+
messageId: 'mustNotMatch',
210+
data: {
211+
jestFunctionName: 'describe',
212+
pattern: /(?:#(?!unit|e2e))\w+/u,
213+
},
214+
column: 12,
215+
line: 8,
216+
},
217+
],
218+
},
219+
{
220+
code: dedent`
221+
describe('things to test', () => {
222+
describe('unit tests #unit', () => {
223+
it('is true', () => {
224+
expect(true).toBe(true);
225+
});
226+
});
227+
228+
describe('e2e tests #e4e', () => {
229+
it('is another test #e2e #jest4life', () => {});
230+
});
231+
});
232+
`,
233+
options: [
234+
{
235+
mustNotMatch: { describe: /(?:#(?!unit|e2e))\w+/u.source },
236+
mustMatch: { it: /^[^#]+$|(?:#(?:unit|e2e))/u.source },
237+
},
238+
],
239+
errors: [
240+
{
241+
messageId: 'mustNotMatch',
242+
data: {
243+
jestFunctionName: 'describe',
244+
pattern: /(?:#(?!unit|e2e))\w+/u,
245+
},
246+
column: 12,
247+
line: 8,
248+
},
249+
],
250+
},
251+
{
252+
code: 'test("the correct way to properly handle all things", () => {});',
253+
options: [{ mustMatch: /#(?:unit|integration|e2e)/u.source }],
254+
errors: [
255+
{
256+
messageId: 'mustMatch',
257+
data: {
258+
jestFunctionName: 'test',
259+
pattern: /#(?:unit|integration|e2e)/u,
260+
},
261+
column: 6,
262+
line: 1,
263+
},
264+
],
265+
},
266+
{
267+
code: 'describe("the test", () => {});',
268+
options: [
269+
{ mustMatch: { describe: /#(?:unit|integration|e2e)/u.source } },
270+
],
271+
errors: [
272+
{
273+
messageId: 'mustMatch',
274+
data: {
275+
jestFunctionName: 'describe',
276+
pattern: /#(?:unit|integration|e2e)/u,
277+
},
278+
column: 10,
279+
line: 1,
280+
},
281+
],
282+
},
283+
{
284+
code: 'xdescribe("the test", () => {});',
285+
options: [
286+
{ mustMatch: { describe: /#(?:unit|integration|e2e)/u.source } },
287+
],
288+
errors: [
289+
{
290+
messageId: 'mustMatch',
291+
data: {
292+
jestFunctionName: 'describe',
293+
pattern: /#(?:unit|integration|e2e)/u,
294+
},
295+
column: 11,
296+
line: 1,
297+
},
298+
],
299+
},
300+
{
301+
code: 'describe.skip("the test", () => {});',
302+
options: [
303+
{ mustMatch: { describe: /#(?:unit|integration|e2e)/u.source } },
304+
],
305+
errors: [
306+
{
307+
messageId: 'mustMatch',
308+
data: {
309+
jestFunctionName: 'describe',
310+
pattern: /#(?:unit|integration|e2e)/u,
311+
},
312+
column: 15,
313+
line: 1,
314+
},
315+
],
316+
},
317+
],
318+
});
319+
103320
ruleTester.run('title-must-be-string', rule, {
104321
valid: [
105322
'it("is a string", () => {});',

0 commit comments

Comments
 (0)