@vitest/eslint-plugin 1.1.16 → 1.1.18
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 +41 -14
- package/dist/index.d.cts +8 -0
- package/dist/index.d.mts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.mjs +41 -14
- package/package.json +1 -1
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.
|
|
26
|
+
const version = "1.1.17";
|
|
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,
|
|
@@ -271,6 +271,12 @@ const resolveVitestFn = (context, node, identifier) => {
|
|
|
271
271
|
const maybeImport = resolveScope(scope, identifier);
|
|
272
272
|
if (maybeImport === "local")
|
|
273
273
|
return null;
|
|
274
|
+
if (maybeImport === "testContext")
|
|
275
|
+
return {
|
|
276
|
+
local: identifier,
|
|
277
|
+
original: null,
|
|
278
|
+
type: "testContext"
|
|
279
|
+
};
|
|
274
280
|
if (maybeImport) {
|
|
275
281
|
if (maybeImport.source === "vitest") {
|
|
276
282
|
return {
|
|
@@ -300,6 +306,16 @@ const resolveScope = (scope, identifier) => {
|
|
|
300
306
|
const ref = currentScope.set.get(identifier);
|
|
301
307
|
if (ref && ref.defs.length > 0) {
|
|
302
308
|
const def = ref.defs[ref.defs.length - 1];
|
|
309
|
+
const objectParam = isFunction(def.node) ? def.node.params.find((params) => params.type === utils.AST_NODE_TYPES.ObjectPattern) : void 0;
|
|
310
|
+
if (objectParam) {
|
|
311
|
+
const property = objectParam.properties.find((property2) => property2.type === utils.AST_NODE_TYPES.Property);
|
|
312
|
+
const key = property?.key.type === utils.AST_NODE_TYPES.Identifier ? property.key : void 0;
|
|
313
|
+
if (key?.name === identifier)
|
|
314
|
+
return "testContext";
|
|
315
|
+
}
|
|
316
|
+
const namedParam = isFunction(def.node) ? def.node.params.find((params) => params.type === utils.AST_NODE_TYPES.Identifier) : void 0;
|
|
317
|
+
if (namedParam)
|
|
318
|
+
return "testContext";
|
|
303
319
|
const importDetails = describePossibleImportDef(def);
|
|
304
320
|
if (importDetails?.local === identifier)
|
|
305
321
|
return importDetails;
|
|
@@ -3681,8 +3697,12 @@ const requireLocalTestContextForConcurrentSnapshots = createEslintRule({
|
|
|
3681
3697
|
create(context) {
|
|
3682
3698
|
return {
|
|
3683
3699
|
CallExpression(node) {
|
|
3684
|
-
const
|
|
3685
|
-
if (
|
|
3700
|
+
const vitestFnCall = parseVitestFnCall(node, context);
|
|
3701
|
+
if (vitestFnCall === null)
|
|
3702
|
+
return;
|
|
3703
|
+
if (vitestFnCall.type !== "expect")
|
|
3704
|
+
return;
|
|
3705
|
+
if (vitestFnCall.type === "expect" && vitestFnCall.head.type === "testContext")
|
|
3686
3706
|
return;
|
|
3687
3707
|
const isNotASnapshotAssertion = ![
|
|
3688
3708
|
"toMatchSnapshot",
|
|
@@ -4071,6 +4091,7 @@ const preferExpectAssertions = createEslintRule({
|
|
|
4071
4091
|
let hasExpectInCallBack = false;
|
|
4072
4092
|
let hasExpectInLoop = false;
|
|
4073
4093
|
let hasExpectAssertAsFirstStatement = false;
|
|
4094
|
+
let testContextName = null;
|
|
4074
4095
|
let inTestCaseCall = false;
|
|
4075
4096
|
let inForLoop = false;
|
|
4076
4097
|
const shouldCheckFunction = (testFunction) => {
|
|
@@ -4144,6 +4165,9 @@ const preferExpectAssertions = createEslintRule({
|
|
|
4144
4165
|
inTestCaseCall = true;
|
|
4145
4166
|
return;
|
|
4146
4167
|
}
|
|
4168
|
+
if (vitestFnCall?.head.type === "testContext" && vitestFnCall.members[0].type === utils.AST_NODE_TYPES.Identifier && vitestFnCall.members[0].name === "expect") {
|
|
4169
|
+
testContextName = `${vitestFnCall.head.local}`;
|
|
4170
|
+
}
|
|
4147
4171
|
if (vitestFnCall?.type === "expect" && inTestCaseCall) {
|
|
4148
4172
|
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]))) {
|
|
4149
4173
|
checkExpectHasAssertions(vitestFnCall, node);
|
|
@@ -4162,12 +4186,6 @@ const preferExpectAssertions = createEslintRule({
|
|
|
4162
4186
|
if (node.arguments.length < 2)
|
|
4163
4187
|
return;
|
|
4164
4188
|
const [, secondArg] = node.arguments;
|
|
4165
|
-
if (secondArg?.type === utils.AST_NODE_TYPES.ArrowFunctionExpression && secondArg.params.length) {
|
|
4166
|
-
if (secondArg?.params[0].type === utils.AST_NODE_TYPES.ObjectPattern) {
|
|
4167
|
-
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")
|
|
4168
|
-
return;
|
|
4169
|
-
}
|
|
4170
|
-
}
|
|
4171
4189
|
if (!isFunction(secondArg) || !shouldCheckFunction(secondArg))
|
|
4172
4190
|
return;
|
|
4173
4191
|
hasExpectInLoop = false;
|
|
@@ -4178,9 +4196,10 @@ const preferExpectAssertions = createEslintRule({
|
|
|
4178
4196
|
}
|
|
4179
4197
|
const suggestions = [];
|
|
4180
4198
|
if (secondArg.body.type === utils.AST_NODE_TYPES.BlockStatement) {
|
|
4199
|
+
const prefix = testContextName ? `${testContextName}.` : "";
|
|
4181
4200
|
suggestions.push(
|
|
4182
|
-
["suggestAddingHasAssertions",
|
|
4183
|
-
["suggestAddingAssertions",
|
|
4201
|
+
["suggestAddingHasAssertions", `${prefix}expect.hasAssertions();`],
|
|
4202
|
+
["suggestAddingAssertions", `${prefix}expect.assertions();`]
|
|
4184
4203
|
);
|
|
4185
4204
|
}
|
|
4186
4205
|
context.report({
|
|
@@ -4998,6 +5017,8 @@ const plugin = {
|
|
|
4998
5017
|
test: "writable",
|
|
4999
5018
|
describe: "writable",
|
|
5000
5019
|
it: "writable",
|
|
5020
|
+
expectTypeOf: "writable",
|
|
5021
|
+
assertType: "writable",
|
|
5001
5022
|
expect: "writable",
|
|
5002
5023
|
assert: "writable",
|
|
5003
5024
|
vitest: "writable",
|
|
@@ -5005,7 +5026,9 @@ const plugin = {
|
|
|
5005
5026
|
beforeAll: "writable",
|
|
5006
5027
|
afterAll: "writable",
|
|
5007
5028
|
beforeEach: "writable",
|
|
5008
|
-
afterEach: "writable"
|
|
5029
|
+
afterEach: "writable",
|
|
5030
|
+
onTestFailed: "writable",
|
|
5031
|
+
onTestFinished: "writable"
|
|
5009
5032
|
}
|
|
5010
5033
|
}
|
|
5011
5034
|
}
|
|
@@ -5017,6 +5040,8 @@ const plugin = {
|
|
|
5017
5040
|
test: true,
|
|
5018
5041
|
describe: true,
|
|
5019
5042
|
it: true,
|
|
5043
|
+
expectTypeOf: true,
|
|
5044
|
+
assertType: true,
|
|
5020
5045
|
expect: true,
|
|
5021
5046
|
assert: true,
|
|
5022
5047
|
vitest: true,
|
|
@@ -5024,7 +5049,9 @@ const plugin = {
|
|
|
5024
5049
|
beforeAll: true,
|
|
5025
5050
|
afterAll: true,
|
|
5026
5051
|
beforeEach: true,
|
|
5027
|
-
afterEach: true
|
|
5052
|
+
afterEach: true,
|
|
5053
|
+
onTestFailed: true,
|
|
5054
|
+
onTestFinished: true
|
|
5028
5055
|
}
|
|
5029
5056
|
}
|
|
5030
5057
|
}
|
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.
|
|
7
|
+
const version = "1.1.17";
|
|
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,
|
|
@@ -252,6 +252,12 @@ const resolveVitestFn = (context, node, identifier) => {
|
|
|
252
252
|
const maybeImport = resolveScope(scope, identifier);
|
|
253
253
|
if (maybeImport === "local")
|
|
254
254
|
return null;
|
|
255
|
+
if (maybeImport === "testContext")
|
|
256
|
+
return {
|
|
257
|
+
local: identifier,
|
|
258
|
+
original: null,
|
|
259
|
+
type: "testContext"
|
|
260
|
+
};
|
|
255
261
|
if (maybeImport) {
|
|
256
262
|
if (maybeImport.source === "vitest") {
|
|
257
263
|
return {
|
|
@@ -281,6 +287,16 @@ const resolveScope = (scope, identifier) => {
|
|
|
281
287
|
const ref = currentScope.set.get(identifier);
|
|
282
288
|
if (ref && ref.defs.length > 0) {
|
|
283
289
|
const def = ref.defs[ref.defs.length - 1];
|
|
290
|
+
const objectParam = isFunction(def.node) ? def.node.params.find((params) => params.type === AST_NODE_TYPES.ObjectPattern) : void 0;
|
|
291
|
+
if (objectParam) {
|
|
292
|
+
const property = objectParam.properties.find((property2) => property2.type === AST_NODE_TYPES.Property);
|
|
293
|
+
const key = property?.key.type === AST_NODE_TYPES.Identifier ? property.key : void 0;
|
|
294
|
+
if (key?.name === identifier)
|
|
295
|
+
return "testContext";
|
|
296
|
+
}
|
|
297
|
+
const namedParam = isFunction(def.node) ? def.node.params.find((params) => params.type === AST_NODE_TYPES.Identifier) : void 0;
|
|
298
|
+
if (namedParam)
|
|
299
|
+
return "testContext";
|
|
284
300
|
const importDetails = describePossibleImportDef(def);
|
|
285
301
|
if (importDetails?.local === identifier)
|
|
286
302
|
return importDetails;
|
|
@@ -3662,8 +3678,12 @@ const requireLocalTestContextForConcurrentSnapshots = createEslintRule({
|
|
|
3662
3678
|
create(context) {
|
|
3663
3679
|
return {
|
|
3664
3680
|
CallExpression(node) {
|
|
3665
|
-
const
|
|
3666
|
-
if (
|
|
3681
|
+
const vitestFnCall = parseVitestFnCall(node, context);
|
|
3682
|
+
if (vitestFnCall === null)
|
|
3683
|
+
return;
|
|
3684
|
+
if (vitestFnCall.type !== "expect")
|
|
3685
|
+
return;
|
|
3686
|
+
if (vitestFnCall.type === "expect" && vitestFnCall.head.type === "testContext")
|
|
3667
3687
|
return;
|
|
3668
3688
|
const isNotASnapshotAssertion = ![
|
|
3669
3689
|
"toMatchSnapshot",
|
|
@@ -4052,6 +4072,7 @@ const preferExpectAssertions = createEslintRule({
|
|
|
4052
4072
|
let hasExpectInCallBack = false;
|
|
4053
4073
|
let hasExpectInLoop = false;
|
|
4054
4074
|
let hasExpectAssertAsFirstStatement = false;
|
|
4075
|
+
let testContextName = null;
|
|
4055
4076
|
let inTestCaseCall = false;
|
|
4056
4077
|
let inForLoop = false;
|
|
4057
4078
|
const shouldCheckFunction = (testFunction) => {
|
|
@@ -4125,6 +4146,9 @@ const preferExpectAssertions = createEslintRule({
|
|
|
4125
4146
|
inTestCaseCall = true;
|
|
4126
4147
|
return;
|
|
4127
4148
|
}
|
|
4149
|
+
if (vitestFnCall?.head.type === "testContext" && vitestFnCall.members[0].type === AST_NODE_TYPES.Identifier && vitestFnCall.members[0].name === "expect") {
|
|
4150
|
+
testContextName = `${vitestFnCall.head.local}`;
|
|
4151
|
+
}
|
|
4128
4152
|
if (vitestFnCall?.type === "expect" && inTestCaseCall) {
|
|
4129
4153
|
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]))) {
|
|
4130
4154
|
checkExpectHasAssertions(vitestFnCall, node);
|
|
@@ -4143,12 +4167,6 @@ const preferExpectAssertions = createEslintRule({
|
|
|
4143
4167
|
if (node.arguments.length < 2)
|
|
4144
4168
|
return;
|
|
4145
4169
|
const [, secondArg] = node.arguments;
|
|
4146
|
-
if (secondArg?.type === AST_NODE_TYPES.ArrowFunctionExpression && secondArg.params.length) {
|
|
4147
|
-
if (secondArg?.params[0].type === AST_NODE_TYPES.ObjectPattern) {
|
|
4148
|
-
if (secondArg.params[0].properties[0].type === AST_NODE_TYPES.Property && secondArg.params[0].properties[0].key.type === AST_NODE_TYPES.Identifier && secondArg.params[0].properties[0].key.name === "expect")
|
|
4149
|
-
return;
|
|
4150
|
-
}
|
|
4151
|
-
}
|
|
4152
4170
|
if (!isFunction(secondArg) || !shouldCheckFunction(secondArg))
|
|
4153
4171
|
return;
|
|
4154
4172
|
hasExpectInLoop = false;
|
|
@@ -4159,9 +4177,10 @@ const preferExpectAssertions = createEslintRule({
|
|
|
4159
4177
|
}
|
|
4160
4178
|
const suggestions = [];
|
|
4161
4179
|
if (secondArg.body.type === AST_NODE_TYPES.BlockStatement) {
|
|
4180
|
+
const prefix = testContextName ? `${testContextName}.` : "";
|
|
4162
4181
|
suggestions.push(
|
|
4163
|
-
["suggestAddingHasAssertions",
|
|
4164
|
-
["suggestAddingAssertions",
|
|
4182
|
+
["suggestAddingHasAssertions", `${prefix}expect.hasAssertions();`],
|
|
4183
|
+
["suggestAddingAssertions", `${prefix}expect.assertions();`]
|
|
4165
4184
|
);
|
|
4166
4185
|
}
|
|
4167
4186
|
context.report({
|
|
@@ -4979,6 +4998,8 @@ const plugin = {
|
|
|
4979
4998
|
test: "writable",
|
|
4980
4999
|
describe: "writable",
|
|
4981
5000
|
it: "writable",
|
|
5001
|
+
expectTypeOf: "writable",
|
|
5002
|
+
assertType: "writable",
|
|
4982
5003
|
expect: "writable",
|
|
4983
5004
|
assert: "writable",
|
|
4984
5005
|
vitest: "writable",
|
|
@@ -4986,7 +5007,9 @@ const plugin = {
|
|
|
4986
5007
|
beforeAll: "writable",
|
|
4987
5008
|
afterAll: "writable",
|
|
4988
5009
|
beforeEach: "writable",
|
|
4989
|
-
afterEach: "writable"
|
|
5010
|
+
afterEach: "writable",
|
|
5011
|
+
onTestFailed: "writable",
|
|
5012
|
+
onTestFinished: "writable"
|
|
4990
5013
|
}
|
|
4991
5014
|
}
|
|
4992
5015
|
}
|
|
@@ -4998,6 +5021,8 @@ const plugin = {
|
|
|
4998
5021
|
test: true,
|
|
4999
5022
|
describe: true,
|
|
5000
5023
|
it: true,
|
|
5024
|
+
expectTypeOf: true,
|
|
5025
|
+
assertType: true,
|
|
5001
5026
|
expect: true,
|
|
5002
5027
|
assert: true,
|
|
5003
5028
|
vitest: true,
|
|
@@ -5005,7 +5030,9 @@ const plugin = {
|
|
|
5005
5030
|
beforeAll: true,
|
|
5006
5031
|
afterAll: true,
|
|
5007
5032
|
beforeEach: true,
|
|
5008
|
-
afterEach: true
|
|
5033
|
+
afterEach: true,
|
|
5034
|
+
onTestFailed: true,
|
|
5035
|
+
onTestFinished: true
|
|
5009
5036
|
}
|
|
5010
5037
|
}
|
|
5011
5038
|
}
|