-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new rule
no-long-arrays-in-test-each
(#3)
* Update wording * Add new rule `no-long-arrays-in-test-each` * Add docs
- Loading branch information
1 parent
a864739
commit 128d66d
Showing
8 changed files
with
321 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
# Disallow mixing expectations for different variables between each other (`proper-tests/no-long-arrays-in-test-each`) | ||
|
||
💼 This rule is enabled in the ✅ `recommended` config. | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
## Rule details | ||
|
||
This rule disallows mixing expectations for different variables between each other. | ||
|
||
The following code is considered errors: | ||
|
||
```ts | ||
test.each([ | ||
{ | ||
description: 'test case name #1', | ||
inputValue: 'a', | ||
expectedOutput: 'aa', | ||
}, | ||
{ | ||
description: 'test case name #2', | ||
inputValue: 'b', | ||
expectedOutput: 'bb', | ||
}, | ||
{ | ||
description: 'test case name #3', | ||
inputValue: 'c', | ||
expectedOutput: 'cc', | ||
}, | ||
{ | ||
description: 'test case name #4', | ||
inputValue: 'd', | ||
expectedOutput: 'dd', | ||
}, | ||
{ | ||
description: 'test case name #5', | ||
inputValue: 'e', | ||
expectedOutput: 'ee', | ||
}, | ||
{ | ||
description: 'test case name #6', | ||
inputValue: 'f', | ||
expectedOutput: 'ff', | ||
}, | ||
])('$description', ({ clientCountry, expectedPaymentMethod, processorName }) => { | ||
// ... | ||
}); | ||
``` | ||
|
||
Consider extracting such long arrays to a separate files with for example `.data.ts` postfix. | ||
|
||
The following code is considered correct: | ||
|
||
```ts | ||
// some-service.data.ts | ||
export type TestCase = Readonly<{ | ||
description: string; | ||
inputValue: string; | ||
expectedOutput: string; | ||
}>; | ||
|
||
export const testCases: TestCase[] = [ | ||
{ | ||
description: 'test case name #1', | ||
inputValue: 'a', | ||
expectedOutput: 'aa', | ||
}, | ||
{ | ||
description: 'test case name #2', | ||
inputValue: 'b', | ||
expectedOutput: 'bb', | ||
}, | ||
{ | ||
description: 'test case name #3', | ||
inputValue: 'c', | ||
expectedOutput: 'cc', | ||
}, | ||
{ | ||
description: 'test case name #4', | ||
inputValue: 'd', | ||
expectedOutput: 'dd', | ||
}, | ||
{ | ||
description: 'test case name #5', | ||
inputValue: 'e', | ||
expectedOutput: 'ee', | ||
}, | ||
{ | ||
description: 'test case name #6', | ||
inputValue: 'f', | ||
expectedOutput: 'ff', | ||
}, | ||
]; | ||
``` | ||
|
||
and now test is more readable: | ||
|
||
```ts | ||
test.each(testCases)('$description', ({ inputValue, expectedOutput }) => { | ||
// ... | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import { RuleTester } from '@typescript-eslint/rule-tester'; | ||
|
||
import { noLongArraysInTestEach } from './no-long-arrays-in-test-each'; | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: '@typescript-eslint/parser', | ||
}); | ||
|
||
ruleTester.run('no-long-arrays-in-test-each', noLongArraysInTestEach, { | ||
valid: [ | ||
{ | ||
name: 'less than default limit using "test"', | ||
filename: 'app.e2e-spec.ts', | ||
code: `test.each([{}])`, | ||
}, | ||
{ | ||
name: 'less than default limit using "it"', | ||
filename: 'app.e2e-spec.ts', | ||
code: `it.each([{}])`, | ||
}, | ||
{ | ||
name: 'with integers using "test"', | ||
filename: 'app.e2e-spec.ts', | ||
code: 'test.each([1, 2, 3, 4, 5, 6]);', | ||
}, | ||
{ | ||
name: 'with integers using "it"', | ||
filename: 'app.e2e-spec.ts', | ||
code: 'it.each([1, 2, 3, 4, 5, 6]);', | ||
}, | ||
{ | ||
name: 'with mixed integers and objects using "it"', | ||
filename: 'app.e2e-spec.ts', | ||
code: 'it.each([{}, {}, {}, {}, {}, 1, 2, 3, 4, 5]);', | ||
}, | ||
{ | ||
name: 'with 6 objects when option is overridden using "it"', | ||
filename: 'app.e2e-spec.ts', | ||
options: [{ limit: 7 }], | ||
code: 'it.each([{}, {}, {}, {}, {}, {}]);', | ||
}, | ||
{ | ||
name: 'with 2 objects when option is overridden using "it"', | ||
filename: 'app.e2e-spec.ts', | ||
options: [{ limit: 4 }], | ||
code: 'it.each([{}, {}]);', | ||
}, | ||
{ | ||
name: 'string contains it.each', | ||
filename: 'app.e2e-spec.ts', | ||
code: '"it.each([{}, {}])";', | ||
}, | ||
{ | ||
name: 'simple function call is executed', | ||
filename: 'app.e2e-spec.ts', | ||
code: 'each([{}, {}]);', | ||
}, | ||
{ | ||
name: 'neither test nor it object is used', | ||
filename: 'app.e2e-spec.ts', | ||
code: 'array.each([{}, {}]);', | ||
}, | ||
{ | ||
name: 'called function is not "each"', | ||
filename: 'app.e2e-spec.ts', | ||
code: 'test.every([{}, {}]);', | ||
}, | ||
{ | ||
name: 'function argument is not an array but string', | ||
filename: 'app.e2e-spec.ts', | ||
code: 'test.each("string");', | ||
}, | ||
{ | ||
name: 'not in e2e test file', | ||
filename: 'non-e2e-test.ts', | ||
code: 'it.each([{}, {}, {}, {}, {}]);', | ||
}, | ||
], | ||
invalid: [ | ||
{ | ||
name: 'when 6 objects are passed while 5 are allowed by default using "test.each"', | ||
filename: 'app.e2e-spec.ts', | ||
code: 'test.each([{}, {}, {}, {}, {}, {}])', | ||
output: null, | ||
errors: [ | ||
{ | ||
messageId: 'noLongArrays', | ||
data: { | ||
testFunctionName: 'test', | ||
actualLength: 6, | ||
limit: 5, | ||
}, | ||
}, | ||
], | ||
}, | ||
{ | ||
name: 'when 6 objects are passed while 5 are allowed by default using "it.each"', | ||
filename: 'app.e2e-spec.ts', | ||
code: 'it.each([{}, {}, {}, {}, {}, {}])', | ||
output: null, | ||
errors: [ | ||
{ | ||
messageId: 'noLongArrays', | ||
data: { | ||
testFunctionName: 'it', | ||
actualLength: 6, | ||
limit: 5, | ||
}, | ||
}, | ||
], | ||
}, | ||
{ | ||
name: 'when 2 objects are passed while 1 is allowed by passed option using "it.each"', | ||
filename: 'app.e2e-spec.ts', | ||
options: [{ limit: 1 }], | ||
code: 'it.each([{}, {}])', | ||
output: null, | ||
errors: [ | ||
{ | ||
messageId: 'noLongArrays', | ||
data: { | ||
testFunctionName: 'it', | ||
actualLength: 2, | ||
limit: 1, | ||
}, | ||
}, | ||
], | ||
}, | ||
], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'; | ||
|
||
type MessageIds = 'noLongArrays'; | ||
type Options = [{ limit?: number }]; | ||
|
||
const DEFAULT_LIMIT = 5; | ||
|
||
export const noLongArraysInTestEach = ESLintUtils.RuleCreator.withoutDocs<Options, MessageIds>({ | ||
create(context, options) { | ||
return { | ||
CallExpression(node) { | ||
if (node.callee.type !== AST_NODE_TYPES.MemberExpression) { | ||
return; | ||
} | ||
|
||
if ( | ||
node.callee.object.type !== AST_NODE_TYPES.Identifier || | ||
!['test', 'it'].includes(node.callee.object.name) | ||
) { | ||
return; | ||
} | ||
|
||
if (node.callee.property.type !== AST_NODE_TYPES.Identifier || node.callee.property.name !== 'each') { | ||
return; | ||
} | ||
|
||
const firstArgument = node.arguments[0]; | ||
|
||
if (firstArgument.type !== AST_NODE_TYPES.ArrayExpression) { | ||
return; | ||
} | ||
|
||
const limit = options[0].limit || DEFAULT_LIMIT; | ||
const elements = firstArgument.elements; | ||
|
||
if (elements.length <= limit) { | ||
return; | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/typedef | ||
const allElementsAreObjects = elements.every(element => element?.type === AST_NODE_TYPES.ObjectExpression); | ||
|
||
if (!allElementsAreObjects) { | ||
return; | ||
} | ||
|
||
context.report({ | ||
node, | ||
messageId: 'noLongArrays', | ||
data: { | ||
testFunctionName: node.callee.object.name, | ||
actualLength: elements.length, | ||
limit: limit, | ||
}, | ||
}); | ||
}, | ||
}; | ||
}, | ||
meta: { | ||
docs: { | ||
description: | ||
'Disallow using long arrays with objects inside `test.each()` or `it.each()`. Force moving them out of the file.', | ||
}, | ||
messages: { | ||
// eslint-disable-next-line max-len | ||
noLongArrays: | ||
'Move the array with objects out of the test file in `{{ testFunctionName }}.each()`. Array length is {{ actualLength }}, but the limit is {{ limit }} items.', | ||
}, | ||
type: 'suggestion', | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
limit: { | ||
type: 'integer', | ||
minimum: 1, | ||
}, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
}, | ||
defaultOptions: [{ limit: DEFAULT_LIMIT }], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters