eslint-plugin-jest-formatting 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/.circleci/config.yml +2 -2
  2. package/.eslintrc +15 -4
  3. package/CHANGELOG.md +28 -6
  4. package/README.md +21 -13
  5. package/docs/rules/padding-around-all.md +13 -0
  6. package/lib/ast-utils.js +41 -0
  7. package/lib/index.js +159 -76
  8. package/lib/rules/padding.js +131 -158
  9. package/package.json +20 -19
  10. package/src/ast-utils.ts +80 -0
  11. package/src/index.ts +166 -81
  12. package/src/rules/padding.ts +263 -252
  13. package/tests/catfood.js +41 -0
  14. package/tests/dogfood.spec.js +2 -0
  15. package/tests/lib/ast-utils.spec.ts +94 -0
  16. package/tests/lib/rules/padding-around-after-all-blocks.spec.js +3 -8
  17. package/tests/lib/rules/padding-around-after-each-blocks.spec.js +3 -8
  18. package/tests/lib/rules/padding-around-before-all-blocks.spec.js +3 -8
  19. package/tests/lib/rules/padding-around-before-each-blocks.spec.js +3 -8
  20. package/tests/lib/rules/padding-around-describe-blocks.spec.js +7 -12
  21. package/tests/lib/rules/padding-around-expect-groups.spec.js +3 -8
  22. package/tests/lib/rules/padding-around-test-blocks.spec.js +15 -12
  23. package/tsconfig.json +1 -1
  24. package/lib/utils.js +0 -15
  25. package/src/utils.ts +0 -40
  26. package/tests/lib/rules/padding-before-after-all-blocks.spec.js +0 -104
  27. package/tests/lib/rules/padding-before-after-each-blocks.spec.js +0 -104
  28. package/tests/lib/rules/padding-before-before-all-blocks.spec.js +0 -104
  29. package/tests/lib/rules/padding-before-before-each-blocks.spec.js +0 -104
  30. package/tests/lib/rules/padding-before-describe-blocks.spec.js +0 -150
  31. package/tests/lib/rules/padding-before-expect-statements.spec.js +0 -144
  32. package/tests/lib/rules/padding-before-test-blocks.spec.js +0 -159
  33. package/yarn.lock +0 -4232
@@ -1,115 +1,144 @@
1
1
  /**
2
- * @fileoverview Rule to require or disallow newlines between jest functions
3
- * Based on eslint/padding-line-between-statements by Toru Nagashima
4
- * See: https://github.com/eslint/eslint/blob/master/lib/rules/padding-line-between-statements.js
2
+ * Require/fix newlines around jest functions
5
3
  *
6
- * STATEMENT_LIST_PARENTS, isTokenOnSameLine and isSemiColonToken borrowed from
7
- * eslint ast-utils by Gyandeep Singh
8
- * See: https://github.com/eslint/eslint/blob/master/lib/rules/utils/ast-utils.js
4
+ * Based on eslint/padding-line-between-statements by Toru Nagashima
5
+ * See: https://github.com/eslint/eslint/blob/master/lib/rules/padding-line-between-statements.js
6
+ *
7
+ * Some helpers borrowed from eslint ast-utils by Gyandeep Singh
8
+ * See: https://github.com/eslint/eslint/blob/master/lib/rules/utils/ast-utils.js
9
9
  */
10
+
10
11
  import { AST, Rule, SourceCode } from 'eslint';
12
+ // This is because we are using @types/estree that are brought in with eslint
13
+ // eslint-disable-next-line import/no-extraneous-dependencies
11
14
  import { Node } from 'estree';
15
+ import * as astUtils from '../ast-utils';
16
+
17
+ // Statement types we'll respond to
18
+ export const enum StatementType {
19
+ Any,
20
+ AfterAllToken,
21
+ AfterEachToken,
22
+ BeforeAllToken,
23
+ BeforeEachToken,
24
+ DescribeToken,
25
+ ExpectToken,
26
+ FdescribeToken,
27
+ FitToken,
28
+ ItToken,
29
+ TestToken,
30
+ XdescribeToken,
31
+ XitToken,
32
+ XtestToken,
33
+ }
12
34
 
13
- type StatementTypeTester = {
14
- [test: string]: (node: Node, sourceCode: SourceCode) => boolean;
15
- };
35
+ type StatementTypes = StatementType | StatementType[];
16
36
 
