-
Notifications
You must be signed in to change notification settings - Fork 237
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Resolves #72
- Loading branch information
Showing
5 changed files
with
274 additions
and
0 deletions.
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,175 @@ | ||
# Disallow setup and teardown hooks (no-hooks) | ||
|
||
Jest provides global functions for setup and teardown tasks, which are called | ||
before/after each test case and each test suite. The use of these hooks promotes | ||
shared state between tests. | ||
|
||
## Rule Details | ||
|
||
This rule reports for the following function calls: | ||
|
||
* `beforeAll` | ||
* `beforeEach` | ||
* `afterAll` | ||
* `afterEach` | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```js | ||
/* eslint jest/no-hooks: "error" */ | ||
|
||
function setupFoo(options) { | ||
/* ... */ | ||
} | ||
|
||
function setupBar(options) { | ||
/* ... */ | ||
} | ||
|
||
describe('foo', () => { | ||
let foo; | ||
|
||
beforeEach(() => { | ||
foo = setupFoo(); | ||
}); | ||
|
||
afterEach(() => { | ||
foo = null; | ||
}); | ||
|
||
it('does something', () => { | ||
expect(foo.doesSomething()).toBe(true); | ||
}); | ||
|
||
describe('with bar', () => { | ||
let bar; | ||
|
||
beforeEach(() => { | ||
bar = setupBar(); | ||
}); | ||
|
||
afterEach(() => { | ||
bar = null; | ||
}); | ||
|
||
it('does something with bar', () => { | ||
expect(foo.doesSomething(bar)).toBe(true); | ||
}); | ||
}); | ||
}); | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```js | ||
/* eslint jest/no-hooks: "error" */ | ||
|
||
function setupFoo(options) { | ||
/* ... */ | ||
} | ||
|
||
function setupBar(options) { | ||
/* ... */ | ||
} | ||
|
||
describe('foo', () => { | ||
it('does something', () => { | ||
const foo = setupFoo(); | ||
expect(foo.doesSomething()).toBe(true); | ||
}); | ||
|
||
it('does something with bar', () => { | ||
const foo = setupFoo(); | ||
const bar = setupBar(); | ||
expect(foo.doesSomething(bar)).toBe(true); | ||
}); | ||
}); | ||
``` | ||
|
||
## Options | ||
|
||
```json | ||
{ | ||
"jest/no-hooks": [ | ||
"error", | ||
{ | ||
"allow": ["afterEach", "afterAll"] | ||
} | ||
] | ||
} | ||
``` | ||
|
||
### `allow` | ||
|
||
This array option whitelists setup and teardown hooks so that this rule does not | ||
report their usage as being incorrect. There are four possible values: | ||
|
||
* `"beforeAll"` | ||
* `"beforeEach"` | ||
* `"afterAll"` | ||
* `"afterEach"` | ||
|
||
By default, none of these options are enabled (the equivalent of | ||
`{ "allow": [] }`). | ||
|
||
Examples of **incorrect** code for the `{ "allow": ["afterEach"] }` option: | ||
|
||
```js | ||
/* eslint jest/no-hooks: ["error", { "allow": ["afterEach"] }] */ | ||
|
||
function setupFoo(options) { | ||
/* ... */ | ||
} | ||
|
||
let foo; | ||
|
||
beforeEach(() => { | ||
foo = setupFoo(); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.resetModules(); | ||
}); | ||
|
||
test('foo does this', () => { | ||
// ... | ||
}); | ||
|
||
test('foo does that', () => { | ||
// ... | ||
}); | ||
``` | ||
|
||
Examples of **correct** code for the `{ "allow": ["afterEach"] }` option: | ||
|
||
```js | ||
/* eslint jest/no-hooks: ["error", { "allow": ["afterEach"] }] */ | ||
|
||
function setupFoo(options) { | ||
/* ... */ | ||
} | ||
|
||
afterEach(() => { | ||
jest.resetModules(); | ||
}); | ||
|
||
test('foo does this', () => { | ||
const foo = setupFoo(); | ||
// ... | ||
}); | ||
|
||
test('foo does that', () => { | ||
const foo = setupFoo(); | ||
// ... | ||
}); | ||
``` | ||
|
||
## When Not To Use It | ||
|
||
If you prefer using the setup and teardown hooks provided by Jest, you can | ||
safely disable this rule. | ||
|
||
## Further Reading | ||
|
||
* [Jest docs - Setup and Teardown](https://facebook.github.io/jest/docs/en/setup-teardown.html) | ||
* [@jamiebuilds Twitter thread](https://twitter.com/jamiebuilds/status/954906997169664000) |
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,44 @@ | ||
'use strict'; | ||
|
||
const RuleTester = require('eslint').RuleTester; | ||
const rules = require('../..').rules; | ||
const ruleTester = new RuleTester({ | ||
parserOptions: { | ||
ecmaVersion: 6, | ||
}, | ||
}); | ||
|
||
ruleTester.run('no-hooks', rules['no-hooks'], { | ||
valid: [ | ||
'test("foo")', | ||
'describe("foo", () => { it("bar") })', | ||
'test("foo", () => { expect(subject.beforeEach()).toBe(true) })', | ||
{ | ||
code: 'afterEach(() => {}); afterAll(() => {});', | ||
options: [{ allow: ['afterEach', 'afterAll'] }], | ||
}, | ||
], | ||
invalid: [ | ||
{ | ||
code: 'beforeAll(() => {})', | ||
errors: [{ message: "Unexpected 'beforeAll' hook" }], | ||
}, | ||
{ | ||
code: 'beforeEach(() => {})', | ||
errors: [{ message: "Unexpected 'beforeEach' hook" }], | ||
}, | ||
{ | ||
code: 'afterAll(() => {})', | ||
errors: [{ message: "Unexpected 'afterAll' hook" }], | ||
}, | ||
{ | ||
code: 'afterEach(() => {})', | ||
errors: [{ message: "Unexpected 'afterEach' hook" }], | ||
}, | ||
{ | ||
code: 'beforeEach(() => {}); afterEach(() => { jest.resetModules() });', | ||
options: [{ allow: ['afterEach'] }], | ||
errors: [{ message: "Unexpected 'beforeEach' hook" }], | ||
}, | ||
], | ||
}); |
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,52 @@ | ||
'use strict'; | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
url: | ||
'https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/no-hooks.md', | ||
}, | ||
}, | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
allow: { | ||
type: 'array', | ||
contains: ['beforeAll', 'beforeEach', 'afterAll', 'afterEach'], | ||
}, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
create(context) { | ||
const testHookNames = Object.assign(Object.create(null), { | ||
beforeAll: true, | ||
beforeEach: true, | ||
afterAll: true, | ||
afterEach: true, | ||
}); | ||
|
||
const whitelistedHookNames = ( | ||
context.options[0] || { allow: [] } | ||
).allow.reduce((hashMap, value) => { | ||
hashMap[value] = true; | ||
return hashMap; | ||
}, Object.create(null)); | ||
|
||
const isHook = node => testHookNames[node.callee.name]; | ||
const isWhitelisted = node => whitelistedHookNames[node.callee.name]; | ||
|
||
return { | ||
CallExpression(node) { | ||
if (isHook(node) && !isWhitelisted(node)) { | ||
context.report({ | ||
node, | ||
message: "Unexpected '{{ hookName }}' hook", | ||
data: { hookName: node.callee.name }, | ||
}); | ||
} | ||
}, | ||
}; | ||
}, | ||
}; |