@vitest/eslint-plugin 0.0.0-0 → 1.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,4625 @@
1
+ 'use strict';
2
+
3
+ const utils = require('@typescript-eslint/utils');
4
+ const path = require('node:path');
5
+ const ts = require('typescript');
6
+ const node_url = require('node:url');
7
+ const node_module = require('node:module');
8
+
9
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
10
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
11
+
12
+ function _interopNamespaceCompat(e) {
13
+ if (e && typeof e === 'object' && 'default' in e) return e;
14
+ const n = Object.create(null);
15
+ if (e) {
16
+ for (const k in e) {
17
+ n[k] = e[k];
18
+ }
19
+ }
20
+ n.default = e;
21
+ return n;
22
+ }
23
+
24
+ const path__namespace = /*#__PURE__*/_interopNamespaceCompat(path);
25
+ const ts__default = /*#__PURE__*/_interopDefaultCompat(ts);
26
+
27
+ const version = "0.6.4";
28
+
29
+ function createEslintRule(rule) {
30
+ const createRule = utils.ESLintUtils.RuleCreator(
31
+ (ruleName) => `https://github.com/veritem/eslint-plugin-vitest/blob/main/docs/rules/${ruleName}.md`
32
+ );
33
+ return createRule(rule);
34
+ }
35
+ const joinNames = (a, b) => a && b ? `${a}.${b}` : null;
36
+ const isFunction = (node) => node.type === utils.AST_NODE_TYPES.FunctionExpression || node.type === utils.AST_NODE_TYPES.ArrowFunctionExpression;
37
+ function getNodeName(node) {
38
+ if (isSupportedAccessor(node))
39
+ return getAccessorValue(node);
40
+ switch (node.type) {
41
+ case utils.AST_NODE_TYPES.TaggedTemplateExpression:
42
+ return getNodeName(node.tag);
43
+ case utils.AST_NODE_TYPES.MemberExpression:
44
+ return joinNames(getNodeName(node.object), getNodeName(node.property));
45
+ case utils.AST_NODE_TYPES.NewExpression:
46
+ case utils.AST_NODE_TYPES.CallExpression:
47
+ return getNodeName(node.callee);
48
+ }
49
+ return null;
50
+ }
51
+ const isSupportedAccessor = (node, value) => {
52
+ return isIdentifier(node, value) || isStringNode(node, value);
53
+ };
54
+ const isIdentifier = (node, name) => {
55
+ return node.type === utils.AST_NODE_TYPES.Identifier && (name === void 0 || node.name === name);
56
+ };
57
+ const isTemplateLiteral = (node, value) => {
58
+ return node.type === utils.AST_NODE_TYPES.TemplateLiteral && node.quasis.length === 1 && (value === void 0 || node.quasis[0].value.raw === value);
59
+ };
60
+ const isStringLiteral = (node, value) => node.type === utils.AST_NODE_TYPES.Literal && typeof node.value === "string" && (value === void 0 || node.value === value);
61
+ const isStringNode = (node, specifics) => isStringLiteral(node, specifics) || isTemplateLiteral(node, specifics);
62
+ const getAccessorValue = (accessor) => accessor.type === utils.AST_NODE_TYPES.Identifier ? accessor.name : getStringValue(accessor);
63
+ const getStringValue = (node) => node?.type === utils.AST_NODE_TYPES.TemplateLiteral ? node.quasis[0].value.raw : node?.value;
64
+ const replaceAccessorFixer = (fixer, node, text) => {
65
+ return fixer.replaceText(
66
+ node,
67
+ node.type === utils.AST_NODE_TYPES.Identifier ? text : `'${text}'`
68
+ );
69
+ };
70
+ const removeExtraArgumentsFixer = (fixer, context, func, from) => {
71
+ const firstArg = func.arguments[from];
72
+ const lastArg = func.arguments[func.arguments.length - 1];
73
+ const { sourceCode } = context;
74
+ let tokenAfterLastParam = sourceCode.getTokenAfter(lastArg);
75
+ if (tokenAfterLastParam.value === ",")
76
+ tokenAfterLastParam = sourceCode.getTokenAfter(tokenAfterLastParam);
77
+ return fixer.removeRange([firstArg.range[0], tokenAfterLastParam.range[0]]);
78
+ };
79
+ const isParsedInstanceOfMatcherCall = (expectFnCall, classArg) => {
80
+ return getAccessorValue(expectFnCall.matcher) === "toBeInstanceOf" && expectFnCall.args.length === 1 && isSupportedAccessor(expectFnCall.args[0], classArg);
81
+ };
82
+
83
+ var DescribeAlias = /* @__PURE__ */ ((DescribeAlias2) => {
84
+ DescribeAlias2["describe"] = "describe";
85
+ DescribeAlias2["fdescribe"] = "fdescribe";
86
+ DescribeAlias2["xdescribe"] = "xdescribe";
87
+ return DescribeAlias2;
88
+ })(DescribeAlias || {});
89
+ var TestCaseName = /* @__PURE__ */ ((TestCaseName2) => {
90
+ TestCaseName2["fit"] = "fit";
91
+ TestCaseName2["it"] = "it";
92
+ TestCaseName2["test"] = "test";
93
+ TestCaseName2["xit"] = "xit";
94
+ TestCaseName2["xtest"] = "xtest";
95
+ TestCaseName2["bench"] = "bench";
96
+ return TestCaseName2;
97
+ })(TestCaseName || {});
98
+ var HookName = /* @__PURE__ */ ((HookName2) => {
99
+ HookName2["beforeAll"] = "beforeAll";
100
+ HookName2["beforeEach"] = "beforeEach";
101
+ HookName2["afterAll"] = "afterAll";
102
+ HookName2["afterEach"] = "afterEach";
103
+ return HookName2;
104
+ })(HookName || {});
105
+ var ModifierName = /* @__PURE__ */ ((ModifierName2) => {
106
+ ModifierName2["not"] = "not";
107
+ ModifierName2["rejects"] = "rejects";
108
+ ModifierName2["resolves"] = "resolves";
109
+ return ModifierName2;
110
+ })(ModifierName || {});
111
+ var EqualityMatcher = /* @__PURE__ */ ((EqualityMatcher2) => {
112
+ EqualityMatcher2["toBe"] = "toBe";
113
+ EqualityMatcher2["toEqual"] = "toEqual";
114
+ EqualityMatcher2["toStrictEqual"] = "toStrictEqual";
115
+ return EqualityMatcher2;
116
+ })(EqualityMatcher || {});
117
+
118
+ const ValidVitestFnCallChains = /* @__PURE__ */ new Set(["beforeEach", "beforeAll", "afterEach", "afterAll", "it", "it.skip", "it.only", "it.concurrent", "it.sequential", "it.todo", "it.fails", "it.extend", "it.skipIf", "it.runIf", "it.each", "it.skip.only", "it.skip.concurrent", "it.skip.sequential", "it.skip.todo", "it.skip.fails", "it.only.skip", "it.only.concurrent", "it.only.sequential", "it.only.todo", "it.only.fails", "it.concurrent.skip", "it.concurrent.only", "it.concurrent.sequential", "it.concurrent.todo", "it.concurrent.fails", "it.sequential.skip", "it.sequential.only", "it.sequential.concurrent", "it.sequential.todo", "it.sequential.fails", "it.todo.skip", "it.todo.only", "it.todo.concurrent", "it.todo.sequential", "it.todo.fails", "it.fails.skip", "it.fails.only", "it.fails.concurrent", "it.fails.sequential", "it.fails.todo", "it.extend.skip", "it.extend.only", "it.extend.concurrent", "it.extend.sequential", "it.extend.todo", "it.extend.fails", "it.skipIf.skip", "it.skipIf.only", "it.skipIf.concurrent", "it.skipIf.sequential", "it.skipIf.todo", "it.skipIf.fails", "it.runIf.skip", "it.runIf.only", "it.runIf.concurrent", "it.runIf.sequential", "it.runIf.todo", "it.runIf.fails", "it.skip.each", "it.only.each", "it.concurrent.each", "it.sequential.each", "it.todo.each", "it.fails.each", "it.extend.skipIf", "it.extend.runIf", "it.extend.each", "it.skipIf.each", "it.runIf.each", "it.skip.only.concurrent", "it.skip.only.sequential", "it.skip.only.todo", "it.skip.only.fails", "it.skip.concurrent.only", "it.skip.concurrent.sequential", "it.skip.concurrent.todo", "it.skip.concurrent.fails", "it.skip.sequential.only", "it.skip.sequential.concurrent", "it.skip.sequential.todo", "it.skip.sequential.fails", "it.skip.todo.only", "it.skip.todo.concurrent", "it.skip.todo.sequential", "it.skip.todo.fails", "it.skip.fails.only", "it.skip.fails.concurrent", "it.skip.fails.sequential", "it.skip.fails.todo", "it.only.skip.concurrent", "it.only.skip.sequential", "it.only.skip.todo", "it.only.skip.fails", "it.only.concurrent.skip", "it.only.concurrent.sequential", "it.only.concurrent.todo", "it.only.concurrent.fails", "it.only.sequential.skip", "it.only.sequential.concurrent", "it.only.sequential.todo", "it.only.sequential.fails", "it.only.todo.skip", "it.only.todo.concurrent", "it.only.todo.sequential", "it.only.todo.fails", "it.only.fails.skip", "it.only.fails.concurrent", "it.only.fails.sequential", "it.only.fails.todo", "it.concurrent.skip.only", "it.concurrent.skip.sequential", "it.concurrent.skip.todo", "it.concurrent.skip.fails", "it.concurrent.only.skip", "it.concurrent.only.sequential", "it.concurrent.only.todo", "it.concurrent.only.fails", "it.concurrent.sequential.skip", "it.concurrent.sequential.only", "it.concurrent.sequential.todo", "it.concurrent.sequential.fails", "it.concurrent.todo.skip", "it.concurrent.todo.only", "it.concurrent.todo.sequential", "it.concurrent.todo.fails", "it.concurrent.fails.skip", "it.concurrent.fails.only", "it.concurrent.fails.sequential", "it.concurrent.fails.todo", "it.sequential.skip.only", "it.sequential.skip.concurrent", "it.sequential.skip.todo", "it.sequential.skip.fails", "it.sequential.only.skip", "it.sequential.only.concurrent", "it.sequential.only.todo", "it.sequential.only.fails", "it.sequential.concurrent.skip", "it.sequential.concurrent.only", "it.sequential.concurrent.todo", "it.sequential.concurrent.fails", "it.sequential.todo.skip", "it.sequential.todo.only", "it.sequential.todo.concurrent", "it.sequential.todo.fails", "it.sequential.fails.skip", "it.sequential.fails.only", "it.sequential.fails.concurrent", "it.sequential.fails.todo", "it.todo.skip.only", "it.todo.skip.concurrent", "it.todo.skip.sequential", "it.todo.skip.fails", "it.todo.only.skip", "it.todo.only.concurrent", "it.todo.only.sequential", "it.todo.only.fails", "it.todo.concurrent.skip", "it.todo.concurrent.only", "it.todo.concurrent.sequential", "it.todo.concurrent.fails", "it.todo.sequential.skip", "it.todo.sequential.only", "it.todo.sequential.concurrent", "it.todo.sequential.fails", "it.todo.fails.skip", "it.todo.fails.only", "it.todo.fails.concurrent", "it.todo.fails.sequential", "it.fails.skip.only", "it.fails.skip.concurrent", "it.fails.skip.sequential", "it.fails.skip.todo", "it.fails.only.skip", "it.fails.only.concurrent", "it.fails.only.sequential", "it.fails.only.todo", "it.fails.concurrent.skip", "it.fails.concurrent.only", "it.fails.concurrent.sequential", "it.fails.concurrent.todo", "it.fails.sequential.skip", "it.fails.sequential.only", "it.fails.sequential.concurrent", "it.fails.sequential.todo", "it.fails.todo.skip", "it.fails.todo.only", "it.fails.todo.concurrent", "it.fails.todo.sequential", "it.extend.skip.only", "it.extend.skip.concurrent", "it.extend.skip.sequential", "it.extend.skip.todo", "it.extend.skip.fails", "it.extend.only.skip", "it.extend.only.concurrent", "it.extend.only.sequential", "it.extend.only.todo", "it.extend.only.fails", "it.extend.concurrent.skip", "it.extend.concurrent.only", "it.extend.concurrent.sequential", "it.extend.concurrent.todo", "it.extend.concurrent.fails", "it.extend.sequential.skip", "it.extend.sequential.only", "it.extend.sequential.concurrent", "it.extend.sequential.todo", "it.extend.sequential.fails", "it.extend.todo.skip", "it.extend.todo.only", "it.extend.todo.concurrent", "it.extend.todo.sequential", "it.extend.todo.fails", "it.extend.fails.skip", "it.extend.fails.only", "it.extend.fails.concurrent", "it.extend.fails.sequential", "it.extend.fails.todo", "it.skipIf.skip.only", "it.skipIf.skip.concurrent", "it.skipIf.skip.sequential", "it.skipIf.skip.todo", "it.skipIf.skip.fails", "it.skipIf.only.skip", "it.skipIf.only.concurrent", "it.skipIf.only.sequential", "it.skipIf.only.todo", "it.skipIf.only.fails", "it.skipIf.concurrent.skip", "it.skipIf.concurrent.only", "it.skipIf.concurrent.sequential", "it.skipIf.concurrent.todo", "it.skipIf.concurrent.fails", "it.skipIf.sequential.skip", "it.skipIf.sequential.only", "it.skipIf.sequential.concurrent", "it.skipIf.sequential.todo", "it.skipIf.sequential.fails", "it.skipIf.todo.skip", "it.skipIf.todo.only", "it.skipIf.todo.concurrent", "it.skipIf.todo.sequential", "it.skipIf.todo.fails", "it.skipIf.fails.skip", "it.skipIf.fails.only", "it.skipIf.fails.concurrent", "it.skipIf.fails.sequential", "it.skipIf.fails.todo", "it.runIf.skip.only", "it.runIf.skip.concurrent", "it.runIf.skip.sequential", "it.runIf.skip.todo", "it.runIf.skip.fails", "it.runIf.only.skip", "it.runIf.only.concurrent", "it.runIf.only.sequential", "it.runIf.only.todo", "it.runIf.only.fails", "it.runIf.concurrent.skip", "it.runIf.concurrent.only", "it.runIf.concurrent.sequential", "it.runIf.concurrent.todo", "it.runIf.concurrent.fails", "it.runIf.sequential.skip", "it.runIf.sequential.only", "it.runIf.sequential.concurrent", "it.runIf.sequential.todo", "it.runIf.sequential.fails", "it.runIf.todo.skip", "it.runIf.todo.only", "it.runIf.todo.concurrent", "it.runIf.todo.sequential", "it.runIf.todo.fails", "it.runIf.fails.skip", "it.runIf.fails.only", "it.runIf.fails.concurrent", "it.runIf.fails.sequential", "it.runIf.fails.todo", "it.skip.only.each", "it.skip.concurrent.each", "it.skip.sequential.each", "it.skip.todo.each", "it.skip.fails.each", "it.only.skip.each", "it.only.concurrent.each", "it.only.sequential.each", "it.only.todo.each", "it.only.fails.each", "it.concurrent.skip.each", "it.concurrent.only.each", "it.concurrent.sequential.each", "it.concurrent.todo.each", "it.concurrent.fails.each", "it.sequential.skip.each", "it.sequential.only.each", "it.sequential.concurrent.each", "it.sequential.todo.each", "it.sequential.fails.each", "it.todo.skip.each", "it.todo.only.each", "it.todo.concurrent.each", "it.todo.sequential.each", "it.todo.fails.each", "it.fails.skip.each", "it.fails.only.each", "it.fails.concurrent.each", "it.fails.sequential.each", "it.fails.todo.each", "it.extend.skipIf.skip", "it.extend.skipIf.only", "it.extend.skipIf.concurrent", "it.extend.skipIf.sequential", "it.extend.skipIf.todo", "it.extend.skipIf.fails", "it.extend.runIf.skip", "it.extend.runIf.only", "it.extend.runIf.concurrent", "it.extend.runIf.sequential", "it.extend.runIf.todo", "it.extend.runIf.fails", "it.extend.skip.each", "it.extend.only.each", "it.extend.concurrent.each", "it.extend.sequential.each", "it.extend.todo.each", "it.extend.fails.each", "it.skipIf.skip.each", "it.skipIf.only.each", "it.skipIf.concurrent.each", "it.skipIf.sequential.each", "it.skipIf.todo.each", "it.skipIf.fails.each", "it.runIf.skip.each", "it.runIf.only.each", "it.runIf.concurrent.each", "it.runIf.sequential.each", "it.runIf.todo.each", "it.runIf.fails.each", "it.extend.skipIf.each", "it.extend.runIf.each", "test", "test.skip", "test.only", "test.concurrent", "test.sequential", "test.todo", "test.fails", "test.extend", "test.skipIf", "test.runIf", "test.each", "test.skip.only", "test.skip.concurrent", "test.skip.sequential", "test.skip.todo", "test.skip.fails", "test.only.skip", "test.only.concurrent", "test.only.sequential", "test.only.todo", "test.only.fails", "test.concurrent.skip", "test.concurrent.only", "test.concurrent.sequential", "test.concurrent.todo", "test.concurrent.fails", "test.sequential.skip", "test.sequential.only", "test.sequential.concurrent", "test.sequential.todo", "test.sequential.fails", "test.todo.skip", "test.todo.only", "test.todo.concurrent", "test.todo.sequential", "test.todo.fails", "test.fails.skip", "test.fails.only", "test.fails.concurrent", "test.fails.sequential", "test.fails.todo", "test.extend.skip", "test.extend.only", "test.extend.concurrent", "test.extend.sequential", "test.extend.todo", "test.extend.fails", "test.skipIf.skip", "test.skipIf.only", "test.skipIf.concurrent", "test.skipIf.sequential", "test.skipIf.todo", "test.skipIf.fails", "test.runIf.skip", "test.runIf.only", "test.runIf.concurrent", "test.runIf.sequential", "test.runIf.todo", "test.runIf.fails", "test.skip.each", "test.only.each", "test.concurrent.each", "test.sequential.each", "test.todo.each", "test.fails.each", "test.extend.skipIf", "test.extend.runIf", "test.extend.each", "test.skipIf.each", "test.runIf.each", "test.skip.only.concurrent", "test.skip.only.sequential", "test.skip.only.todo", "test.skip.only.fails", "test.skip.concurrent.only", "test.skip.concurrent.sequential", "test.skip.concurrent.todo", "test.skip.concurrent.fails", "test.skip.sequential.only", "test.skip.sequential.concurrent", "test.skip.sequential.todo", "test.skip.sequential.fails", "test.skip.todo.only", "test.skip.todo.concurrent", "test.skip.todo.sequential", "test.skip.todo.fails", "test.skip.fails.only", "test.skip.fails.concurrent", "test.skip.fails.sequential", "test.skip.fails.todo", "test.only.skip.concurrent", "test.only.skip.sequential", "test.only.skip.todo", "test.only.skip.fails", "test.only.concurrent.skip", "test.only.concurrent.sequential", "test.only.concurrent.todo", "test.only.concurrent.fails", "test.only.sequential.skip", "test.only.sequential.concurrent", "test.only.sequential.todo", "test.only.sequential.fails", "test.only.todo.skip", "test.only.todo.concurrent", "test.only.todo.sequential", "test.only.todo.fails", "test.only.fails.skip", "test.only.fails.concurrent", "test.only.fails.sequential", "test.only.fails.todo", "test.concurrent.skip.only", "test.concurrent.skip.sequential", "test.concurrent.skip.todo", "test.concurrent.skip.fails", "test.concurrent.only.skip", "test.concurrent.only.sequential", "test.concurrent.only.todo", "test.concurrent.only.fails", "test.concurrent.sequential.skip", "test.concurrent.sequential.only", "test.concurrent.sequential.todo", "test.concurrent.sequential.fails", "test.concurrent.todo.skip", "test.concurrent.todo.only", "test.concurrent.todo.sequential", "test.concurrent.todo.fails", "test.concurrent.fails.skip", "test.concurrent.fails.only", "test.concurrent.fails.sequential", "test.concurrent.fails.todo", "test.sequential.skip.only", "test.sequential.skip.concurrent", "test.sequential.skip.todo", "test.sequential.skip.fails", "test.sequential.only.skip", "test.sequential.only.concurrent", "test.sequential.only.todo", "test.sequential.only.fails", "test.sequential.concurrent.skip", "test.sequential.concurrent.only", "test.sequential.concurrent.todo", "test.sequential.concurrent.fails", "test.sequential.todo.skip", "test.sequential.todo.only", "test.sequential.todo.concurrent", "test.sequential.todo.fails", "test.sequential.fails.skip", "test.sequential.fails.only", "test.sequential.fails.concurrent", "test.sequential.fails.todo", "test.todo.skip.only", "test.todo.skip.concurrent", "test.todo.skip.sequential", "test.todo.skip.fails", "test.todo.only.skip", "test.todo.only.concurrent", "test.todo.only.sequential", "test.todo.only.fails", "test.todo.concurrent.skip", "test.todo.concurrent.only", "test.todo.concurrent.sequential", "test.todo.concurrent.fails", "test.todo.sequential.skip", "test.todo.sequential.only", "test.todo.sequential.concurrent", "test.todo.sequential.fails", "test.todo.fails.skip", "test.todo.fails.only", "test.todo.fails.concurrent", "test.todo.fails.sequential", "test.fails.skip.only", "test.fails.skip.concurrent", "test.fails.skip.sequential", "test.fails.skip.todo", "test.fails.only.skip", "test.fails.only.concurrent", "test.fails.only.sequential", "test.fails.only.todo", "test.fails.concurrent.skip", "test.fails.concurrent.only", "test.fails.concurrent.sequential", "test.fails.concurrent.todo", "test.fails.sequential.skip", "test.fails.sequential.only", "test.fails.sequential.concurrent", "test.fails.sequential.todo", "test.fails.todo.skip", "test.fails.todo.only", "test.fails.todo.concurrent", "test.fails.todo.sequential", "test.extend.skip.only", "test.extend.skip.concurrent", "test.extend.skip.sequential", "test.extend.skip.todo", "test.extend.skip.fails", "test.extend.only.skip", "test.extend.only.concurrent", "test.extend.only.sequential", "test.extend.only.todo", "test.extend.only.fails", "test.extend.concurrent.skip", "test.extend.concurrent.only", "test.extend.concurrent.sequential", "test.extend.concurrent.todo", "test.extend.concurrent.fails", "test.extend.sequential.skip", "test.extend.sequential.only", "test.extend.sequential.concurrent", "test.extend.sequential.todo", "test.extend.sequential.fails", "test.extend.todo.skip", "test.extend.todo.only", "test.extend.todo.concurrent", "test.extend.todo.sequential", "test.extend.todo.fails", "test.extend.fails.skip", "test.extend.fails.only", "test.extend.fails.concurrent", "test.extend.fails.sequential", "test.extend.fails.todo", "test.skipIf.skip.only", "test.skipIf.skip.concurrent", "test.skipIf.skip.sequential", "test.skipIf.skip.todo", "test.skipIf.skip.fails", "test.skipIf.only.skip", "test.skipIf.only.concurrent", "test.skipIf.only.sequential", "test.skipIf.only.todo", "test.skipIf.only.fails", "test.skipIf.concurrent.skip", "test.skipIf.concurrent.only", "test.skipIf.concurrent.sequential", "test.skipIf.concurrent.todo", "test.skipIf.concurrent.fails", "test.skipIf.sequential.skip", "test.skipIf.sequential.only", "test.skipIf.sequential.concurrent", "test.skipIf.sequential.todo", "test.skipIf.sequential.fails", "test.skipIf.todo.skip", "test.skipIf.todo.only", "test.skipIf.todo.concurrent", "test.skipIf.todo.sequential", "test.skipIf.todo.fails", "test.skipIf.fails.skip", "test.skipIf.fails.only", "test.skipIf.fails.concurrent", "test.skipIf.fails.sequential", "test.skipIf.fails.todo", "test.runIf.skip.only", "test.runIf.skip.concurrent", "test.runIf.skip.sequential", "test.runIf.skip.todo", "test.runIf.skip.fails", "test.runIf.only.skip", "test.runIf.only.concurrent", "test.runIf.only.sequential", "test.runIf.only.todo", "test.runIf.only.fails", "test.runIf.concurrent.skip", "test.runIf.concurrent.only", "test.runIf.concurrent.sequential", "test.runIf.concurrent.todo", "test.runIf.concurrent.fails", "test.runIf.sequential.skip", "test.runIf.sequential.only", "test.runIf.sequential.concurrent", "test.runIf.sequential.todo", "test.runIf.sequential.fails", "test.runIf.todo.skip", "test.runIf.todo.only", "test.runIf.todo.concurrent", "test.runIf.todo.sequential", "test.runIf.todo.fails", "test.runIf.fails.skip", "test.runIf.fails.only", "test.runIf.fails.concurrent", "test.runIf.fails.sequential", "test.runIf.fails.todo", "test.skip.only.each", "test.skip.concurrent.each", "test.skip.sequential.each", "test.skip.todo.each", "test.skip.fails.each", "test.only.skip.each", "test.only.concurrent.each", "test.only.sequential.each", "test.only.todo.each", "test.only.fails.each", "test.concurrent.skip.each", "test.concurrent.only.each", "test.concurrent.sequential.each", "test.concurrent.todo.each", "test.concurrent.fails.each", "test.sequential.skip.each", "test.sequential.only.each", "test.sequential.concurrent.each", "test.sequential.todo.each", "test.sequential.fails.each", "test.todo.skip.each", "test.todo.only.each", "test.todo.concurrent.each", "test.todo.sequential.each", "test.todo.fails.each", "test.fails.skip.each", "test.fails.only.each", "test.fails.concurrent.each", "test.fails.sequential.each", "test.fails.todo.each", "test.extend.skipIf.skip", "test.extend.skipIf.only", "test.extend.skipIf.concurrent", "test.extend.skipIf.sequential", "test.extend.skipIf.todo", "test.extend.skipIf.fails", "test.extend.runIf.skip", "test.extend.runIf.only", "test.extend.runIf.concurrent", "test.extend.runIf.sequential", "test.extend.runIf.todo", "test.extend.runIf.fails", "test.extend.skip.each", "test.extend.only.each", "test.extend.concurrent.each", "test.extend.sequential.each", "test.extend.todo.each", "test.extend.fails.each", "test.skipIf.skip.each", "test.skipIf.only.each", "test.skipIf.concurrent.each", "test.skipIf.sequential.each", "test.skipIf.todo.each", "test.skipIf.fails.each", "test.runIf.skip.each", "test.runIf.only.each", "test.runIf.concurrent.each", "test.runIf.sequential.each", "test.runIf.todo.each", "test.runIf.fails.each", "test.extend.skipIf.each", "test.extend.runIf.each", "bench", "bench.skip", "bench.only", "bench.todo", "bench.skipIf", "bench.runIf", "bench.skip.only", "bench.skip.todo", "bench.only.skip", "bench.only.todo", "bench.todo.skip", "bench.todo.only", "bench.skipIf.skip", "bench.skipIf.only", "bench.skipIf.todo", "bench.runIf.skip", "bench.runIf.only", "bench.runIf.todo", "bench.skip.only.todo", "bench.skip.todo.only", "bench.only.skip.todo", "bench.only.todo.skip", "bench.todo.skip.only", "bench.todo.only.skip", "bench.skipIf.skip.only", "bench.skipIf.skip.todo", "bench.skipIf.only.skip", "bench.skipIf.only.todo", "bench.skipIf.todo.skip", "bench.skipIf.todo.only", "bench.runIf.skip.only", "bench.runIf.skip.todo", "bench.runIf.only.skip", "bench.runIf.only.todo", "bench.runIf.todo.skip", "bench.runIf.todo.only", "describe", "describe.skip", "describe.only", "describe.concurrent", "describe.sequential", "describe.shuffle", "describe.todo", "describe.skipIf", "describe.runIf", "describe.each", "describe.skip.only", "describe.skip.concurrent", "describe.skip.sequential", "describe.skip.shuffle", "describe.skip.todo", "describe.only.skip", "describe.only.concurrent", "describe.only.sequential", "describe.only.shuffle", "describe.only.todo", "describe.concurrent.skip", "describe.concurrent.only", "describe.concurrent.sequential", "describe.concurrent.shuffle", "describe.concurrent.todo", "describe.sequential.skip", "describe.sequential.only", "describe.sequential.concurrent", "describe.sequential.shuffle", "describe.sequential.todo", "describe.shuffle.skip", "describe.shuffle.only", "describe.shuffle.concurrent", "describe.shuffle.sequential", "describe.shuffle.todo", "describe.todo.skip", "describe.todo.only", "describe.todo.concurrent", "describe.todo.sequential", "describe.todo.shuffle", "describe.skipIf.skip", "describe.skipIf.only", "describe.skipIf.concurrent", "describe.skipIf.sequential", "describe.skipIf.shuffle", "describe.skipIf.todo", "describe.runIf.skip", "describe.runIf.only", "describe.runIf.concurrent", "describe.runIf.sequential", "describe.runIf.shuffle", "describe.runIf.todo", "describe.skip.each", "describe.only.each", "describe.concurrent.each", "describe.sequential.each", "describe.shuffle.each", "describe.todo.each", "describe.skipIf.each", "describe.runIf.each", "describe.skip.only.concurrent", "describe.skip.only.sequential", "describe.skip.only.shuffle", "describe.skip.only.todo", "describe.skip.concurrent.only", "describe.skip.concurrent.sequential", "describe.skip.concurrent.shuffle", "describe.skip.concurrent.todo", "describe.skip.sequential.only", "describe.skip.sequential.concurrent", "describe.skip.sequential.shuffle", "describe.skip.sequential.todo", "describe.skip.shuffle.only", "describe.skip.shuffle.concurrent", "describe.skip.shuffle.sequential", "describe.skip.shuffle.todo", "describe.skip.todo.only", "describe.skip.todo.concurrent", "describe.skip.todo.sequential", "describe.skip.todo.shuffle", "describe.only.skip.concurrent", "describe.only.skip.sequential", "describe.only.skip.shuffle", "describe.only.skip.todo", "describe.only.concurrent.skip", "describe.only.concurrent.sequential", "describe.only.concurrent.shuffle", "describe.only.concurrent.todo", "describe.only.sequential.skip", "describe.only.sequential.concurrent", "describe.only.sequential.shuffle", "describe.only.sequential.todo", "describe.only.shuffle.skip", "describe.only.shuffle.concurrent", "describe.only.shuffle.sequential", "describe.only.shuffle.todo", "describe.only.todo.skip", "describe.only.todo.concurrent", "describe.only.todo.sequential", "describe.only.todo.shuffle", "describe.concurrent.skip.only", "describe.concurrent.skip.sequential", "describe.concurrent.skip.shuffle", "describe.concurrent.skip.todo", "describe.concurrent.only.skip", "describe.concurrent.only.sequential", "describe.concurrent.only.shuffle", "describe.concurrent.only.todo", "describe.concurrent.sequential.skip", "describe.concurrent.sequential.only", "describe.concurrent.sequential.shuffle", "describe.concurrent.sequential.todo", "describe.concurrent.shuffle.skip", "describe.concurrent.shuffle.only", "describe.concurrent.shuffle.sequential", "describe.concurrent.shuffle.todo", "describe.concurrent.todo.skip", "describe.concurrent.todo.only", "describe.concurrent.todo.sequential", "describe.concurrent.todo.shuffle", "describe.sequential.skip.only", "describe.sequential.skip.concurrent", "describe.sequential.skip.shuffle", "describe.sequential.skip.todo", "describe.sequential.only.skip", "describe.sequential.only.concurrent", "describe.sequential.only.shuffle", "describe.sequential.only.todo", "describe.sequential.concurrent.skip", "describe.sequential.concurrent.only", "describe.sequential.concurrent.shuffle", "describe.sequential.concurrent.todo", "describe.sequential.shuffle.skip", "describe.sequential.shuffle.only", "describe.sequential.shuffle.concurrent", "describe.sequential.shuffle.todo", "describe.sequential.todo.skip", "describe.sequential.todo.only", "describe.sequential.todo.concurrent", "describe.sequential.todo.shuffle", "describe.shuffle.skip.only", "describe.shuffle.skip.concurrent", "describe.shuffle.skip.sequential", "describe.shuffle.skip.todo", "describe.shuffle.only.skip", "describe.shuffle.only.concurrent", "describe.shuffle.only.sequential", "describe.shuffle.only.todo", "describe.shuffle.concurrent.skip", "describe.shuffle.concurrent.only", "describe.shuffle.concurrent.sequential", "describe.shuffle.concurrent.todo", "describe.shuffle.sequential.skip", "describe.shuffle.sequential.only", "describe.shuffle.sequential.concurrent", "describe.shuffle.sequential.todo", "describe.shuffle.todo.skip", "describe.shuffle.todo.only", "describe.shuffle.todo.concurrent", "describe.shuffle.todo.sequential", "describe.todo.skip.only", "describe.todo.skip.concurrent", "describe.todo.skip.sequential", "describe.todo.skip.shuffle", "describe.todo.only.skip", "describe.todo.only.concurrent", "describe.todo.only.sequential", "describe.todo.only.shuffle", "describe.todo.concurrent.skip", "describe.todo.concurrent.only", "describe.todo.concurrent.sequential", "describe.todo.concurrent.shuffle", "describe.todo.sequential.skip", "describe.todo.sequential.only", "describe.todo.sequential.concurrent", "describe.todo.sequential.shuffle", "describe.todo.shuffle.skip", "describe.todo.shuffle.only", "describe.todo.shuffle.concurrent", "describe.todo.shuffle.sequential", "describe.skipIf.skip.only", "describe.skipIf.skip.concurrent", "describe.skipIf.skip.sequential", "describe.skipIf.skip.shuffle", "describe.skipIf.skip.todo", "describe.skipIf.only.skip", "describe.skipIf.only.concurrent", "describe.skipIf.only.sequential", "describe.skipIf.only.shuffle", "describe.skipIf.only.todo", "describe.skipIf.concurrent.skip", "describe.skipIf.concurrent.only", "describe.skipIf.concurrent.sequential", "describe.skipIf.concurrent.shuffle", "describe.skipIf.concurrent.todo", "describe.skipIf.sequential.skip", "describe.skipIf.sequential.only", "describe.skipIf.sequential.concurrent", "describe.skipIf.sequential.shuffle", "describe.skipIf.sequential.todo", "describe.skipIf.shuffle.skip", "describe.skipIf.shuffle.only", "describe.skipIf.shuffle.concurrent", "describe.skipIf.shuffle.sequential", "describe.skipIf.shuffle.todo", "describe.skipIf.todo.skip", "describe.skipIf.todo.only", "describe.skipIf.todo.concurrent", "describe.skipIf.todo.sequential", "describe.skipIf.todo.shuffle", "describe.runIf.skip.only", "describe.runIf.skip.concurrent", "describe.runIf.skip.sequential", "describe.runIf.skip.shuffle", "describe.runIf.skip.todo", "describe.runIf.only.skip", "describe.runIf.only.concurrent", "describe.runIf.only.sequential", "describe.runIf.only.shuffle", "describe.runIf.only.todo", "describe.runIf.concurrent.skip", "describe.runIf.concurrent.only", "describe.runIf.concurrent.sequential", "describe.runIf.concurrent.shuffle", "describe.runIf.concurrent.todo", "describe.runIf.sequential.skip", "describe.runIf.sequential.only", "describe.runIf.sequential.concurrent", "describe.runIf.sequential.shuffle", "describe.runIf.sequential.todo", "describe.runIf.shuffle.skip", "describe.runIf.shuffle.only", "describe.runIf.shuffle.concurrent", "describe.runIf.shuffle.sequential", "describe.runIf.shuffle.todo", "describe.runIf.todo.skip", "describe.runIf.todo.only", "describe.runIf.todo.concurrent", "describe.runIf.todo.sequential", "describe.runIf.todo.shuffle", "describe.skip.only.each", "describe.skip.concurrent.each", "describe.skip.sequential.each", "describe.skip.shuffle.each", "describe.skip.todo.each", "describe.only.skip.each", "describe.only.concurrent.each", "describe.only.sequential.each", "describe.only.shuffle.each", "describe.only.todo.each", "describe.concurrent.skip.each", "describe.concurrent.only.each", "describe.concurrent.sequential.each", "describe.concurrent.shuffle.each", "describe.concurrent.todo.each", "describe.sequential.skip.each", "describe.sequential.only.each", "describe.sequential.concurrent.each", "describe.sequential.shuffle.each", "describe.sequential.todo.each", "describe.shuffle.skip.each", "describe.shuffle.only.each", "describe.shuffle.concurrent.each", "describe.shuffle.sequential.each", "describe.shuffle.todo.each", "describe.todo.skip.each", "describe.todo.only.each", "describe.todo.concurrent.each", "describe.todo.sequential.each", "describe.todo.shuffle.each", "describe.skipIf.skip.each", "describe.skipIf.only.each", "describe.skipIf.concurrent.each", "describe.skipIf.sequential.each", "describe.skipIf.shuffle.each", "describe.skipIf.todo.each", "describe.runIf.skip.each", "describe.runIf.only.each", "describe.runIf.concurrent.each", "describe.runIf.sequential.each", "describe.runIf.shuffle.each", "describe.runIf.todo.each", "suite", "suite.skip", "suite.only", "suite.concurrent", "suite.sequential", "suite.shuffle", "suite.todo", "suite.skipIf", "suite.runIf", "suite.each", "suite.skip.only", "suite.skip.concurrent", "suite.skip.sequential", "suite.skip.shuffle", "suite.skip.todo", "suite.only.skip", "suite.only.concurrent", "suite.only.sequential", "suite.only.shuffle", "suite.only.todo", "suite.concurrent.skip", "suite.concurrent.only", "suite.concurrent.sequential", "suite.concurrent.shuffle", "suite.concurrent.todo", "suite.sequential.skip", "suite.sequential.only", "suite.sequential.concurrent", "suite.sequential.shuffle", "suite.sequential.todo", "suite.shuffle.skip", "suite.shuffle.only", "suite.shuffle.concurrent", "suite.shuffle.sequential", "suite.shuffle.todo", "suite.todo.skip", "suite.todo.only", "suite.todo.concurrent", "suite.todo.sequential", "suite.todo.shuffle", "suite.skipIf.skip", "suite.skipIf.only", "suite.skipIf.concurrent", "suite.skipIf.sequential", "suite.skipIf.shuffle", "suite.skipIf.todo", "suite.runIf.skip", "suite.runIf.only", "suite.runIf.concurrent", "suite.runIf.sequential", "suite.runIf.shuffle", "suite.runIf.todo", "suite.skip.each", "suite.only.each", "suite.concurrent.each", "suite.sequential.each", "suite.shuffle.each", "suite.todo.each", "suite.skipIf.each", "suite.runIf.each", "suite.skip.only.concurrent", "suite.skip.only.sequential", "suite.skip.only.shuffle", "suite.skip.only.todo", "suite.skip.concurrent.only", "suite.skip.concurrent.sequential", "suite.skip.concurrent.shuffle", "suite.skip.concurrent.todo", "suite.skip.sequential.only", "suite.skip.sequential.concurrent", "suite.skip.sequential.shuffle", "suite.skip.sequential.todo", "suite.skip.shuffle.only", "suite.skip.shuffle.concurrent", "suite.skip.shuffle.sequential", "suite.skip.shuffle.todo", "suite.skip.todo.only", "suite.skip.todo.concurrent", "suite.skip.todo.sequential", "suite.skip.todo.shuffle", "suite.only.skip.concurrent", "suite.only.skip.sequential", "suite.only.skip.shuffle", "suite.only.skip.todo", "suite.only.concurrent.skip", "suite.only.concurrent.sequential", "suite.only.concurrent.shuffle", "suite.only.concurrent.todo", "suite.only.sequential.skip", "suite.only.sequential.concurrent", "suite.only.sequential.shuffle", "suite.only.sequential.todo", "suite.only.shuffle.skip", "suite.only.shuffle.concurrent", "suite.only.shuffle.sequential", "suite.only.shuffle.todo", "suite.only.todo.skip", "suite.only.todo.concurrent", "suite.only.todo.sequential", "suite.only.todo.shuffle", "suite.concurrent.skip.only", "suite.concurrent.skip.sequential", "suite.concurrent.skip.shuffle", "suite.concurrent.skip.todo", "suite.concurrent.only.skip", "suite.concurrent.only.sequential", "suite.concurrent.only.shuffle", "suite.concurrent.only.todo", "suite.concurrent.sequential.skip", "suite.concurrent.sequential.only", "suite.concurrent.sequential.shuffle", "suite.concurrent.sequential.todo", "suite.concurrent.shuffle.skip", "suite.concurrent.shuffle.only", "suite.concurrent.shuffle.sequential", "suite.concurrent.shuffle.todo", "suite.concurrent.todo.skip", "suite.concurrent.todo.only", "suite.concurrent.todo.sequential", "suite.concurrent.todo.shuffle", "suite.sequential.skip.only", "suite.sequential.skip.concurrent", "suite.sequential.skip.shuffle", "suite.sequential.skip.todo", "suite.sequential.only.skip", "suite.sequential.only.concurrent", "suite.sequential.only.shuffle", "suite.sequential.only.todo", "suite.sequential.concurrent.skip", "suite.sequential.concurrent.only", "suite.sequential.concurrent.shuffle", "suite.sequential.concurrent.todo", "suite.sequential.shuffle.skip", "suite.sequential.shuffle.only", "suite.sequential.shuffle.concurrent", "suite.sequential.shuffle.todo", "suite.sequential.todo.skip", "suite.sequential.todo.only", "suite.sequential.todo.concurrent", "suite.sequential.todo.shuffle", "suite.shuffle.skip.only", "suite.shuffle.skip.concurrent", "suite.shuffle.skip.sequential", "suite.shuffle.skip.todo", "suite.shuffle.only.skip", "suite.shuffle.only.concurrent", "suite.shuffle.only.sequential", "suite.shuffle.only.todo", "suite.shuffle.concurrent.skip", "suite.shuffle.concurrent.only", "suite.shuffle.concurrent.sequential", "suite.shuffle.concurrent.todo", "suite.shuffle.sequential.skip", "suite.shuffle.sequential.only", "suite.shuffle.sequential.concurrent", "suite.shuffle.sequential.todo", "suite.shuffle.todo.skip", "suite.shuffle.todo.only", "suite.shuffle.todo.concurrent", "suite.shuffle.todo.sequential", "suite.todo.skip.only", "suite.todo.skip.concurrent", "suite.todo.skip.sequential", "suite.todo.skip.shuffle", "suite.todo.only.skip", "suite.todo.only.concurrent", "suite.todo.only.sequential", "suite.todo.only.shuffle", "suite.todo.concurrent.skip", "suite.todo.concurrent.only", "suite.todo.concurrent.sequential", "suite.todo.concurrent.shuffle", "suite.todo.sequential.skip", "suite.todo.sequential.only", "suite.todo.sequential.concurrent", "suite.todo.sequential.shuffle", "suite.todo.shuffle.skip", "suite.todo.shuffle.only", "suite.todo.shuffle.concurrent", "suite.todo.shuffle.sequential", "suite.skipIf.skip.only", "suite.skipIf.skip.concurrent", "suite.skipIf.skip.sequential", "suite.skipIf.skip.shuffle", "suite.skipIf.skip.todo", "suite.skipIf.only.skip", "suite.skipIf.only.concurrent", "suite.skipIf.only.sequential", "suite.skipIf.only.shuffle", "suite.skipIf.only.todo", "suite.skipIf.concurrent.skip", "suite.skipIf.concurrent.only", "suite.skipIf.concurrent.sequential", "suite.skipIf.concurrent.shuffle", "suite.skipIf.concurrent.todo", "suite.skipIf.sequential.skip", "suite.skipIf.sequential.only", "suite.skipIf.sequential.concurrent", "suite.skipIf.sequential.shuffle", "suite.skipIf.sequential.todo", "suite.skipIf.shuffle.skip", "suite.skipIf.shuffle.only", "suite.skipIf.shuffle.concurrent", "suite.skipIf.shuffle.sequential", "suite.skipIf.shuffle.todo", "suite.skipIf.todo.skip", "suite.skipIf.todo.only", "suite.skipIf.todo.concurrent", "suite.skipIf.todo.sequential", "suite.skipIf.todo.shuffle", "suite.runIf.skip.only", "suite.runIf.skip.concurrent", "suite.runIf.skip.sequential", "suite.runIf.skip.shuffle", "suite.runIf.skip.todo", "suite.runIf.only.skip", "suite.runIf.only.concurrent", "suite.runIf.only.sequential", "suite.runIf.only.shuffle", "suite.runIf.only.todo", "suite.runIf.concurrent.skip", "suite.runIf.concurrent.only", "suite.runIf.concurrent.sequential", "suite.runIf.concurrent.shuffle", "suite.runIf.concurrent.todo", "suite.runIf.sequential.skip", "suite.runIf.sequential.only", "suite.runIf.sequential.concurrent", "suite.runIf.sequential.shuffle", "suite.runIf.sequential.todo", "suite.runIf.shuffle.skip", "suite.runIf.shuffle.only", "suite.runIf.shuffle.concurrent", "suite.runIf.shuffle.sequential", "suite.runIf.shuffle.todo", "suite.runIf.todo.skip", "suite.runIf.todo.only", "suite.runIf.todo.concurrent", "suite.runIf.todo.sequential", "suite.runIf.todo.shuffle", "suite.skip.only.each", "suite.skip.concurrent.each", "suite.skip.sequential.each", "suite.skip.shuffle.each", "suite.skip.todo.each", "suite.only.skip.each", "suite.only.concurrent.each", "suite.only.sequential.each", "suite.only.shuffle.each", "suite.only.todo.each", "suite.concurrent.skip.each", "suite.concurrent.only.each", "suite.concurrent.sequential.each", "suite.concurrent.shuffle.each", "suite.concurrent.todo.each", "suite.sequential.skip.each", "suite.sequential.only.each", "suite.sequential.concurrent.each", "suite.sequential.shuffle.each", "suite.sequential.todo.each", "suite.shuffle.skip.each", "suite.shuffle.only.each", "suite.shuffle.concurrent.each", "suite.shuffle.sequential.each", "suite.shuffle.todo.each", "suite.todo.skip.each", "suite.todo.only.each", "suite.todo.concurrent.each", "suite.todo.sequential.each", "suite.todo.shuffle.each", "suite.skipIf.skip.each", "suite.skipIf.only.each", "suite.skipIf.concurrent.each", "suite.skipIf.sequential.each", "suite.skipIf.shuffle.each", "suite.skipIf.todo.each", "suite.runIf.skip.each", "suite.runIf.only.each", "suite.runIf.concurrent.each", "suite.runIf.sequential.each", "suite.runIf.shuffle.each", "suite.runIf.todo.each", "xtest", "xtest.each", "xit", "xit.each", "fit", "xdescribe", "xdescribe.each", "fdescribe"]);
119
+
120
+ const isTypeOfVitestFnCall = (node, context, types) => {
121
+ const vitestFnCall = parseVitestFnCall(node, context);
122
+ return vitestFnCall !== null && types.includes(vitestFnCall.type);
123
+ };
124
+ const parseVitestFnCall = (node, context) => {
125
+ const vitestFnCall = parseVitestFnCallWithReason(node, context);
126
+ if (typeof vitestFnCall === "string")
127
+ return null;
128
+ return vitestFnCall;
129
+ };
130
+ const parseVitestFnCallCache = /* @__PURE__ */ new WeakMap();
131
+ const parseVitestFnCallWithReason = (node, context) => {
132
+ let parsedVitestFnCall = parseVitestFnCallCache.get(node);
133
+ if (parsedVitestFnCall)
134
+ return parsedVitestFnCall;
135
+ parsedVitestFnCall = parseVitestFnCallWithReasonInner(node, context);
136
+ parseVitestFnCallCache.set(node, parsedVitestFnCall);
137
+ return parsedVitestFnCall;
138
+ };
139
+ const determineVitestFnType = (name) => {
140
+ if (name === "expect")
141
+ return "expect";
142
+ if (name === "expectTypeOf")
143
+ return "expectTypeOf";
144
+ if (name === "vi")
145
+ return "vi";
146
+ if (DescribeAlias.hasOwnProperty(name))
147
+ return "describe";
148
+ if (TestCaseName.hasOwnProperty(name))
149
+ return "test";
150
+ if (HookName.hasOwnProperty(name))
151
+ return "hook";
152
+ return "unknown";
153
+ };
154
+ const findModifiersAndMatcher = (members) => {
155
+ const modifiers = [];
156
+ for (const member of members) {
157
+ if (member.parent?.type === utils.AST_NODE_TYPES.MemberExpression && member.parent.parent?.type === utils.AST_NODE_TYPES.CallExpression) {
158
+ return {
159
+ matcher: member,
160
+ args: member.parent.parent.arguments,
161
+ modifiers
162
+ };
163
+ }
164
+ const name = getAccessorValue(member);
165
+ if (modifiers.length === 0) {
166
+ if (!ModifierName.hasOwnProperty(name))
167
+ return "modifier-unknown";
168
+ } else if (modifiers.length === 1) {
169
+ if (name !== ModifierName.not)
170
+ return "modifier-unknown";
171
+ const firstModifier = getAccessorValue(modifiers[0]);
172
+ if (firstModifier !== ModifierName.resolves && firstModifier !== ModifierName.rejects)
173
+ return "modifier-unknown";
174
+ } else {
175
+ return "modifier-unknown";
176
+ }
177
+ modifiers.push(member);
178
+ }
179
+ return "matcher-not-found";
180
+ };
181
+ const parseVitestExpectCall = (typelessParsedVitestFnCall, type) => {
182
+ const modifiersMatcher = findModifiersAndMatcher(typelessParsedVitestFnCall.members);
183
+ if (typeof modifiersMatcher === "string")
184
+ return modifiersMatcher;
185
+ return {
186
+ ...typelessParsedVitestFnCall,
187
+ type,
188
+ ...modifiersMatcher
189
+ };
190
+ };
191
+ const findTopMostCallExpression = (node) => {
192
+ let topMostCallExpression = node;
193
+ let { parent } = node;
194
+ while (parent) {
195
+ if (parent.type === utils.AST_NODE_TYPES.CallExpression) {
196
+ topMostCallExpression = parent;
197
+ parent = parent.parent;
198
+ continue;
199
+ }
200
+ if (parent.type !== utils.AST_NODE_TYPES.MemberExpression)
201
+ break;
202
+ parent = parent.parent;
203
+ }
204
+ return topMostCallExpression;
205
+ };
206
+ const parseVitestFnCallWithReasonInner = (node, context) => {
207
+ const chain = getNodeChain(node);
208
+ if (!chain?.length)
209
+ return null;
210
+ const [first, ...rest] = chain;
211
+ const lastLink = getAccessorValue(chain[chain.length - 1]);
212
+ if (lastLink === "each") {
213
+ if (node.callee.type !== utils.AST_NODE_TYPES.CallExpression && node.callee.type !== utils.AST_NODE_TYPES.TaggedTemplateExpression)
214
+ return null;
215
+ }
216
+ if (node.callee.type === utils.AST_NODE_TYPES.TaggedTemplateExpression && lastLink !== "each")
217
+ return null;
218
+ const resolved = resolveVitestFn(context, node, getAccessorValue(first));
219
+ if (!resolved)
220
+ return null;
221
+ const name = resolved.original ?? resolved.local;
222
+ const links = [name, ...rest.map(getAccessorValue)];
223
+ if (name !== "vi" && name !== "expect" && name !== "expectTypeOf" && !ValidVitestFnCallChains.has(links.join(".")))
224
+ return null;
225
+ const parsedVitestFnCall = {
226
+ name,
227
+ head: { ...resolved, node: first },
228
+ members: rest
229
+ };
230
+ const type = determineVitestFnType(name);
231
+ if (type === "expect" || type === "expectTypeOf") {
232
+ const result = parseVitestExpectCall(parsedVitestFnCall, type);
233
+ if (typeof result === "string" && findTopMostCallExpression(node) !== node)
234
+ return null;
235
+ if (result === "matcher-not-found") {
236
+ if (node.parent?.type === utils.AST_NODE_TYPES.MemberExpression)
237
+ return "matcher-not-called";
238
+ }
239
+ return result;
240
+ }
241
+ if (chain.slice(0, chain.length - 1).some((node2) => node2.parent?.type !== utils.AST_NODE_TYPES.MemberExpression))
242
+ return null;
243
+ if (node.parent?.type === utils.AST_NODE_TYPES.CallExpression || node.parent?.type === utils.AST_NODE_TYPES.MemberExpression)
244
+ return null;
245
+ return { ...parsedVitestFnCall, type };
246
+ };
247
+ const joinChains = (a, b) => a && b ? [...a, ...b] : null;
248
+ function getNodeChain(node) {
249
+ if (isSupportedAccessor(node))
250
+ return [node];
251
+ switch (node.type) {
252
+ case utils.AST_NODE_TYPES.TaggedTemplateExpression:
253
+ return getNodeChain(node.tag);
254
+ case utils.AST_NODE_TYPES.MemberExpression:
255
+ return joinChains(getNodeChain(node.object), getNodeChain(node.property));
256
+ case utils.AST_NODE_TYPES.CallExpression:
257
+ return getNodeChain(node.callee);
258
+ }
259
+ return null;
260
+ }
261
+ const resolveVitestFn = (context, node, identifier) => {
262
+ const scope = context.sourceCode.getScope ? context.sourceCode.getScope(node) : context.getScope();
263
+ const maybeImport = resolveScope(scope, identifier);
264
+ if (maybeImport === "local")
265
+ return null;
266
+ if (maybeImport) {
267
+ if (maybeImport.source === "vitest") {
268
+ return {
269
+ original: maybeImport.imported,
270
+ local: maybeImport.local,
271
+ type: "import"
272
+ };
273
+ }
274
+ return null;
275
+ }
276
+ return {
277
+ original: resolvePossibleAliasedGlobal(identifier, context),
278
+ local: identifier,
279
+ type: "global"
280
+ };
281
+ };
282
+ const resolvePossibleAliasedGlobal = (global, context) => {
283
+ const globalAliases = context.settings.vitest?.globalAliases ?? {};
284
+ const alias = Object.entries(globalAliases).find(([_, aliases]) => aliases.includes(global));
285
+ if (alias)
286
+ return alias[0];
287
+ return null;
288
+ };
289
+ const resolveScope = (scope, identifier) => {
290
+ let currentScope = scope;
291
+ while (currentScope !== null) {
292
+ const ref = currentScope.set.get(identifier);
293
+ if (ref && ref.defs.length > 0) {
294
+ const def = ref.defs[ref.defs.length - 1];
295
+ const importDetails = describePossibleImportDef(def);
296
+ if (importDetails?.local === identifier)
297
+ return importDetails;
298
+ return "local";
299
+ }
300
+ currentScope = currentScope.upper;
301
+ }
302
+ return null;
303
+ };
304
+ const findImportSourceNode = (node) => {
305
+ if (node.type === utils.AST_NODE_TYPES.AwaitExpression) {
306
+ if (node.argument.type === utils.AST_NODE_TYPES.ImportExpression)
307
+ return node.argument.source;
308
+ return null;
309
+ }
310
+ if (node.type === utils.AST_NODE_TYPES.CallExpression && isIdentifier(node.callee, "require"))
311
+ return node.arguments[0] ?? null;
312
+ return null;
313
+ };
314
+ const describeImportDefAsImport = (def) => {
315
+ if (def.parent.type === utils.AST_NODE_TYPES.TSImportEqualsDeclaration)
316
+ return null;
317
+ if (def.node.type !== utils.AST_NODE_TYPES.ImportSpecifier)
318
+ return null;
319
+ if (def.parent.importKind === "type")
320
+ return null;
321
+ return {
322
+ source: def.parent.source.value,
323
+ imported: def.node.imported.name,
324
+ local: def.node.local.name
325
+ };
326
+ };
327
+ const describePossibleImportDef = (def) => {
328
+ if (def.type === "Variable")
329
+ return describeVariableDefAsImport(def);
330
+ if (def.type === "ImportBinding")
331
+ return describeImportDefAsImport(def);
332
+ return null;
333
+ };
334
+ const describeVariableDefAsImport = (def) => {
335
+ if (!def.node.init)
336
+ return null;
337
+ const sourceNode = findImportSourceNode(def.node.init);
338
+ if (!sourceNode || !isStringNode(sourceNode))
339
+ return null;
340
+ if (def.name.parent?.type !== utils.AST_NODE_TYPES.Property)
341
+ return null;
342
+ if (!isSupportedAccessor(def.name.parent.key))
343
+ return null;
344
+ return {
345
+ source: getStringValue(sourceNode),
346
+ imported: getAccessorValue(def.name.parent.key),
347
+ local: def.name.name
348
+ };
349
+ };
350
+ const getTestCallExpressionsFromDeclaredVariables = (declaredVariables, context) => {
351
+ return declaredVariables.reduce(
352
+ (acc, { references }) => acc.concat(
353
+ references.map(({ identifier }) => identifier.parent).filter(
354
+ (node) => node?.type === utils.AST_NODE_TYPES.CallExpression && isTypeOfVitestFnCall(node, context, ["test"])
355
+ )
356
+ ),
357
+ []
358
+ );
359
+ };
360
+ const getFirstMatcherArg = (expectFnCall) => {
361
+ const [firstArg] = expectFnCall.args;
362
+ if (firstArg.type === utils.AST_NODE_TYPES.SpreadElement)
363
+ return firstArg;
364
+ return followTypeAssertionChain(firstArg);
365
+ };
366
+ const isTypeCastExpression = (node) => node.type === utils.AST_NODE_TYPES.TSAsExpression || node.type === utils.AST_NODE_TYPES.TSTypeAssertion;
367
+ const followTypeAssertionChain = (expression) => isTypeCastExpression(expression) ? followTypeAssertionChain(expression.expression) : expression;
368
+
369
+ const RULE_NAME$X = "prefer-lowercase-title";
370
+ const hasStringAsFirstArgument = (node) => node.arguments[0] && isStringNode(node.arguments[0]);
371
+ const populateIgnores = (ignore) => {
372
+ const ignores = [];
373
+ if (ignore.includes(DescribeAlias.describe))
374
+ ignores.push(...Object.keys(DescribeAlias));
375
+ if (ignore.includes(TestCaseName.test)) {
376
+ ignores.push(
377
+ ...Object.keys(TestCaseName).filter((k) => k.endsWith(TestCaseName.test))
378
+ );
379
+ }
380
+ if (ignore.includes(TestCaseName.it)) {
381
+ ignores.push(
382
+ ...Object.keys(TestCaseName).filter((k) => k.endsWith(TestCaseName.it))
383
+ );
384
+ }
385
+ return ignores;
386
+ };
387
+ const lowerCaseTitle = createEslintRule({
388
+ name: RULE_NAME$X,
389
+ meta: {
390
+ type: "problem",
391
+ docs: {
392
+ description: "enforce lowercase titles",
393
+ recommended: false
394
+ },
395
+ fixable: "code",
396
+ messages: {
397
+ lowerCaseTitle: "`{{ method }}`s should begin with lowercase"
398
+ },
399
+ schema: [
400
+ {
401
+ type: "object",
402
+ properties: {
403
+ ignore: {
404
+ type: "array",
405
+ items: {
406
+ type: "string",
407
+ enum: [
408
+ DescribeAlias.describe,
409
+ TestCaseName.test,
410
+ TestCaseName.it
411
+ ]
412
+ }
413
+ },
414
+ allowedPrefixes: {
415
+ type: "array",
416
+ items: { type: "string" },
417
+ additionalItems: false
418
+ },
419
+ ignoreTopLevelDescribe: {
420
+ type: "boolean",
421
+ default: false
422
+ },
423
+ lowercaseFirstCharacterOnly: {
424
+ type: "boolean",
425
+ default: true
426
+ }
427
+ },
428
+ additionalProperties: false
429
+ }
430
+ ]
431
+ },
432
+ defaultOptions: [
433
+ { ignore: [], allowedPrefixes: [], ignoreTopLevelDescribe: false, lowercaseFirstCharacterOnly: true }
434
+ ],
435
+ create: (context, [{ ignore = [], allowedPrefixes = [], ignoreTopLevelDescribe = false, lowercaseFirstCharacterOnly = false }]) => {
436
+ const ignores = populateIgnores(ignore);
437
+ let numberOfDescribeBlocks = 0;
438
+ return {
439
+ CallExpression(node) {
440
+ const vitestFnCall = parseVitestFnCall(node, context);
441
+ if (!vitestFnCall || !hasStringAsFirstArgument)
442
+ return;
443
+ if (vitestFnCall?.type === "describe") {
444
+ numberOfDescribeBlocks++;
445
+ if (ignoreTopLevelDescribe && numberOfDescribeBlocks === 1)
446
+ return;
447
+ } else if (vitestFnCall?.type !== "test") {
448
+ return;
449
+ }
450
+ const [firstArgument] = node.arguments;
451
+ const description = getStringValue(firstArgument);
452
+ if (typeof description !== "string")
453
+ return;
454
+ if (allowedPrefixes.some((prefix) => description.startsWith(prefix)))
455
+ return;
456
+ const firstCharacter = description.charAt(0);
457
+ if (ignores.includes(vitestFnCall.name) || lowercaseFirstCharacterOnly && (!firstCharacter || firstCharacter === firstCharacter.toLowerCase()) || !lowercaseFirstCharacterOnly && description === description.toLowerCase())
458
+ return;
459
+ context.report({
460
+ messageId: "lowerCaseTitle",
461
+ node: node.arguments[0],
462
+ data: {
463
+ method: vitestFnCall.name
464
+ },
465
+ fix: (fixer) => {
466
+ const description2 = getStringValue(firstArgument);
467
+ const rangeIgnoreQuotes = [
468
+ firstArgument.range[0] + 1,
469
+ firstArgument.range[1] - 1
470
+ ];
471
+ const newDescription = lowercaseFirstCharacterOnly ? description2.substring(0, 1).toLowerCase() + description2.substring(1) : description2.toLowerCase();
472
+ return [fixer.replaceTextRange(rangeIgnoreQuotes, newDescription)];
473
+ }
474
+ });
475
+ },
476
+ "CallExpression:exit"(node) {
477
+ if (isTypeOfVitestFnCall(node, context, ["describe"]))
478
+ numberOfDescribeBlocks--;
479
+ }
480
+ };
481
+ }
482
+ });
483
+
484
+ const RULE_NAME$W = "max-nested-describe";
485
+ const maxNestedDescribe = createEslintRule({
486
+ name: RULE_NAME$W,
487
+ meta: {
488
+ type: "problem",
489
+ docs: {
490
+ description: "require describe block to be less than set max value or default value",
491
+ recommended: false
492
+ },
493
+ schema: [
494
+ {
495
+ type: "object",
496
+ properties: {
497
+ max: {
498
+ type: "number"
499
+ }
500
+ },
501
+ additionalProperties: false
502
+ }
503
+ ],
504
+ messages: {
505
+ maxNestedDescribe: "Nested describe block should be less than set max value."
506
+ }
507
+ },
508
+ defaultOptions: [
509
+ {
510
+ max: 5
511
+ }
512
+ ],
513
+ create(context, [{ max }]) {
514
+ const stack = [];
515
+ function pushStack(node) {
516
+ if (node.parent?.type !== "CallExpression")
517
+ return;
518
+ if (node.parent.callee.type !== "Identifier" || node.parent.callee.name !== "describe")
519
+ return;
520
+ stack.push(0);
521
+ if (stack.length > max) {
522
+ context.report({
523
+ node: node.parent,
524
+ messageId: "maxNestedDescribe"
525
+ });
526
+ }
527
+ }
528
+ function popStack(node) {
529
+ if (node.parent?.type !== "CallExpression")
530
+ return;
531
+ if (node.parent.callee.type !== "Identifier" || node.parent.callee.name !== "describe")
532
+ return;
533
+ stack.pop();
534
+ }
535
+ return {
536
+ "FunctionExpression": pushStack,
537
+ "FunctionExpression:exit": popStack,
538
+ "ArrowFunctionExpression": pushStack,
539
+ "ArrowFunctionExpression:exit": popStack
540
+ };
541
+ }
542
+ });
543
+
544
+ const RULE_NAME$V = "no-identical-title";
545
+ const newDescribeContext = () => ({
546
+ describeTitles: [],
547
+ testTitles: []
548
+ });
549
+ const noIdenticalTitle = createEslintRule({
550
+ name: RULE_NAME$V,
551
+ meta: {
552
+ type: "problem",
553
+ docs: {
554
+ description: "disallow identical titles",
555
+ recommended: false
556
+ },
557
+ fixable: "code",
558
+ schema: [],
559
+ messages: {
560
+ multipleTestTitle: "Test is used multiple times in the same describe(suite) block",
561
+ multipleDescribeTitle: "Describe is used multiple times in the same describe(suite) block"
562
+ }
563
+ },
564
+ defaultOptions: [],
565
+ create(context) {
566
+ const stack = [newDescribeContext()];
567
+ return {
568
+ CallExpression(node) {
569
+ const currentStack = stack[stack.length - 1];
570
+ const vitestFnCall = parseVitestFnCall(node, context);
571
+ if (!vitestFnCall)
572
+ return;
573
+ if (vitestFnCall.name === "describe" || vitestFnCall.name === "suite")
574
+ stack.push(newDescribeContext());
575
+ if (vitestFnCall.members.find((s) => isSupportedAccessor(s, "each")))
576
+ return;
577
+ const [argument] = node.arguments;
578
+ if (!argument || !isStringNode(argument))
579
+ return;
580
+ const title = getStringValue(argument);
581
+ if (vitestFnCall.type === "test") {
582
+ if (currentStack?.testTitles.includes(title)) {
583
+ context.report({
584
+ node,
585
+ messageId: "multipleTestTitle"
586
+ });
587
+ }
588
+ currentStack?.testTitles.push(title);
589
+ }
590
+ if (vitestFnCall.type !== "describe")
591
+ return;
592
+ if (currentStack?.describeTitles.includes(title)) {
593
+ context.report({
594
+ node,
595
+ messageId: "multipleDescribeTitle"
596
+ });
597
+ }
598
+ currentStack?.describeTitles.push(title);
599
+ },
600
+ "CallExpression:exit"(node) {
601
+ if (isTypeOfVitestFnCall(node, context, ["describe"]))
602
+ stack.pop();
603
+ }
604
+ };
605
+ }
606
+ });
607
+
608
+ const RULE_NAME$U = "no-focused-tests";
609
+ const isTestOrDescribe = (node) => {
610
+ return node.type === "Identifier" && ["it", "test", "describe"].includes(node.name);
611
+ };
612
+ const isOnly = (node) => {
613
+ return node.type === "Identifier" && node.name === "only";
614
+ };
615
+ const noFocusedTests = createEslintRule({
616
+ name: RULE_NAME$U,
617
+ meta: {
618
+ type: "problem",
619
+ docs: {
620
+ description: "disallow focused tests",
621
+ recommended: false
622
+ },
623
+ fixable: "code",
624
+ schema: [
625
+ {
626
+ type: "object",
627
+ properties: {
628
+ fixable: {
629
+ type: "boolean",
630
+ default: true
631
+ }
632
+ },
633
+ additionalProperties: false
634
+ }
635
+ ],
636
+ messages: {
637
+ noFocusedTests: "Focused tests are not allowed."
638
+ }
639
+ },
640
+ defaultOptions: [{ fixable: true }],
641
+ create: (context) => {
642
+ const fixable = context.options[0]?.fixable;
643
+ return {
644
+ ExpressionStatement(node) {
645
+ if (node.expression.type === "CallExpression") {
646
+ const { callee } = node.expression;
647
+ if (callee.type === "MemberExpression" && isTestOrDescribe(callee.object) && isOnly(callee.property)) {
648
+ context.report({
649
+ node: callee.property,
650
+ messageId: "noFocusedTests",
651
+ fix: (fixer) => fixable ? fixer.removeRange([callee.property.range[0] - 1, callee.property.range[1]]) : null
652
+ });
653
+ }
654
+ if (callee.type === "TaggedTemplateExpression") {
655
+ const tagCall = callee.tag.type === "MemberExpression" ? callee.tag.object : null;
656
+ if (!tagCall)
657
+ return;
658
+ if (tagCall.type === "MemberExpression" && isTestOrDescribe(tagCall.object) && isOnly(tagCall.property)) {
659
+ context.report({
660
+ node: tagCall.property,
661
+ messageId: "noFocusedTests",
662
+ fix: (fixer) => fixable ? fixer.removeRange([tagCall.property.range[0] - 1, tagCall.property.range[1]]) : null
663
+ });
664
+ }
665
+ }
666
+ }
667
+ },
668
+ CallExpression(node) {
669
+ if (node.callee.type === "CallExpression") {
670
+ const { callee } = node.callee;
671
+ if (callee.type === "MemberExpression" && callee.object.type === "MemberExpression" && isTestOrDescribe(callee.object.object) && isOnly(callee.object.property) && callee.property.type === "Identifier" && callee.property.name === "each") {
672
+ const onlyCallee = callee.object.property;
673
+ context.report({
674
+ node: callee.object.property,
675
+ messageId: "noFocusedTests",
676
+ fix: (fixer) => fixable ? fixer.removeRange([
677
+ onlyCallee.range[0] - 1,
678
+ onlyCallee.range[1]
679
+ ]) : null
680
+ });
681
+ }
682
+ }
683
+ }
684
+ };
685
+ }
686
+ });
687
+
688
+ const RULE_NAME$T = "no-conditional-tests";
689
+ const noConditionalTest = createEslintRule({
690
+ name: RULE_NAME$T,
691
+ meta: {
692
+ type: "problem",
693
+ docs: {
694
+ description: "disallow conditional tests",
695
+ recommended: false
696
+ },
697
+ schema: [],
698
+ messages: {
699
+ noConditionalTests: "Avoid using if conditions in a test."
700
+ }
701
+ },
702
+ defaultOptions: [],
703
+ create(context) {
704
+ return {
705
+ Identifier: function(node) {
706
+ if (["test", "it", "describe"].includes(node.name)) {
707
+ if (node.parent?.parent?.parent?.parent?.type === "IfStatement") {
708
+ context.report({
709
+ node,
710
+ messageId: "noConditionalTests"
711
+ });
712
+ }
713
+ }
714
+ }
715
+ };
716
+ }
717
+ });
718
+
719
+ const DEFAULTS = {
720
+ typecheck: false
721
+ };
722
+ function parsePluginSettings(settings) {
723
+ const pluginSettings = typeof settings.vitest !== "object" || settings.vitest === null ? {} : settings.vitest;
724
+ return {
725
+ ...DEFAULTS,
726
+ ...pluginSettings
727
+ };
728
+ }
729
+
730
+ const RULE_NAME$S = "expect-expect";
731
+ function matchesAssertFunctionName(nodeName, patterns) {
732
+ return patterns.some(
733
+ (p) => new RegExp(
734
+ `^${p.split(".").map((x) => {
735
+ if (x === "**")
736
+ return "[a-z\\d\\.]*";
737
+ return x.replace(/\*/gu, "[a-z\\d]*");
738
+ }).join("\\.")}(\\.|$)`,
739
+ "ui"
740
+ ).test(nodeName)
741
+ );
742
+ }
743
+ const expectExpect = createEslintRule({
744
+ name: RULE_NAME$S,
745
+ meta: {
746
+ type: "suggestion",
747
+ docs: {
748
+ description: "enforce having expectation in test body",
749
+ recommended: false
750
+ },
751
+ schema: [
752
+ {
753
+ type: "object",
754
+ properties: {
755
+ assertFunctionNames: {
756
+ type: "array",
757
+ items: [{ type: "string" }]
758
+ },
759
+ additionalTestBlockFunctions: {
760
+ type: "array",
761
+ items: { type: "string" }
762
+ }
763
+ },
764
+ additionalProperties: false
765
+ }
766
+ ],
767
+ messages: {
768
+ noAssertions: "Test has no assertions"
769
+ }
770
+ },
771
+ defaultOptions: [{ assertFunctionNames: ["expect"], additionalTestBlockFunctions: [] }],
772
+ create(context, [{ assertFunctionNames = ["expect"], additionalTestBlockFunctions = [] }]) {
773
+ const unchecked = [];
774
+ const settings = parsePluginSettings(context.settings);
775
+ if (settings.typecheck)
776
+ assertFunctionNames.push("expectTypeOf");
777
+ function checkCallExpression(nodes) {
778
+ for (const node of nodes) {
779
+ const index = node.type === utils.AST_NODE_TYPES.CallExpression ? unchecked.indexOf(node) : -1;
780
+ if (node.type === utils.AST_NODE_TYPES.FunctionDeclaration) {
781
+ const declaredVariables = context.sourceCode.getDeclaredVariables(node);
782
+ const testCallExpressions = getTestCallExpressionsFromDeclaredVariables(declaredVariables, context);
783
+ checkCallExpression(testCallExpressions);
784
+ }
785
+ if (index !== -1) {
786
+ unchecked.splice(index, 1);
787
+ break;
788
+ }
789
+ }
790
+ }
791
+ return {
792
+ CallExpression(node) {
793
+ if (node.callee.type === utils.AST_NODE_TYPES.Identifier && node.callee.name === "bench")
794
+ return;
795
+ if (node?.callee?.type === utils.AST_NODE_TYPES.MemberExpression && node.callee.property.type === utils.AST_NODE_TYPES.Identifier && node.callee.property.name === "extend")
796
+ return;
797
+ if (node?.callee?.type === utils.AST_NODE_TYPES.MemberExpression && node.callee.property.type === utils.AST_NODE_TYPES.Identifier && node.callee.property.name === "skip")
798
+ return;
799
+ const name = getNodeName(node) ?? "";
800
+ if (isTypeOfVitestFnCall(node, context, ["test"]) || additionalTestBlockFunctions.includes(name)) {
801
+ if (node.callee.type === utils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.callee.property, "todo"))
802
+ return;
803
+ unchecked.push(node);
804
+ } else if (matchesAssertFunctionName(name, assertFunctionNames)) {
805
+ checkCallExpression(context.sourceCode.getAncestors(node));
806
+ }
807
+ },
808
+ "Program:exit"() {
809
+ unchecked.forEach((node) => {
810
+ context.report({
811
+ node: node.callee,
812
+ messageId: "noAssertions"
813
+ });
814
+ });
815
+ }
816
+ };
817
+ }
818
+ });
819
+
820
+ const RULE_NAME$R = "consistent-test-it";
821
+ const buildFixer = (callee, nodeName, preferredTestKeyword) => (fixer) => [
822
+ fixer.replaceText(
823
+ callee.type === utils.AST_NODE_TYPES.MemberExpression ? callee.object : callee,
824
+ getPreferredNodeName(nodeName, preferredTestKeyword)
825
+ )
826
+ ];
827
+ function getPreferredNodeName(nodeName, preferredTestKeyword) {
828
+ if (nodeName === TestCaseName.fit)
829
+ return "test.only";
830
+ return nodeName.startsWith("f") || nodeName.startsWith("x") ? nodeName.charAt(0) + preferredTestKeyword : preferredTestKeyword;
831
+ }
832
+ function getOppositeTestKeyword(test) {
833
+ if (test === TestCaseName.test)
834
+ return TestCaseName.it;
835
+ return TestCaseName.test;
836
+ }
837
+ const consistentTestIt = createEslintRule({
838
+ name: RULE_NAME$R,
839
+ meta: {
840
+ type: "suggestion",
841
+ fixable: "code",
842
+ docs: {
843
+ description: "enforce using test or it but not both",
844
+ recommended: false
845
+ },
846
+ messages: {
847
+ consistentMethod: "Prefer using {{ testFnKeyWork }} instead of {{ oppositeTestKeyword }}",
848
+ consistentMethodWithinDescribe: "Prefer using {{ testKeywordWithinDescribe }} instead of {{ oppositeTestKeyword }} within describe"
849
+ },
850
+ schema: [
851
+ {
852
+ type: "object",
853
+ properties: {
854
+ fn: {
855
+ type: "string",
856
+ enum: [TestCaseName.test, TestCaseName.it]
857
+ },
858
+ withinDescribe: {
859
+ type: "string",
860
+ enum: [TestCaseName.test, TestCaseName.it]
861
+ }
862
+ },
863
+ additionalProperties: false
864
+ }
865
+ ]
866
+ },
867
+ defaultOptions: [{ fn: TestCaseName.test, withinDescribe: TestCaseName.it }],
868
+ create(context) {
869
+ const config = context.options[0] ?? {};
870
+ const testFnKeyWork = config.fn || TestCaseName.test;
871
+ const testKeywordWithinDescribe = config?.withinDescribe || config?.fn || TestCaseName?.it;
872
+ const testFnDisabled = testFnKeyWork === testKeywordWithinDescribe ? testFnKeyWork : void 0;
873
+ let describeNestingLevel = 0;
874
+ return {
875
+ ImportDeclaration(node) {
876
+ if (testFnDisabled == null)
877
+ return;
878
+ if (node.source.type !== "Literal" || node.source.value !== "vitest")
879
+ return;
880
+ const oppositeTestKeyword = getOppositeTestKeyword(testFnDisabled);
881
+ for (const specifier of node.specifiers) {
882
+ if (specifier.type !== "ImportSpecifier")
883
+ continue;
884
+ if (specifier.local.name !== specifier.imported.name)
885
+ continue;
886
+ if (specifier.local.name === oppositeTestKeyword) {
887
+ context.report({
888
+ node: specifier,
889
+ data: { testFnKeyWork, oppositeTestKeyword },
890
+ messageId: "consistentMethod",
891
+ fix: (fixer) => fixer.replaceText(
892
+ specifier.local,
893
+ testFnDisabled
894
+ )
895
+ });
896
+ }
897
+ }
898
+ },
899
+ CallExpression(node) {
900
+ if (node.callee.type === utils.AST_NODE_TYPES.Identifier && node.callee.name === "bench")
901
+ return;
902
+ const vitestFnCall = parseVitestFnCall(node, context);
903
+ if (!vitestFnCall)
904
+ return;
905
+ if (vitestFnCall.type === "describe") {
906
+ describeNestingLevel++;
907
+ return;
908
+ }
909
+ const funcNode = node.callee.type === utils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee.type === utils.AST_NODE_TYPES.CallExpression ? node.callee.callee : node.callee;
910
+ if (vitestFnCall.type === "test" && describeNestingLevel === 0 && !vitestFnCall.name.endsWith(testFnKeyWork)) {
911
+ const oppositeTestKeyword = getOppositeTestKeyword(testFnKeyWork);
912
+ context.report({
913
+ node: node.callee,
914
+ data: { testFnKeyWork, oppositeTestKeyword },
915
+ messageId: "consistentMethod",
916
+ fix: buildFixer(funcNode, vitestFnCall.name, testFnKeyWork)
917
+ });
918
+ } else if (vitestFnCall.type === "test" && describeNestingLevel > 0 && !vitestFnCall.name.endsWith(testKeywordWithinDescribe)) {
919
+ const oppositeTestKeyword = getOppositeTestKeyword(testKeywordWithinDescribe);
920
+ context.report({
921
+ messageId: "consistentMethodWithinDescribe",
922
+ node: node.callee,
923
+ data: { testKeywordWithinDescribe, oppositeTestKeyword },
924
+ fix: buildFixer(funcNode, vitestFnCall.name, testKeywordWithinDescribe)
925
+ });
926
+ }
927
+ },
928
+ "CallExpression:exit"(node) {
929
+ if (isTypeOfVitestFnCall(node, context, ["describe"]))
930
+ describeNestingLevel--;
931
+ }
932
+ };
933
+ }
934
+ });
935
+
936
+ const RULE_NAME$Q = "prefer-to-be";
937
+ const isNullLiteral = (node) => node.type === utils.AST_NODE_TYPES.Literal && node.value === null;
938
+ const isNullEqualityMatcher = (expectFnCall) => isNullLiteral(getFirstMatcherArg(expectFnCall));
939
+ const isFirstArgumentIdentifier = (expectFnCall, name) => isIdentifier(getFirstMatcherArg(expectFnCall), name);
940
+ const isFloat = (v) => Math.floor(v) !== Math.ceil(v);
941
+ const shouldUseToBe = (expectFnCall) => {
942
+ let firstArg = getFirstMatcherArg(expectFnCall);
943
+ if (firstArg.type === utils.AST_NODE_TYPES.Literal && typeof firstArg.value === "number" && isFloat(firstArg.value))
944
+ return false;
945
+ if (firstArg.type === utils.AST_NODE_TYPES.UnaryExpression && firstArg.operator === "-")
946
+ firstArg = firstArg.argument;
947
+ if (firstArg.type === utils.AST_NODE_TYPES.Literal) {
948
+ return !("regex" in firstArg);
949
+ }
950
+ return firstArg.type === utils.AST_NODE_TYPES.TemplateLiteral;
951
+ };
952
+ const reportPreferToBe = (context, whatToBe, expectFnCall, func, modifierNode) => {
953
+ context.report({
954
+ messageId: `useToBe${whatToBe}`,
955
+ fix(fixer) {
956
+ const fixes = [
957
+ replaceAccessorFixer(fixer, expectFnCall.matcher, `toBe${whatToBe}`)
958
+ ];
959
+ if (expectFnCall.args?.length && whatToBe !== "")
960
+ fixes.push(removeExtraArgumentsFixer(fixer, context, func, 0));
961
+ if (modifierNode) {
962
+ fixes.push(
963
+ fixer.removeRange([modifierNode.range[0] - 1, modifierNode.range[1]])
964
+ );
965
+ }
966
+ return fixes;
967
+ },
968
+ node: expectFnCall.matcher
969
+ });
970
+ };
971
+ const preferToBe = createEslintRule({
972
+ name: RULE_NAME$Q,
973
+ meta: {
974
+ type: "suggestion",
975
+ docs: {
976
+ description: "enforce using toBe()",
977
+ recommended: false
978
+ },
979
+ schema: [],
980
+ fixable: "code",
981
+ messages: {
982
+ useToBe: "Use `toBe` instead",
983
+ useToBeUndefined: "Use `toBeUndefined()` instead",
984
+ useToBeDefined: "Use `toBeDefined()` instead",
985
+ useToBeNull: "Use `toBeNull()` instead",
986
+ useToBeNaN: "Use `toBeNaN()` instead"
987
+ }
988
+ },
989
+ defaultOptions: [],
990
+ create(context) {
991
+ return {
992
+ CallExpression(node) {
993
+ const vitestFnCall = parseVitestFnCall(node, context);
994
+ if (vitestFnCall?.type !== "expect")
995
+ return;
996
+ const matcherName = getAccessorValue(vitestFnCall.matcher);
997
+ const notModifier = vitestFnCall.modifiers.find((node2) => getAccessorValue(node2) === "not");
998
+ if (notModifier && ["toBeUndefined", "toBeDefined"].includes(matcherName)) {
999
+ reportPreferToBe(context, matcherName === "toBeDefined" ? "Undefined" : "Defined", vitestFnCall, node, notModifier);
1000
+ return;
1001
+ }
1002
+ if (!EqualityMatcher.hasOwnProperty(matcherName) || vitestFnCall.args.length === 0)
1003
+ return;
1004
+ if (isNullEqualityMatcher(vitestFnCall)) {
1005
+ reportPreferToBe(context, "Null", vitestFnCall, node);
1006
+ return;
1007
+ }
1008
+ if (isFirstArgumentIdentifier(vitestFnCall, "undefined")) {
1009
+ const name = notModifier ? "Defined" : "Undefined";
1010
+ reportPreferToBe(context, name, vitestFnCall, node);
1011
+ return;
1012
+ }
1013
+ if (isFirstArgumentIdentifier(vitestFnCall, "NaN")) {
1014
+ reportPreferToBe(context, "NaN", vitestFnCall, node);
1015
+ return;
1016
+ }
1017
+ if (shouldUseToBe(vitestFnCall) && matcherName !== EqualityMatcher.toBe)
1018
+ reportPreferToBe(context, "", vitestFnCall, node);
1019
+ }
1020
+ };
1021
+ }
1022
+ });
1023
+
1024
+ const RULE_NAME$P = "no-hooks";
1025
+ const noHooks = createEslintRule({
1026
+ name: RULE_NAME$P,
1027
+ meta: {
1028
+ type: "suggestion",
1029
+ docs: {
1030
+ description: "disallow setup and teardown hooks",
1031
+ recommended: false
1032
+ },
1033
+ schema: [{
1034
+ type: "object",
1035
+ properties: {
1036
+ allow: {
1037
+ type: "array",
1038
+ //@ts-ignore
1039
+ contains: ["beforeAll", "beforeEach", "afterAll", "afterEach"]
1040
+ }
1041
+ },
1042
+ additionalProperties: false
1043
+ }],
1044
+ messages: {
1045
+ unexpectedHook: "Unexpected '{{ hookName }}' hook"
1046
+ }
1047
+ },
1048
+ defaultOptions: [{ allow: [] }],
1049
+ create(context, [{ allow = [] }]) {
1050
+ return {
1051
+ CallExpression(node) {
1052
+ const vitestFnCall = parseVitestFnCall(node, context);
1053
+ if (vitestFnCall?.type === "hook" && !allow.includes(vitestFnCall.name)) {
1054
+ context.report({
1055
+ node,
1056
+ messageId: "unexpectedHook",
1057
+ data: { hookName: vitestFnCall.name }
1058
+ });
1059
+ }
1060
+ }
1061
+ };
1062
+ }
1063
+ });
1064
+
1065
+ const RULE_NAME$O = "no-restricted-vi-methods";
1066
+ const noRestrictedViMethods = createEslintRule({
1067
+ name: RULE_NAME$O,
1068
+ meta: {
1069
+ type: "suggestion",
1070
+ docs: {
1071
+ description: "disallow specific `vi.` methods",
1072
+ recommended: false
1073
+ },
1074
+ schema: [{
1075
+ type: "object",
1076
+ additionalProperties: { type: ["string", "null"] }
1077
+ }],
1078
+ messages: {
1079
+ restrictedViMethod: "Use of `{{ restriction }}` is disallowed",
1080
+ restrictedViMethodWithMessage: "{{ message }}"
1081
+ }
1082
+ },
1083
+ defaultOptions: [{}],
1084
+ create(context, [restrictedMethods]) {
1085
+ return {
1086
+ CallExpression(node) {
1087
+ const vitestFnCall = parseVitestFnCall(node, context);
1088
+ if (vitestFnCall?.type !== "vi" || vitestFnCall.members.length === 0)
1089
+ return;
1090
+ const method = getAccessorValue(vitestFnCall.members[0]);
1091
+ if (method in restrictedMethods) {
1092
+ const message = restrictedMethods[method];
1093
+ context.report({
1094
+ messageId: message ? "restrictedViMethodWithMessage" : "restrictedViMethod",
1095
+ data: { message, restriction: method },
1096
+ loc: {
1097
+ start: vitestFnCall.members[0].loc.start,
1098
+ end: vitestFnCall.members[vitestFnCall.members.length - 1].loc.end
1099
+ }
1100
+ });
1101
+ }
1102
+ }
1103
+ };
1104
+ }
1105
+ });
1106
+
1107
+ const RULE_NAME$N = "consistent-test-filename";
1108
+ const defaultPattern = /.*\.test\.[tj]sx?$/;
1109
+ const defaultTestsPattern = /.*\.(test|spec)\.[tj]sx?$/;
1110
+ const consistentTestFilename = createEslintRule({
1111
+ name: RULE_NAME$N,
1112
+ meta: {
1113
+ type: "problem",
1114
+ docs: {
1115
+ recommended: false,
1116
+ requiresTypeChecking: false,
1117
+ description: "require .spec test file pattern"
1118
+ },
1119
+ messages: {
1120
+ consistentTestFilename: "use test file name pattern {{pattern}}"
1121
+ },
1122
+ schema: [
1123
+ {
1124
+ type: "object",
1125
+ additionalProperties: false,
1126
+ properties: {
1127
+ pattern: {
1128
+ //@ts-ignore
1129
+ format: "regex",
1130
+ default: defaultPattern.source
1131
+ },
1132
+ allTestPattern: {
1133
+ //@ts-ignore
1134
+ format: "regex",
1135
+ default: defaultTestsPattern.source
1136
+ }
1137
+ }
1138
+ }
1139
+ ]
1140
+ },
1141
+ defaultOptions: [{ pattern: defaultTestsPattern.source, allTestPattern: defaultTestsPattern.source }],
1142
+ create: (context) => {
1143
+ const config = context.options[0] ?? {};
1144
+ const { pattern: patternRaw = defaultPattern, allTestPattern: allTestPatternRaw = defaultTestsPattern } = config;
1145
+ const pattern = typeof patternRaw === "string" ? new RegExp(patternRaw) : patternRaw;
1146
+ const testPattern = typeof allTestPatternRaw === "string" ? new RegExp(allTestPatternRaw) : allTestPatternRaw;
1147
+ const filename = path__namespace.basename(context.filename);
1148
+ if (!testPattern.test(filename))
1149
+ return {};
1150
+ return {
1151
+ Program: (p) => {
1152
+ if (!pattern.test(filename)) {
1153
+ context.report({
1154
+ node: p,
1155
+ messageId: "consistentTestFilename",
1156
+ data: {
1157
+ pattern: pattern.source
1158
+ }
1159
+ });
1160
+ }
1161
+ }
1162
+ };
1163
+ }
1164
+ });
1165
+
1166
+ const RULE_NAME$M = "max-expects";
1167
+ const maxExpect = createEslintRule({
1168
+ name: RULE_NAME$M,
1169
+ meta: {
1170
+ docs: {
1171
+ requiresTypeChecking: false,
1172
+ recommended: false,
1173
+ description: "enforce a maximum number of expect per test"
1174
+ },
1175
+ messages: {
1176
+ maxExpect: "Too many assertion calls ({{count}}). Maximum is {{max}}."
1177
+ },
1178
+ type: "suggestion",
1179
+ schema: [
1180
+ {
1181
+ type: "object",
1182
+ properties: {
1183
+ max: {
1184
+ type: "number"
1185
+ }
1186
+ },
1187
+ additionalProperties: false
1188
+ }
1189
+ ]
1190
+ },
1191
+ defaultOptions: [{ max: 5 }],
1192
+ create(context, [{ max }]) {
1193
+ let assertsCount = 0;
1194
+ const resetAssertCount = (node) => {
1195
+ const isFunctionTest = node.parent?.type !== utils.AST_NODE_TYPES.CallExpression || isTypeOfVitestFnCall(node.parent, context, ["test"]);
1196
+ if (isFunctionTest)
1197
+ assertsCount = 0;
1198
+ };
1199
+ return {
1200
+ "FunctionExpression": resetAssertCount,
1201
+ "FunctionExpression:exit": resetAssertCount,
1202
+ "ArrowFunctionExpression": resetAssertCount,
1203
+ "ArrowFunctionExpression:exit": resetAssertCount,
1204
+ CallExpression(node) {
1205
+ const vitestFnCall = parseVitestFnCall(node, context);
1206
+ if (vitestFnCall?.type !== "expect" || vitestFnCall.head.node.parent?.type === utils.AST_NODE_TYPES.MemberExpression)
1207
+ return;
1208
+ assertsCount += 1;
1209
+ if (assertsCount > max) {
1210
+ context.report({
1211
+ node,
1212
+ messageId: "maxExpect",
1213
+ data: {
1214
+ count: assertsCount,
1215
+ max
1216
+ }
1217
+ });
1218
+ }
1219
+ }
1220
+ };
1221
+ }
1222
+ });
1223
+
1224
+ const RULE_NAME$L = "no-alias-methods";
1225
+ const noAliasMethod = createEslintRule({
1226
+ name: RULE_NAME$L,
1227
+ meta: {
1228
+ docs: {
1229
+ description: "disallow alias methods",
1230
+ requiresTypeChecking: false,
1231
+ recommended: false
1232
+ },
1233
+ messages: {
1234
+ noAliasMethods: "Replace {{ alias }}() with its canonical name {{ canonical }}()"
1235
+ },
1236
+ type: "suggestion",
1237
+ fixable: "code",
1238
+ schema: []
1239
+ },
1240
+ defaultOptions: [],
1241
+ create(context) {
1242
+ const methodNames = {
1243
+ toBeCalled: "toHaveBeenCalled",
1244
+ toBeCalledTimes: "toHaveBeenCalledTimes",
1245
+ toBeCalledWith: "toHaveBeenCalledWith",
1246
+ lastCalledWith: "toHaveBeenLastCalledWith",
1247
+ nthCalledWith: "toHaveBeenNthCalledWith",
1248
+ toReturn: "toHaveReturned",
1249
+ toReturnTimes: "toHaveReturnedTimes",
1250
+ toReturnWith: "toHaveReturnedWith",
1251
+ lastReturnedWith: "toHaveLastReturnedWith",
1252
+ nthReturnedWith: "toHaveNthReturnedWith",
1253
+ toThrowError: "toThrow"
1254
+ };
1255
+ return {
1256
+ CallExpression(node) {
1257
+ const vitestFnCall = parseVitestFnCall(node, context);
1258
+ if (vitestFnCall?.type !== "expect")
1259
+ return;
1260
+ const { matcher } = vitestFnCall;
1261
+ const alias = getAccessorValue(matcher);
1262
+ if (alias in methodNames) {
1263
+ const canonical = methodNames[alias];
1264
+ context.report({
1265
+ messageId: "noAliasMethods",
1266
+ data: { alias, canonical },
1267
+ node: matcher,
1268
+ fix: (fixer) => [replaceAccessorFixer(fixer, matcher, canonical)]
1269
+ });
1270
+ }
1271
+ }
1272
+ };
1273
+ }
1274
+ });
1275
+
1276
+ const RULE_NAME$K = "no-commented-out-tests";
1277
+ function hasTests(node) {
1278
+ return /^\s*[xf]?(test|it|describe)(\.\w+|\[['"]\w+['"]\])?\s*\(/mu.test(node.value);
1279
+ }
1280
+ const noCommentedOutTests = createEslintRule({
1281
+ name: RULE_NAME$K,
1282
+ meta: {
1283
+ docs: {
1284
+ description: "disallow commented out tests",
1285
+ requiresTypeChecking: false,
1286
+ recommended: false
1287
+ },
1288
+ messages: {
1289
+ noCommentedOutTests: "Remove commented out tests. You may want to use `skip` or `only` instead."
1290
+ },
1291
+ schema: [],
1292
+ type: "suggestion"
1293
+ },
1294
+ defaultOptions: [],
1295
+ create(context) {
1296
+ const { sourceCode } = context;
1297
+ function checkNodeForCommentedOutTests(node) {
1298
+ if (!hasTests(node))
1299
+ return;
1300
+ context.report({ messageId: "noCommentedOutTests", node });
1301
+ }
1302
+ return {
1303
+ Program() {
1304
+ const comments = sourceCode.getAllComments();
1305
+ comments.forEach(checkNodeForCommentedOutTests);
1306
+ }
1307
+ };
1308
+ }
1309
+ });
1310
+
1311
+ const RULE_NAME$J = "no-conditional-expect";
1312
+ const isCatchCall = (node) => node.callee.type === utils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.callee.property, "catch");
1313
+ const noConditionalExpect = createEslintRule({
1314
+ name: RULE_NAME$J,
1315
+ meta: {
1316
+ type: "problem",
1317
+ docs: {
1318
+ description: "disallow conditional expects",
1319
+ requiresTypeChecking: false,
1320
+ recommended: false
1321
+ },
1322
+ messages: {
1323
+ noConditionalExpect: "Avoid calling `expect` inside conditional statements"
1324
+ },
1325
+ schema: []
1326
+ },
1327
+ defaultOptions: [],
1328
+ create(context) {
1329
+ let conditionalDepth = 0;
1330
+ let inTestCase = false;
1331
+ let inPromiseCatch = false;
1332
+ const increaseConditionalDepth = () => inTestCase && conditionalDepth++;
1333
+ const decreaseConditionalDepth = () => inTestCase && conditionalDepth--;
1334
+ return {
1335
+ FunctionDeclaration(node) {
1336
+ const declaredVariables = context.sourceCode.getDeclaredVariables(node);
1337
+ const testCallExpressions = getTestCallExpressionsFromDeclaredVariables(declaredVariables, context);
1338
+ if (testCallExpressions.length > 0)
1339
+ inTestCase = true;
1340
+ },
1341
+ CallExpression(node) {
1342
+ const { type: vitestFnCallType } = parseVitestFnCall(node, context) ?? {};
1343
+ if (vitestFnCallType === "test")
1344
+ inTestCase = true;
1345
+ if (isCatchCall(node))
1346
+ inPromiseCatch = true;
1347
+ if (inTestCase && vitestFnCallType === "expect" && conditionalDepth > 0) {
1348
+ context.report({
1349
+ messageId: "noConditionalExpect",
1350
+ node
1351
+ });
1352
+ }
1353
+ if (inPromiseCatch && vitestFnCallType === "expect") {
1354
+ context.report({
1355
+ messageId: "noConditionalExpect",
1356
+ node
1357
+ });
1358
+ }
1359
+ },
1360
+ "CallExpression:exit"(node) {
1361
+ if (isTypeOfVitestFnCall(node, context, ["test"]))
1362
+ inTestCase = false;
1363
+ if (isCatchCall(node))
1364
+ inPromiseCatch = false;
1365
+ },
1366
+ "CatchClause": increaseConditionalDepth,
1367
+ "CatchClause:exit": decreaseConditionalDepth,
1368
+ "IfStatement": increaseConditionalDepth,
1369
+ "IfStatement:exit": decreaseConditionalDepth,
1370
+ "SwitchStatement": increaseConditionalDepth,
1371
+ "SwitchStatement:exit": decreaseConditionalDepth,
1372
+ "ConditionalExpression": increaseConditionalDepth,
1373
+ "ConditionalExpression:exit": decreaseConditionalDepth,
1374
+ "LogicalExpression": increaseConditionalDepth,
1375
+ "LogicalExpression:exit": decreaseConditionalDepth
1376
+ };
1377
+ }
1378
+ });
1379
+
1380
+ const RULE_NAME$I = "no-import-node-test";
1381
+ const noImportNodeTest = createEslintRule({
1382
+ name: RULE_NAME$I,
1383
+ meta: {
1384
+ docs: {
1385
+ description: "disallow importing `node:test`",
1386
+ recommended: false
1387
+ },
1388
+ type: "suggestion",
1389
+ messages: {
1390
+ noImportNodeTest: "Import from `vitest` instead of `node:test`"
1391
+ },
1392
+ fixable: "code",
1393
+ schema: []
1394
+ },
1395
+ defaultOptions: [],
1396
+ create(context) {
1397
+ return {
1398
+ ImportDeclaration(node) {
1399
+ if (node.source.value === "node:test") {
1400
+ context.report({
1401
+ messageId: "noImportNodeTest",
1402
+ node,
1403
+ fix: (fixer) => fixer.replaceText(
1404
+ node.source,
1405
+ node.source.raw.replace("node:test", "vitest")
1406
+ )
1407
+ });
1408
+ }
1409
+ }
1410
+ };
1411
+ }
1412
+ });
1413
+
1414
+ const RULE_NAME$H = "no-conditional-in-test";
1415
+ const noConditionalInTest = createEslintRule({
1416
+ name: RULE_NAME$H,
1417
+ meta: {
1418
+ docs: {
1419
+ description: "disallow conditional tests",
1420
+ requiresTypeChecking: false,
1421
+ recommended: false
1422
+ },
1423
+ messages: {
1424
+ noConditionalInTest: "Remove conditional tests"
1425
+ },
1426
+ schema: [],
1427
+ type: "problem"
1428
+ },
1429
+ defaultOptions: [],
1430
+ create(context) {
1431
+ return {
1432
+ IfStatement(node) {
1433
+ if (node.parent?.parent?.parent?.type === "CallExpression" && isTypeOfVitestFnCall(node.parent?.parent?.parent, context, ["test", "it"])) {
1434
+ context.report({
1435
+ messageId: "noConditionalInTest",
1436
+ node
1437
+ });
1438
+ }
1439
+ }
1440
+ };
1441
+ }
1442
+ });
1443
+
1444
+ const RULE_NAME$G = "no-disabled-tests";
1445
+ const noDisabledTests = createEslintRule({
1446
+ name: RULE_NAME$G,
1447
+ meta: {
1448
+ type: "suggestion",
1449
+ docs: {
1450
+ description: "disallow disabled tests",
1451
+ recommended: false
1452
+ },
1453
+ messages: {
1454
+ missingFunction: "Test is missing function argument",
1455
+ pending: "Call to pending()",
1456
+ pendingSuite: "Call to pending() within test suite",
1457
+ pendingTest: "Call to pending() within test",
1458
+ disabledSuite: "Disabled test suite. If you want to skip a test suite temporarily, use .todo() instead.",
1459
+ disabledTest: "Disabled test. If you want to skip a test temporarily, use .todo() instead."
1460
+ },
1461
+ schema: []
1462
+ },
1463
+ defaultOptions: [],
1464
+ create(context) {
1465
+ let suiteDepth = 0;
1466
+ let testDepth = 0;
1467
+ return {
1468
+ CallExpression(node) {
1469
+ const vitestFnCall = parseVitestFnCall(node, context);
1470
+ if (!vitestFnCall)
1471
+ return;
1472
+ if (vitestFnCall.type === "describe")
1473
+ suiteDepth++;
1474
+ if (vitestFnCall.type === "test") {
1475
+ testDepth++;
1476
+ if (node.arguments.length < 2 && vitestFnCall.members.every((s) => getAccessorValue(s) === "skip")) {
1477
+ context.report({
1478
+ messageId: "missingFunction",
1479
+ node
1480
+ });
1481
+ }
1482
+ }
1483
+ const skipMember = vitestFnCall.members.find((s) => getAccessorValue(s) === "skip");
1484
+ if (vitestFnCall.name.startsWith("x") || skipMember !== void 0) {
1485
+ context.report({
1486
+ messageId: vitestFnCall.type === "describe" ? "disabledSuite" : "disabledTest",
1487
+ node: skipMember ?? vitestFnCall.head.node
1488
+ });
1489
+ }
1490
+ },
1491
+ "CallExpression:exit"(node) {
1492
+ const vitestFnCall = parseVitestFnCall(node, context);
1493
+ if (!vitestFnCall)
1494
+ return;
1495
+ if (vitestFnCall.type === "describe")
1496
+ suiteDepth--;
1497
+ if (vitestFnCall.type === "test")
1498
+ testDepth--;
1499
+ },
1500
+ 'CallExpression[callee.name="pending"]'(node) {
1501
+ const scope = context.sourceCode.getScope ? context.sourceCode.getScope(node) : context.getScope();
1502
+ if (resolveScope(scope, "pending"))
1503
+ return;
1504
+ if (testDepth > 0)
1505
+ context.report({ messageId: "pendingTest", node });
1506
+ else if (suiteDepth > 0)
1507
+ context.report({ messageId: "pendingSuite", node });
1508
+ else
1509
+ context.report({ messageId: "pending", node });
1510
+ }
1511
+ };
1512
+ }
1513
+ });
1514
+
1515
+ const RULE_NAME$F = "no-done-callback";
1516
+ const findCallbackArg = (node, isVitestEach, context) => {
1517
+ if (isVitestEach)
1518
+ return node.arguments[1];
1519
+ const vitestFnCall = parseVitestFnCall(node, context);
1520
+ if (vitestFnCall?.type === "hook" && node.arguments.length >= 1)
1521
+ return node.arguments[0];
1522
+ if (vitestFnCall?.type === "test" && node.arguments.length >= 2)
1523
+ return node.arguments[1];
1524
+ return null;
1525
+ };
1526
+ const noDoneCallback = createEslintRule({
1527
+ name: RULE_NAME$F,
1528
+ meta: {
1529
+ type: "suggestion",
1530
+ docs: {
1531
+ description: "disallow using a callback in asynchronous tests and hooks",
1532
+ recommended: false
1533
+ },
1534
+ deprecated: true,
1535
+ schema: [],
1536
+ messages: {
1537
+ noDoneCallback: "Return a promise instead of relying on callback parameter",
1538
+ suggestWrappingInPromise: "Wrap in `new Promise({{ callback }} => ...`",
1539
+ useAwaitInsteadOfCallback: "Use `await` instead of callback in async function"
1540
+ },
1541
+ hasSuggestions: true
1542
+ },
1543
+ defaultOptions: [],
1544
+ create(context) {
1545
+ return {
1546
+ CallExpression(node) {
1547
+ const isVitestEach = /\.each$|\.concurrent$/.test(getNodeName(node.callee) ?? "");
1548
+ if (isVitestEach && node.callee.type !== utils.AST_NODE_TYPES.TaggedTemplateExpression)
1549
+ return;
1550
+ const isInsideConcurrentTestOrDescribe = context.sourceCode.getAncestors(node).some((ancestor) => {
1551
+ if (ancestor.type !== utils.AST_NODE_TYPES.CallExpression)
1552
+ return false;
1553
+ const isNotInsideDescribeOrTest = !isTypeOfVitestFnCall(ancestor, context, ["describe", "test"]);
1554
+ if (isNotInsideDescribeOrTest)
1555
+ return false;
1556
+ const isTestRunningConcurrently = ancestor.callee.type === utils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(ancestor.callee.property, "concurrent");
1557
+ return isTestRunningConcurrently;
1558
+ });
1559
+ if (isInsideConcurrentTestOrDescribe)
1560
+ return;
1561
+ const callback = findCallbackArg(node, isVitestEach, context);
1562
+ const callbackArgIndex = Number(isVitestEach);
1563
+ if (!callback || !isFunction(callback) || callback.params.length !== 1 + callbackArgIndex)
1564
+ return;
1565
+ const argument = callback.params[callbackArgIndex];
1566
+ if (argument.type !== utils.AST_NODE_TYPES.Identifier) {
1567
+ context.report({
1568
+ node: argument,
1569
+ messageId: "noDoneCallback"
1570
+ });
1571
+ return;
1572
+ }
1573
+ if (callback.async) {
1574
+ context.report({
1575
+ node: argument,
1576
+ messageId: "useAwaitInsteadOfCallback"
1577
+ });
1578
+ return;
1579
+ }
1580
+ context.report({
1581
+ node,
1582
+ messageId: "noDoneCallback",
1583
+ suggest: [
1584
+ {
1585
+ messageId: "suggestWrappingInPromise",
1586
+ data: { callback: argument.name },
1587
+ fix(fixer) {
1588
+ const { body, params } = callback;
1589
+ const { sourceCode } = context;
1590
+ const firstBodyToken = sourceCode.getFirstToken(body);
1591
+ const lastBodyToken = sourceCode.getLastToken(body);
1592
+ const [firstParam] = params;
1593
+ const lastParam = params[params.length - 1];
1594
+ const tokenBeforeFirstParam = sourceCode.getTokenBefore(firstParam);
1595
+ let tokenAfterLastParam = sourceCode.getTokenAfter(lastParam);
1596
+ if (tokenAfterLastParam?.value === ",")
1597
+ tokenAfterLastParam = sourceCode.getTokenAfter(tokenAfterLastParam);
1598
+ if (!firstBodyToken || !lastBodyToken || !tokenBeforeFirstParam || !tokenAfterLastParam)
1599
+ throw new Error(`Unexpected null when attempting to fix ${context.filename} - please file an issue at https://github/veritem/eslint-plugin-vitest`);
1600
+ let argumentFix = fixer.replaceText(firstParam, "()");
1601
+ if (tokenBeforeFirstParam.value === "(" && tokenAfterLastParam.value === ")")
1602
+ argumentFix = fixer.removeRange([tokenBeforeFirstParam.range[1], tokenAfterLastParam.range[0]]);
1603
+ const newCallBack = argument.name;
1604
+ let beforeReplacement = `new Promise(${newCallBack} => `;
1605
+ let afterReplacement = ")";
1606
+ let replaceBefore = true;
1607
+ if (body.type === utils.AST_NODE_TYPES.BlockStatement) {
1608
+ const keyword = "return";
1609
+ beforeReplacement = `${keyword} ${beforeReplacement}{`;
1610
+ afterReplacement += "}";
1611
+ replaceBefore = false;
1612
+ }
1613
+ return [
1614
+ argumentFix,
1615
+ replaceBefore ? fixer.insertTextBefore(firstBodyToken, beforeReplacement) : fixer.insertTextAfter(firstBodyToken, beforeReplacement),
1616
+ fixer.insertTextAfter(lastBodyToken, afterReplacement)
1617
+ ];
1618
+ }
1619
+ }
1620
+ ]
1621
+ });
1622
+ }
1623
+ };
1624
+ }
1625
+ });
1626
+
1627
+ const RULE_NAME$E = "no-duplicate-hooks";
1628
+ const noDuplicateHooks = createEslintRule({
1629
+ name: RULE_NAME$E,
1630
+ meta: {
1631
+ docs: {
1632
+ recommended: false,
1633
+ description: "disallow duplicate hooks and teardown hooks",
1634
+ requiresTypeChecking: false
1635
+ },
1636
+ messages: {
1637
+ noDuplicateHooks: "Duplicate {{hook}} in describe block."
1638
+ },
1639
+ schema: [],
1640
+ type: "suggestion"
1641
+ },
1642
+ defaultOptions: [],
1643
+ create(context) {
1644
+ const hooksContexts = [{}];
1645
+ return {
1646
+ CallExpression(node) {
1647
+ var _a;
1648
+ const vitestFnCall = parseVitestFnCall(node, context);
1649
+ if (vitestFnCall?.type === "describe")
1650
+ hooksContexts.push({});
1651
+ if (vitestFnCall?.type !== "hook")
1652
+ return;
1653
+ const currentLayer = hooksContexts[hooksContexts.length - 1];
1654
+ currentLayer[_a = vitestFnCall.name] || (currentLayer[_a] = 0);
1655
+ currentLayer[vitestFnCall.name] += 1;
1656
+ if (currentLayer[vitestFnCall.name] > 1) {
1657
+ context.report({
1658
+ messageId: "noDuplicateHooks",
1659
+ data: { hook: vitestFnCall.name },
1660
+ node
1661
+ });
1662
+ }
1663
+ },
1664
+ "CallExpression:exit"(node) {
1665
+ if (isTypeOfVitestFnCall(node, context, ["describe"]))
1666
+ hooksContexts.pop();
1667
+ }
1668
+ };
1669
+ }
1670
+ });
1671
+
1672
+ const RULE_NAME$D = "no-large-snapshots";
1673
+ const reportOnViolation = (context, node, { maxSize: lineLimit = 50, allowedSnapshots = {} }) => {
1674
+ const startLine = node.loc.start.line;
1675
+ const endLine = node.loc.end.line;
1676
+ const lineCount = endLine - startLine;
1677
+ const allPathsAreAbsolute = Object.keys(allowedSnapshots).every(path.isAbsolute);
1678
+ if (!allPathsAreAbsolute)
1679
+ throw new Error("All paths for allowedSnapshots must be absolute. You can use JS config and `path.resolve`");
1680
+ let isAllowed = false;
1681
+ if (node.type === utils.AST_NODE_TYPES.ExpressionStatement && "left" in node.expression && node.expression.left.type === utils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.expression.left.property)) {
1682
+ const fileName = context.filename;
1683
+ const allowedSnapshotsInFile = allowedSnapshots[fileName];
1684
+ if (allowedSnapshotsInFile) {
1685
+ const snapshotName = getAccessorValue(node.expression.left.property);
1686
+ isAllowed = allowedSnapshotsInFile.some((name) => {
1687
+ if (name instanceof RegExp)
1688
+ return name.test(snapshotName);
1689
+ return snapshotName === name;
1690
+ });
1691
+ }
1692
+ }
1693
+ if (!isAllowed && lineCount > lineLimit) {
1694
+ context.report({
1695
+ node,
1696
+ messageId: lineLimit === 0 ? "noSnapShot" : "tooLongSnapShot",
1697
+ data: {
1698
+ lineCount,
1699
+ lineLimit
1700
+ }
1701
+ });
1702
+ }
1703
+ };
1704
+ const noLargeSnapshots = createEslintRule({
1705
+ name: RULE_NAME$D,
1706
+ meta: {
1707
+ docs: {
1708
+ description: "disallow large snapshots",
1709
+ recommended: false
1710
+ },
1711
+ messages: {
1712
+ noSnapShot: "`{{ lineCount }}`s should begin with lowercase",
1713
+ tooLongSnapShot: "Expected vitest snapshot to be smaller than {{ lineLimit }} lines but was {{ lineCount }} lines long"
1714
+ },
1715
+ type: "suggestion",
1716
+ schema: [
1717
+ {
1718
+ type: "object",
1719
+ properties: {
1720
+ maxSize: {
1721
+ type: "number"
1722
+ },
1723
+ inlineMaxSize: {
1724
+ type: "number"
1725
+ },
1726
+ allowedSnapshots: {
1727
+ type: "object",
1728
+ additionalProperties: { type: "array" }
1729
+ }
1730
+ },
1731
+ additionalProperties: false
1732
+ }
1733
+ ]
1734
+ },
1735
+ defaultOptions: [{}],
1736
+ create(context, [options]) {
1737
+ if (context.filename.endsWith(".snap")) {
1738
+ return {
1739
+ ExpressionStatement(node) {
1740
+ reportOnViolation(context, node, options);
1741
+ }
1742
+ };
1743
+ }
1744
+ return {
1745
+ CallExpression(node) {
1746
+ const vitestFnCall = parseVitestFnCall(node, context);
1747
+ if (vitestFnCall?.type !== "expect")
1748
+ return;
1749
+ if ([
1750
+ "toMatchInlineSnapshot",
1751
+ "toThrowErrorMatchingInlineSnapshot"
1752
+ ].includes(getAccessorValue(vitestFnCall.matcher)) && vitestFnCall.args.length) {
1753
+ reportOnViolation(context, vitestFnCall.args[0], {
1754
+ ...options,
1755
+ maxSize: options.inlineMaxSize ?? options.maxSize
1756
+ });
1757
+ }
1758
+ }
1759
+ };
1760
+ }
1761
+ });
1762
+
1763
+ const RULE_NAME$C = "no-interpolation-in-snapshots";
1764
+ const nonInterpolationInSnapShots = createEslintRule({
1765
+ name: RULE_NAME$C,
1766
+ meta: {
1767
+ type: "problem",
1768
+ docs: {
1769
+ description: "disallow string interpolation in snapshots",
1770
+ recommended: false
1771
+ },
1772
+ fixable: "code",
1773
+ schema: [],
1774
+ messages: {
1775
+ noInterpolationInSnapshots: "Do not use string interpolation in snapshots"
1776
+ }
1777
+ },
1778
+ defaultOptions: [],
1779
+ create(context) {
1780
+ return {
1781
+ CallExpression(node) {
1782
+ const vitestFnCall = parseVitestFnCall(node, context);
1783
+ if (vitestFnCall?.type !== "expect")
1784
+ return;
1785
+ if ([
1786
+ "toMatchInlineSnapshot",
1787
+ "toThrowErrorMatchingInlineSnapshot"
1788
+ ].includes(getAccessorValue(vitestFnCall.matcher))) {
1789
+ vitestFnCall.args.forEach((argument) => {
1790
+ if (argument.type === utils.AST_NODE_TYPES.TemplateLiteral && argument.expressions.length > 0) {
1791
+ context.report({
1792
+ messageId: "noInterpolationInSnapshots",
1793
+ node: argument
1794
+ });
1795
+ }
1796
+ });
1797
+ }
1798
+ }
1799
+ };
1800
+ }
1801
+ });
1802
+
1803
+ const mocksDirName = "__mocks__";
1804
+ const isMockPath = (path$1) => path$1.split(path.posix.sep).includes(mocksDirName);
1805
+ const isMockImportLiteral = (expression) => isStringNode(expression) && isMockPath(getStringValue(expression));
1806
+ const RULE_NAME$B = "no-mocks-import";
1807
+ const noMocksImport = createEslintRule({
1808
+ name: RULE_NAME$B,
1809
+ meta: {
1810
+ type: "problem",
1811
+ docs: {
1812
+ description: "disallow importing from __mocks__ directory",
1813
+ recommended: false
1814
+ },
1815
+ messages: {
1816
+ noMocksImport: `Mocks should not be manually imported from a ${mocksDirName} directory. Instead use \`jest.mock\` and import from the original module path.`
1817
+ },
1818
+ schema: []
1819
+ },
1820
+ defaultOptions: [],
1821
+ create(context) {
1822
+ return {
1823
+ ImportDeclaration(node) {
1824
+ if (isMockImportLiteral(node.source))
1825
+ context.report({ node, messageId: "noMocksImport" });
1826
+ },
1827
+ 'CallExpression[callee.name="require"]'(node) {
1828
+ const [args] = node.arguments;
1829
+ if (args && isMockImportLiteral(args))
1830
+ context.report({ node: args, messageId: "noMocksImport" });
1831
+ }
1832
+ };
1833
+ }
1834
+ });
1835
+
1836
+ const RULE_NAME$A = "no-restricted-matchers";
1837
+ const isChainRestricted = (chain, restriction) => {
1838
+ if (ModifierName.hasOwnProperty(restriction) || restriction.endsWith(".not"))
1839
+ return chain.startsWith(restriction);
1840
+ return chain === restriction;
1841
+ };
1842
+ const noRestrictedMatchers = createEslintRule({
1843
+ name: RULE_NAME$A,
1844
+ meta: {
1845
+ docs: {
1846
+ description: "disallow the use of certain matchers",
1847
+ recommended: false
1848
+ },
1849
+ type: "suggestion",
1850
+ schema: [
1851
+ {
1852
+ type: "object",
1853
+ additionalProperties: {
1854
+ type: ["string", "null"]
1855
+ }
1856
+ }
1857
+ ],
1858
+ messages: {
1859
+ restrictedChain: "use of {{ restriction }} is disallowed",
1860
+ restrictedChainWithMessage: "{{ message }}"
1861
+ }
1862
+ },
1863
+ defaultOptions: [{}],
1864
+ create(context, [restrictedChains]) {
1865
+ return {
1866
+ CallExpression(node) {
1867
+ const vitestFnCall = parseVitestFnCall(node, context);
1868
+ if (vitestFnCall?.type !== "expect")
1869
+ return;
1870
+ const chain = vitestFnCall.members.map((node2) => getAccessorValue(node2)).join(".");
1871
+ for (const [restriction, message] of Object.entries(restrictedChains)) {
1872
+ if (isChainRestricted(chain, restriction)) {
1873
+ context.report({
1874
+ messageId: message ? "restrictedChainWithMessage" : "restrictedChain",
1875
+ data: { message, restriction },
1876
+ loc: {
1877
+ start: vitestFnCall.members[0].loc.start,
1878
+ end: vitestFnCall.members[vitestFnCall.members.length - 1].loc.end
1879
+ }
1880
+ });
1881
+ break;
1882
+ }
1883
+ }
1884
+ }
1885
+ };
1886
+ }
1887
+ });
1888
+
1889
+ const RULE_NAME$z = "no-standalone-expect";
1890
+ const getBlockType = (statement, context) => {
1891
+ const func = statement.parent;
1892
+ if (!func)
1893
+ throw new Error("Unexpected block statement. If you feel like this is a bug report https://github.com/veritem/eslint-plugin-vitest/issues/new");
1894
+ if (func.type === utils.AST_NODE_TYPES.FunctionDeclaration)
1895
+ return "function";
1896
+ if (isFunction(func) && func.parent) {
1897
+ const expr = func.parent;
1898
+ if (expr.type === utils.AST_NODE_TYPES.VariableDeclarator)
1899
+ return "function";
1900
+ if (expr.type === utils.AST_NODE_TYPES.CallExpression && isTypeOfVitestFnCall(expr, context, ["describe"]))
1901
+ return "describe";
1902
+ }
1903
+ return null;
1904
+ };
1905
+ const noStandaloneExpect = createEslintRule({
1906
+ name: RULE_NAME$z,
1907
+ meta: {
1908
+ docs: {
1909
+ description: "disallow using `expect` outside of `it` or `test` blocks",
1910
+ recommended: false
1911
+ },
1912
+ type: "suggestion",
1913
+ messages: {
1914
+ noStandaloneExpect: "Expect must be called inside a test block"
1915
+ },
1916
+ schema: [
1917
+ {
1918
+ properties: {
1919
+ additionaltestblockfunctions: {
1920
+ //@ts-ignore
1921
+ type: "array",
1922
+ //@ts-ignore
1923
+ items: { type: `string` }
1924
+ }
1925
+ },
1926
+ additionalproperties: false
1927
+ }
1928
+ ]
1929
+ },
1930
+ defaultOptions: [{ additionalTestBlockFunctions: [] }],
1931
+ create(context, [{ additionalTestBlockFunctions = [] }]) {
1932
+ const callStack = [];
1933
+ const isCustomTestBlockFunction = (node) => additionalTestBlockFunctions.includes(getNodeName(node) || "");
1934
+ return {
1935
+ CallExpression(node) {
1936
+ const vitestFnCall = parseVitestFnCall(node, context);
1937
+ if (vitestFnCall?.type === "expect") {
1938
+ if (vitestFnCall.head.node.parent?.type === utils.AST_NODE_TYPES.MemberExpression && vitestFnCall.members.length === 1 && !["assertions", "hasAssertions"].includes(
1939
+ getAccessorValue(vitestFnCall.members[0])
1940
+ ))
1941
+ return;
1942
+ const parent = callStack[callStack.length - 1];
1943
+ if (!parent || parent === DescribeAlias.describe)
1944
+ context.report({ node, messageId: "noStandaloneExpect" });
1945
+ return;
1946
+ }
1947
+ if (vitestFnCall?.type === "test" || isCustomTestBlockFunction(node))
1948
+ callStack.push("test");
1949
+ if (node.callee.type === utils.AST_NODE_TYPES.TaggedTemplateExpression)
1950
+ callStack.push("template");
1951
+ },
1952
+ "CallExpression:exit"(node) {
1953
+ const top = callStack[callStack.length - 1];
1954
+ if (top === "test" && (isTypeOfVitestFnCall(node, context, ["test"]) || isCustomTestBlockFunction(node)) && node.callee.type !== utils.AST_NODE_TYPES.MemberExpression || top === "template" && node.callee.type === utils.AST_NODE_TYPES.TaggedTemplateExpression)
1955
+ callStack.pop();
1956
+ },
1957
+ BlockStatement(statement) {
1958
+ const blockType = getBlockType(statement, context);
1959
+ if (blockType)
1960
+ callStack.push(blockType);
1961
+ },
1962
+ "BlockStatement:exit"(statement) {
1963
+ const blockType = getBlockType(statement, context);
1964
+ if (blockType)
1965
+ callStack.pop();
1966
+ },
1967
+ ArrowFunctionExpression(node) {
1968
+ if (node.parent?.type !== utils.AST_NODE_TYPES.CallExpression)
1969
+ callStack.push("arrow");
1970
+ },
1971
+ "ArrowFunctionExpression:exit"() {
1972
+ if (callStack[callStack.length - 1] === "arrow")
1973
+ callStack.pop();
1974
+ }
1975
+ };
1976
+ }
1977
+ });
1978
+
1979
+ const RULE_NAME$y = "no-test-prefixes";
1980
+ const noTestPrefixes = createEslintRule({
1981
+ name: RULE_NAME$y,
1982
+ meta: {
1983
+ docs: {
1984
+ description: "disallow using `test` as a prefix",
1985
+ recommended: false
1986
+ },
1987
+ type: "suggestion",
1988
+ messages: {
1989
+ usePreferredName: 'Use "{{preferredNodeName}}" instead'
1990
+ },
1991
+ fixable: "code",
1992
+ schema: []
1993
+ },
1994
+ defaultOptions: [],
1995
+ create(context) {
1996
+ return {
1997
+ CallExpression(node) {
1998
+ const vitestFnCall = parseVitestFnCall(node, context);
1999
+ if (vitestFnCall?.type !== "describe" && vitestFnCall?.type !== "test")
2000
+ return;
2001
+ if (vitestFnCall.name[0] !== "f" && vitestFnCall.name[0] !== "x")
2002
+ return;
2003
+ const preferredNodeName = [
2004
+ vitestFnCall.name.slice(1),
2005
+ vitestFnCall.name[0] === "f" ? "only" : "skip",
2006
+ ...vitestFnCall.members.map((m) => getAccessorValue(m))
2007
+ ].join(".");
2008
+ const funcNode = node.callee.type === utils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee.type === utils.AST_NODE_TYPES.CallExpression ? node.callee.callee : node.callee;
2009
+ context.report({
2010
+ messageId: "usePreferredName",
2011
+ node: node.callee,
2012
+ data: { preferredNodeName },
2013
+ fix: (fixer) => [fixer.replaceText(funcNode, preferredNodeName)]
2014
+ });
2015
+ }
2016
+ };
2017
+ }
2018
+ });
2019
+
2020
+ const RULE_NAME$x = "no-test-return-statement";
2021
+ const getBody = (args) => {
2022
+ const [, secondArg] = args;
2023
+ if (secondArg && isFunction(secondArg) && secondArg.body.type === utils.AST_NODE_TYPES.BlockStatement)
2024
+ return secondArg.body.body;
2025
+ return [];
2026
+ };
2027
+ const noTestReturnStatement = createEslintRule({
2028
+ name: RULE_NAME$x,
2029
+ meta: {
2030
+ type: "problem",
2031
+ docs: {
2032
+ description: "disallow return statements in tests",
2033
+ recommended: false
2034
+ },
2035
+ schema: [],
2036
+ messages: {
2037
+ noTestReturnStatement: "Return statements are not allowed in tests"
2038
+ }
2039
+ },
2040
+ defaultOptions: [],
2041
+ create(context) {
2042
+ return {
2043
+ CallExpression(node) {
2044
+ if (!isTypeOfVitestFnCall(node, context, ["test"]))
2045
+ return;
2046
+ const body = getBody(node.arguments);
2047
+ const returnStmt = body.find((stmt) => stmt.type === utils.AST_NODE_TYPES.ReturnStatement);
2048
+ if (!returnStmt)
2049
+ return;
2050
+ context.report({ messageId: "noTestReturnStatement", node: returnStmt });
2051
+ },
2052
+ FunctionDeclaration(node) {
2053
+ const declaredVariables = context.sourceCode.getDeclaredVariables(node);
2054
+ const testCallExpressions = getTestCallExpressionsFromDeclaredVariables(declaredVariables, context);
2055
+ if (testCallExpressions.length === 0)
2056
+ return;
2057
+ const returnStmt = node.body.body.find((stmt) => stmt.type === utils.AST_NODE_TYPES.ReturnStatement);
2058
+ if (!returnStmt)
2059
+ return;
2060
+ context.report({ messageId: "noTestReturnStatement", node: returnStmt });
2061
+ }
2062
+ };
2063
+ }
2064
+ });
2065
+
2066
+ const RULE_NAME$w = "prefer-called-with";
2067
+ const preferCalledWith = createEslintRule({
2068
+ name: RULE_NAME$w,
2069
+ meta: {
2070
+ docs: {
2071
+ description: "enforce using `toBeCalledWith()` or `toHaveBeenCalledWith()`",
2072
+ recommended: false
2073
+ },
2074
+ messages: {
2075
+ preferCalledWith: "Prefer {{ matcherName }}With(/* expected args */)"
2076
+ },
2077
+ type: "suggestion",
2078
+ fixable: "code",
2079
+ schema: []
2080
+ },
2081
+ defaultOptions: [],
2082
+ create(context) {
2083
+ return {
2084
+ CallExpression(node) {
2085
+ const vitestFnCall = parseVitestFnCall(node, context);
2086
+ if (vitestFnCall?.type !== "expect")
2087
+ return;
2088
+ if (vitestFnCall.modifiers.some(
2089
+ (node2) => getAccessorValue(node2) === "not"
2090
+ ))
2091
+ return;
2092
+ const { matcher } = vitestFnCall;
2093
+ const matcherName = getAccessorValue(matcher);
2094
+ if (["toBeCalled", "toHaveBeenCalled"].includes(matcherName)) {
2095
+ context.report({
2096
+ data: { matcherName },
2097
+ messageId: "preferCalledWith",
2098
+ node: matcher,
2099
+ fix: (fixer) => [fixer.replaceText(matcher, `${matcherName}With`)]
2100
+ });
2101
+ }
2102
+ }
2103
+ };
2104
+ }
2105
+ });
2106
+
2107
+ const RULE_NAME$v = "valid-title";
2108
+ const trimFXPrefix = (word) => ["f", "x"].includes(word.charAt(0)) ? word.substring(1) : word;
2109
+ const quoteStringValue = (node) => node.type === utils.AST_NODE_TYPES.TemplateLiteral ? `\`${node.quasis[0].value.raw}\`` : node.raw;
2110
+ const MatcherAndMessageSchema = {
2111
+ type: "array",
2112
+ items: { type: "string" },
2113
+ minItems: 1,
2114
+ maxItems: 2,
2115
+ additionalItems: false
2116
+ };
2117
+ const compileMatcherPattern = (matcherMaybeWithMessage) => {
2118
+ const [matcher, message] = Array.isArray(matcherMaybeWithMessage) ? matcherMaybeWithMessage : [matcherMaybeWithMessage];
2119
+ return [new RegExp(matcher, "u"), message];
2120
+ };
2121
+ function isFunctionType(type) {
2122
+ const symbol = type.getSymbol();
2123
+ if (!symbol) {
2124
+ return false;
2125
+ }
2126
+ return symbol.getDeclarations()?.some((declaration) => ts__default.isFunctionDeclaration(declaration) || ts__default.isMethodDeclaration(declaration) || ts__default.isFunctionExpression(declaration) || ts__default.isArrowFunction(declaration)) ?? false;
2127
+ }
2128
+ function isClassType(type) {
2129
+ const symbol = type.getSymbol();
2130
+ if (!symbol)
2131
+ return false;
2132
+ return symbol.getDeclarations()?.some((declaration) => ts__default.isClassDeclaration(declaration) || ts__default.isClassExpression(declaration)) ?? false;
2133
+ }
2134
+ const compileMatcherPatterns = (matchers) => {
2135
+ if (typeof matchers === "string" || Array.isArray(matchers)) {
2136
+ const compiledMatcher = compileMatcherPattern(matchers);
2137
+ return {
2138
+ describe: compiledMatcher,
2139
+ test: compiledMatcher,
2140
+ it: compiledMatcher
2141
+ };
2142
+ }
2143
+ return {
2144
+ describe: matchers.describe ? compileMatcherPattern(matchers.describe) : null,
2145
+ test: matchers.test ? compileMatcherPattern(matchers.test) : null,
2146
+ it: matchers.it ? compileMatcherPattern(matchers.it) : null
2147
+ };
2148
+ };
2149
+ const doesBinaryExpressionContainStringNode = (binaryExp) => {
2150
+ if (isStringNode(binaryExp.right))
2151
+ return true;
2152
+ if (binaryExp.left.type === utils.AST_NODE_TYPES.BinaryExpression)
2153
+ return doesBinaryExpressionContainStringNode(binaryExp.left);
2154
+ return isStringNode(binaryExp.left);
2155
+ };
2156
+ const validTitle = createEslintRule({
2157
+ name: RULE_NAME$v,
2158
+ meta: {
2159
+ docs: {
2160
+ description: "enforce valid titles",
2161
+ recommended: false
2162
+ },
2163
+ messages: {
2164
+ titleMustBeString: "Test title must be a string, a function or class name",
2165
+ emptyTitle: "{{functionName}} should not have an empty title",
2166
+ duplicatePrefix: "should not have duplicate prefix",
2167
+ accidentalSpace: "should not have leading or trailing spaces",
2168
+ disallowedWord: '"{{word}}" is not allowed in test title',
2169
+ mustNotMatch: "{{functionName}} should not match {{pattern}}",
2170
+ mustMatch: "{{functionName}} should match {{pattern}}",
2171
+ mustNotMatchCustom: "{{message}}",
2172
+ mustMatchCustom: "{{message}}"
2173
+ },
2174
+ type: "suggestion",
2175
+ schema: [
2176
+ {
2177
+ type: "object",
2178
+ properties: {
2179
+ ignoreTypeOfDescribeName: {
2180
+ type: "boolean",
2181
+ default: false
2182
+ },
2183
+ allowArguments: {
2184
+ type: "boolean",
2185
+ default: false
2186
+ },
2187
+ disallowedWords: {
2188
+ type: "array",
2189
+ items: { type: "string" }
2190
+ }
2191
+ },
2192
+ patternProperties: {
2193
+ [/^must(?:Not)?Match$/u.source]: {
2194
+ oneOf: [
2195
+ { type: "string" },
2196
+ MatcherAndMessageSchema,
2197
+ {
2198
+ type: "object",
2199
+ //@ts-ignore
2200
+ propertyNames: { type: "string", enum: ["describe", "test", "it"] },
2201
+ additionalProperties: {
2202
+ oneOf: [{ type: "string" }, MatcherAndMessageSchema]
2203
+ }
2204
+ }
2205
+ ]
2206
+ }
2207
+ },
2208
+ additionalProperties: false
2209
+ }
2210
+ ],
2211
+ fixable: "code"
2212
+ },
2213
+ defaultOptions: [{ ignoreTypeOfDescribeName: false, allowArguments: false, disallowedWords: [] }],
2214
+ create(context, [
2215
+ {
2216
+ ignoreTypeOfDescribeName,
2217
+ allowArguments,
2218
+ disallowedWords = [],
2219
+ mustNotMatch,
2220
+ mustMatch
2221
+ }
2222
+ ]) {
2223
+ const disallowedWordsRegexp = new RegExp(`\\b(${disallowedWords.join("|")})\\b`, "iu");
2224
+ const mustNotMatchPatterns = compileMatcherPatterns(mustNotMatch ?? {});
2225
+ const mustMatchPatterns = compileMatcherPatterns(mustMatch ?? {});
2226
+ const settings = parsePluginSettings(context.settings);
2227
+ return {
2228
+ CallExpression(node) {
2229
+ const vitestFnCall = parseVitestFnCall(node, context);
2230
+ if (vitestFnCall?.type !== "describe" && vitestFnCall?.type !== "test" && vitestFnCall?.type !== "it")
2231
+ return;
2232
+ const [argument] = node.arguments;
2233
+ if (settings.typecheck) {
2234
+ const services = utils.ESLintUtils.getParserServices(context);
2235
+ const type = services.getTypeAtLocation(argument);
2236
+ if (isFunctionType(type) || isClassType(type))
2237
+ return;
2238
+ }
2239
+ if (!argument || allowArguments && argument.type === utils.AST_NODE_TYPES.Identifier)
2240
+ return;
2241
+ if (!isStringNode(argument)) {
2242
+ if (argument.type === utils.AST_NODE_TYPES.BinaryExpression && doesBinaryExpressionContainStringNode(argument))
2243
+ return;
2244
+ if (argument.type !== utils.AST_NODE_TYPES.TemplateLiteral && !(ignoreTypeOfDescribeName && vitestFnCall.type === "describe")) {
2245
+ context.report({
2246
+ messageId: "titleMustBeString",
2247
+ loc: argument.loc
2248
+ });
2249
+ }
2250
+ return;
2251
+ }
2252
+ const title = getStringValue(argument);
2253
+ if (!title) {
2254
+ context.report({
2255
+ messageId: "emptyTitle",
2256
+ data: {
2257
+ functionName: vitestFnCall.type === "describe" ? DescribeAlias.describe : TestCaseName.test
2258
+ },
2259
+ node
2260
+ });
2261
+ return;
2262
+ }
2263
+ if (disallowedWords.length > 0) {
2264
+ const disallowedMatch = disallowedWordsRegexp.exec(title);
2265
+ if (disallowedMatch) {
2266
+ context.report({
2267
+ messageId: "disallowedWord",
2268
+ data: {
2269
+ word: disallowedMatch[1]
2270
+ },
2271
+ node: argument
2272
+ });
2273
+ return;
2274
+ }
2275
+ }
2276
+ if (title.trim().length !== title.length) {
2277
+ context.report({
2278
+ messageId: "accidentalSpace",
2279
+ node: argument,
2280
+ fix: (fixer) => [
2281
+ fixer.replaceTextRange(
2282
+ argument.range,
2283
+ quoteStringValue(argument).replace(/^([`'"]) +?/u, "$1").replace(/ +?([`'"])$/u, "$1")
2284
+ )
2285
+ ]
2286
+ });
2287
+ }
2288
+ const unPrefixedName = trimFXPrefix(vitestFnCall.name);
2289
+ const [firstWord] = title.split(" ");
2290
+ if (firstWord.toLowerCase() === unPrefixedName) {
2291
+ context.report({
2292
+ messageId: "duplicatePrefix",
2293
+ node: argument,
2294
+ fix: (fixer) => [
2295
+ fixer.replaceTextRange(
2296
+ argument.range,
2297
+ quoteStringValue(argument).replace(/^([`'"]).+? /u, "$1")
2298
+ )
2299
+ ]
2300
+ });
2301
+ }
2302
+ const vitestFnName = unPrefixedName;
2303
+ const [mustNotMatchPattern, mustNotMatchMessage] = mustNotMatchPatterns[vitestFnName] ?? [];
2304
+ if (mustNotMatchPattern) {
2305
+ if (mustNotMatchPattern.test(title)) {
2306
+ context.report({
2307
+ messageId: mustNotMatchMessage ? "mustNotMatchCustom" : "mustNotMatch",
2308
+ node: argument,
2309
+ data: {
2310
+ functionName: vitestFnName,
2311
+ pattern: mustNotMatchPattern,
2312
+ message: mustNotMatchMessage
2313
+ }
2314
+ });
2315
+ return;
2316
+ }
2317
+ }
2318
+ const [mustMatchPattern, mustMatchMessage] = mustMatchPatterns[vitestFnName] ?? [];
2319
+ if (mustMatchPattern) {
2320
+ if (!mustMatchPattern.test(title)) {
2321
+ context.report({
2322
+ messageId: mustMatchMessage ? "mustMatchCustom" : "mustMatch",
2323
+ node: argument,
2324
+ data: {
2325
+ functionName: vitestFnName,
2326
+ pattern: mustMatchPattern,
2327
+ message: mustMatchMessage
2328
+ }
2329
+ });
2330
+ }
2331
+ }
2332
+ }
2333
+ };
2334
+ }
2335
+ });
2336
+
2337
+ const RULE_NAME$u = "valid-expect";
2338
+ const defaultAsyncMatchers = ["toReject", "toResolve"];
2339
+ const getPromiseCallExpressionNode = (node) => {
2340
+ if (node.type === utils.AST_NODE_TYPES.ArrayExpression && node.parent && node.parent.type === utils.AST_NODE_TYPES.CallExpression)
2341
+ node = node.parent;
2342
+ if (node.type === utils.AST_NODE_TYPES.CallExpression && node.callee.type === utils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.callee.object, "Promise") && node.parent)
2343
+ return node;
2344
+ return null;
2345
+ };
2346
+ const promiseArrayExceptionKey = ({ start, end }) => `${start.line}:${start.column}-${end.line}:${end.column}`;
2347
+ function getParentIfThenified(node) {
2348
+ const grandParentNode = node.parent?.parent;
2349
+ if (grandParentNode && grandParentNode.type === utils.AST_NODE_TYPES.CallExpression && grandParentNode.callee.type === utils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(grandParentNode.callee.property) && ["then", "catch"].includes(getAccessorValue(grandParentNode.callee.property)) && grandParentNode.parent)
2350
+ return getParentIfThenified(grandParentNode);
2351
+ return node;
2352
+ }
2353
+ const findPromiseCallExpressionNode = (node) => node.parent?.parent && [utils.AST_NODE_TYPES.CallExpression, utils.AST_NODE_TYPES.ArrayExpression].includes(
2354
+ node.parent.type
2355
+ ) ? getPromiseCallExpressionNode(node.parent) : null;
2356
+ const isAcceptableReturnNode = (node, allowReturn) => {
2357
+ if (allowReturn && node.type === utils.AST_NODE_TYPES.ReturnStatement)
2358
+ return true;
2359
+ if (node.type === utils.AST_NODE_TYPES.ConditionalExpression && node.parent)
2360
+ return isAcceptableReturnNode(node.parent, allowReturn);
2361
+ return [
2362
+ utils.AST_NODE_TYPES.ArrowFunctionExpression,
2363
+ utils.AST_NODE_TYPES.AwaitExpression
2364
+ ].includes(node.type);
2365
+ };
2366
+ const validExpect = createEslintRule({
2367
+ name: RULE_NAME$u,
2368
+ meta: {
2369
+ docs: {
2370
+ description: "enforce valid `expect()` usage",
2371
+ recommended: false
2372
+ },
2373
+ messages: {
2374
+ tooManyArgs: "Expect takes most {{ amount}} argument{{s}}",
2375
+ notEnoughArgs: "Expect requires atleast {{ amount }} argument{{s}}",
2376
+ modifierUnknown: "Expect has unknown modifier",
2377
+ matcherNotFound: "Expect must have a corresponding matcher call.",
2378
+ matcherNotCalled: "Matchers must be called to assert.",
2379
+ asyncMustBeAwaited: "Async assertions must be awaited{{orReturned}}",
2380
+ promisesWithAsyncAssertionsMustBeAwaited: "Promises which return async assertions must be awaited{{orReturned}}"
2381
+ },
2382
+ type: "suggestion",
2383
+ schema: [
2384
+ {
2385
+ type: "object",
2386
+ properties: {
2387
+ alwaysAwait: {
2388
+ type: "boolean",
2389
+ default: false
2390
+ },
2391
+ asyncMatchers: {
2392
+ type: "array",
2393
+ items: { type: "string" }
2394
+ },
2395
+ minArgs: {
2396
+ type: "number",
2397
+ minimum: 1
2398
+ },
2399
+ maxArgs: {
2400
+ type: "number",
2401
+ minimum: 1
2402
+ }
2403
+ },
2404
+ additionalProperties: false
2405
+ }
2406
+ ]
2407
+ },
2408
+ defaultOptions: [{
2409
+ alwaysAwait: false,
2410
+ asyncMatchers: defaultAsyncMatchers,
2411
+ minArgs: 1,
2412
+ maxArgs: 1
2413
+ }],
2414
+ create: (context, [{ alwaysAwait, asyncMatchers = defaultAsyncMatchers, minArgs = 1, maxArgs = 1 }]) => {
2415
+ const arrayExceptions = /* @__PURE__ */ new Set();
2416
+ const pushPromiseArrayException = (loc) => arrayExceptions.add(promiseArrayExceptionKey(loc));
2417
+ const promiseArrayExceptionExists = (loc) => arrayExceptions.has(promiseArrayExceptionKey(loc));
2418
+ const findTopMostMemberExpression = (node) => {
2419
+ let topMostMemberExpression = node;
2420
+ let { parent } = node;
2421
+ while (parent) {
2422
+ if (parent.type !== utils.AST_NODE_TYPES.MemberExpression)
2423
+ break;
2424
+ topMostMemberExpression = parent;
2425
+ parent = parent.parent;
2426
+ }
2427
+ return topMostMemberExpression;
2428
+ };
2429
+ return {
2430
+ CallExpression(node) {
2431
+ const vitestFnCall = parseVitestFnCallWithReason(node, context);
2432
+ if (typeof vitestFnCall === "string") {
2433
+ const reportingNode = node.parent?.type === utils.AST_NODE_TYPES.MemberExpression ? findTopMostMemberExpression(node.parent).property : node;
2434
+ if (vitestFnCall === "matcher-not-found") {
2435
+ context.report({
2436
+ messageId: "matcherNotFound",
2437
+ node: reportingNode
2438
+ });
2439
+ return;
2440
+ }
2441
+ if (vitestFnCall === "matcher-not-called") {
2442
+ context.report({
2443
+ messageId: isSupportedAccessor(reportingNode) && ModifierName.hasOwnProperty(getAccessorValue(reportingNode)) ? "matcherNotFound" : "matcherNotCalled",
2444
+ node: reportingNode
2445
+ });
2446
+ }
2447
+ if (vitestFnCall === "modifier-unknown") {
2448
+ context.report({
2449
+ messageId: "modifierUnknown",
2450
+ node: reportingNode
2451
+ });
2452
+ return;
2453
+ }
2454
+ return;
2455
+ } else if (vitestFnCall?.type !== "expect") {
2456
+ return;
2457
+ }
2458
+ const { parent: expect } = vitestFnCall.head.node;
2459
+ if (expect?.type !== utils.AST_NODE_TYPES.CallExpression)
2460
+ return;
2461
+ if (expect.arguments.length < minArgs) {
2462
+ const expectLength = getAccessorValue(vitestFnCall.head.node).length;
2463
+ const loc = {
2464
+ start: {
2465
+ column: expect.loc.start.column + expectLength,
2466
+ line: expect.loc.start.line
2467
+ },
2468
+ end: {
2469
+ column: expect.loc.start.column + expectLength + 1,
2470
+ line: expect.loc.start.line
2471
+ }
2472
+ };
2473
+ context.report({
2474
+ messageId: "notEnoughArgs",
2475
+ data: { amount: minArgs, s: minArgs === 1 ? "" : "s" },
2476
+ node: expect,
2477
+ loc
2478
+ });
2479
+ }
2480
+ if (expect.arguments.length > maxArgs) {
2481
+ const { start } = expect.arguments[maxArgs].loc;
2482
+ const { end } = expect.arguments[expect.arguments.length - 1].loc;
2483
+ const loc = {
2484
+ start,
2485
+ end: {
2486
+ column: end.column + 1,
2487
+ line: end.line
2488
+ }
2489
+ };
2490
+ context.report({
2491
+ messageId: "tooManyArgs",
2492
+ data: { amount: maxArgs, s: maxArgs === 1 ? "" : "s" },
2493
+ node: expect,
2494
+ loc
2495
+ });
2496
+ }
2497
+ const { matcher } = vitestFnCall;
2498
+ const parentNode = matcher.parent.parent;
2499
+ const shouldBeAwaited = vitestFnCall.modifiers.some((nod) => getAccessorValue(nod) !== "not") || asyncMatchers.includes(getAccessorValue(matcher));
2500
+ if (!parentNode?.parent || !shouldBeAwaited)
2501
+ return;
2502
+ const isParentArrayExpression = parentNode.parent.type === utils.AST_NODE_TYPES.ArrayExpression;
2503
+ const orReturned = alwaysAwait ? "" : " or returned";
2504
+ const targetNode = getParentIfThenified(parentNode);
2505
+ const finalNode = findPromiseCallExpressionNode(targetNode) || targetNode;
2506
+ if (finalNode.parent && !isAcceptableReturnNode(finalNode.parent, !alwaysAwait) && !promiseArrayExceptionExists(finalNode.loc)) {
2507
+ context.report({
2508
+ loc: finalNode.loc,
2509
+ data: { orReturned },
2510
+ messageId: finalNode === targetNode ? "asyncMustBeAwaited" : "promisesWithAsyncAssertionsMustBeAwaited",
2511
+ node
2512
+ });
2513
+ if (isParentArrayExpression)
2514
+ pushPromiseArrayException(finalNode.loc);
2515
+ }
2516
+ }
2517
+ };
2518
+ }
2519
+ });
2520
+
2521
+ const isBooleanLiteral = (node) => node.type === utils.AST_NODE_TYPES.Literal && typeof node.value === "boolean";
2522
+ const isBooleanEqualityMatcher = (expectFnCall) => {
2523
+ const matcherName = getAccessorValue(expectFnCall.matcher);
2524
+ if (["toBeTruthy", "toBeFalsy"].includes(matcherName))
2525
+ return true;
2526
+ if (expectFnCall.args.length !== 1)
2527
+ return false;
2528
+ const arg = getFirstMatcherArg(expectFnCall);
2529
+ return EqualityMatcher.hasOwnProperty(matcherName) && isBooleanLiteral(arg);
2530
+ };
2531
+ const isInstanceOfBinaryExpression = (node, className) => node.type === utils.AST_NODE_TYPES.BinaryExpression && node.operator === "instanceof" && isSupportedAccessor(node.right, className);
2532
+ const hasOnlyOneArgument = (call) => call.arguments.length === 1;
2533
+ function getFilename(url) {
2534
+ return path.parse(path.basename(node_url.fileURLToPath(url))).name;
2535
+ }
2536
+
2537
+ const RULE_NAME$t = "prefer-to-be-object";
2538
+ const preferToBeObject = createEslintRule({
2539
+ name: RULE_NAME$t,
2540
+ meta: {
2541
+ type: "suggestion",
2542
+ docs: {
2543
+ description: "enforce using toBeObject()",
2544
+ recommended: false
2545
+ },
2546
+ fixable: "code",
2547
+ messages: {
2548
+ preferToBeObject: "Prefer toBeObject() to test if a value is an object."
2549
+ },
2550
+ schema: []
2551
+ },
2552
+ defaultOptions: [],
2553
+ create(context) {
2554
+ return {
2555
+ CallExpression(node) {
2556
+ const vitestFnCall = parseVitestFnCall(node, context);
2557
+ if (vitestFnCall?.type !== "expectTypeOf")
2558
+ return;
2559
+ if (isParsedInstanceOfMatcherCall(vitestFnCall, "Object")) {
2560
+ context.report({
2561
+ node: vitestFnCall.matcher,
2562
+ messageId: "preferToBeObject",
2563
+ fix: (fixer) => [
2564
+ fixer.replaceTextRange(
2565
+ [
2566
+ vitestFnCall.matcher.range[0],
2567
+ vitestFnCall.matcher.range[1] + "(Object)".length
2568
+ ],
2569
+ "toBeObject()"
2570
+ )
2571
+ ]
2572
+ });
2573
+ return;
2574
+ }
2575
+ const { parent: expectTypeOf } = vitestFnCall.head.node;
2576
+ if (expectTypeOf?.type !== utils.AST_NODE_TYPES.CallExpression)
2577
+ return;
2578
+ const [expectTypeOfArgs] = expectTypeOf.arguments;
2579
+ if (!expectTypeOfArgs || !isBooleanEqualityMatcher(vitestFnCall) || !isInstanceOfBinaryExpression(expectTypeOfArgs, "Object"))
2580
+ return;
2581
+ context.report({
2582
+ node: vitestFnCall.matcher,
2583
+ messageId: "preferToBeObject",
2584
+ fix(fixer) {
2585
+ const fixes = [
2586
+ fixer.replaceText(vitestFnCall.matcher, "toBeObject"),
2587
+ fixer.removeRange([expectTypeOfArgs.left.range[1], expectTypeOfArgs.range[1]])
2588
+ ];
2589
+ let invertCondition = getAccessorValue(vitestFnCall.matcher) === "toBeFalsy";
2590
+ if (vitestFnCall.args.length) {
2591
+ const [matcherArg] = vitestFnCall.args;
2592
+ fixes.push(fixer.remove(matcherArg));
2593
+ invertCondition = matcherArg.type === utils.AST_NODE_TYPES.Literal && followTypeAssertionChain(matcherArg).value === false;
2594
+ }
2595
+ if (invertCondition) {
2596
+ const notModifier = vitestFnCall.modifiers.find((node2) => getAccessorValue(node2) === "not");
2597
+ fixes.push(
2598
+ notModifier ? fixer.removeRange([
2599
+ notModifier.range[0] - 1,
2600
+ notModifier.range[1]
2601
+ ]) : fixer.insertTextBefore(vitestFnCall.matcher, "not.")
2602
+ );
2603
+ }
2604
+ return fixes;
2605
+ }
2606
+ });
2607
+ }
2608
+ };
2609
+ }
2610
+ });
2611
+
2612
+ const RULE_NAME$s = "prefer-to-be-truthy";
2613
+ const isTrueLiteral = (node) => node.type === utils.AST_NODE_TYPES.Literal && node.value === true;
2614
+ const preferToBeTruthy = createEslintRule({
2615
+ name: RULE_NAME$s,
2616
+ meta: {
2617
+ type: "suggestion",
2618
+ docs: {
2619
+ description: "enforce using `toBeTruthy`",
2620
+ recommended: false
2621
+ },
2622
+ messages: {
2623
+ preferToBeTruthy: "Prefer using `toBeTruthy` to test value is `true`"
2624
+ },
2625
+ fixable: "code",
2626
+ schema: []
2627
+ },
2628
+ defaultOptions: [],
2629
+ create(context) {
2630
+ return {
2631
+ CallExpression(node) {
2632
+ const vitestFnCall = parseVitestFnCall(node, context);
2633
+ if (!(vitestFnCall?.type === "expect" || vitestFnCall?.type === "expectTypeOf"))
2634
+ return;
2635
+ if (vitestFnCall.args.length === 1 && isTrueLiteral(getFirstMatcherArg(vitestFnCall)) && EqualityMatcher.hasOwnProperty(getAccessorValue(vitestFnCall.matcher))) {
2636
+ context.report({
2637
+ node: vitestFnCall.matcher,
2638
+ messageId: "preferToBeTruthy",
2639
+ fix: (fixer) => [
2640
+ fixer.replaceText(vitestFnCall.matcher, "toBeTruthy"),
2641
+ fixer.remove(vitestFnCall.args[0])
2642
+ ]
2643
+ });
2644
+ }
2645
+ }
2646
+ };
2647
+ }
2648
+ });
2649
+
2650
+ const RULE_NAME$r = "prefer-to-be-falsy";
2651
+ const isFalseLiteral = (node) => node.type === utils.AST_NODE_TYPES.Literal && node.value === false;
2652
+ const preferToBeFalsy = createEslintRule({
2653
+ name: RULE_NAME$r,
2654
+ meta: {
2655
+ type: "suggestion",
2656
+ docs: {
2657
+ description: "enforce using toBeFalsy()",
2658
+ recommended: false
2659
+ },
2660
+ fixable: "code",
2661
+ schema: [],
2662
+ messages: {
2663
+ preferToBeFalsy: "Prefer using toBeFalsy()"
2664
+ }
2665
+ },
2666
+ defaultOptions: [],
2667
+ create(context) {
2668
+ return {
2669
+ CallExpression(node) {
2670
+ const vitestFnCall = parseVitestFnCall(node, context);
2671
+ if (!(vitestFnCall?.type === "expect" || vitestFnCall?.type === "expectTypeOf"))
2672
+ return;
2673
+ if (vitestFnCall.args.length === 1 && isFalseLiteral(getFirstMatcherArg(vitestFnCall)) && EqualityMatcher.hasOwnProperty(getAccessorValue(vitestFnCall.matcher))) {
2674
+ context.report({
2675
+ node: vitestFnCall.matcher,
2676
+ messageId: "preferToBeFalsy",
2677
+ fix: (fixer) => [
2678
+ fixer.replaceText(vitestFnCall.matcher, "toBeFalsy"),
2679
+ fixer.remove(vitestFnCall.args[0])
2680
+ ]
2681
+ });
2682
+ }
2683
+ }
2684
+ };
2685
+ }
2686
+ });
2687
+
2688
+ const RULE_NAME$q = "prefer-to-have-length";
2689
+ const preferToHaveLength = createEslintRule({
2690
+ name: RULE_NAME$q,
2691
+ meta: {
2692
+ type: "suggestion",
2693
+ docs: {
2694
+ description: "enforce using toHaveLength()",
2695
+ recommended: false
2696
+ },
2697
+ fixable: "code",
2698
+ messages: {
2699
+ preferToHaveLength: "Prefer toHaveLength()"
2700
+ },
2701
+ schema: []
2702
+ },
2703
+ defaultOptions: [],
2704
+ create(context) {
2705
+ return {
2706
+ CallExpression(node) {
2707
+ const vitestFnCall = parseVitestFnCall(node, context);
2708
+ if (vitestFnCall?.type !== "expect")
2709
+ return;
2710
+ const { parent: expect } = vitestFnCall.head.node;
2711
+ if (expect?.type !== utils.AST_NODE_TYPES.CallExpression)
2712
+ return;
2713
+ const [argument] = expect.arguments;
2714
+ const { matcher } = vitestFnCall;
2715
+ if (!EqualityMatcher.hasOwnProperty(getAccessorValue(matcher)) || argument?.type !== utils.AST_NODE_TYPES.MemberExpression || !isSupportedAccessor(argument.property, "length"))
2716
+ return;
2717
+ context.report({
2718
+ node: matcher,
2719
+ messageId: "preferToHaveLength",
2720
+ fix(fixer) {
2721
+ return [
2722
+ fixer.removeRange([
2723
+ argument.property.range[0] - 1,
2724
+ argument.range[1]
2725
+ ]),
2726
+ fixer.replaceTextRange(
2727
+ [matcher.parent.object.range[1], matcher.parent.range[1]],
2728
+ ".toHaveLength"
2729
+ )
2730
+ ];
2731
+ }
2732
+ });
2733
+ }
2734
+ };
2735
+ }
2736
+ });
2737
+
2738
+ const RULE_NAME$p = "prefer-equality-matcher";
2739
+ const preferEqualityMatcher = createEslintRule({
2740
+ name: RULE_NAME$p,
2741
+ meta: {
2742
+ type: "suggestion",
2743
+ docs: {
2744
+ description: "enforce using the built-in quality matchers",
2745
+ recommended: false
2746
+ },
2747
+ messages: {
2748
+ useEqualityMatcher: "Prefer using one of the equality matchers instead",
2749
+ suggestEqualityMatcher: "Use `{{ equalityMatcher }}`"
2750
+ },
2751
+ hasSuggestions: true,
2752
+ schema: []
2753
+ },
2754
+ defaultOptions: [],
2755
+ create(context) {
2756
+ return {
2757
+ CallExpression(node) {
2758
+ const vitestFnCall = parseVitestFnCall(node, context);
2759
+ if (vitestFnCall?.type !== "expect" || vitestFnCall.args.length === 0)
2760
+ return;
2761
+ const { parent: expect } = vitestFnCall.head.node;
2762
+ if (expect?.type !== utils.AST_NODE_TYPES.CallExpression)
2763
+ return;
2764
+ const {
2765
+ arguments: [comparison],
2766
+ range: [, expectCallEnd]
2767
+ } = expect;
2768
+ const { matcher } = vitestFnCall;
2769
+ const matcherArg = getFirstMatcherArg(vitestFnCall);
2770
+ if (comparison?.type !== utils.AST_NODE_TYPES.BinaryExpression || comparison.operator !== "===" && comparison.operator !== "!==" || !EqualityMatcher.hasOwnProperty(getAccessorValue(matcher)) || !isBooleanLiteral(matcherArg))
2771
+ return;
2772
+ const matcherValue = matcherArg.value;
2773
+ const [modifier] = vitestFnCall.modifiers;
2774
+ const hasNot = vitestFnCall.modifiers.some(
2775
+ (nod) => getAccessorValue(nod) === "not"
2776
+ );
2777
+ const addNotModifier = (comparison.operator === "!==" ? !matcherValue : matcherValue) === hasNot;
2778
+ const buildFixer = (equalityMatcher) => (fixer) => {
2779
+ const { sourceCode } = context;
2780
+ let modifierText = modifier && getAccessorValue(modifier) !== "not" ? `.${getAccessorValue(modifier)}` : "";
2781
+ if (addNotModifier)
2782
+ modifierText += `.${ModifierName.not}`;
2783
+ return [
2784
+ fixer.replaceText(
2785
+ comparison,
2786
+ sourceCode.getText(comparison.left)
2787
+ ),
2788
+ fixer.replaceTextRange(
2789
+ [expectCallEnd, matcher.parent.range[1]],
2790
+ `${modifierText}.${equalityMatcher}`
2791
+ ),
2792
+ fixer.replaceText(
2793
+ matcherArg,
2794
+ sourceCode.getText(comparison.right)
2795
+ )
2796
+ ];
2797
+ };
2798
+ context.report({
2799
+ messageId: "useEqualityMatcher",
2800
+ suggest: ["toBe", "toEqual", "toStrictEqual"].map((equalityMatcher) => ({
2801
+ messageId: "suggestEqualityMatcher",
2802
+ data: { equalityMatcher },
2803
+ fix: buildFixer(equalityMatcher)
2804
+ })),
2805
+ node: matcher
2806
+ });
2807
+ }
2808
+ };
2809
+ }
2810
+ });
2811
+
2812
+ const RULE_NAME$o = "prefer-strict-equal";
2813
+ const preferStrictEqual = createEslintRule({
2814
+ name: RULE_NAME$o,
2815
+ meta: {
2816
+ type: "suggestion",
2817
+ docs: {
2818
+ description: "enforce strict equal over equal",
2819
+ recommended: false
2820
+ },
2821
+ messages: {
2822
+ useToStrictEqual: "Use `toStrictEqual()` instead",
2823
+ suggestReplaceWithStrictEqual: "Replace with `toStrictEqual()`"
2824
+ },
2825
+ schema: [],
2826
+ hasSuggestions: true
2827
+ },
2828
+ defaultOptions: [],
2829
+ create(context) {
2830
+ return {
2831
+ CallExpression(node) {
2832
+ const vitestFnCall = parseVitestFnCall(node, context);
2833
+ if (vitestFnCall?.type !== "expect")
2834
+ return;
2835
+ const { matcher } = vitestFnCall;
2836
+ if (isSupportedAccessor(matcher, "toEqual")) {
2837
+ context.report({
2838
+ messageId: "useToStrictEqual",
2839
+ node: matcher,
2840
+ suggest: [
2841
+ {
2842
+ messageId: "suggestReplaceWithStrictEqual",
2843
+ fix: (fixer) => [
2844
+ replaceAccessorFixer(fixer, matcher, EqualityMatcher.toStrictEqual)
2845
+ ]
2846
+ }
2847
+ ]
2848
+ });
2849
+ }
2850
+ }
2851
+ };
2852
+ }
2853
+ });
2854
+
2855
+ const RULE_NAME$n = "prefer-expect-resolves";
2856
+ const preferExpectResolves = createEslintRule({
2857
+ name: RULE_NAME$n,
2858
+ meta: {
2859
+ type: "suggestion",
2860
+ docs: {
2861
+ description: "enforce using `expect().resolves` over `expect(await ...)` syntax",
2862
+ recommended: false
2863
+ },
2864
+ fixable: "code",
2865
+ messages: {
2866
+ expectResolves: "Use `expect().resolves` instead"
2867
+ },
2868
+ schema: []
2869
+ },
2870
+ defaultOptions: [],
2871
+ create: (context) => ({
2872
+ CallExpression(node) {
2873
+ const vitestFnCall = parseVitestFnCall(node, context);
2874
+ if (vitestFnCall?.type !== "expect")
2875
+ return;
2876
+ const { parent } = vitestFnCall.head.node;
2877
+ if (parent?.type !== utils.AST_NODE_TYPES.CallExpression)
2878
+ return;
2879
+ const [awaitNode] = parent.arguments;
2880
+ if (awaitNode?.type === utils.AST_NODE_TYPES.AwaitExpression) {
2881
+ context.report({
2882
+ node: awaitNode,
2883
+ messageId: "expectResolves",
2884
+ fix(fixer) {
2885
+ return [
2886
+ fixer.insertTextBefore(parent, "await "),
2887
+ fixer.removeRange([
2888
+ awaitNode.range[0],
2889
+ awaitNode.argument.range[0]
2890
+ ]),
2891
+ fixer.insertTextAfter(parent, ".resolves")
2892
+ ];
2893
+ }
2894
+ });
2895
+ }
2896
+ }
2897
+ })
2898
+ });
2899
+
2900
+ const RULE_NAME$m = "prefer-each";
2901
+ const preferEach = createEslintRule({
2902
+ name: RULE_NAME$m,
2903
+ meta: {
2904
+ type: "suggestion",
2905
+ docs: {
2906
+ description: "enforce using `each` rather than manual loops",
2907
+ recommended: false
2908
+ },
2909
+ schema: [],
2910
+ messages: {
2911
+ preferEach: "Prefer using `{{ fn }}.each` rather than a manual loop"
2912
+ }
2913
+ },
2914
+ defaultOptions: [],
2915
+ create(context) {
2916
+ const vitestFnCalls = [];
2917
+ let inTestCaseCall = false;
2918
+ const recommendFn = () => {
2919
+ if (vitestFnCalls.length === 1 && vitestFnCalls[0] === "test")
2920
+ return "it";
2921
+ return "describe";
2922
+ };
2923
+ const enterForLoop = () => {
2924
+ if (vitestFnCalls.length === 0 || inTestCaseCall)
2925
+ return;
2926
+ vitestFnCalls.length = 0;
2927
+ };
2928
+ const exitForLoop = (node) => {
2929
+ if (vitestFnCalls.length === 0 || inTestCaseCall)
2930
+ return;
2931
+ context.report({
2932
+ node,
2933
+ messageId: "preferEach",
2934
+ data: { fn: recommendFn() }
2935
+ });
2936
+ vitestFnCalls.length = 0;
2937
+ };
2938
+ return {
2939
+ "ForStatement": enterForLoop,
2940
+ "ForStatement:exit": exitForLoop,
2941
+ "ForInStatement": enterForLoop,
2942
+ "ForInStatement:exit": exitForLoop,
2943
+ "ForOfStatement": enterForLoop,
2944
+ "ForOfStatement:exit": exitForLoop,
2945
+ CallExpression(node) {
2946
+ const { type: vitestFnCallType } = parseVitestFnCall(node, context) ?? {};
2947
+ if (vitestFnCallType === "hook" || vitestFnCallType === "describe" || vitestFnCallType === "test")
2948
+ vitestFnCalls.push(vitestFnCallType);
2949
+ if (vitestFnCallType === "test")
2950
+ inTestCaseCall = true;
2951
+ },
2952
+ "CallExpression:exit"(node) {
2953
+ const { type: vitestFnCallType } = parseVitestFnCall(node, context) ?? {};
2954
+ if (vitestFnCallType === "test")
2955
+ inTestCaseCall = false;
2956
+ }
2957
+ };
2958
+ }
2959
+ });
2960
+
2961
+ const RULE_NAME$l = "prefer-hooks-on-top";
2962
+ const preferHooksOnTop = createEslintRule({
2963
+ name: RULE_NAME$l,
2964
+ meta: {
2965
+ type: "suggestion",
2966
+ docs: {
2967
+ description: "enforce having hooks before any test cases",
2968
+ recommended: false
2969
+ },
2970
+ messages: {
2971
+ noHookOnTop: "Hooks should come before test cases"
2972
+ },
2973
+ schema: []
2974
+ },
2975
+ defaultOptions: [],
2976
+ create(context) {
2977
+ const hooksContext = [false];
2978
+ return {
2979
+ CallExpression(node) {
2980
+ if (isTypeOfVitestFnCall(node, context, ["test"]))
2981
+ hooksContext[hooksContext.length - 1] = true;
2982
+ if (hooksContext[hooksContext.length - 1] && isTypeOfVitestFnCall(node, context, ["hook"])) {
2983
+ context.report({
2984
+ messageId: "noHookOnTop",
2985
+ node
2986
+ });
2987
+ }
2988
+ hooksContext.push(false);
2989
+ },
2990
+ "CallExpression:exit"() {
2991
+ hooksContext.pop();
2992
+ }
2993
+ };
2994
+ }
2995
+ });
2996
+
2997
+ const RULE_NAME$k = "prefer-hooks-in-order";
2998
+ const HooksOrder = ["beforeAll", "beforeEach", "afterEach", "afterAll"];
2999
+ const preferHooksInOrder = createEslintRule({
3000
+ name: RULE_NAME$k,
3001
+ meta: {
3002
+ type: "suggestion",
3003
+ docs: {
3004
+ description: "enforce having hooks in consistent order",
3005
+ recommended: false
3006
+ },
3007
+ messages: {
3008
+ reorderHooks: "`{{ currentHook }}` hooks should be before any `{{ previousHook }}` hooks"
3009
+ },
3010
+ schema: []
3011
+ },
3012
+ defaultOptions: [],
3013
+ create(context) {
3014
+ let previousHookIndex = -1;
3015
+ let inHook = false;
3016
+ return {
3017
+ CallExpression(node) {
3018
+ if (inHook)
3019
+ return;
3020
+ const vitestFnCall = parseVitestFnCall(node, context);
3021
+ if (vitestFnCall?.type !== "hook") {
3022
+ previousHookIndex = -1;
3023
+ return;
3024
+ }
3025
+ inHook = true;
3026
+ const currentHook = vitestFnCall.name;
3027
+ const currentHookIndex = HooksOrder.indexOf(currentHook);
3028
+ if (currentHookIndex < previousHookIndex) {
3029
+ context.report({
3030
+ messageId: "reorderHooks",
3031
+ data: {
3032
+ previousHook: HooksOrder[previousHookIndex],
3033
+ currentHook
3034
+ },
3035
+ node
3036
+ });
3037
+ inHook = false;
3038
+ return;
3039
+ }
3040
+ previousHookIndex = currentHookIndex;
3041
+ },
3042
+ "CallExpression:exit"(node) {
3043
+ if (isTypeOfVitestFnCall(node, context, ["hook"])) {
3044
+ inHook = false;
3045
+ return;
3046
+ }
3047
+ if (inHook)
3048
+ return;
3049
+ previousHookIndex = -1;
3050
+ }
3051
+ };
3052
+ }
3053
+ });
3054
+
3055
+ const RULE_NAME$j = "prefer-mock-promise-shorthand";
3056
+ const withOnce = (name, addOnce) => {
3057
+ return `${name}${addOnce ? "Once" : ""}`;
3058
+ };
3059
+ const findSingleReturnArgumentNode = (fnNode) => {
3060
+ if (fnNode.body.type !== utils.AST_NODE_TYPES.BlockStatement)
3061
+ return fnNode.body;
3062
+ if (fnNode.body.body[0]?.type === utils.AST_NODE_TYPES.ReturnStatement)
3063
+ return fnNode.body.body[0].argument;
3064
+ return null;
3065
+ };
3066
+ const preferMockPromiseShorthand = createEslintRule({
3067
+ name: RULE_NAME$j,
3068
+ meta: {
3069
+ type: "suggestion",
3070
+ docs: {
3071
+ description: "enforce mock resolved/rejected shorthands for promises",
3072
+ recommended: false
3073
+ },
3074
+ messages: {
3075
+ useMockShorthand: "Prefer {{ replacement }}"
3076
+ },
3077
+ schema: [],
3078
+ fixable: "code"
3079
+ },
3080
+ defaultOptions: [],
3081
+ create(context) {
3082
+ const report = (property, isOnce, outerArgNode, innerArgNode = outerArgNode) => {
3083
+ if (innerArgNode?.type !== utils.AST_NODE_TYPES.CallExpression)
3084
+ return;
3085
+ const argName = getNodeName(innerArgNode);
3086
+ if (argName !== "Promise.resolve" && argName !== "Promise.reject")
3087
+ return;
3088
+ const replacement = withOnce(argName.endsWith("reject") ? "mockRejectedValue" : "mockResolvedValue", isOnce);
3089
+ context.report({
3090
+ node: property,
3091
+ messageId: "useMockShorthand",
3092
+ data: { replacement },
3093
+ fix(fixer) {
3094
+ const { sourceCode } = context;
3095
+ if (innerArgNode.arguments.length > 1)
3096
+ return null;
3097
+ return [
3098
+ fixer.replaceText(property, replacement),
3099
+ fixer.replaceText(outerArgNode, innerArgNode.arguments.length === 1 ? sourceCode.getText(innerArgNode.arguments[0]) : "undefined")
3100
+ ];
3101
+ }
3102
+ });
3103
+ };
3104
+ return {
3105
+ CallExpression(node) {
3106
+ if (node.callee.type !== utils.AST_NODE_TYPES.MemberExpression || !isSupportedAccessor(node.callee.property) || node.arguments.length === 0)
3107
+ return;
3108
+ const mockFnName = getAccessorValue(node.callee.property);
3109
+ const isOnce = mockFnName.endsWith("Once");
3110
+ if (mockFnName === withOnce("mockReturnValue", isOnce)) {
3111
+ report(node.callee.property, isOnce, node.arguments[0]);
3112
+ } else if (mockFnName === withOnce("mockImplementation", isOnce)) {
3113
+ const [arg] = node.arguments;
3114
+ if (!isFunction(arg) || arg.params.length !== 0)
3115
+ return;
3116
+ report(
3117
+ node.callee.property,
3118
+ isOnce,
3119
+ arg,
3120
+ findSingleReturnArgumentNode(arg)
3121
+ );
3122
+ }
3123
+ }
3124
+ };
3125
+ }
3126
+ });
3127
+
3128
+ const RULE_NAME$i = "prefer-snapshot-hint";
3129
+ const snapshotMatchers = ["toMatchSnapshot", "toThrowErrorMatchingSnapshot"];
3130
+ const snapshotMatcherNames = snapshotMatchers;
3131
+ const isSnapshotMatcherWithoutHint = (expectFnCall) => {
3132
+ if (expectFnCall.args.length === 0)
3133
+ return true;
3134
+ if (!isSupportedAccessor(expectFnCall.matcher, "toMatchSnapshot"))
3135
+ return expectFnCall.args.length !== 1;
3136
+ if (expectFnCall.args.length === 2)
3137
+ return false;
3138
+ const [arg] = expectFnCall.args;
3139
+ return !isStringNode(arg);
3140
+ };
3141
+ const preferSnapshotHint = createEslintRule({
3142
+ name: RULE_NAME$i,
3143
+ meta: {
3144
+ type: "suggestion",
3145
+ docs: {
3146
+ description: "enforce including a hint with external snapshots",
3147
+ recommended: false
3148
+ },
3149
+ messages: {
3150
+ missingHint: "You should provide a hint for this snapshot"
3151
+ },
3152
+ schema: [
3153
+ {
3154
+ type: "string",
3155
+ enum: ["always", "multi"]
3156
+ }
3157
+ ]
3158
+ },
3159
+ defaultOptions: ["multi"],
3160
+ create(context, [mode]) {
3161
+ const snapshotMatchers2 = [];
3162
+ let expressionDepth = 0;
3163
+ const depths = [];
3164
+ const reportSnapshotMatchersWithoutHints = () => {
3165
+ for (const snapshotMatcher of snapshotMatchers2) {
3166
+ if (isSnapshotMatcherWithoutHint(snapshotMatcher)) {
3167
+ context.report({
3168
+ messageId: "missingHint",
3169
+ node: snapshotMatcher.matcher
3170
+ });
3171
+ }
3172
+ }
3173
+ };
3174
+ const enterExpression = () => {
3175
+ expressionDepth++;
3176
+ };
3177
+ const exitExpression = () => {
3178
+ expressionDepth--;
3179
+ if (mode === "always") {
3180
+ reportSnapshotMatchersWithoutHints();
3181
+ snapshotMatchers2.length = 0;
3182
+ }
3183
+ if (mode === "multi" && expressionDepth === 0) {
3184
+ if (snapshotMatchers2.length > 1)
3185
+ reportSnapshotMatchersWithoutHints();
3186
+ snapshotMatchers2.length = 0;
3187
+ }
3188
+ };
3189
+ return {
3190
+ "Program:exit"() {
3191
+ enterExpression();
3192
+ exitExpression();
3193
+ },
3194
+ "FunctionExpression": enterExpression,
3195
+ "FunctionExpression:exit": exitExpression,
3196
+ "ArrowFunctionExpression": enterExpression,
3197
+ "ArrowFunctionExpression:exit": exitExpression,
3198
+ "CallExpression:exit"(node) {
3199
+ if (isTypeOfVitestFnCall(node, context, ["describe", "test"]))
3200
+ expressionDepth = depths.pop() ?? 0;
3201
+ },
3202
+ CallExpression(node) {
3203
+ const vitestFnCall = parseVitestFnCall(node, context);
3204
+ if (vitestFnCall?.type !== "expect") {
3205
+ if (vitestFnCall?.type === "describe" || vitestFnCall?.type === "test") {
3206
+ depths.push(expressionDepth);
3207
+ expressionDepth = 0;
3208
+ }
3209
+ return;
3210
+ }
3211
+ const matcherName = getAccessorValue(vitestFnCall.matcher);
3212
+ if (!snapshotMatcherNames.includes(matcherName))
3213
+ return;
3214
+ snapshotMatchers2.push(vitestFnCall);
3215
+ }
3216
+ };
3217
+ }
3218
+ });
3219
+
3220
+ const RULE_NAME$h = "valid-describe-callback";
3221
+ const paramsLocation = (params) => {
3222
+ const [first] = params;
3223
+ const last = params[params.length - 1];
3224
+ return {
3225
+ start: first.loc.start,
3226
+ end: last.loc.end
3227
+ };
3228
+ };
3229
+ const validDescribeCallback = createEslintRule({
3230
+ name: RULE_NAME$h,
3231
+ meta: {
3232
+ type: "problem",
3233
+ docs: {
3234
+ description: "enforce valid describe callback",
3235
+ recommended: false
3236
+ },
3237
+ messages: {
3238
+ nameAndCallback: "Describe requires a name and callback arguments",
3239
+ secondArgumentMustBeFunction: "Second argument must be a function",
3240
+ noAsyncDescribeCallback: "Describe callback cannot be async",
3241
+ unexpectedDescribeArgument: "Unexpected argument in describe callback",
3242
+ unexpectedReturnInDescribe: "Unexpected return statement in describe callback"
3243
+ },
3244
+ schema: []
3245
+ },
3246
+ defaultOptions: [],
3247
+ create(context) {
3248
+ return {
3249
+ CallExpression(node) {
3250
+ const vitestFnCall = parseVitestFnCall(node, context);
3251
+ if (vitestFnCall?.type !== "describe")
3252
+ return;
3253
+ if (vitestFnCall?.members[0]?.type === utils.AST_NODE_TYPES.Identifier && vitestFnCall.members[0].name === "todo")
3254
+ return;
3255
+ if (node.arguments.length < 1) {
3256
+ return context.report({
3257
+ messageId: "nameAndCallback",
3258
+ loc: node.loc
3259
+ });
3260
+ }
3261
+ const [, callback] = node.arguments;
3262
+ if (!callback) {
3263
+ context.report({
3264
+ messageId: "nameAndCallback",
3265
+ loc: paramsLocation(node.arguments)
3266
+ });
3267
+ return;
3268
+ }
3269
+ if (!isFunction(callback)) {
3270
+ context.report({
3271
+ messageId: "secondArgumentMustBeFunction",
3272
+ loc: paramsLocation(node.arguments)
3273
+ });
3274
+ return;
3275
+ }
3276
+ if (callback.async) {
3277
+ context.report({
3278
+ messageId: "noAsyncDescribeCallback",
3279
+ node: callback
3280
+ });
3281
+ }
3282
+ if (vitestFnCall.members.every((s) => getAccessorValue(s) !== "each") && callback.params.length) {
3283
+ context.report({
3284
+ messageId: "unexpectedDescribeArgument",
3285
+ node: callback
3286
+ });
3287
+ }
3288
+ if (callback.body.type === utils.AST_NODE_TYPES.CallExpression) {
3289
+ context.report({
3290
+ messageId: "unexpectedReturnInDescribe",
3291
+ node: callback
3292
+ });
3293
+ }
3294
+ if (callback.body.type === utils.AST_NODE_TYPES.BlockStatement) {
3295
+ callback.body.body.forEach((node2) => {
3296
+ if (node2.type === utils.AST_NODE_TYPES.ReturnStatement) {
3297
+ context.report({
3298
+ messageId: "unexpectedReturnInDescribe",
3299
+ node: node2
3300
+ });
3301
+ }
3302
+ });
3303
+ }
3304
+ }
3305
+ };
3306
+ }
3307
+ });
3308
+
3309
+ const RULE_NAME$g = "require-top-level-describe";
3310
+ const requireTopLevelDescribe = createEslintRule({
3311
+ name: RULE_NAME$g,
3312
+ meta: {
3313
+ docs: {
3314
+ description: "enforce that all tests are in a top-level describe",
3315
+ recommended: false
3316
+ },
3317
+ messages: {
3318
+ tooManyDescribes: "There should not be more than {{ max }} describe{{ s }} at the top level",
3319
+ unexpectedTestCase: "All test cases must be wrapped in a describe block.",
3320
+ unexpectedHook: "All hooks must be wrapped in a describe block."
3321
+ },
3322
+ type: "suggestion",
3323
+ schema: [
3324
+ {
3325
+ type: "object",
3326
+ properties: {
3327
+ maxNumberOfTopLevelDescribes: {
3328
+ type: "number",
3329
+ minimum: 1
3330
+ }
3331
+ },
3332
+ additionalProperties: false
3333
+ }
3334
+ ]
3335
+ },
3336
+ defaultOptions: [{}],
3337
+ create(context) {
3338
+ const { maxNumberOfTopLevelDescribes = Infinity } = context.options[0] ?? {};
3339
+ let numberOfTopLevelDescribeBlocks = 0;
3340
+ let numberOfDescribeBlocks = 0;
3341
+ return {
3342
+ CallExpression(node) {
3343
+ const vitestFnCall = parseVitestFnCall(node, context);
3344
+ if (!vitestFnCall)
3345
+ return;
3346
+ if (vitestFnCall.type === "describe") {
3347
+ numberOfDescribeBlocks++;
3348
+ if (numberOfDescribeBlocks === 1) {
3349
+ numberOfTopLevelDescribeBlocks++;
3350
+ if (numberOfTopLevelDescribeBlocks > maxNumberOfTopLevelDescribes) {
3351
+ context.report({
3352
+ node,
3353
+ messageId: "tooManyDescribes",
3354
+ data: {
3355
+ max: maxNumberOfTopLevelDescribes,
3356
+ s: maxNumberOfTopLevelDescribes === 1 ? "" : "s"
3357
+ }
3358
+ });
3359
+ }
3360
+ }
3361
+ return;
3362
+ }
3363
+ if (numberOfDescribeBlocks === 0) {
3364
+ if (vitestFnCall.type === "test") {
3365
+ context.report({ node, messageId: "unexpectedTestCase" });
3366
+ return;
3367
+ }
3368
+ if (vitestFnCall.type === "hook")
3369
+ context.report({ node, messageId: "unexpectedHook" });
3370
+ }
3371
+ },
3372
+ "CallExpression:exit"(node) {
3373
+ if (isTypeOfVitestFnCall(node, context, ["describe"]))
3374
+ numberOfDescribeBlocks--;
3375
+ }
3376
+ };
3377
+ }
3378
+ });
3379
+
3380
+ const RULE_NAME$f = "require-to-throw-message";
3381
+ const requireToThrowMessage = createEslintRule({
3382
+ name: RULE_NAME$f,
3383
+ meta: {
3384
+ type: "suggestion",
3385
+ docs: {
3386
+ description: "require toThrow() to be called with an error message",
3387
+ recommended: false
3388
+ },
3389
+ schema: [],
3390
+ messages: {
3391
+ addErrorMessage: "Add an error message to {{ matcherName }}()"
3392
+ }
3393
+ },
3394
+ defaultOptions: [],
3395
+ create(context) {
3396
+ return {
3397
+ CallExpression(node) {
3398
+ const vitestFnCall = parseVitestFnCall(node, context);
3399
+ if (vitestFnCall?.type !== "expect")
3400
+ return;
3401
+ const { matcher } = vitestFnCall;
3402
+ const matcherName = getAccessorValue(matcher);
3403
+ if (vitestFnCall.args.length === 0 && ["toThrow", "toThrowError"].includes(matcherName) && !vitestFnCall.modifiers.some((nod) => getAccessorValue(nod) === "not")) {
3404
+ context.report({
3405
+ messageId: "addErrorMessage",
3406
+ data: { matcherName },
3407
+ node: matcher
3408
+ });
3409
+ }
3410
+ }
3411
+ };
3412
+ }
3413
+ });
3414
+
3415
+ const RULE_NAME$e = "require-hook";
3416
+ const isVitestFnCall = (node, context) => {
3417
+ if (parseVitestFnCall(node, context))
3418
+ return true;
3419
+ return !!getNodeName(node)?.startsWith("vi");
3420
+ };
3421
+ const isNullOrUndefined = (node) => {
3422
+ return node.type === utils.AST_NODE_TYPES.Literal && node.value === null || isIdentifier(node, "undefined");
3423
+ };
3424
+ const shouldBeInHook = (node, context, allowedFunctionCalls = []) => {
3425
+ switch (node.type) {
3426
+ case utils.AST_NODE_TYPES.ExpressionStatement:
3427
+ return shouldBeInHook(node.expression, context, allowedFunctionCalls);
3428
+ case utils.AST_NODE_TYPES.CallExpression:
3429
+ return !(isVitestFnCall(node, context) || allowedFunctionCalls.includes(getNodeName(node)));
3430
+ case utils.AST_NODE_TYPES.VariableDeclaration: {
3431
+ if (node.kind === "const")
3432
+ return false;
3433
+ return node.declarations.some(
3434
+ ({ init }) => init !== null && !isNullOrUndefined(init)
3435
+ );
3436
+ }
3437
+ default:
3438
+ return false;
3439
+ }
3440
+ };
3441
+ const requireHook = createEslintRule({
3442
+ name: RULE_NAME$e,
3443
+ meta: {
3444
+ docs: {
3445
+ description: "require setup and teardown to be within a hook",
3446
+ recommended: false
3447
+ },
3448
+ messages: {
3449
+ useHook: "This should be done within a hook"
3450
+ },
3451
+ type: "suggestion",
3452
+ schema: [
3453
+ {
3454
+ type: "object",
3455
+ properties: {
3456
+ allowedFunctionCalls: {
3457
+ type: "array",
3458
+ items: { type: "string" }
3459
+ }
3460
+ },
3461
+ additionalProperties: false
3462
+ }
3463
+ ]
3464
+ },
3465
+ defaultOptions: [
3466
+ {
3467
+ allowedFunctionCalls: []
3468
+ }
3469
+ ],
3470
+ create(context) {
3471
+ const { allowedFunctionCalls } = context.options[0] ?? {};
3472
+ const checkBlockBody = (body) => {
3473
+ for (const statement of body) {
3474
+ if (shouldBeInHook(statement, context, allowedFunctionCalls)) {
3475
+ context.report({
3476
+ node: statement,
3477
+ messageId: "useHook"
3478
+ });
3479
+ }
3480
+ }
3481
+ };
3482
+ return {
3483
+ Program(program) {
3484
+ checkBlockBody(program.body);
3485
+ },
3486
+ CallExpression(node) {
3487
+ if (!isTypeOfVitestFnCall(node, context, ["describe"]) || node.arguments.length < 2)
3488
+ return;
3489
+ const [, testFn] = node.arguments;
3490
+ if (!isFunction(testFn) || testFn.body.type !== utils.AST_NODE_TYPES.BlockStatement)
3491
+ return;
3492
+ checkBlockBody(testFn.body.body);
3493
+ }
3494
+ };
3495
+ }
3496
+ });
3497
+
3498
+ const RULE_NAME$d = "require-local-test-context-for-concurrent-snapshots";
3499
+ const requireLocalTestContextForConcurrentSnapshots = createEslintRule({
3500
+ name: RULE_NAME$d,
3501
+ meta: {
3502
+ docs: {
3503
+ description: "require local Test Context for concurrent snapshot tests",
3504
+ recommended: false
3505
+ },
3506
+ messages: {
3507
+ requireLocalTestContext: "Use local Test Context instead"
3508
+ },
3509
+ type: "problem",
3510
+ schema: []
3511
+ },
3512
+ defaultOptions: [],
3513
+ create(context) {
3514
+ return {
3515
+ CallExpression(node) {
3516
+ const isNotAnAssertion = !isTypeOfVitestFnCall(node, context, ["expect"]);
3517
+ if (isNotAnAssertion)
3518
+ return;
3519
+ const isNotASnapshotAssertion = ![
3520
+ "toMatchSnapshot",
3521
+ "toMatchInlineSnapshot",
3522
+ "toMatchFileSnapshot",
3523
+ "toThrowErrorMatchingSnapshot",
3524
+ "toThrowErrorMatchingInlineSnapshot"
3525
+ //@ts-ignore
3526
+ ].includes(node.callee?.property.name);
3527
+ if (isNotASnapshotAssertion)
3528
+ return;
3529
+ const isInsideSequentialDescribeOrTest = !context.sourceCode.getAncestors(node).some((ancestor) => {
3530
+ if (ancestor.type !== utils.AST_NODE_TYPES.CallExpression)
3531
+ return false;
3532
+ const isNotInsideDescribeOrTest = !isTypeOfVitestFnCall(ancestor, context, ["describe", "test"]);
3533
+ if (isNotInsideDescribeOrTest)
3534
+ return false;
3535
+ const isTestRunningConcurrently = ancestor.callee.type === utils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(ancestor.callee.property, "concurrent");
3536
+ return isTestRunningConcurrently;
3537
+ });
3538
+ if (isInsideSequentialDescribeOrTest)
3539
+ return;
3540
+ context.report({
3541
+ node,
3542
+ messageId: "requireLocalTestContext"
3543
+ });
3544
+ }
3545
+ };
3546
+ }
3547
+ });
3548
+
3549
+ const RULE_NAME$c = "prefer-todo";
3550
+ const isTargetedTestCase = (vitestFnCall) => {
3551
+ if (vitestFnCall.members.some((s) => getAccessorValue(s) !== "skip"))
3552
+ return false;
3553
+ if (vitestFnCall.name.startsWith("x"))
3554
+ return false;
3555
+ return !vitestFnCall.name.startsWith("f");
3556
+ };
3557
+ function isEmptyFunction(node) {
3558
+ if (!isFunction(node))
3559
+ return false;
3560
+ return node.body.type === utils.AST_NODE_TYPES.BlockStatement && !node.body.body.length;
3561
+ }
3562
+ function createTodoFixer(vitestFnCall, fixer) {
3563
+ if (vitestFnCall.members.length)
3564
+ return replaceAccessorFixer(fixer, vitestFnCall.members[0], "todo");
3565
+ return fixer.replaceText(vitestFnCall.head.node, `${vitestFnCall.head.local}.todo`);
3566
+ }
3567
+ const preferTodo = createEslintRule({
3568
+ name: RULE_NAME$c,
3569
+ meta: {
3570
+ type: "layout",
3571
+ docs: {
3572
+ description: "enforce using `test.todo`",
3573
+ recommended: false
3574
+ },
3575
+ messages: {
3576
+ emptyTest: "Prefer todo test case over empty test case",
3577
+ unimplementedTest: "Prefer todo test case over unimplemented test case"
3578
+ },
3579
+ fixable: "code",
3580
+ schema: []
3581
+ },
3582
+ defaultOptions: [],
3583
+ create(context) {
3584
+ return {
3585
+ CallExpression(node) {
3586
+ const [title, callback] = node.arguments;
3587
+ const vitestFnCall = parseVitestFnCall(node, context);
3588
+ if (!title || vitestFnCall?.type !== "test" || !isTargetedTestCase(vitestFnCall) || !isStringNode(title))
3589
+ return;
3590
+ if (callback && isEmptyFunction(callback)) {
3591
+ context.report({
3592
+ messageId: "emptyTest",
3593
+ node,
3594
+ fix: (fixer) => [
3595
+ fixer.removeRange([title.range[1], callback.range[1]]),
3596
+ createTodoFixer(vitestFnCall, fixer)
3597
+ ]
3598
+ });
3599
+ }
3600
+ if (hasOnlyOneArgument(node)) {
3601
+ context.report({
3602
+ messageId: "unimplementedTest",
3603
+ node,
3604
+ fix: (fixer) => createTodoFixer(vitestFnCall, fixer)
3605
+ });
3606
+ }
3607
+ }
3608
+ };
3609
+ }
3610
+ });
3611
+
3612
+ const RULE_NAME$b = "prefer-spy-on";
3613
+ const findNodeObject = (node) => {
3614
+ if ("object" in node)
3615
+ return node.object;
3616
+ if (node.callee.type === utils.AST_NODE_TYPES.MemberExpression)
3617
+ return node.callee.object;
3618
+ return null;
3619
+ };
3620
+ const getVitestFnCall = (node) => {
3621
+ if (node.type !== utils.AST_NODE_TYPES.CallExpression && node.type !== utils.AST_NODE_TYPES.MemberExpression)
3622
+ return null;
3623
+ const obj = findNodeObject(node);
3624
+ if (!obj)
3625
+ return null;
3626
+ if (obj.type === utils.AST_NODE_TYPES.Identifier) {
3627
+ return node.type === utils.AST_NODE_TYPES.CallExpression && getNodeName(node.callee) === "vi.fn" ? node : null;
3628
+ }
3629
+ return getVitestFnCall(obj);
3630
+ };
3631
+ const getAutoFixMockImplementation = (vitestFnCall, context) => {
3632
+ const hasMockImplementationAlready = vitestFnCall.parent?.type === utils.AST_NODE_TYPES.MemberExpression && vitestFnCall.parent.property.type === utils.AST_NODE_TYPES.Identifier && vitestFnCall.parent.property.name === "mockImplementation";
3633
+ if (hasMockImplementationAlready)
3634
+ return "";
3635
+ const [arg] = vitestFnCall.arguments;
3636
+ const argSource = arg && context.sourceCode.getText(arg);
3637
+ return argSource ? `.mockImplementation(${argSource})` : ".mockImplementation()";
3638
+ };
3639
+ const preferSpyOn = createEslintRule({
3640
+ name: RULE_NAME$b,
3641
+ meta: {
3642
+ type: "suggestion",
3643
+ docs: {
3644
+ description: "enforce using `vi.spyOn`",
3645
+ recommended: false
3646
+ },
3647
+ messages: {
3648
+ useViSpayOn: "Use `vi.spyOn` instead"
3649
+ },
3650
+ fixable: "code",
3651
+ schema: []
3652
+ },
3653
+ defaultOptions: [],
3654
+ create(context) {
3655
+ return {
3656
+ AssignmentExpression(node) {
3657
+ const { left, right } = node;
3658
+ if (left.type !== utils.AST_NODE_TYPES.MemberExpression)
3659
+ return;
3660
+ const vitestFnCall = getVitestFnCall(right);
3661
+ if (!vitestFnCall)
3662
+ return;
3663
+ context.report({
3664
+ node,
3665
+ messageId: "useViSpayOn",
3666
+ fix(fixer) {
3667
+ const lefPropQuote = left.property.type === utils.AST_NODE_TYPES.Identifier && !left.computed ? "'" : "";
3668
+ const mockImplementation = getAutoFixMockImplementation(vitestFnCall, context);
3669
+ return [
3670
+ fixer.insertTextBefore(left, "vi.spyOn("),
3671
+ fixer.replaceTextRange(
3672
+ [left.object.range[1], left.property.range[0]],
3673
+ `, ${lefPropQuote}`
3674
+ ),
3675
+ fixer.replaceTextRange(
3676
+ [left.property.range[1], vitestFnCall.range[1]],
3677
+ `${lefPropQuote})${mockImplementation}`
3678
+ )
3679
+ ];
3680
+ }
3681
+ });
3682
+ }
3683
+ };
3684
+ }
3685
+ });
3686
+
3687
+ const RULE_NAME$a = "prefer-comparison-matcher";
3688
+ const isString = (node) => {
3689
+ return isStringNode(node) || node?.type === utils.AST_NODE_TYPES.TemplateLiteral;
3690
+ };
3691
+ const isComparingToString = (expression) => {
3692
+ return isString(expression.left) || isString(expression.right);
3693
+ };
3694
+ const invertOperator = (operator) => {
3695
+ switch (operator) {
3696
+ case ">":
3697
+ return "<=";
3698
+ case "<":
3699
+ return ">=";
3700
+ case ">=":
3701
+ return "<";
3702
+ case "<=":
3703
+ return ">";
3704
+ }
3705
+ return null;
3706
+ };
3707
+ const determineMatcher = (operator, negated) => {
3708
+ const op = negated ? invertOperator(operator) : operator;
3709
+ switch (op) {
3710
+ case ">":
3711
+ return "toBeGreaterThan";
3712
+ case "<":
3713
+ return "toBeLessThan";
3714
+ case ">=":
3715
+ return "toBeGreaterThanOrEqual";
3716
+ case "<=":
3717
+ return "toBeLessThanOrEqual";
3718
+ }
3719
+ return null;
3720
+ };
3721
+ const preferComparisonMatcher = createEslintRule({
3722
+ name: RULE_NAME$a,
3723
+ meta: {
3724
+ type: "suggestion",
3725
+ docs: {
3726
+ description: "enforce using the built-in comparison matchers",
3727
+ recommended: false
3728
+ },
3729
+ schema: [],
3730
+ fixable: "code",
3731
+ messages: {
3732
+ useToBeComparison: "Prefer using `{{ preferredMatcher }}` instead"
3733
+ }
3734
+ },
3735
+ defaultOptions: [],
3736
+ create(context) {
3737
+ return {
3738
+ CallExpression(node) {
3739
+ const vitestFnCall = parseVitestFnCall(node, context);
3740
+ if (vitestFnCall?.type !== "expect" || vitestFnCall.args.length === 0)
3741
+ return;
3742
+ const { parent: expect } = vitestFnCall.head.node;
3743
+ if (expect?.type !== utils.AST_NODE_TYPES.CallExpression)
3744
+ return;
3745
+ const {
3746
+ arguments: [comparison],
3747
+ range: [, expectCallEnd]
3748
+ } = expect;
3749
+ const { matcher } = vitestFnCall;
3750
+ const matcherArg = getFirstMatcherArg(vitestFnCall);
3751
+ if (comparison?.type !== utils.AST_NODE_TYPES.BinaryExpression || isComparingToString(comparison) || !EqualityMatcher.hasOwnProperty(getAccessorValue(matcher)) || !isBooleanLiteral(matcherArg))
3752
+ return;
3753
+ const [modifier] = vitestFnCall.modifiers;
3754
+ const hasNot = vitestFnCall.modifiers.some((nod) => getAccessorValue(nod) === "not");
3755
+ const preferredMatcher = determineMatcher(comparison.operator, matcherArg.value === hasNot);
3756
+ if (!preferredMatcher)
3757
+ return;
3758
+ context.report({
3759
+ fix(fixer) {
3760
+ const { sourceCode } = context;
3761
+ const modifierText = modifier && getAccessorValue(modifier) !== "not" ? `.${getAccessorValue(modifier)}` : "";
3762
+ return [
3763
+ fixer.replaceText(
3764
+ comparison,
3765
+ sourceCode.getText(comparison.left)
3766
+ ),
3767
+ fixer.replaceTextRange(
3768
+ [expectCallEnd, matcher.parent.range[1]],
3769
+ `${modifierText}.${preferredMatcher}`
3770
+ ),
3771
+ fixer.replaceText(
3772
+ matcherArg,
3773
+ sourceCode.getText(comparison.right)
3774
+ )
3775
+ ];
3776
+ },
3777
+ messageId: "useToBeComparison",
3778
+ data: { preferredMatcher },
3779
+ node: matcher
3780
+ });
3781
+ }
3782
+ };
3783
+ }
3784
+ });
3785
+
3786
+ const RULE_NAME$9 = "prefer-to-contain";
3787
+ const isFixableIncludesCallExpression = (node) => node.type === utils.AST_NODE_TYPES.CallExpression && node.callee.type === utils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.callee.property, "includes") && hasOnlyOneArgument(node) && node.arguments[0].type !== utils.AST_NODE_TYPES.SpreadElement;
3788
+ const preferToContain = createEslintRule({
3789
+ name: RULE_NAME$9,
3790
+ meta: {
3791
+ docs: {
3792
+ description: "enforce using toContain()",
3793
+ recommended: false
3794
+ },
3795
+ messages: {
3796
+ useToContain: "Use toContain() instead"
3797
+ },
3798
+ fixable: "code",
3799
+ type: "suggestion",
3800
+ schema: []
3801
+ },
3802
+ defaultOptions: [],
3803
+ create(context) {
3804
+ return {
3805
+ CallExpression(node) {
3806
+ const vitestFnCall = parseVitestFnCall(node, context);
3807
+ if (vitestFnCall?.type !== "expect" || vitestFnCall.args.length === 0)
3808
+ return;
3809
+ const { parent: expect } = vitestFnCall.head.node;
3810
+ if (expect?.type !== utils.AST_NODE_TYPES.CallExpression)
3811
+ return;
3812
+ const {
3813
+ arguments: [includesCall],
3814
+ range: [, expectCallEnd]
3815
+ } = expect;
3816
+ const { matcher } = vitestFnCall;
3817
+ const matcherArg = getFirstMatcherArg(vitestFnCall);
3818
+ if (!includesCall || matcherArg.type === utils.AST_NODE_TYPES.SpreadElement || !EqualityMatcher.hasOwnProperty(getAccessorValue(matcher)) || !isBooleanLiteral(matcherArg) || !isFixableIncludesCallExpression(includesCall))
3819
+ return;
3820
+ const hasNot = vitestFnCall.modifiers.some((nod) => getAccessorValue(nod) === "not");
3821
+ context.report({
3822
+ fix(fixer) {
3823
+ const { sourceCode } = context;
3824
+ const addNotModifier = matcherArg.value === hasNot;
3825
+ return [
3826
+ fixer.removeRange([
3827
+ includesCall.callee.property.range[0] - 1,
3828
+ includesCall.range[1]
3829
+ ]),
3830
+ fixer.replaceTextRange(
3831
+ [expectCallEnd, matcher.parent.range[1]],
3832
+ addNotModifier ? `.${ModifierName.not}.toContain` : ".toContain"
3833
+ ),
3834
+ fixer.replaceText(
3835
+ vitestFnCall.args[0],
3836
+ sourceCode.getText(includesCall.arguments[0])
3837
+ )
3838
+ ];
3839
+ },
3840
+ messageId: "useToContain",
3841
+ node: matcher
3842
+ });
3843
+ }
3844
+ };
3845
+ }
3846
+ });
3847
+
3848
+ const RULE_NAME$8 = "prefer-expect-assertions";
3849
+ const isFirstStatement = (node) => {
3850
+ let parent = node;
3851
+ while (parent) {
3852
+ if (parent.parent?.type === utils.AST_NODE_TYPES.BlockStatement)
3853
+ return parent.parent.body[0] === parent;
3854
+ if (parent.parent?.type === utils.AST_NODE_TYPES.ArrowFunctionExpression)
3855
+ return true;
3856
+ parent = parent.parent;
3857
+ }
3858
+ throw new Error("Could not find parent block statement");
3859
+ };
3860
+ const suggestRemovingExtraArguments = (context, func, from) => ({
3861
+ messageId: "suggestRemovingExtraArguments",
3862
+ fix: (fixer) => removeExtraArgumentsFixer(fixer, context, func, from)
3863
+ });
3864
+ const preferExpectAssertions = createEslintRule({
3865
+ name: "prefer-expect-assertions",
3866
+ meta: {
3867
+ docs: {
3868
+ description: "enforce using expect assertions instead of callbacks",
3869
+ recommended: false
3870
+ },
3871
+ messages: {
3872
+ hasAssertionsTakesNoArguments: "`expect.hasAssertions` expects no arguments",
3873
+ assertionsRequiresOneArgument: "`expect.assertions` excepts a single argument of type number",
3874
+ assertionsRequiresNumberArgument: "This argument should be a number",
3875
+ haveExpectAssertions: "Every test should have either `expect.assertions(<number of assertions>)` or `expect.hasAssertions()` as its first expression",
3876
+ suggestAddingHasAssertions: "Add `expect.hasAssertions()`",
3877
+ suggestAddingAssertions: "Add `expect.assertions(<number of assertions>)`",
3878
+ suggestRemovingExtraArguments: "Remove extra arguments"
3879
+ },
3880
+ type: "suggestion",
3881
+ hasSuggestions: true,
3882
+ schema: [
3883
+ {
3884
+ type: "object",
3885
+ properties: {
3886
+ onlyFunctionsWithAsyncKeyword: { type: "boolean" },
3887
+ onlyFunctionsWithExpectInLoop: { type: "boolean" },
3888
+ onlyFunctionsWithExpectInCallback: { type: "boolean" }
3889
+ },
3890
+ additionalProperties: false
3891
+ }
3892
+ ]
3893
+ },
3894
+ defaultOptions: [
3895
+ {
3896
+ onlyFunctionsWithAsyncKeyword: false,
3897
+ onlyFunctionsWithExpectInCallback: false,
3898
+ onlyFunctionsWithExpectInLoop: false
3899
+ }
3900
+ ],
3901
+ create(context, [options]) {
3902
+ let expressionDepth = 0;
3903
+ let hasExpectInCallBack = false;
3904
+ let hasExpectInLoop = false;
3905
+ let hasExpectAssertAsFirstStatement = false;
3906
+ let inTestCaseCall = false;
3907
+ let inForLoop = false;
3908
+ const shouldCheckFunction = (testFunction) => {
3909
+ if (!options.onlyFunctionsWithAsyncKeyword && !options.onlyFunctionsWithExpectInCallback && !options.onlyFunctionsWithExpectInLoop)
3910
+ return true;
3911
+ if (options.onlyFunctionsWithAsyncKeyword) {
3912
+ if (testFunction.async)
3913
+ return true;
3914
+ }
3915
+ if (options.onlyFunctionsWithExpectInCallback) {
3916
+ if (hasExpectInCallBack)
3917
+ return true;
3918
+ }
3919
+ if (options.onlyFunctionsWithExpectInLoop) {
3920
+ if (hasExpectInLoop)
3921
+ return true;
3922
+ }
3923
+ return false;
3924
+ };
3925
+ function checkExpectHasAssertions(expectFnCall, func) {
3926
+ if (getAccessorValue(expectFnCall.members[0]) === "hasAssertions") {
3927
+ if (expectFnCall.args.length) {
3928
+ context.report({
3929
+ messageId: "hasAssertionsTakesNoArguments",
3930
+ node: expectFnCall.matcher,
3931
+ suggest: [suggestRemovingExtraArguments(context, func, 0)]
3932
+ });
3933
+ }
3934
+ return;
3935
+ }
3936
+ if (expectFnCall.args.length !== 1) {
3937
+ let { loc } = expectFnCall.matcher;
3938
+ const suggestions = [];
3939
+ if (expectFnCall.args.length) {
3940
+ loc = expectFnCall.args[1].loc;
3941
+ suggestions.push(suggestRemovingExtraArguments(context, func, 1));
3942
+ }
3943
+ context.report({
3944
+ messageId: "assertionsRequiresOneArgument",
3945
+ suggest: suggestions,
3946
+ loc
3947
+ });
3948
+ return;
3949
+ }
3950
+ const [arg] = expectFnCall.args;
3951
+ if (arg.type === utils.AST_NODE_TYPES.Literal && typeof arg.value === "number" && Number.isInteger(arg.value))
3952
+ return;
3953
+ context.report({
3954
+ messageId: "assertionsRequiresNumberArgument",
3955
+ node: arg
3956
+ });
3957
+ }
3958
+ const enterExpression = () => inTestCaseCall && expressionDepth++;
3959
+ const exitExpression = () => inTestCaseCall && expressionDepth--;
3960
+ const enterForLoop = () => inForLoop = true;
3961
+ const exitForLoop = () => inForLoop = false;
3962
+ return {
3963
+ "FunctionExpression": enterExpression,
3964
+ "FunctionExpression:exit": exitExpression,
3965
+ "ArrowFunctionExpression": enterExpression,
3966
+ "ArrowFunctionExpression:exit": exitExpression,
3967
+ "ForStatement": enterForLoop,
3968
+ "ForStatement:exit": exitForLoop,
3969
+ "ForInStatement": enterForLoop,
3970
+ "ForInStatement:exit": exitForLoop,
3971
+ "ForOfStatement": enterForLoop,
3972
+ "ForOfStatement:exit": exitForLoop,
3973
+ CallExpression(node) {
3974
+ const vitestFnCall = parseVitestFnCall(node, context);
3975
+ if (vitestFnCall?.type === "test") {
3976
+ inTestCaseCall = true;
3977
+ return;
3978
+ }
3979
+ if (vitestFnCall?.type === "expect" && inTestCaseCall) {
3980
+ if (expressionDepth === 1 && isFirstStatement(node) && vitestFnCall.head.node.parent?.type === utils.AST_NODE_TYPES.MemberExpression && vitestFnCall.members.length === 1 && ["assertions", "hasAssertions"].includes(getAccessorValue(vitestFnCall.members[0]))) {
3981
+ checkExpectHasAssertions(vitestFnCall, node);
3982
+ hasExpectAssertAsFirstStatement = true;
3983
+ }
3984
+ if (inForLoop)
3985
+ hasExpectInLoop = true;
3986
+ if (expressionDepth > 1)
3987
+ hasExpectInCallBack = true;
3988
+ }
3989
+ },
3990
+ "CallExpression:exit"(node) {
3991
+ if (!isTypeOfVitestFnCall(node, context, ["test"]))
3992
+ return;
3993
+ inTestCaseCall = false;
3994
+ if (node.arguments.length < 2)
3995
+ return;
3996
+ const [, secondArg] = node.arguments;
3997
+ if (secondArg?.type === utils.AST_NODE_TYPES.ArrowFunctionExpression && secondArg.params.length) {
3998
+ if (secondArg?.params[0].type === utils.AST_NODE_TYPES.ObjectPattern) {
3999
+ if (secondArg.params[0].properties[0].type === utils.AST_NODE_TYPES.Property && secondArg.params[0].properties[0].key.type === utils.AST_NODE_TYPES.Identifier && secondArg.params[0].properties[0].key.name === "expect")
4000
+ return;
4001
+ }
4002
+ }
4003
+ if (!isFunction(secondArg) || !shouldCheckFunction(secondArg))
4004
+ return;
4005
+ hasExpectInLoop = false;
4006
+ hasExpectInCallBack = false;
4007
+ if (hasExpectAssertAsFirstStatement) {
4008
+ hasExpectAssertAsFirstStatement = false;
4009
+ return;
4010
+ }
4011
+ const suggestions = [];
4012
+ if (secondArg.body.type === utils.AST_NODE_TYPES.BlockStatement) {
4013
+ suggestions.push(
4014
+ ["suggestAddingHasAssertions", "expect.hasAssertions();"],
4015
+ ["suggestAddingAssertions", "expect.assertions();"]
4016
+ );
4017
+ }
4018
+ context.report({
4019
+ messageId: "haveExpectAssertions",
4020
+ node,
4021
+ suggest: suggestions.map(([messageId, text]) => ({
4022
+ messageId,
4023
+ fix: (fixer) => fixer.insertTextBeforeRange(
4024
+ [secondArg.body.range[0] + 1, secondArg.body.range[1]],
4025
+ text
4026
+ )
4027
+ }))
4028
+ });
4029
+ }
4030
+ };
4031
+ }
4032
+ });
4033
+
4034
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
4035
+ const eslintRequire = node_module.createRequire(require$1.resolve("eslint"));
4036
+ eslintRequire.resolve("espree");
4037
+ const STATEMENT_LIST_PARENTS = /* @__PURE__ */ new Set([
4038
+ utils.AST_NODE_TYPES.Program,
4039
+ utils.AST_NODE_TYPES.BlockStatement,
4040
+ utils.AST_NODE_TYPES.SwitchCase,
4041
+ utils.AST_NODE_TYPES.SwitchStatement
4042
+ ]);
4043
+ const isValidParent = (parentType) => {
4044
+ return STATEMENT_LIST_PARENTS.has(parentType);
4045
+ };
4046
+ const isTokenASemicolon = (token) => token.value === ";" && token.type === utils.AST_TOKEN_TYPES.Punctuator;
4047
+ const getActualLastToken = (sourceCode, node) => {
4048
+ const semiToken = sourceCode.getLastToken(node);
4049
+ const prevToken = sourceCode.getTokenBefore(semiToken);
4050
+ const nextToken = sourceCode.getTokenAfter(semiToken);
4051
+ const isSemicolonLessStyle = Boolean(
4052
+ 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
4053
+ );
4054
+ return isSemicolonLessStyle ? prevToken : semiToken;
4055
+ };
4056
+ const getPaddingLineSequences = (prevNode, nextNode, sourceCode) => {
4057
+ const pairs = [];
4058
+ const includeComments = true;
4059
+ let prevToken = getActualLastToken(sourceCode, prevNode);
4060
+ if (nextNode.loc.start.line - prevNode.loc.end.line >= 2) {
4061
+ do {
4062
+ const token = sourceCode.getTokenAfter(prevToken, { includeComments });
4063
+ if (token.loc.start.line - prevToken.loc.end.line >= 2) {
4064
+ pairs.push([prevToken, token]);
4065
+ }
4066
+ prevToken = token;
4067
+ } while (prevToken.range[0] < nextNode.range[0]);
4068
+ }
4069
+ return pairs;
4070
+ };
4071
+ const areTokensOnSameLine = (left, right) => left.loc.end.line === right.loc.start.line;
4072
+
4073
+ var PaddingType = /* @__PURE__ */ ((PaddingType2) => {
4074
+ PaddingType2[PaddingType2["Any"] = 0] = "Any";
4075
+ PaddingType2[PaddingType2["Always"] = 1] = "Always";
4076
+ return PaddingType2;
4077
+ })(PaddingType || {});
4078
+ var StatementType = /* @__PURE__ */ ((StatementType2) => {
4079
+ StatementType2[StatementType2["Any"] = 0] = "Any";
4080
+ StatementType2[StatementType2["AfterAllToken"] = 1] = "AfterAllToken";
4081
+ StatementType2[StatementType2["AfterEachToken"] = 2] = "AfterEachToken";
4082
+ StatementType2[StatementType2["BeforeAllToken"] = 3] = "BeforeAllToken";
4083
+ StatementType2[StatementType2["BeforeEachToken"] = 4] = "BeforeEachToken";
4084
+ StatementType2[StatementType2["DescribeToken"] = 5] = "DescribeToken";
4085
+ StatementType2[StatementType2["ExpectToken"] = 6] = "ExpectToken";
4086
+ StatementType2[StatementType2["FdescribeToken"] = 7] = "FdescribeToken";
4087
+ StatementType2[StatementType2["FitToken"] = 8] = "FitToken";
4088
+ StatementType2[StatementType2["ItToken"] = 9] = "ItToken";
4089
+ StatementType2[StatementType2["TestToken"] = 10] = "TestToken";
4090
+ StatementType2[StatementType2["XdescribeToken"] = 11] = "XdescribeToken";
4091
+ StatementType2[StatementType2["XitToken"] = 12] = "XitToken";
4092
+ StatementType2[StatementType2["XtestToken"] = 13] = "XtestToken";
4093
+ return StatementType2;
4094
+ })(StatementType || {});
4095
+ const paddingAlwaysTester = (prevNode, nextNode, paddingContext) => {
4096
+ const { sourceCode, ruleContext } = paddingContext;
4097
+ const paddingLines = getPaddingLineSequences(prevNode, nextNode, sourceCode);
4098
+ if (paddingLines.length > 0)
4099
+ return;
4100
+ ruleContext.report({
4101
+ node: nextNode,
4102
+ messageId: "missingPadding",
4103
+ fix(fixer) {
4104
+ let prevToken = getActualLastToken(sourceCode, prevNode);
4105
+ const nextToken = sourceCode.getFirstTokenBetween(prevToken, nextNode, {
4106
+ includeComments: true,
4107
+ /**
4108
+ * Skip the trailing comments of the previous node.
4109
+ * This inserts a blank line after the last trailing comment.
4110
+ *
4111
+ * For example:
4112
+ *
4113
+ * foo(); // trailing comment.
4114
+ * // comment.
4115
+ * bar();
4116
+ *
4117
+ * Get fixed to:
4118
+ *
4119
+ * foo(); // trailing comment.
4120
+ *
4121
+ * // comment.
4122
+ * bar();
4123
+ */
4124
+ filter(token) {
4125
+ if (areTokensOnSameLine(prevToken, token)) {
4126
+ prevToken = token;
4127
+ return false;
4128
+ }
4129
+ return true;
4130
+ }
4131
+ }) || nextNode;
4132
+ const insertText = areTokensOnSameLine(prevToken, nextToken) ? "\n\n" : "\n";
4133
+ return fixer.insertTextAfter(prevToken, insertText);
4134
+ }
4135
+ });
4136
+ };
4137
+ const paddingTesters = {
4138
+ [0 /* Any */]: () => true,
4139
+ [1 /* Always */]: paddingAlwaysTester
4140
+ };
4141
+ const createScopeInfo = () => {
4142
+ let scope = null;
4143
+ return {
4144
+ get prevNode() {
4145
+ return scope.prevNode;
4146
+ },
4147
+ set prevNode(node) {
4148
+ scope.prevNode = node;
4149
+ },
4150
+ enter() {
4151
+ scope = { upper: scope, prevNode: null };
4152
+ },
4153
+ exit() {
4154
+ scope = scope.upper;
4155
+ }
4156
+ };
4157
+ };
4158
+ const createTokenTester = (tokenName) => {
4159
+ return (node, sourceCode) => {
4160
+ let activeNode = node;
4161
+ if (activeNode.type === utils.AST_NODE_TYPES.ExpressionStatement) {
4162
+ if (activeNode.expression.type === utils.AST_NODE_TYPES.AwaitExpression) {
4163
+ activeNode = activeNode.expression.argument;
4164
+ }
4165
+ const token = sourceCode.getFirstToken(activeNode);
4166
+ return token?.type === utils.AST_TOKEN_TYPES.Identifier && token.value === tokenName;
4167
+ }
4168
+ return false;
4169
+ };
4170
+ };
4171
+ const statementTesters = {
4172
+ [0 /* Any */]: () => true,
4173
+ [1 /* AfterAllToken */]: createTokenTester("afterAll"),
4174
+ [2 /* AfterEachToken */]: createTokenTester("afterEach"),
4175
+ [3 /* BeforeAllToken */]: createTokenTester("beforeAll"),
4176
+ [4 /* BeforeEachToken */]: createTokenTester("beforeEach"),
4177
+ [5 /* DescribeToken */]: createTokenTester("describe"),
4178
+ [6 /* ExpectToken */]: createTokenTester("expect"),
4179
+ [7 /* FdescribeToken */]: createTokenTester("fdescribe"),
4180
+ [8 /* FitToken */]: createTokenTester("fit"),
4181
+ [9 /* ItToken */]: createTokenTester("it"),
4182
+ [10 /* TestToken */]: createTokenTester("test"),
4183
+ [11 /* XdescribeToken */]: createTokenTester("xdescribe"),
4184
+ [12 /* XitToken */]: createTokenTester("xit"),
4185
+ [13 /* XtestToken */]: createTokenTester("xtest")
4186
+ };
4187
+ const nodeMatchesType = (node, statementType, paddingContext) => {
4188
+ let innerStatementNode = node;
4189
+ const { sourceCode } = paddingContext;
4190
+ while (innerStatementNode.type === utils.AST_NODE_TYPES.LabeledStatement) {
4191
+ innerStatementNode = innerStatementNode.body;
4192
+ }
4193
+ if (Array.isArray(statementType)) {
4194
+ return statementType.some(
4195
+ (type) => nodeMatchesType(innerStatementNode, type, paddingContext)
4196
+ );
4197
+ }
4198
+ return statementTesters[statementType](innerStatementNode, sourceCode);
4199
+ };
4200
+ const testPadding = (prevNode, nextNode, paddingContext) => {
4201
+ const { configs } = paddingContext;
4202
+ const testType = (type) => paddingTesters[type](prevNode, nextNode, paddingContext);
4203
+ for (let i = configs.length - 1; i >= 0; --i) {
4204
+ const { prevStatementType: prevType, nextStatementType: nextType, paddingType } = configs[i];
4205
+ if (nodeMatchesType(prevNode, prevType, paddingContext) && nodeMatchesType(nextNode, nextType, paddingContext)) {
4206
+ return testType(paddingType);
4207
+ }
4208
+ }
4209
+ return testType(0 /* Any */);
4210
+ };
4211
+ const verifyNode = (node, paddingContext) => {
4212
+ const { scopeInfo } = paddingContext;
4213
+ if (!isValidParent(node?.parent.type))
4214
+ return;
4215
+ if (scopeInfo.prevNode) {
4216
+ testPadding(scopeInfo.prevNode, node, paddingContext);
4217
+ }
4218
+ scopeInfo.prevNode = node;
4219
+ };
4220
+ const createPaddingRule = (name, description, configs, deprecated = false) => {
4221
+ return createEslintRule({
4222
+ name,
4223
+ meta: {
4224
+ docs: { description },
4225
+ fixable: "whitespace",
4226
+ deprecated,
4227
+ messages: {
4228
+ missingPadding: "expect blank line before this statement"
4229
+ },
4230
+ schema: [],
4231
+ type: "suggestion"
4232
+ },
4233
+ defaultOptions: [],
4234
+ create(context) {
4235
+ const paddingContext = {
4236
+ ruleContext: context,
4237
+ sourceCode: context.sourceCode ?? context.getSourceCode(),
4238
+ scopeInfo: createScopeInfo(),
4239
+ configs
4240
+ };
4241
+ const { scopeInfo } = paddingContext;
4242
+ return {
4243
+ Program: scopeInfo.enter,
4244
+ "Program:exit": scopeInfo.exit,
4245
+ BlockStatement: scopeInfo.enter,
4246
+ "BlockStatement:exit": scopeInfo.exit,
4247
+ SwitchStatement: scopeInfo.enter,
4248
+ "SwitchStatement:exit": scopeInfo.exit,
4249
+ ":statement": (node) => verifyNode(node, paddingContext),
4250
+ SwitchCase(node) {
4251
+ verifyNode(node, paddingContext);
4252
+ scopeInfo.enter();
4253
+ },
4254
+ "SwitchCase:exit": scopeInfo.exit
4255
+ };
4256
+ }
4257
+ });
4258
+ };
4259
+
4260
+ const RULE_NAME$7 = getFilename((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
4261
+ const config$6 = [
4262
+ {
4263
+ paddingType: PaddingType.Always,
4264
+ prevStatementType: StatementType.Any,
4265
+ nextStatementType: StatementType.AfterAllToken
4266
+ },
4267
+ {
4268
+ paddingType: PaddingType.Always,
4269
+ prevStatementType: StatementType.AfterAllToken,
4270
+ nextStatementType: StatementType.Any
4271
+ }
4272
+ ];
4273
+ const paddingAroundAfterAllBlocks = createPaddingRule(RULE_NAME$7, "Enforce padding around `afterAll` blocks", config$6);
4274
+
4275
+ const RULE_NAME$6 = getFilename((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
4276
+ const config$5 = [
4277
+ {
4278
+ paddingType: PaddingType.Always,
4279
+ prevStatementType: StatementType.Any,
4280
+ nextStatementType: StatementType.AfterEachToken
4281
+ },
4282
+ {
4283
+ paddingType: PaddingType.Always,
4284
+ prevStatementType: StatementType.AfterEachToken,
4285
+ nextStatementType: StatementType.Any
4286
+ }
4287
+ ];
4288
+ const paddingAroundAfterEachBlocks = createPaddingRule(RULE_NAME$6, "Enforce padding around `afterEach` blocks", config$5);
4289
+
4290
+ const RULE_NAME$5 = getFilename((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
4291
+ const config$4 = [
4292
+ {
4293
+ paddingType: PaddingType.Always,
4294
+ prevStatementType: StatementType.Any,
4295
+ nextStatementType: StatementType.BeforeAllToken
4296
+ },
4297
+ {
4298
+ paddingType: PaddingType.Always,
4299
+ prevStatementType: StatementType.BeforeAllToken,
4300
+ nextStatementType: StatementType.Any
4301
+ }
4302
+ ];
4303
+ const paddingAroundBeforeAllBlocks = createPaddingRule(RULE_NAME$5, "Enforce padding around `beforeAll` blocks", config$4);
4304
+
4305
+ const RULE_NAME$4 = getFilename((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
4306
+ const config$3 = [
4307
+ {
4308
+ paddingType: PaddingType.Always,
4309
+ prevStatementType: StatementType.Any,
4310
+ nextStatementType: StatementType.BeforeEachToken
4311
+ },
4312
+ {
4313
+ paddingType: PaddingType.Always,
4314
+ prevStatementType: StatementType.BeforeEachToken,
4315
+ nextStatementType: StatementType.Any
4316
+ }
4317
+ ];
4318
+ const paddingAroundBeforeEachBlocks = createPaddingRule(
4319
+ RULE_NAME$4,
4320
+ "Enforce padding around `beforeEach` blocks",
4321
+ config$3
4322
+ );
4323
+
4324
+ const RULE_NAME$3 = getFilename((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
4325
+ const config$2 = [
4326
+ {
4327
+ paddingType: PaddingType.Always,
4328
+ prevStatementType: StatementType.Any,
4329
+ nextStatementType: [
4330
+ StatementType.DescribeToken,
4331
+ StatementType.FdescribeToken,
4332
+ StatementType.XdescribeToken
4333
+ ]
4334
+ },
4335
+ {
4336
+ paddingType: PaddingType.Always,
4337
+ prevStatementType: [
4338
+ StatementType.DescribeToken,
4339
+ StatementType.FdescribeToken,
4340
+ StatementType.XdescribeToken
4341
+ ],
4342
+ nextStatementType: StatementType.Any
4343
+ }
4344
+ ];
4345
+ const paddingAroundDescribeBlocks = createPaddingRule(
4346
+ RULE_NAME$3,
4347
+ "Enforce padding around `describe` blocks",
4348
+ config$2
4349
+ );
4350
+
4351
+ const RULE_NAME$2 = getFilename((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
4352
+ const config$1 = [
4353
+ {
4354
+ paddingType: PaddingType.Always,
4355
+ prevStatementType: StatementType.Any,
4356
+ nextStatementType: StatementType.ExpectToken
4357
+ },
4358
+ {
4359
+ paddingType: PaddingType.Always,
4360
+ prevStatementType: StatementType.ExpectToken,
4361
+ nextStatementType: StatementType.Any
4362
+ },
4363
+ {
4364
+ paddingType: PaddingType.Any,
4365
+ prevStatementType: StatementType.ExpectToken,
4366
+ nextStatementType: StatementType.ExpectToken
4367
+ }
4368
+ ];
4369
+ const paddingAroundExpectGroups = createPaddingRule(
4370
+ RULE_NAME$2,
4371
+ "Enforce padding around `expect` groups",
4372
+ config$1
4373
+ );
4374
+
4375
+ const RULE_NAME$1 = getFilename((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
4376
+ const config = [
4377
+ {
4378
+ paddingType: PaddingType.Always,
4379
+ prevStatementType: StatementType.Any,
4380
+ nextStatementType: [
4381
+ StatementType.TestToken,
4382
+ StatementType.ItToken,
4383
+ StatementType.FitToken,
4384
+ StatementType.XitToken,
4385
+ StatementType.XtestToken
4386
+ ]
4387
+ },
4388
+ {
4389
+ paddingType: PaddingType.Always,
4390
+ prevStatementType: [
4391
+ StatementType.TestToken,
4392
+ StatementType.ItToken,
4393
+ StatementType.FitToken,
4394
+ StatementType.XitToken,
4395
+ StatementType.XtestToken
4396
+ ],
4397
+ nextStatementType: StatementType.Any
4398
+ }
4399
+ ];
4400
+ const paddingAroundTestBlocks = createPaddingRule(
4401
+ RULE_NAME$1,
4402
+ "Enforce padding around afterAll blocks",
4403
+ config
4404
+ );
4405
+
4406
+ const RULE_NAME = getFilename((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
4407
+ const paddingAroundAll = createPaddingRule(
4408
+ RULE_NAME,
4409
+ "Enforce padding around vitest functions",
4410
+ [
4411
+ ...config$6,
4412
+ ...config$5,
4413
+ ...config$4,
4414
+ ...config$3,
4415
+ ...config$2,
4416
+ ...config$1,
4417
+ ...config
4418
+ ]
4419
+ );
4420
+
4421
+ const createConfig = (rules) => Object.keys(rules).reduce((acc, ruleName) => {
4422
+ return {
4423
+ ...acc,
4424
+ [`vitest/${ruleName}`]: rules[ruleName]
4425
+ };
4426
+ }, {});
4427
+ const createConfigLegacy = (rules) => ({
4428
+ plugins: ["vitest"],
4429
+ rules: Object.keys(rules).reduce((acc, ruleName) => {
4430
+ return {
4431
+ ...acc,
4432
+ [`vitest/${ruleName}`]: rules[ruleName]
4433
+ };
4434
+ }, {})
4435
+ });
4436
+ const allRules = {
4437
+ [RULE_NAME$X]: "warn",
4438
+ [RULE_NAME$W]: "warn",
4439
+ [RULE_NAME$U]: "warn",
4440
+ [RULE_NAME$T]: "warn",
4441
+ [RULE_NAME$R]: "warn",
4442
+ [RULE_NAME$P]: "warn",
4443
+ [RULE_NAME$O]: "warn",
4444
+ [RULE_NAME$N]: "warn",
4445
+ [RULE_NAME$M]: "warn",
4446
+ [RULE_NAME$L]: "warn",
4447
+ [RULE_NAME$J]: "warn",
4448
+ [RULE_NAME$H]: "warn",
4449
+ [RULE_NAME$G]: "warn",
4450
+ [RULE_NAME$F]: "warn",
4451
+ [RULE_NAME$E]: "warn",
4452
+ [RULE_NAME$D]: "warn",
4453
+ [RULE_NAME$C]: "warn",
4454
+ [RULE_NAME$B]: "warn",
4455
+ [RULE_NAME$A]: "warn",
4456
+ [RULE_NAME$z]: "warn",
4457
+ [RULE_NAME$y]: "warn",
4458
+ [RULE_NAME$x]: "warn",
4459
+ [RULE_NAME$w]: "warn",
4460
+ [RULE_NAME$r]: "warn",
4461
+ [RULE_NAME$t]: "warn",
4462
+ [RULE_NAME$s]: "warn",
4463
+ [RULE_NAME$q]: "warn",
4464
+ [RULE_NAME$p]: "warn",
4465
+ [RULE_NAME$o]: "warn",
4466
+ [RULE_NAME$n]: "warn",
4467
+ [RULE_NAME$m]: "warn",
4468
+ [RULE_NAME$l]: "warn",
4469
+ [RULE_NAME$k]: "warn",
4470
+ [RULE_NAME$j]: "warn",
4471
+ [RULE_NAME$i]: "warn",
4472
+ [RULE_NAME$g]: "warn",
4473
+ [RULE_NAME$f]: "warn",
4474
+ [RULE_NAME$e]: "warn",
4475
+ [RULE_NAME$c]: "warn",
4476
+ [RULE_NAME$b]: "warn",
4477
+ [RULE_NAME$a]: "warn",
4478
+ [RULE_NAME$9]: "warn",
4479
+ [RULE_NAME$8]: "warn",
4480
+ [RULE_NAME$Q]: "warn",
4481
+ [RULE_NAME$7]: "warn",
4482
+ [RULE_NAME$6]: "warn",
4483
+ [RULE_NAME]: "warn",
4484
+ [RULE_NAME$5]: "warn",
4485
+ [RULE_NAME$4]: "warn",
4486
+ [RULE_NAME$3]: "warn",
4487
+ [RULE_NAME$2]: "warn",
4488
+ [RULE_NAME$1]: "warn"
4489
+ };
4490
+ const recommended = {
4491
+ [RULE_NAME$S]: "error",
4492
+ [RULE_NAME$V]: "error",
4493
+ [RULE_NAME$K]: "error",
4494
+ [RULE_NAME$v]: "error",
4495
+ [RULE_NAME$u]: "error",
4496
+ [RULE_NAME$h]: "error",
4497
+ [RULE_NAME$d]: "error",
4498
+ [RULE_NAME$I]: "error"
4499
+ };
4500
+ const plugin = {
4501
+ meta: {
4502
+ name: "vitest",
4503
+ version
4504
+ },
4505
+ rules: {
4506
+ [RULE_NAME$X]: lowerCaseTitle,
4507
+ [RULE_NAME$W]: maxNestedDescribe,
4508
+ [RULE_NAME$V]: noIdenticalTitle,
4509
+ [RULE_NAME$U]: noFocusedTests,
4510
+ [RULE_NAME$T]: noConditionalTest,
4511
+ [RULE_NAME$S]: expectExpect,
4512
+ [RULE_NAME$R]: consistentTestIt,
4513
+ [RULE_NAME$Q]: preferToBe,
4514
+ [RULE_NAME$P]: noHooks,
4515
+ [RULE_NAME$O]: noRestrictedViMethods,
4516
+ [RULE_NAME$N]: consistentTestFilename,
4517
+ [RULE_NAME$M]: maxExpect,
4518
+ [RULE_NAME$L]: noAliasMethod,
4519
+ [RULE_NAME$K]: noCommentedOutTests,
4520
+ [RULE_NAME$J]: noConditionalExpect,
4521
+ [RULE_NAME$H]: noConditionalInTest,
4522
+ [RULE_NAME$G]: noDisabledTests,
4523
+ [RULE_NAME$F]: noDoneCallback,
4524
+ [RULE_NAME$E]: noDuplicateHooks,
4525
+ [RULE_NAME$D]: noLargeSnapshots,
4526
+ [RULE_NAME$C]: nonInterpolationInSnapShots,
4527
+ [RULE_NAME$B]: noMocksImport,
4528
+ [RULE_NAME$A]: noRestrictedMatchers,
4529
+ [RULE_NAME$z]: noStandaloneExpect,
4530
+ [RULE_NAME$y]: noTestPrefixes,
4531
+ [RULE_NAME$x]: noTestReturnStatement,
4532
+ [RULE_NAME$I]: noImportNodeTest,
4533
+ [RULE_NAME$w]: preferCalledWith,
4534
+ [RULE_NAME$v]: validTitle,
4535
+ [RULE_NAME$u]: validExpect,
4536
+ [RULE_NAME$r]: preferToBeFalsy,
4537
+ [RULE_NAME$t]: preferToBeObject,
4538
+ [RULE_NAME$s]: preferToBeTruthy,
4539
+ [RULE_NAME$q]: preferToHaveLength,
4540
+ [RULE_NAME$p]: preferEqualityMatcher,
4541
+ [RULE_NAME$o]: preferStrictEqual,
4542
+ [RULE_NAME$n]: preferExpectResolves,
4543
+ [RULE_NAME$m]: preferEach,
4544
+ [RULE_NAME$l]: preferHooksOnTop,
4545
+ [RULE_NAME$k]: preferHooksInOrder,
4546
+ [RULE_NAME$d]: requireLocalTestContextForConcurrentSnapshots,
4547
+ [RULE_NAME$j]: preferMockPromiseShorthand,
4548
+ [RULE_NAME$i]: preferSnapshotHint,
4549
+ [RULE_NAME$h]: validDescribeCallback,
4550
+ [RULE_NAME$g]: requireTopLevelDescribe,
4551
+ [RULE_NAME$f]: requireToThrowMessage,
4552
+ [RULE_NAME$e]: requireHook,
4553
+ [RULE_NAME$c]: preferTodo,
4554
+ [RULE_NAME$b]: preferSpyOn,
4555
+ [RULE_NAME$a]: preferComparisonMatcher,
4556
+ [RULE_NAME$9]: preferToContain,
4557
+ [RULE_NAME$8]: preferExpectAssertions,
4558
+ [RULE_NAME$7]: paddingAroundAfterAllBlocks,
4559
+ [RULE_NAME$6]: paddingAroundAfterEachBlocks,
4560
+ [RULE_NAME]: paddingAroundAll,
4561
+ [RULE_NAME$5]: paddingAroundBeforeAllBlocks,
4562
+ [RULE_NAME$4]: paddingAroundBeforeEachBlocks,
4563
+ [RULE_NAME$3]: paddingAroundDescribeBlocks,
4564
+ [RULE_NAME$2]: paddingAroundExpectGroups,
4565
+ [RULE_NAME$1]: paddingAroundTestBlocks
4566
+ },
4567
+ configs: {
4568
+ "legacy-recommended": createConfigLegacy(recommended),
4569
+ "legacy-all": createConfigLegacy(allRules),
4570
+ "recommended": {
4571
+ plugins: {
4572
+ get vitest() {
4573
+ return plugin;
4574
+ }
4575
+ },
4576
+ rules: createConfig(recommended)
4577
+ },
4578
+ "all": {
4579
+ plugins: {
4580
+ get vitest() {
4581
+ return plugin;
4582
+ }
4583
+ },
4584
+ rules: createConfig(allRules)
4585
+ },
4586
+ "env": {
4587
+ languageOptions: {
4588
+ globals: {
4589
+ suite: "writable",
4590
+ test: "writable",
4591
+ describe: "writable",
4592
+ it: "writable",
4593
+ expect: "writable",
4594
+ assert: "writable",
4595
+ vitest: "writable",
4596
+ vi: "writable",
4597
+ beforeAll: "writable",
4598
+ afterAll: "writable",
4599
+ beforeEach: "writable",
4600
+ afterEach: "writable"
4601
+ }
4602
+ }
4603
+ }
4604
+ },
4605
+ environments: {
4606
+ env: {
4607
+ globals: {
4608
+ suite: true,
4609
+ test: true,
4610
+ describe: true,
4611
+ it: true,
4612
+ expect: true,
4613
+ assert: true,
4614
+ vitest: true,
4615
+ vi: true,
4616
+ beforeAll: true,
4617
+ afterAll: true,
4618
+ beforeEach: true,
4619
+ afterEach: true
4620
+ }
4621
+ }
4622
+ }
4623
+ };
4624
+
4625
+ module.exports = plugin;