-
Notifications
You must be signed in to change notification settings - Fork 242
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
feat: import formatting rules from
eslint-plugin-jest-formatting
(#…
…1563) * feat: naively copy and paste `eslint-plugin-jest-formatting` rules * refactor: remove unneeded exports * refactor: give each rule a dedicated file * refactor: rename and convert test files to typescript * refactor: use `import` instead of `require` and clean up other imports * refactor: use `createRule` * fix: pass in the correct `name` for each padding rule * refactor: use `@typescript-eslint` types and constants * fix: address some initial typescript errors * refactor: use `messageId` instead of `message` * test: use flat compat `RuleTester` * test: specify full errors instead of counts * test: add basic file for `padding-around-all` * docs: add entries for new padding rules * refactor: address remaining TypeScript issues * refactor: address deprecated `context` method usage * test: remove comments * test: cleanup cases * test: add more cases for coverage * refactor: remove unneeded checks
Showing
30 changed files
with
2,204 additions
and
56 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Enforce padding around `afterAll` blocks (`padding-around-after-all-blocks`) | ||
|
||
🔧 This rule is automatically fixable by the | ||
[`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
## Rule Details | ||
|
||
This rule enforces a line of padding before _and_ after 1 or more `afterAll` | ||
statements. | ||
|
||
Note that it doesn't add/enforce a padding line if it's the last statement in | ||
its scope. | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```js | ||
const someText = 'abc'; | ||
afterAll(() => {}); | ||
describe('someText', () => {}); | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```js | ||
const someText = 'abc'; | ||
|
||
afterAll(() => {}); | ||
|
||
describe('someText', () => {}); | ||
``` |
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,36 @@ | ||
# Enforce padding around `afterEach` blocks (`padding-around-after-each-blocks`) | ||
|
||
🔧 This rule is automatically fixable by the | ||
[`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
## Rule Details | ||
|
||
This rule enforces a line of padding before _and_ after 1 or more `afterEach` | ||
statements. | ||
|
||
Note that it doesn't add/enforce a padding line if it's the last statement in | ||
its scope. | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```js | ||
const something = 123; | ||
afterEach(() => { | ||
// more stuff | ||
}); | ||
describe('foo', () => {}); | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```js | ||
const something = 123; | ||
|
||
afterEach(() => { | ||
// more stuff | ||
}); | ||
|
||
describe('foo', () => {}); | ||
``` |
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,18 @@ | ||
# Enforce padding around Jest functions (`padding-around-all`) | ||
|
||
🔧 This rule is automatically fixable by the | ||
[`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
## Rule Details | ||
|
||
This is a meta rule that simply enables all of the following rules: | ||
|
||
- [padding-around-after-all-blocks](padding-around-after-all-blocks.md) | ||
- [padding-around-after-each-blocks](padding-around-after-each-blocks.md) | ||
- [padding-around-before-all-blocks](padding-around-before-all-blocks.md) | ||
- [padding-around-before-each-blocks](padding-around-before-each-blocks.md) | ||
- [padding-around-expect-groups](padding-around-expect-groups.md) | ||
- [padding-around-describe-blocks](padding-around-describe-blocks.md) | ||
- [padding-around-test-blocks](padding-around-test-blocks.md) |
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,35 @@ | ||
# Enforce padding around `beforeAll` blocks (`padding-around-before-all-blocks`) | ||
|
||
🔧 This rule is automatically fixable by the | ||
[`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
## Rule Details | ||
|
||
This rule enforces a line of padding before _and_ after `beforeAll` statements. | ||
|
||
Note that it doesn't add/enforce a padding line if it's the last statement in | ||
its scope. | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```js | ||
const something = 123; | ||
beforeAll(() => { | ||
// more stuff | ||
}); | ||
describe('foo', () => {}); | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```js | ||
const something = 123; | ||
|
||
beforeAll(() => { | ||
// more stuff | ||
}); | ||
|
||
describe('foo', () => {}); | ||
``` |
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,36 @@ | ||
# Enforce padding around `beforeEach` blocks (`padding-around-before-each-blocks`) | ||
|
||
🔧 This rule is automatically fixable by the | ||
[`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
## Rule Details | ||
|
||
This rule enforces a line of padding before _and_ after 1 or more `beforeEach` | ||
statements | ||
|
||
Note that it doesn't add/enforce a padding line if it's the last statement in | ||
its scope | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```js | ||
const something = 123; | ||
beforeEach(() => { | ||
// more stuff | ||
}); | ||
describe('foo', () => {}); | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```js | ||
const something = 123; | ||
|
||
beforeEach(() => { | ||
// more stuff | ||
}); | ||
|
||
describe('foo', () => {}); | ||
``` |
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,40 @@ | ||
# Enforce padding around `describe` blocks (`padding-around-describe-blocks`) | ||
|
||
🔧 This rule is automatically fixable by the | ||
[`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
## Rule Details | ||
|
||
This rule enforces a line of padding before _and_ after 1 or more `describe` | ||
statements | ||
|
||
Note that it doesn't add/enforce a padding line if it's the last statement in | ||
its scope | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```js | ||
const thing = 123; | ||
describe('foo', () => { | ||
// stuff | ||
}); | ||
describe('bar', () => { | ||
// more stuff | ||
}); | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```js | ||
const thing = 123; | ||
|
||
describe('foo', () => { | ||
// stuff | ||
}); | ||
|
||
describe('bar', () => { | ||
// more stuff | ||
}); | ||
``` |
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,42 @@ | ||
# Enforce padding around `expect` groups (`padding-around-expect-groups`) | ||
|
||
🔧 This rule is automatically fixable by the | ||
[`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
## Rule Details | ||
|
||
This rule enforces a line of padding before _and_ after 1 or more `expect` | ||
statements | ||
|
||
Note that it doesn't add/enforce a padding line if it's the last statement in | ||
its scope and it doesn't add/enforce padding between two or more adjacent | ||
`expect` statements. | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```js | ||
test('thing one', () => { | ||
let abc = 123; | ||
expect(abc).toEqual(123); | ||
expect(123).toEqual(abc); | ||
abc = 456; | ||
expect(abc).toEqual(456); | ||
}); | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```js | ||
test('thing one', () => { | ||
let abc = 123; | ||
|
||
expect(abc).toEqual(123); | ||
expect(123).toEqual(abc); | ||
|
||
abc = 456; | ||
|
||
expect(abc).toEqual(456); | ||
}); | ||
``` |
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,46 @@ | ||
# Enforce padding around afterAll blocks (`padding-around-test-blocks`) | ||
|
||
🔧 This rule is automatically fixable by the | ||
[`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
## Rule Details | ||
|
||
This rule enforces a line of padding before _and_ after 1 or more `test`/`it` | ||
statements | ||
|
||
Note that it doesn't add/enforce a padding line if it's the last statement in | ||
its scope | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```js | ||
const thing = 123; | ||
test('foo', () => {}); | ||
test('bar', () => {}); | ||
``` | ||
|
||
```js | ||
const thing = 123; | ||
it('foo', () => {}); | ||
it('bar', () => {}); | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```js | ||
const thing = 123; | ||
|
||
test('foo', () => {}); | ||
|
||
test('bar', () => {}); | ||
``` | ||
|
||
```js | ||
const thing = 123; | ||
|
||
it('foo', () => {}); | ||
|
||
it('bar', () => {}); | ||
``` |
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
96 changes: 96 additions & 0 deletions
96
src/rules/__tests__/padding-around-after-all-blocks.test.ts
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,96 @@ | ||
import type { TSESLint } from '@typescript-eslint/utils'; | ||
import rule from '../padding-around-after-all-blocks'; | ||
import { FlatCompatRuleTester as RuleTester, espreeParser } from './test-utils'; | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: espreeParser, | ||
parserOptions: { | ||
ecmaVersion: 6, | ||
}, | ||
}); | ||
|
||
const testCase = { | ||
code: ` | ||
const someText = 'abc'; | ||
afterAll(() => { | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
// A comment | ||
afterAll(() => { | ||
// stuff | ||
}); | ||
afterAll(() => { | ||
// other stuff | ||
}); | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
afterAll(() => { | ||
// stuff | ||
}); | ||
}); | ||
`, | ||
output: ` | ||
const someText = 'abc'; | ||
afterAll(() => { | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
// A comment | ||
afterAll(() => { | ||
// stuff | ||
}); | ||
afterAll(() => { | ||
// other stuff | ||
}); | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
afterAll(() => { | ||
// stuff | ||
}); | ||
}); | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'missingPadding', | ||
line: 3, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 5, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 8, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 11, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 18, | ||
column: 3, | ||
}, | ||
], | ||
} satisfies TSESLint.InvalidTestCase<'missingPadding', never>; | ||
|
||
ruleTester.run('padding-around-after-all-blocks', rule, { | ||
valid: [testCase.output], | ||
invalid: ['src/component.test.jsx', 'src/component.test.js'].map( | ||
filename => ({ ...testCase, filename }), | ||
), | ||
}); |
94 changes: 94 additions & 0 deletions
94
src/rules/__tests__/padding-around-after-each-blocks.test.ts
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,94 @@ | ||
import type { TSESLint } from '@typescript-eslint/utils'; | ||
import rule from '../padding-around-after-each-blocks'; | ||
import { FlatCompatRuleTester as RuleTester, espreeParser } from './test-utils'; | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: espreeParser, | ||
parserOptions: { | ||
ecmaVersion: 6, | ||
}, | ||
}); | ||
|
||
const testCase = { | ||
code: ` | ||
const someText = 'abc'; | ||
afterEach(() => { | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
// A comment | ||
afterEach(() => { | ||
// stuff | ||
}); | ||
afterEach(() => { | ||
// other stuff | ||
}); | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
afterEach(() => { | ||
// stuff | ||
}); | ||
}); | ||
`, | ||
output: ` | ||
const someText = 'abc'; | ||
afterEach(() => { | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
// A comment | ||
afterEach(() => { | ||
// stuff | ||
}); | ||
afterEach(() => { | ||
// other stuff | ||
}); | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
afterEach(() => { | ||
// stuff | ||
}); | ||
}); | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'missingPadding', | ||
line: 3, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 5, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 8, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 11, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 17, | ||
column: 3, | ||
}, | ||
], | ||
} satisfies TSESLint.InvalidTestCase<'missingPadding', never>; | ||
|
||
ruleTester.run('padding-around-after-each-blocks', rule, { | ||
valid: [testCase.output], | ||
invalid: ['src/component.test.jsx', 'src/component.test.js'].map( | ||
filename => ({ ...testCase, filename }), | ||
), | ||
}); |
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,229 @@ | ||
import type { TSESLint } from '@typescript-eslint/utils'; | ||
import dedent from 'dedent'; | ||
import rule from '../padding-around-all'; | ||
import { FlatCompatRuleTester as RuleTester, espreeParser } from './test-utils'; | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: espreeParser, | ||
parserOptions: { | ||
ecmaVersion: 6, | ||
}, | ||
}); | ||
|
||
// todo: these should be more fulsome | ||
const testCase = { | ||
code: ` | ||
const someText = 'abc'; | ||
afterAll(() => { | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
// A comment | ||
afterAll(() => { | ||
// stuff | ||
}); | ||
afterAll(() => { | ||
// other stuff | ||
}); | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
afterAll(() => { | ||
// stuff | ||
}); | ||
}); | ||
`, | ||
output: ` | ||
const someText = 'abc'; | ||
afterAll(() => { | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
// A comment | ||
afterAll(() => { | ||
// stuff | ||
}); | ||
afterAll(() => { | ||
// other stuff | ||
}); | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
afterAll(() => { | ||
// stuff | ||
}); | ||
}); | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'missingPadding', | ||
line: 3, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 5, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 8, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 11, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 18, | ||
column: 3, | ||
}, | ||
], | ||
} satisfies TSESLint.InvalidTestCase<'missingPadding', never>; | ||
|
||
ruleTester.run('padding-around-all', rule, { | ||
valid: [ | ||
testCase.output, | ||
dedent` | ||
xyz: | ||
afterEach(() => {}); | ||
`, | ||
], | ||
invalid: [ | ||
...['src/component.test.jsx', 'src/component.test.js'].map(filename => ({ | ||
...testCase, | ||
filename, | ||
})), | ||
{ | ||
code: dedent` | ||
const someText = 'abc' | ||
;afterEach(() => {}) | ||
`, | ||
output: dedent` | ||
const someText = 'abc' | ||
;afterEach(() => {}) | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'missingPadding', | ||
line: 2, | ||
column: 2, | ||
}, | ||
], | ||
}, | ||
{ | ||
code: dedent` | ||
const someText = 'abc'; | ||
xyz: | ||
afterEach(() => {}); | ||
`, | ||
output: dedent` | ||
const someText = 'abc'; | ||
xyz: | ||
afterEach(() => {}); | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'missingPadding', | ||
line: 2, | ||
column: 1, | ||
}, | ||
], | ||
}, | ||
{ | ||
code: dedent` | ||
const expr = 'Papayas'; | ||
beforeEach(() => {}); | ||
it('does something?', () => { | ||
switch (expr) { | ||
case 'Oranges': | ||
expect(expr).toBe('Oranges'); | ||
break; | ||
case 'Mangoes': | ||
case 'Papayas': | ||
const v = 1; | ||
expect(v).toBe(1); | ||
console.log('Mangoes and papayas are $2.79 a pound.'); | ||
// Expected output: "Mangoes and papayas are $2.79 a pound." | ||
break; | ||
default: | ||
console.log(\`Sorry, we are out of $\{expr}.\`); | ||
} | ||
}); | ||
`, | ||
output: dedent` | ||
const expr = 'Papayas'; | ||
beforeEach(() => {}); | ||
it('does something?', () => { | ||
switch (expr) { | ||
case 'Oranges': | ||
expect(expr).toBe('Oranges'); | ||
break; | ||
case 'Mangoes': | ||
case 'Papayas': | ||
const v = 1; | ||
expect(v).toBe(1); | ||
console.log('Mangoes and papayas are $2.79 a pound.'); | ||
// Expected output: "Mangoes and papayas are $2.79 a pound." | ||
break; | ||
default: | ||
console.log(\`Sorry, we are out of $\{expr}.\`); | ||
} | ||
}); | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'missingPadding', | ||
line: 2, | ||
column: 1, | ||
endLine: 2, | ||
endColumn: 22, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 3, | ||
column: 1, | ||
endLine: 18, | ||
endColumn: 4, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 7, | ||
column: 7, | ||
endLine: 7, | ||
endColumn: 13, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 11, | ||
column: 7, | ||
endLine: 11, | ||
endColumn: 25, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 12, | ||
column: 7, | ||
endLine: 12, | ||
endColumn: 61, | ||
}, | ||
], | ||
}, | ||
], | ||
}); |
96 changes: 96 additions & 0 deletions
96
src/rules/__tests__/padding-around-before-all-blocks.test.ts
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,96 @@ | ||
import type { TSESLint } from '@typescript-eslint/utils'; | ||
import rule from '../padding-around-before-all-blocks'; | ||
import { FlatCompatRuleTester as RuleTester, espreeParser } from './test-utils'; | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: espreeParser, | ||
parserOptions: { | ||
ecmaVersion: 6, | ||
}, | ||
}); | ||
|
||
const testCase = { | ||
code: ` | ||
const someText = 'abc'; | ||
beforeAll(() => { | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
// A comment | ||
beforeAll(() => { | ||
// stuff | ||
}); | ||
beforeAll(() => { | ||
// other stuff | ||
}); | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
beforeAll(() => { | ||
// stuff | ||
}); | ||
}); | ||
`, | ||
output: ` | ||
const someText = 'abc'; | ||
beforeAll(() => { | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
// A comment | ||
beforeAll(() => { | ||
// stuff | ||
}); | ||
beforeAll(() => { | ||
// other stuff | ||
}); | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
beforeAll(() => { | ||
// stuff | ||
}); | ||
}); | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'missingPadding', | ||
line: 3, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 5, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 8, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 11, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 18, | ||
column: 3, | ||
}, | ||
], | ||
} satisfies TSESLint.InvalidTestCase<'missingPadding', never>; | ||
|
||
ruleTester.run('padding-around-before-all-blocks', rule, { | ||
valid: [testCase.output], | ||
invalid: ['src/component.test.jsx', 'src/component.test.js'].map( | ||
filename => ({ ...testCase, filename }), | ||
), | ||
}); |
96 changes: 96 additions & 0 deletions
96
src/rules/__tests__/padding-around-before-each-blocks.test.ts
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,96 @@ | ||
import type { TSESLint } from '@typescript-eslint/utils'; | ||
import rule from '../padding-around-before-each-blocks'; | ||
import { FlatCompatRuleTester as RuleTester, espreeParser } from './test-utils'; | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: espreeParser, | ||
parserOptions: { | ||
ecmaVersion: 6, | ||
}, | ||
}); | ||
|
||
const testCase = { | ||
code: ` | ||
const someText = 'abc'; | ||
beforeEach(() => { | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
// A comment | ||
beforeEach(() => { | ||
// stuff | ||
}); | ||
beforeEach(() => { | ||
// other stuff | ||
}); | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
beforeEach(() => { | ||
// stuff | ||
}); | ||
}); | ||
`, | ||
output: ` | ||
const someText = 'abc'; | ||
beforeEach(() => { | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
// A comment | ||
beforeEach(() => { | ||
// stuff | ||
}); | ||
beforeEach(() => { | ||
// other stuff | ||
}); | ||
}); | ||
describe('someText', () => { | ||
const something = 'abc'; | ||
beforeEach(() => { | ||
// stuff | ||
}); | ||
}); | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'missingPadding', | ||
line: 3, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 5, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 8, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 11, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 18, | ||
column: 3, | ||
}, | ||
], | ||
} satisfies TSESLint.InvalidTestCase<'missingPadding', never>; | ||
|
||
ruleTester.run('padding-around-before-each-blocks', rule, { | ||
valid: [testCase.output], | ||
invalid: ['src/component.test.jsx', 'src/component.test.js'].map( | ||
filename => ({ ...testCase, filename }), | ||
), | ||
}); |
133 changes: 133 additions & 0 deletions
133
src/rules/__tests__/padding-around-describe-blocks.test.ts
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,133 @@ | ||
import type { TSESLint } from '@typescript-eslint/utils'; | ||
import rule from '../padding-around-describe-blocks'; | ||
import { FlatCompatRuleTester as RuleTester, espreeParser } from './test-utils'; | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: espreeParser, | ||
parserOptions: { | ||
ecmaVersion: 6, | ||
}, | ||
}); | ||
|
||
const testCase = { | ||
code: ` | ||
foo(); | ||
bar(); | ||
const someText = 'abc'; | ||
const someObject = { | ||
one: 1, | ||
two: 2, | ||
}; | ||
// A comment before describe | ||
describe('someText', () => { | ||
describe('some condition', () => { | ||
}); | ||
describe('some other condition', () => { | ||
}); | ||
}); | ||
xdescribe('someObject', () => { | ||
// Another comment | ||
describe('some condition', () => { | ||
const anotherThing = 500; | ||
describe('yet another condition', () => { // A comment over here! | ||
}); | ||
}); | ||
});fdescribe('weird', () => {}); | ||
describe.skip('skip me', () => {}); | ||
const BOOP = "boop"; | ||
describe | ||
.skip('skip me too', () => { | ||
// stuff | ||
}); | ||
`, | ||
output: ` | ||
foo(); | ||
bar(); | ||
const someText = 'abc'; | ||
const someObject = { | ||
one: 1, | ||
two: 2, | ||
}; | ||
// A comment before describe | ||
describe('someText', () => { | ||
describe('some condition', () => { | ||
}); | ||
describe('some other condition', () => { | ||
}); | ||
}); | ||
xdescribe('someObject', () => { | ||
// Another comment | ||
describe('some condition', () => { | ||
const anotherThing = 500; | ||
describe('yet another condition', () => { // A comment over here! | ||
}); | ||
}); | ||
}); | ||
fdescribe('weird', () => {}); | ||
describe.skip('skip me', () => {}); | ||
const BOOP = "boop"; | ||
describe | ||
.skip('skip me too', () => { | ||
// stuff | ||
}); | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'missingPadding', | ||
line: 11, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 14, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 17, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 21, | ||
column: 5, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 24, | ||
column: 4, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 25, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 26, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 27, | ||
column: 1, | ||
}, | ||
], | ||
} satisfies TSESLint.InvalidTestCase<'missingPadding', never>; | ||
|
||
ruleTester.run('padding-around-describe-blocks', rule, { | ||
valid: [testCase.output], | ||
invalid: ['src/component.test.jsx', 'src/component.test.js'].map( | ||
filename => ({ ...testCase, filename }), | ||
), | ||
}); |
198 changes: 198 additions & 0 deletions
198
src/rules/__tests__/padding-around-expect-groups.test.ts
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,198 @@ | ||
import type { TSESLint } from '@typescript-eslint/utils'; | ||
import rule from '../padding-around-expect-groups'; | ||
import { FlatCompatRuleTester as RuleTester, espreeParser } from './test-utils'; | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: espreeParser, | ||
parserOptions: { | ||
ecmaVersion: 2017, | ||
}, | ||
}); | ||
|
||
const testCase = { | ||
code: ` | ||
foo(); | ||
bar(); | ||
const someText = 'abc'; | ||
const someObject = { | ||
one: 1, | ||
two: 2, | ||
}; | ||
test('thing one', () => { | ||
let abc = 123; | ||
expect(abc).toEqual(123); | ||
expect(123).toEqual(abc); // Line comment | ||
abc = 456; | ||
expect(abc).toEqual(456); | ||
}); | ||
test('thing one', () => { | ||
const abc = 123; | ||
expect(abc).toEqual(123); | ||
const xyz = 987; | ||
expect(123).toEqual(abc); // Line comment | ||
}); | ||
describe('someText', () => { | ||
describe('some condition', () => { | ||
test('foo', () => { | ||
const xyz = 987; | ||
// Comment | ||
expect(xyz).toEqual(987); | ||
expect(1) | ||
.toEqual(1); | ||
expect(true).toEqual(true); | ||
}); | ||
}); | ||
}); | ||
test('awaited expect', async () => { | ||
const abc = 123; | ||
const hasAPromise = () => Promise.resolve('foo'); | ||
await expect(hasAPromise()).resolves.toEqual('foo'); | ||
expect(abc).toEqual(123); | ||
const efg = 456; | ||
expect(123).toEqual(abc); | ||
await expect(hasAPromise()).resolves.toEqual('foo'); | ||
const hij = 789; | ||
await expect(hasAPromise()).resolves.toEqual('foo'); | ||
await expect(hasAPromise()).resolves.toEqual('foo'); | ||
const somethingElseAsync = () => Promise.resolve('bar'); | ||
await somethingElseAsync(); | ||
await expect(hasAPromise()).resolves.toEqual('foo'); | ||
}); | ||
`, | ||
output: ` | ||
foo(); | ||
bar(); | ||
const someText = 'abc'; | ||
const someObject = { | ||
one: 1, | ||
two: 2, | ||
}; | ||
test('thing one', () => { | ||
let abc = 123; | ||
expect(abc).toEqual(123); | ||
expect(123).toEqual(abc); // Line comment | ||
abc = 456; | ||
expect(abc).toEqual(456); | ||
}); | ||
test('thing one', () => { | ||
const abc = 123; | ||
expect(abc).toEqual(123); | ||
const xyz = 987; | ||
expect(123).toEqual(abc); // Line comment | ||
}); | ||
describe('someText', () => { | ||
describe('some condition', () => { | ||
test('foo', () => { | ||
const xyz = 987; | ||
// Comment | ||
expect(xyz).toEqual(987); | ||
expect(1) | ||
.toEqual(1); | ||
expect(true).toEqual(true); | ||
}); | ||
}); | ||
}); | ||
test('awaited expect', async () => { | ||
const abc = 123; | ||
const hasAPromise = () => Promise.resolve('foo'); | ||
await expect(hasAPromise()).resolves.toEqual('foo'); | ||
expect(abc).toEqual(123); | ||
const efg = 456; | ||
expect(123).toEqual(abc); | ||
await expect(hasAPromise()).resolves.toEqual('foo'); | ||
const hij = 789; | ||
await expect(hasAPromise()).resolves.toEqual('foo'); | ||
await expect(hasAPromise()).resolves.toEqual('foo'); | ||
const somethingElseAsync = () => Promise.resolve('bar'); | ||
await somethingElseAsync(); | ||
await expect(hasAPromise()).resolves.toEqual('foo'); | ||
}); | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'missingPadding', | ||
line: 13, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 15, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 16, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 21, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 24, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 32, | ||
column: 7, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 43, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 47, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 51, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 56, | ||
column: 3, | ||
}, | ||
], | ||
} satisfies TSESLint.InvalidTestCase<'missingPadding', never>; | ||
|
||
ruleTester.run('padding-around-expect-groups', rule, { | ||
valid: [testCase.output], | ||
invalid: ['src/component.test.jsx', 'src/component.test.js'].map( | ||
filename => ({ ...testCase, filename }), | ||
), | ||
}); |
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,143 @@ | ||
import type { TSESLint } from '@typescript-eslint/utils'; | ||
import rule from '../padding-around-test-blocks'; | ||
import { FlatCompatRuleTester as RuleTester, espreeParser } from './test-utils'; | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: espreeParser, | ||
parserOptions: { | ||
ecmaVersion: 6, | ||
}, | ||
}); | ||
|
||
const testCase = { | ||
code: ` | ||
const foo = 'bar'; | ||
const bar = 'baz'; | ||
it('foo', () => { | ||
// stuff | ||
}); | ||
fit('bar', () => { | ||
// stuff | ||
}); | ||
test('foo foo', () => {}); | ||
test('bar bar', () => {}); | ||
// Nesting | ||
describe('other bar', () => { | ||
const thing = 123; | ||
test('is another bar w/ test', () => { | ||
}); | ||
// With a comment | ||
it('is another bar w/ it', () => { | ||
}); | ||
test.skip('skipping', () => {}); // Another comment | ||
it.skip('skipping too', () => {}); | ||
});xtest('weird', () => {}); | ||
test | ||
.skip('skippy skip', () => {}); | ||
xit('bar foo', () => {}); | ||
`, | ||
output: ` | ||
const foo = 'bar'; | ||
const bar = 'baz'; | ||
it('foo', () => { | ||
// stuff | ||
}); | ||
fit('bar', () => { | ||
// stuff | ||
}); | ||
test('foo foo', () => {}); | ||
test('bar bar', () => {}); | ||
// Nesting | ||
describe('other bar', () => { | ||
const thing = 123; | ||
test('is another bar w/ test', () => { | ||
}); | ||
// With a comment | ||
it('is another bar w/ it', () => { | ||
}); | ||
test.skip('skipping', () => {}); // Another comment | ||
it.skip('skipping too', () => {}); | ||
}); | ||
xtest('weird', () => {}); | ||
test | ||
.skip('skippy skip', () => {}); | ||
xit('bar foo', () => {}); | ||
`, | ||
errors: [ | ||
{ | ||
messageId: 'missingPadding', | ||
line: 4, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 7, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 10, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 11, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 16, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 19, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 21, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 22, | ||
column: 3, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 23, | ||
column: 4, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 24, | ||
column: 1, | ||
}, | ||
{ | ||
messageId: 'missingPadding', | ||
line: 26, | ||
column: 1, | ||
}, | ||
], | ||
} satisfies TSESLint.InvalidTestCase<'missingPadding', never>; | ||
|
||
ruleTester.run('padding-around-test-blocks', rule, { | ||
valid: [testCase.output], | ||
invalid: ['src/component.test.jsx', 'src/component.test.js'].map( | ||
filename => ({ ...testCase, filename }), | ||
), | ||
}); |
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,20 @@ | ||
import { PaddingType, StatementType, createPaddingRule } from './utils/padding'; | ||
|
||
export const config = [ | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.Any, | ||
nextStatementType: StatementType.AfterAllToken, | ||
}, | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.AfterAllToken, | ||
nextStatementType: StatementType.Any, | ||
}, | ||
]; | ||
|
||
export default createPaddingRule( | ||
__filename, | ||
'Enforce padding around `afterAll` blocks', | ||
config, | ||
); |
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,20 @@ | ||
import { PaddingType, StatementType, createPaddingRule } from './utils/padding'; | ||
|
||
export const config = [ | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.Any, | ||
nextStatementType: StatementType.AfterEachToken, | ||
}, | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.AfterEachToken, | ||
nextStatementType: StatementType.Any, | ||
}, | ||
]; | ||
|
||
export default createPaddingRule( | ||
__filename, | ||
'Enforce padding around `afterEach` blocks', | ||
config, | ||
); |
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,22 @@ | ||
import { config as paddingAroundAfterAllBlocksConfig } from './padding-around-after-all-blocks'; | ||
import { config as paddingAroundAfterEachBlocksConfig } from './padding-around-after-each-blocks'; | ||
import { config as paddingAroundBeforeAllBlocksConfig } from './padding-around-before-all-blocks'; | ||
import { config as paddingAroundBeforeEachBlocksConfig } from './padding-around-before-each-blocks'; | ||
import { config as paddingAroundDescribeBlocksConfig } from './padding-around-describe-blocks'; | ||
import { config as paddingAroundExpectGroupsConfig } from './padding-around-expect-groups'; | ||
import { config as paddingAroundTestBlocksConfig } from './padding-around-test-blocks'; | ||
import { createPaddingRule } from './utils/padding'; | ||
|
||
export default createPaddingRule( | ||
__filename, | ||
'Enforce padding around Jest functions', | ||
[ | ||
...paddingAroundAfterAllBlocksConfig, | ||
...paddingAroundAfterEachBlocksConfig, | ||
...paddingAroundBeforeAllBlocksConfig, | ||
...paddingAroundBeforeEachBlocksConfig, | ||
...paddingAroundDescribeBlocksConfig, | ||
...paddingAroundExpectGroupsConfig, | ||
...paddingAroundTestBlocksConfig, | ||
], | ||
); |
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,20 @@ | ||
import { PaddingType, StatementType, createPaddingRule } from './utils/padding'; | ||
|
||
export const config = [ | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.Any, | ||
nextStatementType: StatementType.BeforeAllToken, | ||
}, | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.BeforeAllToken, | ||
nextStatementType: StatementType.Any, | ||
}, | ||
]; | ||
|
||
export default createPaddingRule( | ||
__filename, | ||
'Enforce padding around `beforeAll` blocks', | ||
config, | ||
); |
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,20 @@ | ||
import { PaddingType, StatementType, createPaddingRule } from './utils/padding'; | ||
|
||
export const config = [ | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.Any, | ||
nextStatementType: StatementType.BeforeEachToken, | ||
}, | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.BeforeEachToken, | ||
nextStatementType: StatementType.Any, | ||
}, | ||
]; | ||
|
||
export default createPaddingRule( | ||
__filename, | ||
'Enforce padding around `beforeEach` blocks', | ||
config, | ||
); |
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,28 @@ | ||
import { PaddingType, StatementType, createPaddingRule } from './utils/padding'; | ||
|
||
export const config = [ | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.Any, | ||
nextStatementType: [ | ||
StatementType.DescribeToken, | ||
StatementType.FdescribeToken, | ||
StatementType.XdescribeToken, | ||
], | ||
}, | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: [ | ||
StatementType.DescribeToken, | ||
StatementType.FdescribeToken, | ||
StatementType.XdescribeToken, | ||
], | ||
nextStatementType: StatementType.Any, | ||
}, | ||
]; | ||
|
||
export default createPaddingRule( | ||
__filename, | ||
'Enforce padding around `describe` blocks', | ||
config, | ||
); |
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,25 @@ | ||
import { PaddingType, StatementType, createPaddingRule } from './utils/padding'; | ||
|
||
export const config = [ | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.Any, | ||
nextStatementType: StatementType.ExpectToken, | ||
}, | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.ExpectToken, | ||
nextStatementType: StatementType.Any, | ||
}, | ||
{ | ||
paddingType: PaddingType.Any, | ||
prevStatementType: StatementType.ExpectToken, | ||
nextStatementType: StatementType.ExpectToken, | ||
}, | ||
]; | ||
|
||
export default createPaddingRule( | ||
__filename, | ||
'Enforce padding around `expect` groups', | ||
config, | ||
); |
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,32 @@ | ||
import { PaddingType, StatementType, createPaddingRule } from './utils/padding'; | ||
|
||
export const config = [ | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: StatementType.Any, | ||
nextStatementType: [ | ||
StatementType.TestToken, | ||
StatementType.ItToken, | ||
StatementType.FitToken, | ||
StatementType.XitToken, | ||
StatementType.XtestToken, | ||
], | ||
}, | ||
{ | ||
paddingType: PaddingType.Always, | ||
prevStatementType: [ | ||
StatementType.TestToken, | ||
StatementType.ItToken, | ||
StatementType.FitToken, | ||
StatementType.XitToken, | ||
StatementType.XtestToken, | ||
], | ||
nextStatementType: StatementType.Any, | ||
}, | ||
]; | ||
|
||
export default createPaddingRule( | ||
__filename, | ||
'Enforce padding around afterAll blocks', | ||
config, | ||
); |
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,104 @@ | ||
import { | ||
AST_NODE_TYPES, | ||
AST_TOKEN_TYPES, | ||
type TSESTree, | ||
} from '@typescript-eslint/utils'; | ||
import { | ||
areTokensOnSameLine, | ||
isTokenASemicolon, | ||
isValidParent, | ||
} from '../ast-utils'; | ||
|
||
describe('isValidParent', () => { | ||
test.each` | ||
type | expected | ||
${AST_NODE_TYPES.Program} | ${true} | ||
${AST_NODE_TYPES.BlockStatement} | ${true} | ||
${AST_NODE_TYPES.SwitchCase} | ${true} | ||
${AST_NODE_TYPES.SwitchStatement} | ${true} | ||
${AST_NODE_TYPES.Identifier} | ${false} | ||
`('returns $expected for parent value of $type', ({ type, expected }) => { | ||
expect(isValidParent(type)).toBe(expected); | ||
}); | ||
}); | ||
|
||
describe('isTokenASemicolon', () => { | ||
test.each` | ||
type | value | expected | ||
${AST_TOKEN_TYPES.Punctuator} | ${';'} | ${true} | ||
${AST_TOKEN_TYPES.Punctuator} | ${'.'} | ${false} | ||
${AST_TOKEN_TYPES.String} | ${';'} | ${false} | ||
`('returns $expected for $type and $value', ({ type, value, expected }) => { | ||
const token: TSESTree.Token = { | ||
type, | ||
value, | ||
range: [0, 1], | ||
loc: { | ||
start: { | ||
line: 0, | ||
column: 0, | ||
}, | ||
end: { | ||
line: 0, | ||
column: 1, | ||
}, | ||
}, | ||
}; | ||
|
||
expect(isTokenASemicolon(token)).toBe(expected); | ||
}); | ||
}); | ||
|
||
describe('areTokensOnSameLine', () => { | ||
const makeNode = (line: number): TSESTree.Node => { | ||
return { | ||
type: AST_NODE_TYPES.Identifier, | ||
name: 'describe', | ||
loc: { | ||
start: { | ||
line, | ||
column: 10, | ||
}, | ||
end: { | ||
line, | ||
column: 10, | ||
}, | ||
}, | ||
} as TSESTree.Node; | ||
}; | ||
|
||
const makeToken = (line: number): TSESTree.Token => { | ||
return { | ||
type: AST_TOKEN_TYPES.Punctuator, | ||
value: ';', | ||
range: [0, 1], | ||
loc: { | ||
start: { | ||
line, | ||
column: 10, | ||
}, | ||
end: { | ||
line, | ||
column: 10, | ||
}, | ||
}, | ||
}; | ||
}; | ||
|
||
test.each` | ||
left | right | expected | ||
${makeNode(1)} | ${makeNode(1)} | ${true} | ||
${makeNode(1)} | ${makeToken(1)} | ${true} | ||
${makeToken(1)} | ${makeNode(1)} | ${true} | ||
${makeToken(1)} | ${makeToken(1)} | ${true} | ||
${makeNode(1)} | ${makeNode(2)} | ${false} | ||
${makeNode(1)} | ${makeToken(2)} | ${false} | ||
${makeToken(1)} | ${makeNode(2)} | ${false} | ||
${makeToken(1)} | ${makeToken(2)} | ${false} | ||
`( | ||
'returns $expected for left node/token ending on $left.loc.end.line and right node/token starting on $right.loc.start.line', | ||
({ left, right, expected }) => { | ||
expect(areTokensOnSameLine(left, right)).toBe(expected); | ||
}, | ||
); | ||
}); |
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, | ||
AST_TOKEN_TYPES, | ||
type TSESLint, | ||
type TSESTree, | ||
} from '@typescript-eslint/utils'; | ||
|
||
export const isTokenASemicolon = (token: TSESTree.Token): boolean => | ||
token.value === ';' && token.type === AST_TOKEN_TYPES.Punctuator; | ||
|
||
export const areTokensOnSameLine = ( | ||
left: TSESTree.Node | TSESTree.Token, | ||
right: TSESTree.Node | TSESTree.Token, | ||
): boolean => left.loc.end.line === right.loc.start.line; | ||
|
||
// We'll only verify nodes with these parent types | ||
const STATEMENT_LIST_PARENTS = new Set([ | ||
AST_NODE_TYPES.Program, | ||
AST_NODE_TYPES.BlockStatement, | ||
AST_NODE_TYPES.SwitchCase, | ||
AST_NODE_TYPES.SwitchStatement, | ||
]); | ||
|
||
export const isValidParent = (parentType: AST_NODE_TYPES): boolean => { | ||
return STATEMENT_LIST_PARENTS.has(parentType); | ||
}; | ||
|
||
/** | ||
* Gets the actual last token. | ||
* | ||
* If a semicolon is semicolon-less style's semicolon, this ignores it. | ||
* For example: | ||
* | ||
* foo() | ||
* ;[1, 2, 3].forEach(bar) | ||
*/ | ||
export const getActualLastToken = ( | ||
sourceCode: TSESLint.SourceCode, | ||
node: TSESTree.Node, | ||
): TSESTree.Token => { | ||
const semiToken = sourceCode.getLastToken(node)!; | ||
const prevToken = sourceCode.getTokenBefore(semiToken)!; | ||
const nextToken = sourceCode.getTokenAfter(semiToken); | ||
const isSemicolonLessStyle = Boolean( | ||
prevToken && | ||
nextToken && | ||
prevToken.range[0] >= node.range[0] && | ||
isTokenASemicolon(semiToken) && | ||
semiToken.loc.start.line !== prevToken.loc.end.line && | ||
semiToken.loc.end.line === nextToken.loc.start.line, | ||
); | ||
|
||
return isSemicolonLessStyle ? prevToken : semiToken; | ||
}; | ||
|
||
/** | ||
* Gets padding line sequences between the given 2 statements. | ||
* Comments are separators of the padding line sequences. | ||
*/ | ||
export const getPaddingLineSequences = ( | ||
prevNode: TSESTree.Node, | ||
nextNode: TSESTree.Node, | ||
sourceCode: TSESLint.SourceCode, | ||
): TSESTree.Token[][] => { | ||
const pairs: TSESTree.Token[][] = []; | ||
const includeComments = true; | ||
let prevToken = getActualLastToken(sourceCode, prevNode); | ||
|
||
if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) { | ||
do { | ||
const token = sourceCode.getTokenAfter(prevToken, { | ||
includeComments, | ||
}) as TSESTree.Token; | ||
|
||
if (token.loc.start.line - prevToken.loc.end.line >= 2) { | ||
pairs.push([prevToken, token]); | ||
} | ||
|
||
prevToken = token; | ||
} while (prevToken.range[0] < nextNode.range[0]); | ||
} | ||
|
||
return pairs; | ||
}; |
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,379 @@ | ||
/** | ||
* Require/fix newlines around jest functions | ||
* | ||
* Based on eslint/padding-line-between-statements by Toru Nagashima | ||
* See: https://github.com/eslint/eslint/blob/master/lib/rules/padding-line-between-statements.js | ||
* | ||
* Some helpers borrowed from eslint ast-utils by Gyandeep Singh | ||
* See: https://github.com/eslint/eslint/blob/master/lib/rules/utils/ast-utils.js | ||
*/ | ||
|
||
import { | ||
AST_NODE_TYPES, | ||
AST_TOKEN_TYPES, | ||
type TSESLint, | ||
type TSESTree, | ||
} from '@typescript-eslint/utils'; | ||
import * as astUtils from './ast-utils'; | ||
import { createRule, getSourceCode } from './misc'; | ||
|
||
// Statement types we'll respond to | ||
export const enum StatementType { | ||
Any, | ||
AfterAllToken, | ||
AfterEachToken, | ||
BeforeAllToken, | ||
BeforeEachToken, | ||
DescribeToken, | ||
ExpectToken, | ||
FdescribeToken, | ||
FitToken, | ||
ItToken, | ||
TestToken, | ||
XdescribeToken, | ||
XitToken, | ||
XtestToken, | ||
} | ||
|
||
type StatementTypes = StatementType | StatementType[]; | ||
|
||
type StatementTester = ( | ||
node: TSESTree.Node, | ||
sourceCode: TSESLint.SourceCode, | ||
) => boolean; | ||
|
||
// Padding type to apply between statements | ||
export const enum PaddingType { | ||
Any, | ||
Always, | ||
} | ||
|
||
// A configuration object for padding type and the two statement types | ||
interface Config { | ||
paddingType: PaddingType; | ||
prevStatementType: StatementTypes; | ||
nextStatementType: StatementTypes; | ||
} | ||
|
||
interface ScopeInfo { | ||
prevNode: TSESTree.Node | null; | ||
enter: () => void; | ||
exit: () => void; | ||
} | ||
|
||
interface PaddingContext { | ||
ruleContext: TSESLint.RuleContext<'missingPadding', unknown[]>; | ||
sourceCode: TSESLint.SourceCode; | ||
scopeInfo: ScopeInfo; | ||
configs: Config[]; | ||
} | ||
|
||
type PaddingTester = ( | ||
prevNode: TSESTree.Node, | ||
nextNode: TSESTree.Node, | ||
paddingContext: PaddingContext, | ||
) => void; | ||
|
||
// Tracks position in scope and prevNode. Used to compare current and prev node | ||
// and then to walk back up to the parent scope or down into the next one. | ||
// And so on... | ||
interface Scope { | ||
upper: Scope | null; | ||
prevNode: TSESTree.Node | null; | ||
} | ||
|
||
// Creates a StatementTester to test an ExpressionStatement's first token name | ||
const createTokenTester = (tokenName: string): StatementTester => { | ||
return (node: TSESTree.Node, sourceCode: TSESLint.SourceCode): boolean => { | ||
let activeNode = node; | ||
|
||
if (activeNode.type === AST_NODE_TYPES.ExpressionStatement) { | ||
// In the case of `await`, we actually care about its argument | ||
if (activeNode.expression.type === AST_NODE_TYPES.AwaitExpression) { | ||
activeNode = activeNode.expression.argument; | ||
} | ||
|
||
const token = sourceCode.getFirstToken(activeNode); | ||
|
||
return ( | ||
token?.type === AST_TOKEN_TYPES.Identifier && token.value === tokenName | ||
); | ||
} | ||
|
||
return false; | ||
}; | ||
}; | ||
|
||
// A mapping of StatementType to StatementTester for... testing statements | ||
const statementTesters: { [T in StatementType]: StatementTester } = { | ||
[StatementType.Any]: () => true, | ||
[StatementType.AfterAllToken]: createTokenTester('afterAll'), | ||
[StatementType.AfterEachToken]: createTokenTester('afterEach'), | ||
[StatementType.BeforeAllToken]: createTokenTester('beforeAll'), | ||
[StatementType.BeforeEachToken]: createTokenTester('beforeEach'), | ||
[StatementType.DescribeToken]: createTokenTester('describe'), | ||
[StatementType.ExpectToken]: createTokenTester('expect'), | ||
[StatementType.FdescribeToken]: createTokenTester('fdescribe'), | ||
[StatementType.FitToken]: createTokenTester('fit'), | ||
[StatementType.ItToken]: createTokenTester('it'), | ||
[StatementType.TestToken]: createTokenTester('test'), | ||
[StatementType.XdescribeToken]: createTokenTester('xdescribe'), | ||
[StatementType.XitToken]: createTokenTester('xit'), | ||
[StatementType.XtestToken]: createTokenTester('xtest'), | ||
}; | ||
|
||
/** | ||
* Check and report statements for `PaddingType.Always` configuration. | ||
* This autofix inserts a blank line between the given 2 statements. | ||
* If the `prevNode` has trailing comments, it inserts a blank line after the | ||
* trailing comments. | ||
*/ | ||
const paddingAlwaysTester = ( | ||
prevNode: TSESTree.Node, | ||
nextNode: TSESTree.Node, | ||
paddingContext: PaddingContext, | ||
): void => { | ||
const { sourceCode, ruleContext } = paddingContext; | ||
const paddingLines = astUtils.getPaddingLineSequences( | ||
prevNode, | ||
nextNode, | ||
sourceCode, | ||
); | ||
|
||
// We've got some padding lines. Great. | ||
if (paddingLines.length > 0) { | ||
return; | ||
} | ||
|
||
// Missing padding line | ||
ruleContext.report({ | ||
node: nextNode, | ||
messageId: 'missingPadding', | ||
fix(fixer: TSESLint.RuleFixer) { | ||
let prevToken = astUtils.getActualLastToken(sourceCode, prevNode); | ||
const nextToken = (sourceCode.getFirstTokenBetween(prevToken, nextNode, { | ||
includeComments: true, | ||
/** | ||
* Skip the trailing comments of the previous node. | ||
* This inserts a blank line after the last trailing comment. | ||
* | ||
* For example: | ||
* | ||
* foo(); // trailing comment. | ||
* // comment. | ||
* bar(); | ||
* | ||
* Get fixed to: | ||
* | ||
* foo(); // trailing comment. | ||
* | ||
* // comment. | ||
* bar(); | ||
*/ | ||
filter(token: TSESTree.Token): boolean { | ||
if (astUtils.areTokensOnSameLine(prevToken, token)) { | ||
prevToken = token; | ||
|
||
return false; | ||
} | ||
|
||
return true; | ||
}, | ||
}) || nextNode) as TSESTree.Token; | ||
|
||
const insertText = astUtils.areTokensOnSameLine(prevToken, nextToken) | ||
? '\n\n' | ||
: '\n'; | ||
|
||
return fixer.insertTextAfter(prevToken, insertText); | ||
}, | ||
}); | ||
}; | ||
|
||
// A mapping of PaddingType to PaddingTester | ||
const paddingTesters: { [T in PaddingType]: PaddingTester } = { | ||
[PaddingType.Any]: () => true, | ||
[PaddingType.Always]: paddingAlwaysTester, | ||
}; | ||
|
||
const createScopeInfo = (): ScopeInfo => { | ||
let scope: Scope | null = null; | ||
|
||
// todo: explore seeing if we can refactor to a more TypeScript friendly structure | ||
return { | ||
get prevNode() { | ||
return scope!.prevNode; | ||
}, | ||
set prevNode(node) { | ||
scope!.prevNode = node; | ||
}, | ||
enter() { | ||
scope = { upper: scope, prevNode: null }; | ||
}, | ||
exit() { | ||
scope = scope!.upper; | ||
}, | ||
}; | ||
}; | ||
|
||
/** | ||
* Check whether the given node matches the statement type | ||
*/ | ||
const nodeMatchesType = ( | ||
node: TSESTree.Node, | ||
statementType: StatementTypes, | ||
paddingContext: PaddingContext, | ||
): boolean => { | ||
let innerStatementNode = node; | ||
const { sourceCode } = paddingContext; | ||
|
||
// Dig into LabeledStatement body until it's not that anymore | ||
while (innerStatementNode.type === AST_NODE_TYPES.LabeledStatement) { | ||
innerStatementNode = innerStatementNode.body; | ||
} | ||
|
||
// If it's an array recursively check if any of the statement types match | ||
// the node | ||
if (Array.isArray(statementType)) { | ||
return statementType.some(type => | ||
nodeMatchesType(innerStatementNode, type, paddingContext), | ||
); | ||
} | ||
|
||
return statementTesters[statementType](innerStatementNode, sourceCode); | ||
}; | ||
|
||
/** | ||
* Executes matching padding tester for last matched padding config for given | ||
* nodes | ||
*/ | ||
const testPadding = ( | ||
prevNode: TSESTree.Node, | ||
nextNode: TSESTree.Node, | ||
paddingContext: PaddingContext, | ||
): void => { | ||
const { configs } = paddingContext; | ||
|
||
const testType = (type: PaddingType) => | ||
paddingTesters[type](prevNode, nextNode, paddingContext); | ||
|
||
for (let i = configs.length - 1; i >= 0; --i) { | ||
const { | ||
prevStatementType: prevType, | ||
nextStatementType: nextType, | ||
paddingType, | ||
} = configs[i]; | ||
|
||
if ( | ||
nodeMatchesType(prevNode, prevType, paddingContext) && | ||
nodeMatchesType(nextNode, nextType, paddingContext) | ||
) { | ||
return testType(paddingType); | ||
} | ||
} | ||
|
||
// There were no matching padding rules for the prevNode, nextNode, | ||
// paddingType combination... so we'll use PaddingType.Any which is always ok | ||
return testType(PaddingType.Any); | ||
}; | ||
|
||
/** | ||
* Verify padding lines between the given node and the previous node. | ||
*/ | ||
const verifyNode = ( | ||
node: TSESTree.Node, | ||
paddingContext: PaddingContext, | ||
): void => { | ||
const { scopeInfo } = paddingContext; | ||
|
||
// NOTE: ESLint types use ESTree which provides a Node type, however | ||
// ESTree.Node doesn't support the parent property which is added by | ||
// ESLint during traversal. Our best bet is to ignore the property access | ||
// here as it's the only place that it's checked. | ||
|
||
if (!astUtils.isValidParent((node as any).parent.type)) { | ||
return; | ||
} | ||
|
||
if (scopeInfo.prevNode) { | ||
testPadding(scopeInfo.prevNode, node, paddingContext); | ||
} | ||
|
||
scopeInfo.prevNode = node; | ||
}; | ||
|
||
/** | ||
* Creates an ESLint rule for a given set of padding Config objects. | ||
* | ||
* The algorithm is approximately this: | ||
* | ||
* For each 'scope' in the program | ||
* - Enter the scope (store the parent scope and previous node) | ||
* - For each statement in the scope | ||
* - Check the current node and previous node against the Config objects | ||
* - If the current node and previous node match a Config, check the padding. | ||
* Otherwise, ignore it. | ||
* - If the padding is missing (and required), report and fix | ||
* - Store the current node as the previous | ||
* - Repeat | ||
* - Exit scope (return to parent scope and clear previous node) | ||
* | ||
* The items we're looking for with this rule are ExpressionStatement nodes | ||
* where the first token is an Identifier with a name matching one of the Jest | ||
* functions. It's not foolproof, of course, but it's probably good enough for | ||
* almost all cases. | ||
* | ||
* The Config objects specify a padding type, a previous statement type, and a | ||
* next statement type. Wildcard statement types and padding types are | ||
* supported. The current node and previous node are checked against the | ||
* statement types. If they match then the specified padding type is | ||
* tested/enforced. | ||
* | ||
* See src/index.ts for examples of Config usage. | ||
*/ | ||
export const createPaddingRule = ( | ||
name: string, | ||
description: string, | ||
configs: Config[], | ||
deprecated = false, | ||
) => { | ||
return createRule({ | ||
name, | ||
meta: { | ||
docs: { description }, | ||
fixable: 'whitespace', | ||
deprecated, | ||
messages: { | ||
missingPadding: 'Expected blank line before this statement.', | ||
}, | ||
schema: [], | ||
type: 'suggestion', | ||
}, | ||
defaultOptions: [], | ||
create(context) { | ||
const paddingContext = { | ||
ruleContext: context, | ||
sourceCode: getSourceCode(context), | ||
scopeInfo: createScopeInfo(), | ||
configs, | ||
}; | ||
|
||
const { scopeInfo } = paddingContext; | ||
|
||
return { | ||
Program: scopeInfo.enter, | ||
'Program:exit': scopeInfo.enter, | ||
BlockStatement: scopeInfo.enter, | ||
'BlockStatement:exit': scopeInfo.exit, | ||
SwitchStatement: scopeInfo.enter, | ||
'SwitchStatement:exit': scopeInfo.exit, | ||
':statement': (node: TSESTree.Node) => verifyNode(node, paddingContext), | ||
SwitchCase(node: TSESTree.Node) { | ||
verifyNode(node, paddingContext); | ||
scopeInfo.enter(); | ||
}, | ||
'SwitchCase:exit': scopeInfo.exit, | ||
}; | ||
}, | ||
}); | ||
}; |