From c51eb0b4343483f5becc0777f7024c7204197cce Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 20 Jan 2020 17:27:47 +1300 Subject: [PATCH 1/6] feat(valid-title): support `disallowedWords` option --- docs/rules/valid-title.md | 46 +++++++++++++++++++++++-- src/rules/__tests__/valid-title.test.ts | 42 +++++++++++++++++++++- src/rules/valid-title.ts | 29 ++++++++++++++-- 3 files changed, 112 insertions(+), 5 deletions(-) diff --git a/docs/rules/valid-title.md b/docs/rules/valid-title.md index 579fdb48e..5bac979b6 100644 --- a/docs/rules/valid-title.md +++ b/docs/rules/valid-title.md @@ -87,7 +87,7 @@ describe(6, function() {}); **duplicatePrefix** -A describe/ test block should not start with duplicatePrefix +A describe / test block should not start with duplicatePrefix Examples of **incorrect** code for this rule @@ -117,7 +117,7 @@ describe('foo', () => { **accidentalSpace** -A describe/ test block should not contain accidentalSpace +A describe / test block should not contain accidentalSpace Examples of **incorrect** code for this rule @@ -148,3 +148,45 @@ describe('foo', () => { test('bar', () => {}); }); ``` + +## Options + +```ts +interface { + ignoreTypeOfDescribeName?: boolean; + disallowedWords?: string[]; +} +``` + +#### `ignoreTypeOfDescribeName` + +Default: `false` + +When enabled, the type of the first argument to `describe` blocks won't be +checked. + +#### `disallowedWords` + +Default: `[]` + +A string array of words that are not allowed to be used in test titles. Matching +is not case-sensitive, and looks for complete words: + +Examples of **incorrect** code using `disallowedWords`: + +```js +// with disallowedWords: ['correct', 'all', 'every', 'properly'] +describe('the correct way to do things', function() {}); +it('has ALL the things', () => {}); +xdescribe('every single one of them', function() {}); +test(`that the value is set properly`, function() {}); +``` + +Examples of **correct** code when using `disallowedWords`: + +```js +// with disallowedWords: ['correct', 'all', 'every', 'properly'] +it('correctly sets the value', () => {}); +test('that everything is as it should be', () => {}); +describe('the proper way to handle things', () => {}); +``` diff --git a/src/rules/__tests__/valid-title.test.ts b/src/rules/__tests__/valid-title.test.ts index 7c293c49e..2b61030fe 100644 --- a/src/rules/__tests__/valid-title.test.ts +++ b/src/rules/__tests__/valid-title.test.ts @@ -9,6 +9,46 @@ const ruleTester = new TSESLint.RuleTester({ }, }); +ruleTester.run('disallowedWords option', rule, { + valid: [ + 'describe("the correct way to properly handle all the things", () => {});', + 'test("that all is as it should be", () => {});', + { + code: 'it("correctly sets the value", () => {});', + options: [ + { ignoreTypeOfDescribeName: false, disallowedWords: ['correct'] }, + ], + }, + ], + invalid: [ + { + code: 'describe("the correct way to do things", function () {})', + options: [{ disallowedWords: ['correct'] }], + errors: [{ messageId: 'disallowedWord', column: 10, line: 1 }], + }, + { + code: 'it("has ALL the things", () => {})', + errors: [{ messageId: 'disallowedWord', column: 4, line: 1 }], + options: [{ disallowedWords: ['all'] }], + }, + { + code: 'xdescribe("every single one of them", function () {})', + options: [{ disallowedWords: ['every'] }], + errors: [{ messageId: 'disallowedWord', column: 11, line: 1 }], + }, + { + code: "describe('Very Descriptive Title Goes Here', function () {})", + options: [{ disallowedWords: ['descriptive'] }], + errors: [{ messageId: 'disallowedWord', column: 10, line: 1 }], + }, + { + code: 'test(`that the value is set properly`, function () {})', + options: [{ disallowedWords: ['properly'] }], + errors: [{ messageId: 'disallowedWord', column: 6, line: 1 }], + }, + ], +}); + ruleTester.run('title-must-be-string', rule, { valid: [ 'it("is a string", () => {});', @@ -31,7 +71,7 @@ ruleTester.run('title-must-be-string', rule, { }, { code: 'xdescribe(skipFunction, () => {});', - options: [{ ignoreTypeOfDescribeName: true }], + options: [{ ignoreTypeOfDescribeName: true, disallowedWords: [] }], }, ], invalid: [ diff --git a/src/rules/valid-title.ts b/src/rules/valid-title.ts index a98e70342..a14827ab0 100644 --- a/src/rules/valid-title.ts +++ b/src/rules/valid-title.ts @@ -50,6 +50,7 @@ export default createRule({ emptyTitle: '{{ jestFunctionName }} should not have an empty title', duplicatePrefix: 'should not have duplicate prefix', accidentalSpace: 'should not have leading or trailing spaces', + disallowedWord: '"{{ word }}" is not allowed.', }, type: 'suggestion', schema: [ @@ -60,14 +61,24 @@ export default createRule({ type: 'boolean', default: false, }, + disallowedWords: { + type: 'array', + items: { type: 'string' }, + default: [], + }, }, additionalProperties: false, }, ], fixable: 'code', }, - defaultOptions: [{ ignoreTypeOfDescribeName: false }], - create(context, [{ ignoreTypeOfDescribeName }]) { + defaultOptions: [{ ignoreTypeOfDescribeName: false, disallowedWords: [] }], + create(context, [{ ignoreTypeOfDescribeName, disallowedWords }]) { + const disallowedWordsRegexp = new RegExp( + `\\b(${disallowedWords.join('|')})\\b`, + 'iu', + ); + return { CallExpression(node: TSESTree.CallExpression) { if (!(isDescribe(node) || isTestCase(node)) || !node.arguments.length) { @@ -113,6 +124,20 @@ export default createRule({ return; } + if (disallowedWords.length) { + const disallowedMatch = disallowedWordsRegexp.exec(title); + + if (disallowedMatch) { + context.report({ + data: { word: disallowedMatch[1] }, + messageId: 'disallowedWord', + node: argument, + }); + + return; + } + } + if (title.trim().length !== title.length) { context.report({ messageId: 'accidentalSpace', From b33ecfadc87ef2c75ace199fbfaaee62000b0ee0 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 20 Jan 2020 17:33:41 +1300 Subject: [PATCH 2/6] test(valid-title): include `data` in `disallowedWord` tests --- src/rules/__tests__/valid-title.test.ts | 45 ++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/rules/__tests__/valid-title.test.ts b/src/rules/__tests__/valid-title.test.ts index 2b61030fe..d3baf7ee5 100644 --- a/src/rules/__tests__/valid-title.test.ts +++ b/src/rules/__tests__/valid-title.test.ts @@ -24,27 +24,62 @@ ruleTester.run('disallowedWords option', rule, { { code: 'describe("the correct way to do things", function () {})', options: [{ disallowedWords: ['correct'] }], - errors: [{ messageId: 'disallowedWord', column: 10, line: 1 }], + errors: [ + { + messageId: 'disallowedWord', + data: { word: 'correct' }, + column: 10, + line: 1, + }, + ], }, { code: 'it("has ALL the things", () => {})', - errors: [{ messageId: 'disallowedWord', column: 4, line: 1 }], options: [{ disallowedWords: ['all'] }], + errors: [ + { + messageId: 'disallowedWord', + data: { word: 'ALL' }, + column: 4, + line: 1, + }, + ], }, { code: 'xdescribe("every single one of them", function () {})', options: [{ disallowedWords: ['every'] }], - errors: [{ messageId: 'disallowedWord', column: 11, line: 1 }], + errors: [ + { + messageId: 'disallowedWord', + data: { word: 'every' }, + column: 11, + line: 1, + }, + ], }, { code: "describe('Very Descriptive Title Goes Here', function () {})", options: [{ disallowedWords: ['descriptive'] }], - errors: [{ messageId: 'disallowedWord', column: 10, line: 1 }], + errors: [ + { + messageId: 'disallowedWord', + data: { word: 'Descriptive' }, + column: 10, + line: 1, + }, + ], }, { code: 'test(`that the value is set properly`, function () {})', options: [{ disallowedWords: ['properly'] }], - errors: [{ messageId: 'disallowedWord', column: 6, line: 1 }], + errors: [ + { + messageId: 'disallowedWord', + data: { word: 'properly' }, + column: 6, + line: 1, + }, + ], }, ], }); From 705c27aee231c843bcc204bd71a53006c337311e Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 20 Jan 2020 17:35:58 +1300 Subject: [PATCH 3/6] test(valid-title): add multi-disallowedWords test --- src/rules/__tests__/valid-title.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/rules/__tests__/valid-title.test.ts b/src/rules/__tests__/valid-title.test.ts index d3baf7ee5..3584b4909 100644 --- a/src/rules/__tests__/valid-title.test.ts +++ b/src/rules/__tests__/valid-title.test.ts @@ -21,6 +21,18 @@ ruleTester.run('disallowedWords option', rule, { }, ], invalid: [ + { + code: 'test("the correct way to properly handle all things", () => {});', + options: [{ disallowedWords: ['correct', 'properly', 'all'] }], + errors: [ + { + messageId: 'disallowedWord', + data: { word: 'correct' }, + column: 6, + line: 1, + }, + ], + }, { code: 'describe("the correct way to do things", function () {})', options: [{ disallowedWords: ['correct'] }], From 5377b96b5677aea1bed321ad00e6b6b04b252703 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Tue, 21 Jan 2020 08:29:42 +1300 Subject: [PATCH 4/6] docs(valid-title): use code quotes & arrow functions --- docs/rules/valid-title.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/rules/valid-title.md b/docs/rules/valid-title.md index 5bac979b6..38fef1bb7 100644 --- a/docs/rules/valid-title.md +++ b/docs/rules/valid-title.md @@ -45,7 +45,7 @@ xtest('foo', () => {}); Titles for test blocks should always be a string literal or expression. -This is also applied to describe blocks by default, but can be turned off via +This is also applied to `describe` blocks by default, but can be turned off via the `ignoreTypeOfDescribeName` option: Examples of **incorrect** code for this rule: @@ -87,7 +87,7 @@ describe(6, function() {}); **duplicatePrefix** -A describe / test block should not start with duplicatePrefix +A `describe` / `test` block should not start with `duplicatePrefix` Examples of **incorrect** code for this rule @@ -117,7 +117,7 @@ describe('foo', () => { **accidentalSpace** -A describe / test block should not contain accidentalSpace +A `describe` / `test` block should not contain accidentalSpace Examples of **incorrect** code for this rule @@ -176,10 +176,10 @@ Examples of **incorrect** code using `disallowedWords`: ```js // with disallowedWords: ['correct', 'all', 'every', 'properly'] -describe('the correct way to do things', function() {}); +describe('the correct way to do things', () => {}); it('has ALL the things', () => {}); -xdescribe('every single one of them', function() {}); -test(`that the value is set properly`, function() {}); +xdescribe('every single one of them', () => {}); +test(`that the value is set properly`, () => {}); ``` Examples of **correct** code when using `disallowedWords`: From 5da90fe1bc31c81020b4230ee7a8e9984ca233a3 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Tue, 21 Jan 2020 08:30:01 +1300 Subject: [PATCH 5/6] chore(valid-title): use "greater than 0" in check --- src/rules/valid-title.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/valid-title.ts b/src/rules/valid-title.ts index a14827ab0..49e609d3a 100644 --- a/src/rules/valid-title.ts +++ b/src/rules/valid-title.ts @@ -124,7 +124,7 @@ export default createRule({ return; } - if (disallowedWords.length) { + if (disallowedWords.length > 0) { const disallowedMatch = disallowedWordsRegexp.exec(title); if (disallowedMatch) { From 0b5671c4aff421a7b927b9fec8f31509c809eaac Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Sat, 8 Feb 2020 09:22:14 +1300 Subject: [PATCH 6/6] chore(valid-title): reword `disallowedWord` message --- src/rules/valid-title.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/valid-title.ts b/src/rules/valid-title.ts index 49e609d3a..43e7ffd21 100644 --- a/src/rules/valid-title.ts +++ b/src/rules/valid-title.ts @@ -50,7 +50,7 @@ export default createRule({ emptyTitle: '{{ jestFunctionName }} should not have an empty title', duplicatePrefix: 'should not have duplicate prefix', accidentalSpace: 'should not have leading or trailing spaces', - disallowedWord: '"{{ word }}" is not allowed.', + disallowedWord: '"{{ word }}" is not allowed in test titles.', }, type: 'suggestion', schema: [