eslint-node-test 0.0.1
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.
- package/configs/core-rule-replacements.js +9 -0
- package/configs/flat-config-base.js +9 -0
- package/index.d.ts +11 -0
- package/index.js +51 -0
- package/license +9 -0
- package/package.json +106 -0
- package/readme.md +143 -0
- package/rules/assertion-arguments.js +134 -0
- package/rules/ast/call-or-new-expression.js +100 -0
- package/rules/ast/function-types.js +7 -0
- package/rules/ast/index.js +17 -0
- package/rules/ast/is-expression-statement.js +7 -0
- package/rules/ast/is-function.js +5 -0
- package/rules/ast/is-loop.js +5 -0
- package/rules/ast/is-member-expression.js +98 -0
- package/rules/ast/is-method-call.js +62 -0
- package/rules/ast/literal.js +32 -0
- package/rules/ast/loop-types.js +9 -0
- package/rules/consistent-modifier-style.js +95 -0
- package/rules/consistent-test-context-name.js +75 -0
- package/rules/consistent-test-filename.js +70 -0
- package/rules/consistent-test-it.js +86 -0
- package/rules/fix/index.js +5 -0
- package/rules/fix/remove-argument.js +58 -0
- package/rules/fix/replace-member-expression-property.js +25 -0
- package/rules/hooks-order.js +132 -0
- package/rules/index.js +66 -0
- package/rules/max-assertions.js +87 -0
- package/rules/max-nested-describe.js +70 -0
- package/rules/no-assert-in-describe.js +51 -0
- package/rules/no-assert-in-hook.js +51 -0
- package/rules/no-assert-throws-async.js +114 -0
- package/rules/no-assert-throws-string.js +65 -0
- package/rules/no-async-describe.js +50 -0
- package/rules/no-async-fn-without-await.js +74 -0
- package/rules/no-callback-and-promise.js +56 -0
- package/rules/no-commented-tests.js +59 -0
- package/rules/no-conditional-assertion.js +101 -0
- package/rules/no-conditional-in-test.js +66 -0
- package/rules/no-conditional-tests.js +75 -0
- package/rules/no-conflicting-modifiers.js +73 -0
- package/rules/no-done-callback.js +58 -0
- package/rules/no-duplicate-hooks.js +75 -0
- package/rules/no-export.js +79 -0
- package/rules/no-identical-assertion-arguments.js +71 -0
- package/rules/no-identical-title.js +101 -0
- package/rules/no-incorrect-deep-equal.js +100 -0
- package/rules/no-incorrect-strict-equal.js +86 -0
- package/rules/no-loop-static-title.js +93 -0
- package/rules/no-misused-concurrency.js +85 -0
- package/rules/no-mock-timers-destructured-import.js +150 -0
- package/rules/no-nested-tests.js +71 -0
- package/rules/no-only-test.js +11 -0
- package/rules/no-skip-test.js +11 -0
- package/rules/no-skip-without-reason.js +88 -0
- package/rules/no-skip-without-return.js +127 -0
- package/rules/no-standalone-assert.js +51 -0
- package/rules/no-test-inside-hook.js +68 -0
- package/rules/no-test-return-statement.js +114 -0
- package/rules/no-todo-test.js +11 -0
- package/rules/no-unawaited-rejects.js +74 -0
- package/rules/no-unawaited-subtest.js +66 -0
- package/rules/no-unknown-test-options.js +77 -0
- package/rules/no-useless-assertion.js +47 -0
- package/rules/prefer-assert-match.js +245 -0
- package/rules/prefer-assert-throws.js +90 -0
- package/rules/prefer-async-await.js +203 -0
- package/rules/prefer-context-mock.js +59 -0
- package/rules/prefer-diagnostic.js +94 -0
- package/rules/prefer-equality-assertion.js +101 -0
- package/rules/prefer-hooks-on-top.js +73 -0
- package/rules/prefer-lowercase-title.js +119 -0
- package/rules/prefer-mock-method.js +115 -0
- package/rules/prefer-strict-assert.js +69 -0
- package/rules/prefer-test-context-assert.js +125 -0
- package/rules/prefer-todo.js +98 -0
- package/rules/require-assertion.js +92 -0
- package/rules/require-await-concurrent-subtests.js +119 -0
- package/rules/require-context-assert-with-plan.js +127 -0
- package/rules/require-hook.js +108 -0
- package/rules/require-throws-expectation.js +52 -0
- package/rules/require-top-level-describe.js +89 -0
- package/rules/rule/index.js +9 -0
- package/rules/rule/to-eslint-create.js +37 -0
- package/rules/rule/to-eslint-listener.js +40 -0
- package/rules/rule/to-eslint-problem.js +38 -0
- package/rules/rule/to-eslint-rule-fixer.js +49 -0
- package/rules/rule/to-eslint-rule.js +38 -0
- package/rules/rule/to-eslint-rules.js +10 -0
- package/rules/rule/unicorn-context.js +36 -0
- package/rules/rule/unicorn-listeners.js +65 -0
- package/rules/rule/utilities.js +26 -0
- package/rules/shared/test-modifier-rule.js +92 -0
- package/rules/test-title-format.js +86 -0
- package/rules/test-title.js +139 -0
- package/rules/utils/contains-suspension-point.js +35 -0
- package/rules/utils/escape-string.js +24 -0
- package/rules/utils/get-comments.js +15 -0
- package/rules/utils/get-documentation-url.js +9 -0
- package/rules/utils/get-enclosing-function.js +18 -0
- package/rules/utils/index.js +16 -0
- package/rules/utils/is-conditional-branch.js +37 -0
- package/rules/utils/is-promise-type.js +28 -0
- package/rules/utils/is-same-reference.js +179 -0
- package/rules/utils/is-value-not-usable.js +5 -0
- package/rules/utils/node-test.js +713 -0
- package/rules/utils/parentheses/get-parent-syntax-opening-parenthesis.js +80 -0
- package/rules/utils/parentheses/iterate-surrounding-parentheses.js +82 -0
- package/rules/utils/parentheses/parentheses.js +69 -0
- package/rules/utils/types.js +5 -0
- package/rules/utils/unwrap-typescript-expression.js +16 -0
- package/rules/valid-describe-callback.js +63 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {isOpeningParenToken as isOpeningParenthesisToken} from '@eslint-community/eslint-utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
@import {TSESTree as ESTree} from '@typescript-eslint/types';
|
|
5
|
+
@import * as ESLint from 'eslint';
|
|
6
|
+
@import {
|
|
7
|
+
OpeningParenToken as OpeningParenthesisToken,
|
|
8
|
+
} from '@eslint-community/eslint-utils';
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
Get the opening parenthesis of the parent node syntax if it exists.
|
|
13
|
+
E.g., `if (a) {}` then the `(`.
|
|
14
|
+
@param {ESTree.Node} node The AST node to check.
|
|
15
|
+
@param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
|
|
16
|
+
@returns {OpeningParenthesisToken | void} The left parenthesis of the parent node syntax
|
|
17
|
+
*/
|
|
18
|
+
function getParentSyntaxOpeningParenthesis(node, context) {
|
|
19
|
+
const {parent} = node;
|
|
20
|
+
|
|
21
|
+
switch (parent.type) {
|
|
22
|
+
case 'CallExpression':
|
|
23
|
+
case 'NewExpression': {
|
|
24
|
+
if (parent.arguments.length === 1 && parent.arguments[0] === node) {
|
|
25
|
+
return context.sourceCode.getTokenAfter(
|
|
26
|
+
parent.typeArguments ?? parent.callee,
|
|
27
|
+
isOpeningParenthesisToken,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
case 'DoWhileStatement': {
|
|
35
|
+
if (parent.test === node) {
|
|
36
|
+
return context.sourceCode.getTokenAfter(
|
|
37
|
+
parent.body,
|
|
38
|
+
isOpeningParenthesisToken,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
case 'IfStatement':
|
|
46
|
+
case 'WhileStatement': {
|
|
47
|
+
if (parent.test === node) {
|
|
48
|
+
return context.sourceCode.getFirstToken(parent, 1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
case 'ImportExpression': {
|
|
55
|
+
if (parent.source === node) {
|
|
56
|
+
return context.sourceCode.getFirstToken(parent, 1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
case 'SwitchStatement': {
|
|
63
|
+
if (parent.discriminant === node) {
|
|
64
|
+
return context.sourceCode.getFirstToken(parent, 1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
case 'WithStatement': {
|
|
71
|
+
if (parent.object === node) {
|
|
72
|
+
return context.sourceCode.getFirstToken(parent, 1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// No default
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default getParentSyntaxOpeningParenthesis;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Based on https://github.com/eslint-community/eslint-utils/blob/1da21d4440028679f3c8d5841b85f9d97ca7f0f7/src/is-parenthesized.mjs#L1
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
isOpeningParenToken as isOpeningParenthesisToken,
|
|
5
|
+
isClosingParenToken as isClosingParenthesisToken,
|
|
6
|
+
} from '@eslint-community/eslint-utils';
|
|
7
|
+
import getParentSyntaxOpeningParenthesis from './get-parent-syntax-opening-parenthesis.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
@import {TSESTree as ESTree} from '@typescript-eslint/types';
|
|
11
|
+
@import * as ESLint from 'eslint';
|
|
12
|
+
@import {
|
|
13
|
+
OpeningParenToken as OpeningParenthesisToken,
|
|
14
|
+
ClosingParenToken as ClosingParenthesisToken,
|
|
15
|
+
} from '@eslint-community/eslint-utils';
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
@typedef {[OpeningParenthesisToken, ClosingParenthesisToken]} ParenthesisTokenPair
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
Get surrounding parenthesis of the tokens or nodes.
|
|
24
|
+
|
|
25
|
+
@param {[ESTree.Node | OpeningParenthesisToken, ESTree.Node | ClosingParenthesisToken]} param0
|
|
26
|
+
@param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
|
|
27
|
+
@returns [ParenthesisTokenPair | void]
|
|
28
|
+
*/
|
|
29
|
+
function getSurroundingParentheses([head, tail], context) {
|
|
30
|
+
const tokenBefore = context.sourceCode.getTokenBefore(head);
|
|
31
|
+
|
|
32
|
+
if (!tokenBefore || !isOpeningParenthesisToken(tokenBefore)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const tokenAfter = context.sourceCode.getTokenAfter(tail);
|
|
37
|
+
|
|
38
|
+
if (!tokenAfter || !isClosingParenthesisToken(tokenAfter)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return [tokenBefore, tokenAfter];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const SYNTAX_OPENING_PARENTHESIS_INITIAL_VALUE = Symbol('SYNTAX_OPENING_PARENTHESIS_INITIAL_VALUE');
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
Iterate surrounding parenthesis of the node.
|
|
49
|
+
|
|
50
|
+
@param {ESTree.Node} node
|
|
51
|
+
@param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
|
|
52
|
+
@returns {IterableIterator<ParenthesisTokenPair>}
|
|
53
|
+
*/
|
|
54
|
+
function * iterateSurroundingParentheses(node, context) {
|
|
55
|
+
if (
|
|
56
|
+
!node
|
|
57
|
+
// `Program` can't be parenthesized
|
|
58
|
+
|| !node.parent
|
|
59
|
+
// `CatchClause.param` can't be parenthesized, example `try {} catch (error) {}`
|
|
60
|
+
|| (node.parent.type === 'CatchClause' && node.parent.param === node)
|
|
61
|
+
) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let syntaxOpeningParenthesis = SYNTAX_OPENING_PARENTHESIS_INITIAL_VALUE;
|
|
66
|
+
let parentheses = [node, node];
|
|
67
|
+
while ((parentheses = getSurroundingParentheses(parentheses, context))) {
|
|
68
|
+
const [openingParenthesisToken] = parentheses;
|
|
69
|
+
|
|
70
|
+
if (syntaxOpeningParenthesis === SYNTAX_OPENING_PARENTHESIS_INITIAL_VALUE) {
|
|
71
|
+
syntaxOpeningParenthesis = getParentSyntaxOpeningParenthesis(node, context);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (openingParenthesisToken === syntaxOpeningParenthesis) {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
yield parentheses;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default iterateSurroundingParentheses;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import iterateSurroundingParentheses from './iterate-surrounding-parentheses.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
@import {TSESTree as ESTree} from '@typescript-eslint/types';
|
|
5
|
+
@import * as ESLint from 'eslint';
|
|
6
|
+
@import {
|
|
7
|
+
OpeningParenToken as OpeningParenthesisToken,
|
|
8
|
+
ClosingParenToken as ClosingParenthesisToken,
|
|
9
|
+
} from '@eslint-community/eslint-utils';
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** @typedef {WeakMap<ESTree.Node, (OpeningParenthesisToken | ClosingParenthesisToken)[]>} */
|
|
13
|
+
const parenthesesCache = new WeakMap();
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
Get surrounding parenthesis of the node.
|
|
17
|
+
|
|
18
|
+
@param {ESTree.Node} node
|
|
19
|
+
@param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
|
|
20
|
+
@returns [(OpeningParenthesisToken | ClosingParenthesisToken)[]]
|
|
21
|
+
*/
|
|
22
|
+
export function getParentheses(node, context) {
|
|
23
|
+
if (!node || !parenthesesCache.has(node)) {
|
|
24
|
+
const parenthesis = [];
|
|
25
|
+
for (const [openingParenthesisToken, closingParenthesisToken] of iterateSurroundingParentheses(node, context)) {
|
|
26
|
+
parenthesis.unshift(openingParenthesisToken);
|
|
27
|
+
parenthesis.push(closingParenthesisToken);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
parenthesesCache.set(node, parenthesis);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return parenthesesCache.get(node);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/*
|
|
37
|
+
Get the parenthesized range of the node.
|
|
38
|
+
|
|
39
|
+
@param {ESTree.Node} node - The node to be checked.
|
|
40
|
+
@param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
|
|
41
|
+
@returns {number[]}
|
|
42
|
+
*/
|
|
43
|
+
export function getParenthesizedRange(node, context) {
|
|
44
|
+
const parentheses = getParentheses(node, context);
|
|
45
|
+
const [start] = context.sourceCode.getRange(parentheses[0] ?? node);
|
|
46
|
+
const [, end] = context.sourceCode.getRange(parentheses.at(-1) ?? node);
|
|
47
|
+
return [start, end];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
Check whether a given node is parenthesized or not.
|
|
52
|
+
|
|
53
|
+
@param {ESTree.Node} node The AST node to check.
|
|
54
|
+
@param {ESLint.Rule.RuleContext} context - The ESLint rule context object.
|
|
55
|
+
@returns {boolean} `true` if the node is parenthesized.
|
|
56
|
+
*/
|
|
57
|
+
export function isParenthesized(node, context) {
|
|
58
|
+
if (parenthesesCache.has(node)) {
|
|
59
|
+
return parenthesesCache.get(node).length > 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const isNotParenthesized = iterateSurroundingParentheses(node, context).next().done;
|
|
63
|
+
|
|
64
|
+
if (isNotParenthesized) {
|
|
65
|
+
parenthesesCache.set(node, []);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return !isNotParenthesized;
|
|
69
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const typeScriptExpressionWrapperTypes = new Set([
|
|
2
|
+
'TSAsExpression',
|
|
3
|
+
'TSSatisfiesExpression',
|
|
4
|
+
'TSNonNullExpression',
|
|
5
|
+
'TSTypeAssertion',
|
|
6
|
+
]);
|
|
7
|
+
|
|
8
|
+
export const isTypeScriptExpressionWrapper = node => typeScriptExpressionWrapperTypes.has(node?.type);
|
|
9
|
+
|
|
10
|
+
export default function unwrapTypeScriptExpression(node) {
|
|
11
|
+
while (isTypeScriptExpressionWrapper(node)) {
|
|
12
|
+
node = node.expression;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return node;
|
|
16
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {resolveImports, parseTestCall, getTestCallback} from './utils/node-test.js';
|
|
2
|
+
|
|
3
|
+
const MESSAGE_ID_PARAMETER = 'valid-describe-callback/parameter';
|
|
4
|
+
const MESSAGE_ID_RETURN = 'valid-describe-callback/return';
|
|
5
|
+
|
|
6
|
+
const messages = {
|
|
7
|
+
[MESSAGE_ID_PARAMETER]: 'The `{{name}}` callback is called with no arguments. The test context is only passed to `test`/`it` callbacks.',
|
|
8
|
+
[MESSAGE_ID_RETURN]: 'The `{{name}}` callback should not return a value, `node:test` ignores it. Use a block body.',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/** @param {import('eslint').Rule.RuleContext} context */
|
|
12
|
+
const create = context => {
|
|
13
|
+
const imports = resolveImports(context);
|
|
14
|
+
if (!imports.isTestFile) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
context.on('CallExpression', function * (node) {
|
|
19
|
+
const parsed = parseTestCall(node, imports);
|
|
20
|
+
if (parsed?.kind !== 'suite') {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const callback = getTestCallback(node);
|
|
25
|
+
if (!callback) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (callback.params.length > 0) {
|
|
30
|
+
yield {
|
|
31
|
+
node: callback.params[0],
|
|
32
|
+
messageId: MESSAGE_ID_PARAMETER,
|
|
33
|
+
data: {name: parsed.name},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// An arrow with an expression body implicitly returns a value.
|
|
38
|
+
if (callback.type === 'ArrowFunctionExpression' && callback.body.type !== 'BlockStatement') {
|
|
39
|
+
yield {
|
|
40
|
+
node: callback.body,
|
|
41
|
+
messageId: MESSAGE_ID_RETURN,
|
|
42
|
+
data: {name: parsed.name},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
49
|
+
const config = {
|
|
50
|
+
create,
|
|
51
|
+
meta: {
|
|
52
|
+
type: 'problem',
|
|
53
|
+
docs: {
|
|
54
|
+
description: 'Enforce valid `describe` callbacks.',
|
|
55
|
+
recommended: 'unopinionated',
|
|
56
|
+
},
|
|
57
|
+
schema: [],
|
|
58
|
+
messages,
|
|
59
|
+
languages: ['js/js'],
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default config;
|