Skip to content

Commit

Permalink
feat(rules): prefer-hooks-on-top (jest-community#273)
Browse files Browse the repository at this point in the history
  • Loading branch information
thatsitu committed Oct 17, 2019
1 parent 5bd8f61 commit be1ba8b
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 0 deletions.
65 changes: 65 additions & 0 deletions docs/rules/valid-title.md
@@ -0,0 +1,65 @@
# Disallow duplicate setup and teardown hooks (no-duplicate-hooks)

A describe/ test block should not contain accidentalSpace or duplicatePrefix.

## Rule Details

**duplicatePrefix**

A describe/ test block should not start with duplicatePrefix

Examples of **incorrect** code for this rule

```js
test('test foo', () => {});
it('it foo', () => {});

describe('foo', () => {
test('test bar', () => {});
});

describe('describe foo', () => {
test('bar', () => {});
});
```

Examples of **correct** code for this rule

```js
test('foo', () => {});
it('foo', () => {});

describe('foo', () => {
test('bar', () => {});
});
```

**accidentalSpace**

A describe/ test block should not contain accidentalSpace

Examples of **incorrect** code for this rule

```js
test(' foo', () => {});
it(' foo', () => {});

describe('foo', () => {
test(' bar', () => {});
});

describe(' foo', () => {
test('bar', () => {});
});
```

Examples of **correct** code for this rule

```js
test('foo', () => {});
it('foo', () => {});

describe('foo', () => {
test('bar', () => {});
});
```
160 changes: 160 additions & 0 deletions src/rules/__tests__/valid-title.test.ts
@@ -0,0 +1,160 @@
import { TSESLint } from '@typescript-eslint/experimental-utils';
import rule from '../valid-title';

const ruleTester = new TSESLint.RuleTester({
parserOptions: {
ecmaVersion: 8,
},
});

ruleTester.run('no-accidental-space', rule, {
valid: [
'describe("foo", function () {})',
'fdescribe("foo", function () {})',
'xdescribe("foo", function () {})',
'it("foo", function () {})',
'fit("foo", function () {})',
'xit("foo", function () {})',
'test("foo", function () {})',
'xtest("foo", function () {})',
'someFn("foo", function () {})',
`
describe('foo', () => {
it('bar', () => {})
})
`,
],
invalid: [
{
code: 'describe(" foo", function () {})',
errors: [{ messageId: 'accidentalSpace', column: 1, line: 1 }],
},
{
code: 'fdescribe(" foo", function () {})',
errors: [{ messageId: 'accidentalSpace', column: 1, line: 1 }],
},
{
code: 'xdescribe(" foo", function () {})',
errors: [{ messageId: 'accidentalSpace', column: 1, line: 1 }],
},
{
code: 'it(" foo", function () {})',
errors: [{ messageId: 'accidentalSpace', column: 1, line: 1 }],
},
{
code: 'fit(" foo", function () {})',
errors: [{ messageId: 'accidentalSpace', column: 1, line: 1 }],
},
{
code: 'xit(" foo", function () {})',
errors: [{ messageId: 'accidentalSpace', column: 1, line: 1 }],
},
{
code: 'test(" foo", function () {})',
errors: [{ messageId: 'accidentalSpace', column: 1, line: 1 }],
},
{
code: 'xtest(" foo", function () {})',
errors: [{ messageId: 'accidentalSpace', column: 1, line: 1 }],
},
{
code: `
describe(' foo', () => {
it('bar', () => {})
})
`,
errors: [{ messageId: 'accidentalSpace', column: 7, line: 2 }],
},
{
code: `
describe('foo', () => {
it(' bar', () => {})
})
`,
errors: [{ messageId: 'accidentalSpace', column: 9, line: 3 }],
},
],
});

ruleTester.run('no-duplicate-prefix ft describe', rule, {
valid: [
'describe("foo", function () {})',
'fdescribe("foo", function () {})',
'xdescribe("foo", function () {})',
],
invalid: [
{
code: 'describe("describe foo", function () {})',
errors: [{ messageId: 'duplicatePrefix', column: 1, line: 1 }],
},
{
code: 'fdescribe("describe foo", function () {})',
errors: [{ messageId: 'duplicatePrefix', column: 1, line: 1 }],
},
{
code: 'xdescribe("describe foo", function () {})',
errors: [{ messageId: 'duplicatePrefix', column: 1, line: 1 }],
},
],
});

ruleTester.run('no-duplicate-prefix ft test', rule, {
valid: ['test("foo", function () {})', 'xtest("foo", function () {})'],
invalid: [
{
code: 'test("test foo", function () {})',
errors: [{ messageId: 'duplicatePrefix', column: 1, line: 1 }],
},
{
code: 'xtest("test foo", function () {})',
errors: [{ messageId: 'duplicatePrefix', column: 1, line: 1 }],
},
],
});

ruleTester.run('no-duplicate-prefix ft it', rule, {
valid: [
'it("foo", function () {})',
'fit("foo", function () {})',
'xit("foo", function () {})',
],
invalid: [
{
code: 'it("it foo", function () {})',
errors: [{ messageId: 'duplicatePrefix', column: 1, line: 1 }],
},
{
code: 'fit("it foo", function () {})',
errors: [{ messageId: 'duplicatePrefix', column: 1, line: 1 }],
},
{
code: 'xit("it foo", function () {})',
errors: [{ messageId: 'duplicatePrefix', column: 1, line: 1 }],
},
],
});

ruleTester.run('no-duplicate-prefix ft nested', rule, {
valid: [
`
describe('foo', () => {
it('bar', () => {})
})`,
],
invalid: [
{
code: `
describe('describe foo', () => {
it('bar', () => {})
})`,
errors: [{ messageId: 'duplicatePrefix', column: 7, line: 2 }],
},
{
code: `
describe('foo', () => {
it('it bar', () => {})
})`,
errors: [{ messageId: 'duplicatePrefix', column: 9, line: 3 }],
},
],
});
10 changes: 10 additions & 0 deletions src/rules/utils.ts
Expand Up @@ -652,6 +652,16 @@ export const isDescribe = (
);
};

