Skip to content

Commit

Permalink
feat(no-standalone-expect): support additionalTestBlockFunctions
Browse files Browse the repository at this point in the history
  • Loading branch information
G-Rath committed May 17, 2020
1 parent 325ec77 commit c4f907b
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 6 deletions.
30 changes: 30 additions & 0 deletions docs/rules/no-standalone-expect.md
Expand Up @@ -64,6 +64,36 @@ describe('a test', () => {
thought the `expect` will not execute. Rely on a rule like no-unused-vars for
this case.

### Options

#### `additionalTestBlockFunctions`

This array can be used to specify the names of functions that should also be
treated as test blocks:

```json
{
"rules": {
"jest/no-standalone-expect": [
"error",
{ "additionalTestBlockFunctions": ["each.test"] }
]
}
}
```

The following is _correct_ when using the above configuration:

```js
each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
]).test('returns the result of adding %d to %d', (a, b, expected) => {
expect(a + b).toBe(expected);
});
```

## When Not To Use It

Don't use this rule on non-jest test files.
78 changes: 78 additions & 0 deletions src/rules/__tests__/no-standalone-expect.test.ts
Expand Up @@ -34,8 +34,86 @@ ruleTester.run('no-standalone-expect', rule, {
'it.only("an only", value => { expect(value).toBe(true); });',
'it.concurrent("an concurrent", value => { expect(value).toBe(true); });',
'describe.each([1, true])("trues", value => { it("an it", () => expect(value).toBe(true) ); });',
{
code: `
describe('scenario', () => {
const t = Math.random() ? it.only : it;
t('testing', () => expect(true));
});
`,
options: [{ additionalTestBlockFunctions: ['t'] }],
},
{
code: `
each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
]).test('returns the result of adding %d to %d', (a, b, expected) => {
expect(a + b).toBe(expected);
});
`,
options: [{ additionalTestBlockFunctions: ['each.test'] }],
},
],
invalid: [
{
code: `
describe('scenario', () => {
const t = Math.random() ? it.only : it;
t('testing', () => expect(true));
});
`,
errors: [{ endColumn: 42, column: 30, messageId: 'unexpectedExpect' }],
},
{
code: `
describe('scenario', () => {
const t = Math.random() ? it.only : it;
t('testing', () => expect(true));
});
`,
options: [{ additionalTestBlockFunctions: undefined }],
errors: [{ endColumn: 42, column: 30, messageId: 'unexpectedExpect' }],
},
{
code: `
each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
]).test('returns the result of adding %d to %d', (a, b, expected) => {
expect(a + b).toBe(expected);
});
`,
errors: [{ endColumn: 24, column: 11, messageId: 'unexpectedExpect' }],
},
{
code: `
each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
]).test('returns the result of adding %d to %d', (a, b, expected) => {
expect(a + b).toBe(expected);
});
`,
options: [{ additionalTestBlockFunctions: ['each'] }],
errors: [{ endColumn: 24, column: 11, messageId: 'unexpectedExpect' }],
},
{
code: `
each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
]).test('returns the result of adding %d to %d', (a, b, expected) => {
expect(a + b).toBe(expected);
});
`,
options: [{ additionalTestBlockFunctions: ['test'] }],
errors: [{ endColumn: 24, column: 11, messageId: 'unexpectedExpect' }],
},
{
code: 'describe("a test", () => { expect(1).toBe(1); });',
errors: [{ endColumn: 37, column: 28, messageId: 'unexpectedExpect' }],
Expand Down
32 changes: 26 additions & 6 deletions src/rules/no-standalone-expect.ts
Expand Up @@ -6,6 +6,7 @@ import {
DescribeAlias,
TestCaseName,
createRule,
getNodeName,
isDescribe,
isExpectCall,
isFunction,
Expand Down Expand Up @@ -56,7 +57,10 @@ type callStackEntry =
| 'arrowFunc'
| 'template';

export default createRule({
export default createRule<
[{ additionalTestBlockFunctions: string[] }],
'unexpectedExpect'
>({
name: __filename,
meta: {
docs: {
Expand All @@ -68,12 +72,27 @@ export default createRule({
unexpectedExpect: 'Expect must be inside of a test block.',
},
type: 'suggestion',
schema: [],
schema: [
{
properties: {
additionalTestBlockFunctions: {
type: 'array',
items: { type: 'string' },
},
},
additionalProperties: false,
},
],
},
defaultOptions: [],
create(context) {
defaultOptions: [{ additionalTestBlockFunctions: [] }],
create(context, [{ additionalTestBlockFunctions = [] }]) {
const callStack: callStackEntry[] = [];

const isCustomTestBlockFunction = (
node: TSESTree.CallExpression,
): boolean =>
additionalTestBlockFunctions.includes(getNodeName(node) || '');

return {
CallExpression(node) {
if (isExpectCall(node)) {
Expand All @@ -83,7 +102,7 @@ export default createRule({
}
return;
}
if (isTestCase(node)) {
if (isTestCase(node) || isCustomTestBlockFunction(node)) {
callStack.push(TestCaseName.test);
}
if (node.callee.type === AST_NODE_TYPES.TaggedTemplateExpression) {
Expand All @@ -92,8 +111,9 @@ export default createRule({
},
'CallExpression:exit'(node: TSESTree.CallExpression) {
const top = callStack[callStack.length - 1];

if (
(((isTestCase(node) &&
((((isTestCase(node) || isCustomTestBlockFunction(node)) &&
node.callee.type !== AST_NODE_TYPES.MemberExpression) ||
isEach(node)) &&
top === TestCaseName.test) ||
Expand Down

0 comments on commit c4f907b

Please sign in to comment.