17
- type PaddingTypeTester = {
18
- [verify: string]: (
19
- context: Rule.RuleContext,
20
- prevNode: Node,
21
- nextNode: Node,
22
- paddingLines: AST.Token[][],
23
- ) => void;
24
- };
37
+ type StatementTester = (node: Node, sourceCode: SourceCode) => boolean;
25
38
 
26
- const STATEMENT_LIST_PARENTS = new Set([
27
- 'Program',
28
- 'BlockStatement',
29
- 'SwitchCase',
30
- 'SwitchStatement',
31
- ]);
39
+ // Padding type to apply between statements
40
+ export const enum PaddingType {
41
+ Any,
42
+ Always,
43
+ }
32
44
 
33
- const isTokenOnSameLine = (
34
- left: Node | AST.Token,
35
- right: Node | AST.Token,
36
- ): boolean => left.loc.end.line === right.loc.start.line;
45
+ type PaddingTester = (
46
+ prevNode: Node,
47
+ nextNode: Node,
48
+ paddingContext: PaddingContext,
49
+ ) => void;
50
+
51
+ // A configuration object for padding type and the two statement types
52
+ export interface Config {
53
+ paddingType: PaddingType;
54
+ prevStatementType: StatementTypes;
55
+ nextStatementType: StatementTypes;
56
+ }
37
57
 
38
- const isSemicolonToken = (token: AST.Token): boolean =>
39
- token.value === ';' && token.type === 'Punctuator';
58
+ // Tracks position in scope and prevNode. Used to compare current and prev node
59
+ // and then to walk back up to the parent scope or down into the next one.
60
+ // And so on...
61
+ interface Scope {
62
+ upper: Scope | null;
63
+ prevNode: Node | null;
64
+ }
40
65
 
41
- // NOTE: Should probably be configurable
42
- const isTestFile = (filename: string): boolean =>
43
- filename.includes('test') || filename.includes('spec');
66
+ interface ScopeInfo {
67
+ prevNode: Node | null;
68
+ enter: () => void;
69
+ exit: () => void;
70
+ }
44
71
 