export const getNodeTitle = (node: TSESTree.CallExpression): string | null => {
const [argument] = node.arguments;

if (!isStringNode(argument) || AST_NODE_TYPES.Literal !== argument.type) {
return null;
}

return getAccessorValue(argument);
};

const collectReferences = (scope: TSESLint.Scope.Scope) => {
const locals = new Set();
const unresolved = new Set();
Expand Down
56 changes: 56 additions & 0 deletions src/rules/valid-title.ts
@@ -0,0 +1,56 @@
import {
createRule,
getNodeName,
getNodeTitle,
isDescribe,
isTestCase,
} from './utils';

function trimFXprefix(word: string) {
return ['f', 'x'].includes(word.charAt(0)) ? word.substr(1) : word;
}

export default createRule({
name: __filename,
meta: {
docs: {
category: 'Best Practices',
description: 'Enforce valid titles',
recommended: false,
},
messages: {
duplicatePrefix: 'should not have duplicate prefix',
accidentalSpace: 'should not have space in the beginning',
},
type: 'suggestion',
schema: [],
},
defaultOptions: [],
create(context) {
return {
CallExpression(node) {
if (!isDescribe(node) && !isTestCase(node)) return;

const title = getNodeTitle(node);
if (!title) return;

if (title.charAt(0) === ' ') {
context.report({
messageId: 'accidentalSpace',
node,
});
}

const nodeName = trimFXprefix(getNodeName(node.callee));
const [firstWord] = title.split(' ');

if (firstWord.toLowerCase() === nodeName) {
context.report({
messageId: 'duplicatePrefix',
node,
});
}
},
};
},
});

0 comments on commit be1ba8b

Please sign in to comment.