@vitest/eslint-plugin 1.1.17 → 1.1.19

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 CHANGED
@@ -23,7 +23,7 @@ function _interopNamespaceCompat(e) {
23
23
  const path__namespace = /*#__PURE__*/_interopNamespaceCompat(path);
24
24
  const ts__default = /*#__PURE__*/_interopDefaultCompat(ts);
25
25
 
26
- const version = "1.1.16";
26
+ const version = "1.1.18";
27
27
 
28
28
  function createEslintRule(rule) {
29
29
  const createRule = utils.ESLintUtils.RuleCreator(
@@ -228,7 +228,7 @@ const parseVitestFnCallWithReasonInner = (node, context) => {
228
228
  return null;
229
229
  const name = resolved.original ?? resolved.local;
230
230
  const links = [name, ...rest.map(getAccessorValue)];
231
- if (name !== "vi" && name !== "expect" && name !== "expectTypeOf" && !ValidVitestFnCallChains.has(links.join(".")))
231
+ if (resolved.type !== "testContext" && name !== "vi" && name !== "expect" && name !== "expectTypeOf" && !ValidVitestFnCallChains.has(links.join(".")))
232
232
  return null;
233
233
  const parsedVitestFnCall = {
234
234
  name,
@@ -300,6 +300,10 @@ const resolvePossibleAliasedGlobal = (global, context) => {
300
300
  return alias[0];
301
301
  return null;
302
302
  };
303
+ const isAncestorTestCaseCall = ({ parent }) => {
304
+ if (parent?.type === utils.AST_NODE_TYPES.CallExpression && parent.callee.type === utils.AST_NODE_TYPES.Identifier)
305
+ return TestCaseName.hasOwnProperty(parent.callee.name);
306
+ };
303
307
  const resolveScope = (scope, identifier) => {
304
308
  let currentScope = scope;
305
309
  while (currentScope !== null) {
@@ -313,6 +317,9 @@ const resolveScope = (scope, identifier) => {
313
317
  if (key?.name === identifier)
314
318
  return "testContext";
315
319
  }
320
+ const namedParam = isFunction(def.node) ? def.node.params.find((params) => params.type === utils.AST_NODE_TYPES.Identifier) : void 0;
321
+ if (namedParam && isAncestorTestCaseCall(namedParam.parent))
322
+ return "testContext";
316
323
  const importDetails = describePossibleImportDef(def);
317
324
  if (importDetails?.local === identifier)
318
325
  return importDetails;
@@ -1057,7 +1064,7 @@ const noHooks = createEslintRule({
1057
1064
  properties: {
1058
1065
  allow: {
1059
1066
  type: "array",
1060
- //@ts-ignore
1067
+ // @ts-ignore
1061
1068
  contains: ["beforeAll", "beforeEach", "afterAll", "afterEach"]
1062
1069
  }
1063
1070
  },
@@ -1147,12 +1154,12 @@ const consistentTestFilename = createEslintRule({
1147
1154
  additionalProperties: false,
1148
1155
  properties: {
1149
1156
  pattern: {
1150
- //@ts-ignore
1157
+ // @ts-ignore
1151
1158
  format: "regex",
1152
1159
  default: defaultPattern.source
1153
1160
  },
1154
1161
  allTestPattern: {
1155
- //@ts-ignore
1162
+ // @ts-ignore
1156
1163
  format: "regex",
1157
1164
  default: defaultTestsPattern.source
1158
1165
  }
@@ -1939,9 +1946,9 @@ const noStandaloneExpect = createEslintRule({
1939
1946
  {
1940
1947
  properties: {
1941
1948
  additionaltestblockfunctions: {
1942
- //@ts-ignore
1949
+ // @ts-ignore
1943
1950
  type: "array",
1944
- //@ts-ignore
1951
+ // @ts-ignore
1945
1952
  items: { type: `string` }
1946
1953
  }
1947
1954
  },
@@ -2218,7 +2225,7 @@ const validTitle = createEslintRule({
2218
2225
  MatcherAndMessageSchema,
2219
2226
  {
2220
2227
  type: "object",
2221
- //@ts-ignore
2228
+ // @ts-ignore
2222
2229
  propertyNames: { type: "string", enum: ["describe", "test", "it"] },
2223
2230
  additionalProperties: {
2224
2231
  oneOf: [{ type: "string" }, MatcherAndMessageSchema]
@@ -3244,7 +3251,7 @@ const preferViMocked = createEslintRule({
3244
3251
  meta: {
3245
3252
  type: "suggestion",
3246
3253
  docs: {
3247
- description: "Prefer `vi.mocked()` over `fn as Mock`",
3254
+ description: "require `vi.mocked()` over `fn as Mock`",
3248
3255
  requiresTypeChecking: true,
3249
3256
  recommended: false
3250
3257
  },
@@ -3707,7 +3714,7 @@ const requireLocalTestContextForConcurrentSnapshots = createEslintRule({
3707
3714
  "toMatchFileSnapshot",
3708
3715
  "toThrowErrorMatchingSnapshot",
3709
3716
  "toThrowErrorMatchingInlineSnapshot"
3710
- //@ts-ignore
3717
+ // @ts-ignore
3711
3718
  ].includes(node.callee?.property.name);
3712
3719
  if (isNotASnapshotAssertion)
3713
3720
  return;
@@ -4088,6 +4095,7 @@ const preferExpectAssertions = createEslintRule({
4088
4095
  let hasExpectInCallBack = false;
4089
4096
  let hasExpectInLoop = false;
4090
4097
  let hasExpectAssertAsFirstStatement = false;
4098
+ let testContextName = null;
4091
4099
  let inTestCaseCall = false;
4092
4100
  let inForLoop = false;
4093
4101
  const shouldCheckFunction = (testFunction) => {
@@ -4161,6 +4169,9 @@ const preferExpectAssertions = createEslintRule({
4161
4169
  inTestCaseCall = true;
4162
4170
  return;
4163
4171
  }
4172
+ if (vitestFnCall?.head.type === "testContext" && vitestFnCall.members[0].type === utils.AST_NODE_TYPES.Identifier && vitestFnCall.members[0].name === "expect") {
4173
+ testContextName = `${vitestFnCall.head.local}`;
4174
+ }
4164
4175
  if (vitestFnCall?.type === "expect" && inTestCaseCall) {
4165
4176
  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]))) {
4166
4177
  checkExpectHasAssertions(vitestFnCall, node);
@@ -4189,9 +4200,10 @@ const preferExpectAssertions = createEslintRule({
4189
4200
  }
4190
4201
  const suggestions = [];
4191
4202
  if (secondArg.body.type === utils.AST_NODE_TYPES.BlockStatement) {
4203
+ const prefix = testContextName ? `${testContextName}.` : "";
4192
4204
  suggestions.push(
4193
- ["suggestAddingHasAssertions", "expect.hasAssertions();"],
4194
- ["suggestAddingAssertions", "expect.assertions();"]
4205
+ ["suggestAddingHasAssertions", `${prefix}expect.hasAssertions();`],
4206
+ ["suggestAddingAssertions", `${prefix}expect.assertions();`]
4195
4207
  );
4196
4208
  }
4197
4209
  context.report({
@@ -4382,11 +4394,11 @@ const createPaddingRule = (name, description, configs, deprecated = false) => {
4382
4394
  };
4383
4395
  const { scopeInfo } = paddingContext;
4384
4396
  return {
4385
- Program: scopeInfo.enter,
4397
+ "Program": scopeInfo.enter,
4386
4398
  "Program:exit": scopeInfo.exit,
4387
- BlockStatement: scopeInfo.enter,
4399
+ "BlockStatement": scopeInfo.enter,
4388
4400
  "BlockStatement:exit": scopeInfo.exit,
4389
- SwitchStatement: scopeInfo.enter,
4401
+ "SwitchStatement": scopeInfo.enter,
4390
4402
  "SwitchStatement:exit": scopeInfo.exit,
4391
4403
  ":statement": (node) => verifyNode(node, paddingContext),
4392
4404
  SwitchCase(node) {
@@ -4743,7 +4755,7 @@ const validExpectInPromise = createEslintRule({
4743
4755
  name: RULE_NAME,
4744
4756
  meta: {
4745
4757
  docs: {
4746
- description: "Require promises that have expectations in their chain to be valid"
4758
+ description: "require promises that have expectations in their chain to be valid"
4747
4759
  },
4748
4760
  messages: {
4749
4761
  expectInFloatingPromise: "This promise should either be returned or awaited to ensure the expects in its chain are called"
@@ -5009,6 +5021,8 @@ const plugin = {
5009
5021
  test: "writable",
5010
5022
  describe: "writable",
5011
5023
  it: "writable",
5024
+ expectTypeOf: "writable",
5025
+ assertType: "writable",
5012
5026
  expect: "writable",
5013
5027
  assert: "writable",
5014
5028
  vitest: "writable",
@@ -5016,7 +5030,9 @@ const plugin = {
5016
5030
  beforeAll: "writable",
5017
5031
  afterAll: "writable",
5018
5032
  beforeEach: "writable",
5019
- afterEach: "writable"
5033
+ afterEach: "writable",
5034
+ onTestFailed: "writable",
5035
+ onTestFinished: "writable"
5020
5036
  }
5021
5037
  }
5022
5038
  }
@@ -5028,6 +5044,8 @@ const plugin = {
5028
5044
  test: true,
5029
5045
  describe: true,
5030
5046
  it: true,
5047
+ expectTypeOf: true,
5048
+ assertType: true,
5031
5049
  expect: true,
5032
5050
  assert: true,
5033
5051
  vitest: true,
@@ -5035,7 +5053,9 @@ const plugin = {
5035
5053
  beforeAll: true,
5036
5054
  afterAll: true,
5037
5055
  beforeEach: true,
5038
- afterEach: true
5056
+ afterEach: true,
5057
+ onTestFailed: true,
5058
+ onTestFinished: true
5039
5059
  }
5040
5060
  }
5041
5061
  }
package/dist/index.d.cts CHANGED
@@ -170,6 +170,8 @@ declare const plugin: {
170
170
  test: "writable";
171
171
  describe: "writable";
172
172
  it: "writable";
173
+ expectTypeOf: "writable";
174
+ assertType: "writable";
173
175
  expect: "writable";
174
176
  assert: "writable";
175
177
  vitest: "writable";
@@ -178,6 +180,8 @@ declare const plugin: {
178
180
  afterAll: "writable";
179
181
  beforeEach: "writable";
180
182
  afterEach: "writable";
183
+ onTestFailed: "writable";
184
+ onTestFinished: "writable";
181
185
  };
182
186
  };
183
187
  };
@@ -189,6 +193,8 @@ declare const plugin: {
189
193
  test: true;
190
194
  describe: true;
191
195
  it: true;
196
+ expectTypeOf: true;
197
+ assertType: true;
192
198
  expect: true;
193
199
  assert: true;
194
200
  vitest: true;
@@ -197,6 +203,8 @@ declare const plugin: {
197
203
  afterAll: true;
198
204
  beforeEach: true;
199
205
  afterEach: true;
206
+ onTestFailed: true;
207
+ onTestFinished: true;
200
208
  };
201
209
  };
202
210
  };
package/dist/index.d.mts CHANGED
@@ -170,6 +170,8 @@ declare const plugin: {
170
170
  test: "writable";
171
171
  describe: "writable";
172
172
  it: "writable";
173
+ expectTypeOf: "writable";
174
+ assertType: "writable";
173
175
  expect: "writable";
174
176
  assert: "writable";
175
177
  vitest: "writable";
@@ -178,6 +180,8 @@ declare const plugin: {
178
180
  afterAll: "writable";
179
181
  beforeEach: "writable";
180
182
  afterEach: "writable";
183
+ onTestFailed: "writable";
184
+ onTestFinished: "writable";
181
185
  };
182
186
  };
183
187
  };
@@ -189,6 +193,8 @@ declare const plugin: {
189
193
  test: true;
190
194
  describe: true;
191
195
  it: true;
196
+ expectTypeOf: true;
197
+ assertType: true;
192
198
  expect: true;
193
199
  assert: true;
194
200
  vitest: true;
@@ -197,6 +203,8 @@ declare const plugin: {
197
203
  afterAll: true;
198
204
  beforeEach: true;
199
205
  afterEach: true;
206
+ onTestFailed: true;
207
+ onTestFinished: true;
200
208
  };
201
209
  };
202
210
  };
package/dist/index.d.ts CHANGED
@@ -170,6 +170,8 @@ declare const plugin: {
170
170
  test: "writable";
171
171
  describe: "writable";
172
172
  it: "writable";
173
+ expectTypeOf: "writable";
174
+ assertType: "writable";
173
175
  expect: "writable";
174
176
  assert: "writable";
175
177
  vitest: "writable";
@@ -178,6 +180,8 @@ declare const plugin: {
178
180
  afterAll: "writable";
179
181
  beforeEach: "writable";
180
182
  afterEach: "writable";
183
+ onTestFailed: "writable";
184
+ onTestFinished: "writable";
181
185
  };
182
186
  };
183
187
  };
@@ -189,6 +193,8 @@ declare const plugin: {
189
193
  test: true;
190
194
  describe: true;
191
195
  it: true;
196
+ expectTypeOf: true;
197
+ assertType: true;
192
198
  expect: true;
193
199
  assert: true;
194
200
  vitest: true;
@@ -197,6 +203,8 @@ declare const plugin: {
197
203
  afterAll: true;
198
204
  beforeEach: true;
199
205
  afterEach: true;
206
+ onTestFailed: true;
207
+ onTestFinished: true;
200
208
  };
201
209
  };
202
210
  };
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import { isAbsolute, posix } from 'node:path';
4
4
  import ts from 'typescript';
5
5
  import { createRequire } from 'node:module';
6
6
 
7
- const version = "1.1.16";
7
+ const version = "1.1.18";
8
8
 
9
9
  function createEslintRule(rule) {
10
10
  const createRule = ESLintUtils.RuleCreator(
@@ -209,7 +209,7 @@ const parseVitestFnCallWithReasonInner = (node, context) => {
209
209
  return null;
210
210
  const name = resolved.original ?? resolved.local;
211
211
  const links = [name, ...rest.map(getAccessorValue)];
212
- if (name !== "vi" && name !== "expect" && name !== "expectTypeOf" && !ValidVitestFnCallChains.has(links.join(".")))
212
+ if (resolved.type !== "testContext" && name !== "vi" && name !== "expect" && name !== "expectTypeOf" && !ValidVitestFnCallChains.has(links.join(".")))
213
213
  return null;
214
214
  const parsedVitestFnCall = {
215
215
  name,
@@ -281,6 +281,10 @@ const resolvePossibleAliasedGlobal = (global, context) => {
281
281
  return alias[0];
282
282
  return null;
283
283
  };
284
+ const isAncestorTestCaseCall = ({ parent }) => {
285
+ if (parent?.type === AST_NODE_TYPES.CallExpression && parent.callee.type === AST_NODE_TYPES.Identifier)
286
+ return TestCaseName.hasOwnProperty(parent.callee.name);
287
+ };
284
288
  const resolveScope = (scope, identifier) => {
285
289
  let currentScope = scope;
286
290
  while (currentScope !== null) {
@@ -294,6 +298,9 @@ const resolveScope = (scope, identifier) => {
294
298
  if (key?.name === identifier)
295
299
  return "testContext";
296
300
  }
301
+ const namedParam = isFunction(def.node) ? def.node.params.find((params) => params.type === AST_NODE_TYPES.Identifier) : void 0;
302
+ if (namedParam && isAncestorTestCaseCall(namedParam.parent))
303
+ return "testContext";
297
304
  const importDetails = describePossibleImportDef(def);
298
305
  if (importDetails?.local === identifier)
299
306
  return importDetails;
@@ -1038,7 +1045,7 @@ const noHooks = createEslintRule({
1038
1045
  properties: {
1039
1046
  allow: {
1040
1047
  type: "array",
1041
- //@ts-ignore
1048
+ // @ts-ignore
1042
1049
  contains: ["beforeAll", "beforeEach", "afterAll", "afterEach"]
1043
1050
  }
1044
1051
  },
@@ -1128,12 +1135,12 @@ const consistentTestFilename = createEslintRule({
1128
1135
  additionalProperties: false,
1129
1136
  properties: {
1130
1137
  pattern: {
1131
- //@ts-ignore
1138
+ // @ts-ignore
1132
1139
  format: "regex",
1133
1140
  default: defaultPattern.source
1134
1141
  },
1135
1142
  allTestPattern: {
1136
- //@ts-ignore
1143
+ // @ts-ignore
1137
1144
  format: "regex",
1138
1145
  default: defaultTestsPattern.source
1139
1146
  }
@@ -1920,9 +1927,9 @@ const noStandaloneExpect = createEslintRule({
1920
1927
  {
1921
1928
  properties: {
1922
1929
  additionaltestblockfunctions: {
1923
- //@ts-ignore
1930
+ // @ts-ignore
1924
1931
  type: "array",
1925
- //@ts-ignore
1932
+ // @ts-ignore
1926
1933
  items: { type: `string` }
1927
1934
  }
1928
1935
  },
@@ -2199,7 +2206,7 @@ const validTitle = createEslintRule({
2199
2206
  MatcherAndMessageSchema,
2200
2207
  {
2201
2208
  type: "object",
2202
- //@ts-ignore
2209
+ // @ts-ignore
2203
2210
  propertyNames: { type: "string", enum: ["describe", "test", "it"] },
2204
2211
  additionalProperties: {
2205
2212
  oneOf: [{ type: "string" }, MatcherAndMessageSchema]
@@ -3225,7 +3232,7 @@ const preferViMocked = createEslintRule({
3225
3232
  meta: {
3226
3233
  type: "suggestion",
3227
3234
  docs: {
3228
- description: "Prefer `vi.mocked()` over `fn as Mock`",
3235
+ description: "require `vi.mocked()` over `fn as Mock`",
3229
3236
  requiresTypeChecking: true,
3230
3237
  recommended: false
3231
3238
  },
@@ -3688,7 +3695,7 @@ const requireLocalTestContextForConcurrentSnapshots = createEslintRule({
3688
3695
  "toMatchFileSnapshot",
3689
3696
  "toThrowErrorMatchingSnapshot",
3690
3697
  "toThrowErrorMatchingInlineSnapshot"
3691
- //@ts-ignore
3698
+ // @ts-ignore
3692
3699
  ].includes(node.callee?.property.name);
3693
3700
  if (isNotASnapshotAssertion)
3694
3701
  return;
@@ -4069,6 +4076,7 @@ const preferExpectAssertions = createEslintRule({
4069
4076
  let hasExpectInCallBack = false;
4070
4077
  let hasExpectInLoop = false;
4071
4078
  let hasExpectAssertAsFirstStatement = false;
4079
+ let testContextName = null;
4072
4080
  let inTestCaseCall = false;
4073
4081
  let inForLoop = false;
4074
4082
  const shouldCheckFunction = (testFunction) => {
@@ -4142,6 +4150,9 @@ const preferExpectAssertions = createEslintRule({
4142
4150
  inTestCaseCall = true;
4143
4151
  return;
4144
4152
  }
4153
+ if (vitestFnCall?.head.type === "testContext" && vitestFnCall.members[0].type === AST_NODE_TYPES.Identifier && vitestFnCall.members[0].name === "expect") {
4154
+ testContextName = `${vitestFnCall.head.local}`;
4155
+ }
4145
4156
  if (vitestFnCall?.type === "expect" && inTestCaseCall) {
4146
4157
  if (expressionDepth === 1 && isFirstStatement(node) && vitestFnCall.head.node.parent?.type === AST_NODE_TYPES.MemberExpression && vitestFnCall.members.length === 1 && ["assertions", "hasAssertions"].includes(getAccessorValue(vitestFnCall.members[0]))) {
4147
4158
  checkExpectHasAssertions(vitestFnCall, node);
@@ -4170,9 +4181,10 @@ const preferExpectAssertions = createEslintRule({
4170
4181
  }
4171
4182
  const suggestions = [];
4172
4183
  if (secondArg.body.type === AST_NODE_TYPES.BlockStatement) {
4184
+ const prefix = testContextName ? `${testContextName}.` : "";
4173
4185
  suggestions.push(
4174
- ["suggestAddingHasAssertions", "expect.hasAssertions();"],
4175
- ["suggestAddingAssertions", "expect.assertions();"]
4186
+ ["suggestAddingHasAssertions", `${prefix}expect.hasAssertions();`],
4187
+ ["suggestAddingAssertions", `${prefix}expect.assertions();`]
4176
4188
  );
4177
4189
  }
4178
4190
  context.report({
@@ -4363,11 +4375,11 @@ const createPaddingRule = (name, description, configs, deprecated = false) => {
4363
4375
  };
4364
4376
  const { scopeInfo } = paddingContext;
4365
4377
  return {
4366
- Program: scopeInfo.enter,
4378
+ "Program": scopeInfo.enter,
4367
4379
  "Program:exit": scopeInfo.exit,
4368
- BlockStatement: scopeInfo.enter,
4380
+ "BlockStatement": scopeInfo.enter,
4369
4381
  "BlockStatement:exit": scopeInfo.exit,
4370
- SwitchStatement: scopeInfo.enter,
4382
+ "SwitchStatement": scopeInfo.enter,
4371
4383
  "SwitchStatement:exit": scopeInfo.exit,
4372
4384
  ":statement": (node) => verifyNode(node, paddingContext),
4373
4385
  SwitchCase(node) {
@@ -4724,7 +4736,7 @@ const validExpectInPromise = createEslintRule({
4724
4736
  name: RULE_NAME,
4725
4737
  meta: {
4726
4738
  docs: {
4727
- description: "Require promises that have expectations in their chain to be valid"
4739
+ description: "require promises that have expectations in their chain to be valid"
4728
4740
  },
4729
4741
  messages: {
4730
4742
  expectInFloatingPromise: "This promise should either be returned or awaited to ensure the expects in its chain are called"
@@ -4990,6 +5002,8 @@ const plugin = {
4990
5002
  test: "writable",
4991
5003
  describe: "writable",
4992
5004
  it: "writable",
5005
+ expectTypeOf: "writable",
5006
+ assertType: "writable",
4993
5007
  expect: "writable",
4994
5008
  assert: "writable",
4995
5009
  vitest: "writable",
@@ -4997,7 +5011,9 @@ const plugin = {
4997
5011
  beforeAll: "writable",
4998
5012
  afterAll: "writable",
4999
5013
  beforeEach: "writable",
5000
- afterEach: "writable"
5014
+ afterEach: "writable",
5015
+ onTestFailed: "writable",
5016
+ onTestFinished: "writable"
5001
5017
  }
5002
5018
  }
5003
5019
  }
@@ -5009,6 +5025,8 @@ const plugin = {
5009
5025
  test: true,
5010
5026
  describe: true,
5011
5027
  it: true,
5028
+ expectTypeOf: true,
5029
+ assertType: true,
5012
5030
  expect: true,
5013
5031
  assert: true,
5014
5032
  vitest: true,
@@ -5016,7 +5034,9 @@ const plugin = {
5016
5034
  beforeAll: true,
5017
5035
  afterAll: true,
5018
5036
  beforeEach: true,
5019
- afterEach: true
5037
+ afterEach: true,
5038
+ onTestFailed: true,
5039
+ onTestFinished: true
5020
5040
  }
5021
5041
  }
5022
5042
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitest/eslint-plugin",
3
- "version": "1.1.17",
3
+ "version": "1.1.19",
4
4
  "license": "MIT",
5
5
  "description": "Eslint plugin for vitest",
6
6
  "repository": "vitest-dev/eslint-plugin-vitest",