45
- /**
46
- * Creates tester which check if an expression node has a certain name
47
- */
48
- function newJestTokenTester(name: string): StatementTypeTester {
49
- return {
50
- test: (node: Node, sourceCode: SourceCode): boolean => {
51
- const token = sourceCode.getFirstToken(node);
52
-
53
- return (
54
- node.type === 'ExpressionStatement' &&
55
- token.type === 'Identifier' &&
56
- token.value === name
57
- );
58
- },
59
- };
72
+ interface PaddingContext {
73
+ ruleContext: Rule.RuleContext;
74
+ sourceCode: SourceCode;
75
+ scopeInfo: ScopeInfo;
76
+ configs: Config[];
60
77
  }
61
78
 
62
- /**
63
- * Gets the actual last token.
64
- *
65
- * If a semicolon is semicolon-less style's semicolon, this ignores it.
66
- * For example:
67
- *
68
- * foo()
69
- * ;[1, 2, 3].forEach(bar)
70
- */
71
- function getActualLastToken(sourceCode: SourceCode, node: Node): AST.Token {
72
- const semiToken = sourceCode.getLastToken(node);
73
- const prevToken = sourceCode.getTokenBefore(semiToken);
74
- const nextToken = sourceCode.getTokenAfter(semiToken);
75
- const isSemicolonLessStyle = Boolean(
76
- prevToken &&
77
- nextToken &&
78
- prevToken.range[0] >= node.range[0] &&
79
- isSemicolonToken(semiToken) &&
80
- semiToken.loc.start.line !== prevToken.loc.end.line &&
81
- semiToken.loc.end.line === nextToken.loc.start.line,
82
- );
79
+ // Creates a StatementTester to test an ExpressionStatement's first token name
80
+ const createTokenTester = (tokenName: string): StatementTester => {
81
+ return (node: Node, sourceCode: SourceCode): boolean => {
82
+ const token = sourceCode.getFirstToken(node);
83
83
 
84
- return isSemicolonLessStyle ? prevToken : semiToken;
85
- }
84
+ return (
85
+ node.type === 'ExpressionStatement' &&
86
+ token.type === 'Identifier' &&
87
+ token.value === tokenName
88
+ );
89
+ };
90
+ };
91
+
92
+ // A mapping of StatementType to StatementTester for... testing statements
93
+ const statementTesters: { [T in StatementType]: StatementTester } = {
94
+ [StatementType.Any]: () => true,
95
+ [StatementType.AfterAllToken]: createTokenTester('afterAll'),
96
+ [StatementType.AfterEachToken]: createTokenTester('afterEach'),
97
+ [StatementType.BeforeAllToken]: createTokenTester('beforeAll'),
98
+ [StatementType.BeforeEachToken]: createTokenTester('beforeEach'),
99
+ [StatementType.DescribeToken]: createTokenTester('describe'),
100
+ [StatementType.ExpectToken]: createTokenTester('expect'),
101
+ [StatementType.FdescribeToken]: createTokenTester('fdescribe'),
102
+ [StatementType.FitToken]: createTokenTester('fit'),
103
+ [StatementType.ItToken]: createTokenTester('it'),
104
+ [StatementType.TestToken]: createTokenTester('test'),
105
+ [StatementType.XdescribeToken]: createTokenTester('xdescribe'),
106
+ [StatementType.XitToken]: createTokenTester('xit'),
107
+ [StatementType.XtestToken]: createTokenTester('xtest'),
108
+ };
86
109
 
87
110
  /**
88
- * Check and report statements for `always` configuration.
111
+ * Check and report statements for `PaddingType.Always` configuration.
89
112
  * This autofix inserts a blank line between the given 2 statements.
90
113
  * If the `prevNode` has trailing comments, it inserts a blank line after the
91
114
  * trailing comments.
92
115
  */
93
- function verifyForAlways(
94
- context: Rule.RuleContext,
116
+ const paddingAlwaysTester = (
95
117
  prevNode: Node,
96
118
  nextNode: Node,
97
- paddingLines: AST.Token[],
98
- ): void {
119
+ paddingContext: PaddingContext,
120
+ ): void => {
121
+ const { sourceCode, ruleContext } = paddingContext;
122
+ const paddingLines = astUtils.getPaddingLineSequences(
123
+ prevNode,
124
+ nextNode,
125
+ sourceCode,
126
+ );
127
+
128
+ // We've got some padding lines. Great.
99
129
  if (paddingLines.length > 0) {
100
130
  return;
101
131
  }
102
132
 
103
- context.report({
133
+ // Missing padding line
134
+ ruleContext.report({
104
135
  node: nextNode,
105
136
  message: 'Expected blank line before this statement.',
106
137
  fix(fixer: Rule.RuleFixer): Rule.Fix {
107
- const sourceCode = context.getSourceCode();
108
- let prevToken = getActualLastToken(sourceCode, prevNode);
138
+ let prevToken = astUtils.getActualLastToken(sourceCode, prevNode);
109
139
  const nextToken =
110
140
  sourceCode.getFirstTokenBetween(prevToken, nextNode, {
111
141
  includeComments: true,
112
-
113
142
  /**
114
143
  * Skip the trailing comments of the previous node.
115
144
  * This inserts a blank line after the last trailing comment.
@@ -128,7 +157,7 @@ function verifyForAlways(
128
157
  * bar();
129
158
  */
130
159
  filter(token: AST.Token): boolean {
131
- if (isTokenOnSameLine(prevToken, token)) {
160
+ if (astUtils.areTokensOnSameLine(prevToken, token)) {
132
161
  prevToken = token;
133
162
  return false;
134
163
  }
@@ -137,203 +166,185 @@ function verifyForAlways(
137
166
  },
138
167
  }) || nextNode;
139
168
 
140
- const insertText = isTokenOnSameLine(prevToken, nextToken)
169
+ const insertText = astUtils.areTokensOnSameLine(prevToken, nextToken)
141
170
  ? '\n\n'
142
171
  : '\n';
143
172
 
144
173
  return fixer.insertTextAfter(prevToken, insertText);
145
174
  },
146
175
  });
147
- }
148
-
149
- /**
150
- * Types of blank lines.
151
- * `any` and `always` are defined.
152
- * Those have `verify` method to check and report statements.
153
- */
154
- const PaddingTypes = {
155
- any: { verify: () => {} },
156
- always: { verify: verifyForAlways },
157
176
  };
158
177
 
159
- /**
160
- * Types of statements.
161
- * Those have `test` method to check it matches to the given statement.
162
- */
163
- const StatementTypes = {
164
- '*': { test: () => true },
165
- afterAll: newJestTokenTester('afterAll'),
166
- afterEach: newJestTokenTester('afterEach'),
167
- beforeAll: newJestTokenTester('beforeAll'),
168
- beforeEach: newJestTokenTester('beforeEach'),
169
- describe: newJestTokenTester('describe'),
170
- expect: newJestTokenTester('expect'),
171
- it: newJestTokenTester('it'),
172
- test: newJestTokenTester('test'),
178
+ // A mapping of PaddingType to PaddingTester
179
+ const paddingTesters: { [T in PaddingType]: PaddingTester } = {
180
+ [PaddingType.Any]: () => true,
181
+ [PaddingType.Always]: paddingAlwaysTester,
173
182
  };
174
183
 
175
- //------------------------------------------------------------------------------
176
- // Rule Definition
177
- //------------------------------------------------------------------------------
184
+ const createScopeInfo = (): ScopeInfo => {
185
+ return (() => {
186
+ let scope: Scope = null;
178
187
 
179
- export default <Rule.RuleModule>{
180
- meta: {
181
- fixable: 'whitespace',
182
- schema: {
183
- definitions: {
184
- paddingType: {
185
- enum: Object.keys(PaddingTypes),
186
- },
187
- statementType: {
188
- anyOf: [
189
- { enum: Object.keys(StatementTypes) },
190
- {
191
- type: 'array',
192
- items: { enum: Object.keys(StatementTypes) },
193
- minItems: 1,
194
- uniqueItems: true,
195
- additionalItems: false,
196
- },
197
- ],
198
- },
188
+ return {
189
+ get prevNode() {
190
+ return scope.prevNode;
199
191
  },
200
- type: 'array',
201
- items: {
202
- type: 'object',
203
- properties: {
204
- blankLine: { $ref: '#/definitions/paddingType' },
205
- prev: { $ref: '#/definitions/statementType' },
206
- next: { $ref: '#/definitions/statementType' },
207
- },
208
- additionalProperties: false,
209
- required: ['blankLine', 'prev', 'next'],
192
+ set prevNode(node) {
193
+ scope.prevNode = node;
210
194
  },
211
- additionalItems: false,
212
- },
213
- },
214
- create(context: Rule.RuleContext): Rule.RuleListener {
215
- const filename = context.getFilename();
216
-
217
- if (!isTestFile(filename)) {
218
- return {};
219
- }
220
-
221
- const sourceCode = context.getSourceCode();
222
- const configureList = context.options || [];
223
- let scopeInfo = null;
224
-
225
- function enterScope(): void {
226
- scopeInfo = { upper: scopeInfo, prevNode: null };
227
- }
228
-
229
- function exitScope(): void {
230
- scopeInfo = scopeInfo.upper;
231
- }
232
-
233
- /**
234
- * Checks whether the given node matches the given type.
235
- */
236
- function match(node: Node, type: string | string[]): boolean {
237
- let innerStatementNode = node;
238
-
239
- while (innerStatementNode.type === 'LabeledStatement') {
240
- innerStatementNode = innerStatementNode.body;
241
- }
242
-
243
- if (Array.isArray(type)) {
244
- return type.some(match.bind(null, innerStatementNode));
245
- }
246
-
247
- return StatementTypes[type].test(innerStatementNode, sourceCode);
248
- }
195
+ enter() {
196
+ scope = { upper: scope, prevNode: null };
197
+ },
198
+ exit() {
199
+ scope = scope.upper;
200
+ },
201
+ };
202
+ })();
203
+ };
249
204
 
250
- /**
251
- * Finds the last matched configure from configureList.
252
- */
253
- function getPaddingType(prevNode: Node, nextNode: Node): PaddingTypeTester {
254
- for (let i = configureList.length - 1; i >= 0; --i) {
255
- const { prev, next, blankLine } = configureList[i];
205
+ /**
206
+ * Check whether the given node matches the statement type
207
+ */
208
+ const nodeMatchesType = (
209
+ node: Node,
210
+ statementType: StatementTypes,
211
+ paddingContext: PaddingContext,
212
+ ): boolean => {
213
+ let innerStatementNode = node;
214
+ const { sourceCode } = paddingContext;
215
+
216
+ // Dig into LabeledStatement body until it's not that anymore
217
+ while (innerStatementNode.type === 'LabeledStatement') {
218
+ innerStatementNode = innerStatementNode.body;
219
+ }
256
220
 
257
- if (match(prevNode, prev) && match(nextNode, next)) {
258
- return PaddingTypes[blankLine];
259
- }
260
- }
221
+ // If it's an array recursively check if any of the statement types match
222
+ // the node
223
+ if (Array.isArray(statementType)) {
224
+ return statementType.some((type) =>
225
+ nodeMatchesType(innerStatementNode, type, paddingContext),
226
+ );
227
+ }
261
228
 
262
- return PaddingTypes.any;
263
- }
229
+ return statementTesters[statementType](innerStatementNode, sourceCode);
230
+ };
264
231
 
265
- /**
266
- * Gets padding line sequences between the given 2 statements.
267
- * Comments are separators of the padding line sequences.
268
- */
269
- function getPaddingLineSequences(
270
- prevNode: Node,
271
- nextNode: Node,
272
- ): AST.Token[][] {
273
- const pairs: AST.Token[][] = [];
274
- let prevToken = getActualLastToken(sourceCode, prevNode);
275
-
276
- if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) {
277
- do {
278
- const token = sourceCode.getTokenAfter(prevToken, {
279
- includeComments: true,
280
- });
281
-
282
- if (token.loc.start.line - prevToken.loc.end.line >= 2) {
283
- pairs.push([prevToken, token]);
284
- }
285
-
286
- prevToken = token;
287
- } while (prevToken.range[0] < nextNode.range[0]);
288
- }
289
-
290
- return pairs;
232
+ /**
233
+ * Executes matching padding tester for last matched padding config for given
234
+ * nodes
235
+ */
236
+ const testPadding = (
237
+ prevNode: Node,
238
+ nextNode: Node,
239
+ paddingContext: PaddingContext,
240
+ ): void => {
241
+ const { configs } = paddingContext;
242
+
243
+ const testType = (type: PaddingType) =>
244
+ paddingTesters[type](prevNode, nextNode, paddingContext);
245
+
246
+ for (let i = configs.length - 1; i >= 0; --i) {
247
+ const {
248
+ prevStatementType: prevType,
249
+ nextStatementType: nextType,
250
+ paddingType,
251
+ } = configs[i];
252
+
253
+ if (
254
+ nodeMatchesType(prevNode, prevType, paddingContext) &&
255
+ nodeMatchesType(nextNode, nextType, paddingContext)
256
+ ) {
257
+ return testType(paddingType);
291
258
  }
259
+ }
292
260
 
293
- /**
294
- * Verify padding lines between the given node and the previous node.
295
- */
296
- function verify(node): void {
297
- const parentType = node.parent.type;
298
- const validParent = STATEMENT_LIST_PARENTS.has(parentType);
299
-
300
- if (!validParent) {
301
- return;
302
- }
261
+ // There were no matching padding rules for the prevNode, nextNode,
262
+ // paddingType combination... so we'll use PaddingType.Any which is always ok
263
+ return testType(PaddingType.Any);
264
+ };
303
265
 
304
- // Save this node as the current previous statement.
305
- const { prevNode } = scopeInfo;
266
+ /**
267
+ * Verify padding lines between the given node and the previous node.
268
+ */
269
+ const verifyNode = (node: Node, paddingContext: PaddingContext): void => {
270
+ const { scopeInfo } = paddingContext;
271
+
272
+ // NOTE: ESLint types use ESTree which provides a Node type, however
273
+ // ESTree.Node doesn't support the parent property which is added by
274
+ // ESLint during traversal. Our best bet is to ignore the property access
275
+ // here as it's the only place that it's checked.
276
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
277
+ if (!astUtils.isValidParent((node as any).parent.type)) {
278
+ return;
279
+ }
306
280
 
307
- // Verify.
308
- if (prevNode) {
309
- const type = getPaddingType(prevNode, node);
310
- const paddingLines = getPaddingLineSequences(prevNode, node);
281
+ if (scopeInfo.prevNode) {
282
+ testPadding(scopeInfo.prevNode, node, paddingContext);
283
+ }
311
284
 
312
- type.verify(context, prevNode, node, paddingLines);
313
- }
285
+ scopeInfo.prevNode = node;
286
+ };
314
287
 
315
- scopeInfo.prevNode = node;
316
- }
288
+ /**
289
+ * Creates an ESLint rule for a given set of padding Config objects.
290
+ *
291
+ * The algorithm is approximately this:
292
+ *
293
+ * For each 'scope' in the program
294
+ * - Enter the scope (store the parent scope and previous node)
295
+ * - For each statement in the scope
296
+ * - Check the current node and previous node against the Config objects
297
+ * - If the current node and previous node match a Config, check the padding.
298
+ * Otherwise, ignore it.
299
+ * - If the padding is missing (and required), report and fix
300
+ * - Store the current node as the previous
301
+ * - Repeat
302
+ * - Exit scope (return to parent scope and clear previous node)
303
+ *
304
+ * The items we're looking for with this rule are ExpressionStatement nodes
305
+ * where the first token is an Identifier with a name matching one of the Jest
306
+ * functions. It's not foolproof, of course, but it's probably good enough for
307
+ * almost all cases.
308
+ *
309
+ * The Config objects specify a padding type, a previous statement type, and a
310
+ * next statement type. Wildcard statement types and padding types are
311
+ * supported. The current node and previous node are checked against the
312
+ * statement types. If they match then the specified padding type is
313
+ * tested/enforced.
314
+ *
315
+ * See src/index.ts for examples of Config usage.
316
+ */
317
+ export const createRule = (
318
+ configs: Config[],
319
+ deprecated = false,
320
+ ): Rule.RuleModule => ({
321
+ meta: {
322
+ fixable: 'whitespace',
323
+ deprecated,
324
+ },
325
+ create(context: Rule.RuleContext) {
326
+ const paddingContext = {
327
+ ruleContext: context,
328
+ sourceCode: context.getSourceCode(),
329
+ scopeInfo: createScopeInfo(),
330
+ configs,
331
+ };
317
332
 
318
- /**
319
- * Verify padding lines between the given node and the previous node.
320
- * Then process to enter to new scope.
321
- */
322
- function verifyThenEnterScope(node: Node): void {
323
- verify(node);
324
- enterScope();
325
- }
333
+ const { scopeInfo } = paddingContext;
326
334
 
327
335
  return {
328
- Program: enterScope,
329
- BlockStatement: enterScope,
330
- SwitchStatement: enterScope,
331
- 'Program:exit': exitScope,
332
- 'BlockStatement:exit': exitScope,
333
- 'SwitchStatement:exit': exitScope,
334
- ':statement': verify,
335
- SwitchCase: verifyThenEnterScope,
336
- 'SwitchCase:exit': exitScope,
336
+ Program: scopeInfo.enter,
337
+ 'Program:exit': scopeInfo.enter,
338
+ BlockStatement: scopeInfo.enter,
339
+ 'BlockStatement:exit': scopeInfo.exit,
340
+ SwitchStatement: scopeInfo.enter,
341
+ 'SwitchStatement:exit': scopeInfo.exit,
342
+ ':statement': (node: Node) => verifyNode(node, paddingContext),
343
+ SwitchCase: (node: Node) => {
344
+ verifyNode(node, paddingContext);
345
+ scopeInfo.enter();
346
+ },
347
+ 'SwitchCase:exit': scopeInfo.exit,
337
348
  };
338
349
  },
339
- };
350
+ });
@@ -0,0 +1,41 @@
1
+ /**
2
+ * This file should be IGNORED by jest-formatting due to the `overrides.files`
3
+ * value in `recommended` and `strict` config. This file does not match the
4
+ * globs.
5
+ */
6
+
7
+ /* eslint-disable @typescript-eslint/no-empty-function */
8
+
9
+ beforeAll(() => {});
10
+ beforeEach(() => {});
11
+ afterAll(() => {});
12
+ afterEach(() => {});
13
+ describe('no padding on top', () => {
14
+ it('has no padding above', () => {
15
+ expect(true);
16
+ });
17
+ expect(true);
18
+ expect(1);
19
+ xit('has padding above', () => {
20
+ expect(true);
21
+ });
22
+ it('also has padding above but not below', () => {
23
+ expect(true);
24
+ });
25
+ });
26
+ xdescribe('padding above', () => {
27
+ it('has no padding above or below', () => {
28
+ expect(true);
29
+ });
30
+ it('has no padding above or below', () => {
31
+ expect(true);
32
+ });
33
+ });
34
+ describe('padding above', () => {
35
+ it('has no padding above or below', () => {
36
+ expect(true);
37
+ });
38
+ it('has no padding above or below', () => {
39
+ expect(true);
40
+ });
41
+ });
@@ -1,3 +1,5 @@
1
+ /* eslint-disable @typescript-eslint/no-empty-function */
2
+
1
3
  beforeAll(() => {});
2
4
 
3
5
  beforeEach(() => {});