eslint-plugin-playwright 2.7.1 → 2.8.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/README.md +2 -2
- package/dist/index.cjs +327 -124
- package/package.json +2 -2
package/README.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -118,27 +118,55 @@ var Chain = class {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
};
|
|
121
|
-
|
|
121
|
+
function resolvePossibleAliasedGlobal(context, name) {
|
|
122
122
|
const settings = context.settings;
|
|
123
123
|
const globalAliases = settings?.playwright?.globalAliases ?? {};
|
|
124
|
-
const alias = Object.entries(globalAliases).find(([, aliases]) => aliases.includes(
|
|
124
|
+
const alias = Object.entries(globalAliases).find(([, aliases]) => aliases.includes(name));
|
|
125
125
|
return alias?.[0] ?? null;
|
|
126
|
-
}
|
|
126
|
+
}
|
|
127
|
+
function resolveImportAlias(context, node) {
|
|
128
|
+
if (node.type !== "Identifier") {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const scope = context.sourceCode.getScope(node);
|
|
132
|
+
const ref = scope.references.find((r) => r.identifier === node);
|
|
133
|
+
for (const def of ref?.resolved?.defs ?? []) {
|
|
134
|
+
if (def.type === "ImportBinding" && def.node.type === "ImportSpecifier") {
|
|
135
|
+
const imported = getStringValue(def.node.imported);
|
|
136
|
+
if (imported !== node.name) {
|
|
137
|
+
return imported;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
127
143
|
var resolveToPlaywrightFn = (context, accessor) => {
|
|
128
144
|
const ident = getStringValue(accessor);
|
|
129
145
|
const resolved = /(^expect|Expect)$/.test(ident) ? "expect" : ident;
|
|
146
|
+
if (resolved === "test" || resolved === "expect") {
|
|
147
|
+
return { original: null, local: resolved };
|
|
148
|
+
}
|
|
130
149
|
return {
|
|
131
|
-
|
|
132
|
-
original: resolvePossibleAliasedGlobal(context, resolved),
|
|
150
|
+
original: resolvePossibleAliasedGlobal(context, resolved) ?? resolveImportAlias(context, accessor),
|
|
133
151
|
local: resolved
|
|
134
152
|
};
|
|
135
153
|
};
|
|
136
154
|
function determinePlaywrightFnGroup(name) {
|
|
137
|
-
if (name === "step")
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (name === "
|
|
141
|
-
|
|
155
|
+
if (name === "step") {
|
|
156
|
+
return "step";
|
|
157
|
+
}
|
|
158
|
+
if (name === "expect") {
|
|
159
|
+
return "expect";
|
|
160
|
+
}
|
|
161
|
+
if (name === "describe") {
|
|
162
|
+
return "describe";
|
|
163
|
+
}
|
|
164
|
+
if (name === "test") {
|
|
165
|
+
return "test";
|
|
166
|
+
}
|
|
167
|
+
if (testHooks.has(name)) {
|
|
168
|
+
return "hook";
|
|
169
|
+
}
|
|
142
170
|
return "unknown";
|
|
143
171
|
}
|
|
144
172
|
var modifiers = /* @__PURE__ */ new Set(["not", "resolves", "rejects"]);
|
|
@@ -213,14 +241,43 @@ var findTopMostCallExpression = (node) => {
|
|
|
213
241
|
}
|
|
214
242
|
return top;
|
|
215
243
|
};
|
|
244
|
+
function isTestExtendCall(context, node) {
|
|
245
|
+
if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression" || !isPropertyAccessor(node.callee, "extend")) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
const object = node.callee.object;
|
|
249
|
+
if (object.type === "Identifier") {
|
|
250
|
+
const resolved = resolveToPlaywrightFn(context, object);
|
|
251
|
+
if ((resolved?.original ?? resolved?.local) === "test") {
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
const dereferenced = dereference(context, object);
|
|
255
|
+
if (dereferenced) {
|
|
256
|
+
return isTestExtendCall(context, dereferenced);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
216
261
|
function parse(context, node) {
|
|
217
262
|
const chain = new Chain(node);
|
|
218
|
-
if (!chain.nodes?.length)
|
|
263
|
+
if (!chain.nodes?.length) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
219
266
|
const [first, ...rest] = chain.nodes;
|
|
220
|
-
|
|
221
|
-
if (!resolved)
|
|
267
|
+
let resolved = resolveToPlaywrightFn(context, first);
|
|
268
|
+
if (!resolved) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
222
271
|
let name = resolved.original ?? resolved.local;
|
|
223
272
|
const links = [name, ...rest.map((link) => getStringValue(link))];
|
|
273
|
+
if (determinePlaywrightFnGroup(name) === "unknown") {
|
|
274
|
+
const dereferenced = dereference(context, first);
|
|
275
|
+
if (dereferenced && isTestExtendCall(context, dereferenced)) {
|
|
276
|
+
name = "test";
|
|
277
|
+
links[0] = "test";
|
|
278
|
+
resolved = { local: resolved.local, original: "test" };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
224
281
|
if (name === "test" && links.length > 1) {
|
|
225
282
|
const nextLinkName = links[1];
|
|
226
283
|
const nextLinkGroup = determinePlaywrightFnGroup(nextLinkName);
|
|
@@ -246,7 +303,9 @@ function parse(context, node) {
|
|
|
246
303
|
parsedFnCall.members.shift();
|
|
247
304
|
}
|
|
248
305
|
const result = parseExpectCall(chain, parsedFnCall, stage);
|
|
249
|
-
if (!result)
|
|
306
|
+
if (!result) {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
250
309
|
if (typeof result === "string" && findTopMostCallExpression(node) !== node) {
|
|
251
310
|
return null;
|
|
252
311
|
}
|
|
@@ -294,7 +353,9 @@ var isTypeOfFnCall = (context, node, types) => {
|
|
|
294
353
|
|
|
295
354
|
// src/utils/ast.ts
|
|
296
355
|
function getStringValue(node) {
|
|
297
|
-
if (!node)
|
|
356
|
+
if (!node) {
|
|
357
|
+
return "";
|
|
358
|
+
}
|
|
298
359
|
return node.type === "Identifier" ? node.name : node.type === "TemplateLiteral" ? node.quasis[0].value.raw : node.type === "Literal" && typeof node.value === "string" ? node.value : "";
|
|
299
360
|
}
|
|
300
361
|
function getRawValue(node) {
|
|
@@ -323,7 +384,9 @@ function isPropertyAccessor(node, name) {
|
|
|
323
384
|
}
|
|
324
385
|
function findParent(node, type) {
|
|
325
386
|
const parent = node.parent;
|
|
326
|
-
if (!parent)
|
|
387
|
+
if (!parent) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
327
390
|
return parent.type === type ? parent : findParent(parent, type);
|
|
328
391
|
}
|
|
329
392
|
function dig(node, identifier) {
|
|
@@ -394,6 +457,9 @@ var getPaddingLineSequences = (prevNode, nextNode, sourceCode) => {
|
|
|
394
457
|
return pairs;
|
|
395
458
|
};
|
|
396
459
|
var areTokensOnSameLine = (left, right) => left.loc.end.line === right.loc.start.line;
|
|
460
|
+
var isPromiseAccessor = (node) => {
|
|
461
|
+
return node.type === "MemberExpression" && isIdentifier(node.property, /^(then|catch|finally)$/);
|
|
462
|
+
};
|
|
397
463
|
|
|
398
464
|
// src/utils/createRule.ts
|
|
399
465
|
function interpolate(str, data) {
|
|
@@ -683,7 +749,7 @@ var max_expects_default = createRule({
|
|
|
683
749
|
"ArrowFunctionExpression:exit": maybeResetCount,
|
|
684
750
|
"CallExpression"(node) {
|
|
685
751
|
const call = parseFnCall(context, node);
|
|
686
|
-
if (call?.type !== "expect"
|
|
752
|
+
if (call?.type !== "expect") {
|
|
687
753
|
return;
|
|
688
754
|
}
|
|
689
755
|
count += 1;
|
|
@@ -872,29 +938,65 @@ var missing_playwright_await_default = createRule({
|
|
|
872
938
|
// Add any custom matchers to the set
|
|
873
939
|
...options.customMatchers || []
|
|
874
940
|
]);
|
|
941
|
+
function isVariableConsumed(declarator, checkValidity2, validTypes2, visited) {
|
|
942
|
+
const variables = context.sourceCode.getDeclaredVariables(declarator);
|
|
943
|
+
for (const variable of variables) {
|
|
944
|
+
for (const ref of variable.references) {
|
|
945
|
+
if (!ref.isRead()) {
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
const refParent = ref.identifier.parent;
|
|
949
|
+
if (visited.has(refParent)) {
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
if (validTypes2.has(refParent.type)) {
|
|
953
|
+
return true;
|
|
954
|
+
}
|
|
955
|
+
if (refParent.type === "VariableDeclarator") {
|
|
956
|
+
if (checkValidity2(ref.identifier, visited)) {
|
|
957
|
+
return true;
|
|
958
|
+
}
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
if (checkValidity2(refParent, visited)) {
|
|
962
|
+
return true;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return false;
|
|
967
|
+
}
|
|
875
968
|
function checkValidity(node, visited) {
|
|
876
969
|
const parent = node.parent;
|
|
877
|
-
if (!parent)
|
|
878
|
-
|
|
970
|
+
if (!parent) {
|
|
971
|
+
return false;
|
|
972
|
+
}
|
|
973
|
+
if (visited.has(parent)) {
|
|
974
|
+
return false;
|
|
975
|
+
}
|
|
879
976
|
visited.add(parent);
|
|
880
|
-
if (validTypes.has(parent.type))
|
|
881
|
-
|
|
977
|
+
if (validTypes.has(parent.type)) {
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
980
|
+
if (isPromiseAccessor(parent) && parent.parent?.type === "CallExpression") {
|
|
882
981
|
return checkValidity(parent.parent, visited);
|
|
883
982
|
}
|
|
983
|
+
if (parent.type === "CallExpression" && parent.callee === node && isPromiseAccessor(node)) {
|
|
984
|
+
return checkValidity(parent, visited);
|
|
985
|
+
}
|
|
884
986
|
if (parent.type === "ArrayExpression") {
|
|
885
987
|
return checkValidity(parent, visited);
|
|
886
988
|
}
|
|
989
|
+
if (parent.type === "ConditionalExpression") {
|
|
990
|
+
return checkValidity(parent, visited);
|
|
991
|
+
}
|
|
992
|
+
if (parent.type === "SpreadElement") {
|
|
993
|
+
return checkValidity(parent, visited);
|
|
994
|
+
}
|
|
887
995
|
if (parent.type === "CallExpression" && parent.callee.type === "MemberExpression" && isIdentifier(parent.callee.object, "Promise") && isIdentifier(parent.callee.property, "all")) {
|
|
888
996
|
return true;
|
|
889
997
|
}
|
|
890
998
|
if (parent.type === "VariableDeclarator") {
|
|
891
|
-
|
|
892
|
-
for (const ref of scope.references) {
|
|
893
|
-
const refParent = ref.identifier.parent;
|
|
894
|
-
if (visited.has(refParent)) continue;
|
|
895
|
-
if (validTypes.has(refParent.type)) return true;
|
|
896
|
-
if (checkValidity(refParent, visited)) return true;
|
|
897
|
-
}
|
|
999
|
+
return isVariableConsumed(parent, checkValidity, validTypes, visited);
|
|
898
1000
|
}
|
|
899
1001
|
return false;
|
|
900
1002
|
}
|
|
@@ -912,7 +1014,9 @@ var missing_playwright_await_default = createRule({
|
|
|
912
1014
|
return;
|
|
913
1015
|
}
|
|
914
1016
|
const call = parseFnCall(context, node);
|
|
915
|
-
if (call?.type !== "step" && call?.type !== "expect")
|
|
1017
|
+
if (call?.type !== "step" && call?.type !== "expect") {
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
916
1020
|
const result = getCallType(call, awaitableMatchers);
|
|
917
1021
|
const isValid = result ? checkValidity(node, /* @__PURE__ */ new Set()) : false;
|
|
918
1022
|
if (result && !isValid) {
|
|
@@ -970,7 +1074,9 @@ function hasTests(context, node) {
|
|
|
970
1074
|
var no_commented_out_tests_default = createRule({
|
|
971
1075
|
create(context) {
|
|
972
1076
|
function checkNode(node) {
|
|
973
|
-
if (!hasTests(context, node))
|
|
1077
|
+
if (!hasTests(context, node)) {
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
974
1080
|
context.report({
|
|
975
1081
|
messageId: "commentedTests",
|
|
976
1082
|
node
|
|
@@ -997,18 +1103,6 @@ var no_commented_out_tests_default = createRule({
|
|
|
997
1103
|
|
|
998
1104
|
// src/rules/no-conditional-expect.ts
|
|
999
1105
|
var isCatchCall = (node) => node.callee.type === "MemberExpression" && isPropertyAccessor(node.callee, "catch");
|
|
1000
|
-
var getTestCallExpressionsFromDeclaredVariables = (context, declaredVariables) => {
|
|
1001
|
-
return declaredVariables.reduce(
|
|
1002
|
-
(acc, { references }) => [
|
|
1003
|
-
...acc,
|
|
1004
|
-
...references.map(({ identifier }) => identifier.parent).filter(
|
|
1005
|
-
// ESLint types are infurating
|
|
1006
|
-
(node) => node?.type === "CallExpression" && isTypeOfFnCall(context, node, ["test"])
|
|
1007
|
-
)
|
|
1008
|
-
],
|
|
1009
|
-
[]
|
|
1010
|
-
);
|
|
1011
|
-
};
|
|
1012
1106
|
var no_conditional_expect_default = createRule({
|
|
1013
1107
|
create(context) {
|
|
1014
1108
|
let conditionalDepth = 0;
|
|
@@ -1050,16 +1144,6 @@ var no_conditional_expect_default = createRule({
|
|
|
1050
1144
|
"CatchClause:exit": decreaseConditionalDepth,
|
|
1051
1145
|
"ConditionalExpression": increaseConditionalDepth,
|
|
1052
1146
|
"ConditionalExpression:exit": decreaseConditionalDepth,
|
|
1053
|
-
"FunctionDeclaration"(node) {
|
|
1054
|
-
const declaredVariables = context.sourceCode.getDeclaredVariables(node);
|
|
1055
|
-
const testCallExpressions = getTestCallExpressionsFromDeclaredVariables(
|
|
1056
|
-
context,
|
|
1057
|
-
declaredVariables
|
|
1058
|
-
);
|
|
1059
|
-
if (testCallExpressions.length > 0) {
|
|
1060
|
-
inTestCase = true;
|
|
1061
|
-
}
|
|
1062
|
-
},
|
|
1063
1147
|
"IfStatement": increaseConditionalDepth,
|
|
1064
1148
|
"IfStatement:exit": decreaseConditionalDepth,
|
|
1065
1149
|
"LogicalExpression": increaseConditionalDepth,
|
|
@@ -1089,11 +1173,15 @@ var no_conditional_in_test_default = createRule({
|
|
|
1089
1173
|
return;
|
|
1090
1174
|
}
|
|
1091
1175
|
const call = findParent(node, "CallExpression");
|
|
1092
|
-
if (!call)
|
|
1176
|
+
if (!call) {
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1093
1179
|
if (isTypeOfFnCall(context, call, ["test", "step"])) {
|
|
1094
1180
|
const testFunction = call.arguments[call.arguments.length - 1];
|
|
1095
1181
|
const functionBody = findParent(node, "BlockStatement");
|
|
1096
|
-
if (!functionBody)
|
|
1182
|
+
if (!functionBody) {
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1097
1185
|
let currentParent = functionBody.parent;
|
|
1098
1186
|
while (currentParent && currentParent !== testFunction) {
|
|
1099
1187
|
currentParent = currentParent.parent;
|
|
@@ -1131,7 +1219,9 @@ var no_duplicate_hooks_default = createRule({
|
|
|
1131
1219
|
return {
|
|
1132
1220
|
"CallExpression"(node) {
|
|
1133
1221
|
const call = parseFnCall(context, node);
|
|
1134
|
-
if (!call)
|
|
1222
|
+
if (!call) {
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1135
1225
|
if (call.type === "describe") {
|
|
1136
1226
|
hookContexts.push({});
|
|
1137
1227
|
}
|
|
@@ -1177,7 +1267,9 @@ var no_duplicate_slow_default = createRule({
|
|
|
1177
1267
|
return {
|
|
1178
1268
|
"CallExpression"(node) {
|
|
1179
1269
|
const call = parseFnCall(context, node);
|
|
1180
|
-
if (!call)
|
|
1270
|
+
if (!call) {
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1181
1273
|
if (call.type === "test" || call.type === "describe") {
|
|
1182
1274
|
scopes.push(scopes[scopes.length - 1]);
|
|
1183
1275
|
}
|
|
@@ -1298,7 +1390,9 @@ var no_focused_test_default = createRule({
|
|
|
1298
1390
|
return;
|
|
1299
1391
|
}
|
|
1300
1392
|
const onlyNode = call.members.find((s) => getStringValue(s) === "only");
|
|
1301
|
-
if (!onlyNode)
|
|
1393
|
+
if (!onlyNode) {
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1302
1396
|
context.report({
|
|
1303
1397
|
messageId: "noFocusedTest",
|
|
1304
1398
|
node: onlyNode,
|
|
@@ -1383,7 +1477,7 @@ var no_get_by_title_default = createRule({
|
|
|
1383
1477
|
create(context) {
|
|
1384
1478
|
return {
|
|
1385
1479
|
CallExpression(node) {
|
|
1386
|
-
if (
|
|
1480
|
+
if (node.callee.type === "MemberExpression" && getStringValue(node.callee.property) === "getByTitle") {
|
|
1387
1481
|
context.report({ messageId: "noGetByTitle", node });
|
|
1388
1482
|
}
|
|
1389
1483
|
}
|
|
@@ -1412,7 +1506,9 @@ var no_hooks_default = createRule({
|
|
|
1412
1506
|
return {
|
|
1413
1507
|
CallExpression(node) {
|
|
1414
1508
|
const call = parseFnCall(context, node);
|
|
1415
|
-
if (!call)
|
|
1509
|
+
if (!call) {
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1416
1512
|
if (call.type === "hook" && !options.allow.includes(call.name)) {
|
|
1417
1513
|
context.report({
|
|
1418
1514
|
data: { hookName: call.name },
|
|
@@ -1507,20 +1603,26 @@ var no_networkidle_default = createRule({
|
|
|
1507
1603
|
create(context) {
|
|
1508
1604
|
return {
|
|
1509
1605
|
CallExpression(node) {
|
|
1510
|
-
if (node.callee.type !== "MemberExpression")
|
|
1606
|
+
if (node.callee.type !== "MemberExpression") {
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1511
1609
|
const methodName = getStringValue(node.callee.property);
|
|
1512
|
-
if (!methods.has(methodName))
|
|
1610
|
+
if (!methods.has(methodName)) {
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1513
1613
|
if (methodName === "waitForLoadState") {
|
|
1514
1614
|
const arg = node.arguments[0];
|
|
1515
|
-
if (arg &&
|
|
1615
|
+
if (arg && isStringNode(arg, "networkidle")) {
|
|
1516
1616
|
context.report({ messageId, node: arg });
|
|
1517
1617
|
}
|
|
1518
1618
|
return;
|
|
1519
1619
|
}
|
|
1520
1620
|
if (node.arguments.length >= 2) {
|
|
1521
1621
|
const [_, arg] = node.arguments;
|
|
1522
|
-
if (arg.type !== "ObjectExpression")
|
|
1523
|
-
|
|
1622
|
+
if (arg.type !== "ObjectExpression") {
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
const property = arg.properties.filter((p) => p.type === "Property").find((p) => isStringNode(p.value, "networkidle"));
|
|
1524
1626
|
if (property) {
|
|
1525
1627
|
context.report({ messageId, node: property.value });
|
|
1526
1628
|
}
|
|
@@ -1547,9 +1649,13 @@ var no_nth_methods_default = createRule({
|
|
|
1547
1649
|
create(context) {
|
|
1548
1650
|
return {
|
|
1549
1651
|
CallExpression(node) {
|
|
1550
|
-
if (node.callee.type !== "MemberExpression")
|
|
1652
|
+
if (node.callee.type !== "MemberExpression") {
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1551
1655
|
const method = getStringValue(node.callee.property);
|
|
1552
|
-
if (!methods2.has(method))
|
|
1656
|
+
if (!methods2.has(method)) {
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1553
1659
|
context.report({
|
|
1554
1660
|
data: { method },
|
|
1555
1661
|
loc: {
|
|
@@ -1614,8 +1720,9 @@ var no_raw_locators_default = createRule({
|
|
|
1614
1720
|
}
|
|
1615
1721
|
return {
|
|
1616
1722
|
CallExpression(node) {
|
|
1617
|
-
if (node.callee.type !== "MemberExpression" || node.arguments[0]?.type === "Identifier")
|
|
1723
|
+
if (node.callee.type !== "MemberExpression" || node.arguments[0]?.type === "Identifier") {
|
|
1618
1724
|
return;
|
|
1725
|
+
}
|
|
1619
1726
|
const method = getStringValue(node.callee.property);
|
|
1620
1727
|
const arg = getStringValue(node.arguments[0]);
|
|
1621
1728
|
const isLocator = isPageMethod(node, "locator") || method === "locator";
|
|
@@ -1673,7 +1780,7 @@ var no_restricted_locators_default = createRule({
|
|
|
1673
1780
|
return;
|
|
1674
1781
|
}
|
|
1675
1782
|
for (const [restrictedType, message] of restrictionMap.entries()) {
|
|
1676
|
-
if (
|
|
1783
|
+
if (getStringValue(node.callee.property) === restrictedType) {
|
|
1677
1784
|
context.report({
|
|
1678
1785
|
data: {
|
|
1679
1786
|
message: message ?? "",
|
|
@@ -1728,7 +1835,9 @@ var no_restricted_matchers_default = createRule({
|
|
|
1728
1835
|
return {
|
|
1729
1836
|
CallExpression(node) {
|
|
1730
1837
|
const call = parseFnCall(context, node);
|
|
1731
|
-
if (call?.type !== "expect")
|
|
1838
|
+
if (call?.type !== "expect") {
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1732
1841
|
Object.entries(restrictedChains).map(([restriction, message]) => {
|
|
1733
1842
|
const chain = call.members;
|
|
1734
1843
|
const restrictionLinks = restriction.split(".").length;
|
|
@@ -1860,9 +1969,11 @@ var no_skipped_test_default = createRule({
|
|
|
1860
1969
|
return;
|
|
1861
1970
|
}
|
|
1862
1971
|
const skipNode = call.members.find((s) => getStringValue(s) === "skip");
|
|
1863
|
-
if (!skipNode)
|
|
1972
|
+
if (!skipNode) {
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1864
1975
|
const isStandalone = call.type === "config";
|
|
1865
|
-
if (isStandalone && allowConditional && (node.arguments.length !== 0 || findParent(node, "BlockStatement")?.parent?.type === "IfStatement")) {
|
|
1976
|
+
if (isStandalone && allowConditional && (node.arguments.length !== 0 || findParent(node, "BlockStatement")?.parent?.type === "IfStatement" || findParent(node, "SwitchCase") !== void 0)) {
|
|
1866
1977
|
return;
|
|
1867
1978
|
}
|
|
1868
1979
|
context.report({
|
|
@@ -1922,7 +2033,9 @@ var no_slowed_test_default = createRule({
|
|
|
1922
2033
|
return;
|
|
1923
2034
|
}
|
|
1924
2035
|
const slowNode = call.members.find((s) => getStringValue(s) === "slow");
|
|
1925
|
-
if (!slowNode)
|
|
2036
|
+
if (!slowNode) {
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
1926
2039
|
const isStandalone = call.type === "config";
|
|
1927
2040
|
if (isStandalone && allowConditional && (node.arguments.length !== 0 || findParent(node, "BlockStatement")?.parent?.type === "IfStatement")) {
|
|
1928
2041
|
return;
|
|
@@ -2046,7 +2159,7 @@ var no_standalone_expect_default = createRule({
|
|
|
2046
2159
|
},
|
|
2047
2160
|
"CallExpression:exit"(node) {
|
|
2048
2161
|
const top = callStack.at(-1);
|
|
2049
|
-
if (top === "test" && isTypeOfFnCall(context, node, ["test"]) && node
|
|
2162
|
+
if (top === "test" && isTypeOfFnCall(context, node, ["test"]) || top === "hook" && isTypeOfFnCall(context, node, ["hook"]) || top === "template" && node.callee.type === "TaggedTemplateExpression" || top === "fixture" && node.callee.type === "MemberExpression" && isPropertyAccessor(node.callee, "extend")) {
|
|
2050
2163
|
callStack.pop();
|
|
2051
2164
|
}
|
|
2052
2165
|
}
|
|
@@ -2075,16 +2188,22 @@ var truthy = Boolean;
|
|
|
2075
2188
|
|
|
2076
2189
|
// src/rules/no-unsafe-references.ts
|
|
2077
2190
|
function collectVariables(scope) {
|
|
2078
|
-
if (!scope || scope.type === "global")
|
|
2191
|
+
if (!scope || scope.type === "global") {
|
|
2192
|
+
return [];
|
|
2193
|
+
}
|
|
2079
2194
|
return [...collectVariables(scope.upper), ...scope.variables.map((ref) => ref.name)];
|
|
2080
2195
|
}
|
|
2081
2196
|
function addArgument(fixer, node, refs) {
|
|
2082
|
-
if (!node.arguments.length)
|
|
2197
|
+
if (!node.arguments.length) {
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2083
2200
|
if (node.arguments.length === 1) {
|
|
2084
2201
|
return fixer.insertTextAfter(node.arguments[0], `, [${refs}]`);
|
|
2085
2202
|
}
|
|
2086
2203
|
const arg = node.arguments.at(-1);
|
|
2087
|
-
if (!arg)
|
|
2204
|
+
if (!arg) {
|
|
2205
|
+
return;
|
|
2206
|
+
}
|
|
2088
2207
|
if (arg.type !== "ArrayExpression") {
|
|
2089
2208
|
return fixer.replaceText(arg, `[${getStringValue(arg)}, ${refs}]`);
|
|
2090
2209
|
}
|
|
@@ -2110,9 +2229,13 @@ var no_unsafe_references_default = createRule({
|
|
|
2110
2229
|
create(context) {
|
|
2111
2230
|
return {
|
|
2112
2231
|
CallExpression(node) {
|
|
2113
|
-
if (!isPageMethod(node, "evaluate") && !isPageMethod(node, "addInitScript"))
|
|
2232
|
+
if (!isPageMethod(node, "evaluate") && !isPageMethod(node, "addInitScript")) {
|
|
2233
|
+
return;
|
|
2234
|
+
}
|
|
2114
2235
|
const [fn] = node.arguments;
|
|
2115
|
-
if (!fn || !isFunction(fn))
|
|
2236
|
+
if (!fn || !isFunction(fn)) {
|
|
2237
|
+
return;
|
|
2238
|
+
}
|
|
2116
2239
|
const { through, upper } = context.sourceCode.getScope(fn.body);
|
|
2117
2240
|
const allRefs = new Set(collectVariables(upper));
|
|
2118
2241
|
through.filter((ref) => {
|
|
@@ -2245,7 +2368,9 @@ var expectMatchers = /* @__PURE__ */ new Set([
|
|
|
2245
2368
|
"toThrowError"
|
|
2246
2369
|
]);
|
|
2247
2370
|
function isSupportedMethod(node) {
|
|
2248
|
-
if (node.callee.type !== "MemberExpression")
|
|
2371
|
+
if (node.callee.type !== "MemberExpression") {
|
|
2372
|
+
return false;
|
|
2373
|
+
}
|
|
2249
2374
|
const name = getStringValue(node.callee.property);
|
|
2250
2375
|
return locatorMethods.has(name) || pageMethods.has(name) && isPageMethod(node, name);
|
|
2251
2376
|
}
|
|
@@ -2300,7 +2425,9 @@ function replaceAccessorFixer(fixer, node, text) {
|
|
|
2300
2425
|
}
|
|
2301
2426
|
function removePropertyFixer(fixer, property) {
|
|
2302
2427
|
const parent = property.parent;
|
|
2303
|
-
if (parent?.type !== "ObjectExpression")
|
|
2428
|
+
if (parent?.type !== "ObjectExpression") {
|
|
2429
|
+
return;
|
|
2430
|
+
}
|
|
2304
2431
|
if (parent.properties.length === 1) {
|
|
2305
2432
|
return fixer.remove(parent);
|
|
2306
2433
|
}
|
|
@@ -2324,7 +2451,9 @@ var matcherConfig = {
|
|
|
2324
2451
|
};
|
|
2325
2452
|
function getOptions(call, name) {
|
|
2326
2453
|
const [arg] = call.matcherArgs;
|
|
2327
|
-
if (arg?.type !== "ObjectExpression")
|
|
2454
|
+
if (arg?.type !== "ObjectExpression") {
|
|
2455
|
+
return;
|
|
2456
|
+
}
|
|
2328
2457
|
const property = arg.properties.find(
|
|
2329
2458
|
(p) => p.type === "Property" && getStringValue(p.key) === name && isBooleanLiteral(p.value)
|
|
2330
2459
|
);
|
|
@@ -2339,13 +2468,21 @@ var no_useless_not_default = createRule({
|
|
|
2339
2468
|
return {
|
|
2340
2469
|
CallExpression(node) {
|
|
2341
2470
|
const call = parseFnCall(context, node);
|
|
2342
|
-
if (call?.type !== "expect")
|
|
2471
|
+
if (call?.type !== "expect") {
|
|
2472
|
+
return;
|
|
2473
|
+
}
|
|
2343
2474
|
const config = matcherConfig[call.matcherName];
|
|
2344
|
-
if (!config)
|
|
2475
|
+
if (!config) {
|
|
2476
|
+
return;
|
|
2477
|
+
}
|
|
2345
2478
|
const options = config.argName ? getOptions(call, config.argName) : void 0;
|
|
2346
|
-
if (options?.arg && options.value === void 0)
|
|
2479
|
+
if (options?.arg && options.value === void 0) {
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
2347
2482
|
const notModifier = call.modifiers.find((mod) => getStringValue(mod) === "not");
|
|
2348
|
-
if (!notModifier && !options?.property)
|
|
2483
|
+
if (!notModifier && !options?.property) {
|
|
2484
|
+
return;
|
|
2485
|
+
}
|
|
2349
2486
|
const isInverted = !!notModifier !== (options?.value === false);
|
|
2350
2487
|
const newMatcherName = isInverted ? config.inverse : call.matcherName;
|
|
2351
2488
|
context.report({
|
|
@@ -2438,7 +2575,7 @@ var no_wait_for_selector_default = createRule({
|
|
|
2438
2575
|
suggest: [
|
|
2439
2576
|
{
|
|
2440
2577
|
fix: (fixer) => fixer.remove(
|
|
2441
|
-
node.parent && node.parent.type !== "AwaitExpression" ? node.parent : node.parent.parent
|
|
2578
|
+
node.parent && node.parent.type !== "AwaitExpression" && node.parent.type !== "VariableDeclarator" ? node.parent : node.parent.parent
|
|
2442
2579
|
),
|
|
2443
2580
|
messageId: "removeWaitForSelector"
|
|
2444
2581
|
}
|
|
@@ -2475,7 +2612,7 @@ var no_wait_for_timeout_default = createRule({
|
|
|
2475
2612
|
suggest: [
|
|
2476
2613
|
{
|
|
2477
2614
|
fix: (fixer) => fixer.remove(
|
|
2478
|
-
node.parent && node.parent.type !== "AwaitExpression" ? node.parent : node.parent.parent
|
|
2615
|
+
node.parent && node.parent.type !== "AwaitExpression" && node.parent.type !== "VariableDeclarator" ? node.parent : node.parent.parent
|
|
2479
2616
|
),
|
|
2480
2617
|
messageId: "removeWaitForTimeout"
|
|
2481
2618
|
}
|
|
@@ -2507,6 +2644,7 @@ var isString = (node) => {
|
|
|
2507
2644
|
var isComparingToString = (expression) => {
|
|
2508
2645
|
return isString(expression.left) || isString(expression.right);
|
|
2509
2646
|
};
|
|
2647
|
+
var skipModifiers = /* @__PURE__ */ new Set(["not", "soft", "poll"]);
|
|
2510
2648
|
var invertedOperators = {
|
|
2511
2649
|
"<": ">=",
|
|
2512
2650
|
"<=": ">",
|
|
@@ -2528,9 +2666,16 @@ var prefer_comparison_matcher_default = createRule({
|
|
|
2528
2666
|
return {
|
|
2529
2667
|
CallExpression(node) {
|
|
2530
2668
|
const call = parseFnCall(context, node);
|
|
2531
|
-
if (call?.type !== "expect" || call.matcherArgs.length === 0)
|
|
2532
|
-
|
|
2533
|
-
|
|
2669
|
+
if (call?.type !== "expect" || call.matcherArgs.length === 0) {
|
|
2670
|
+
return;
|
|
2671
|
+
}
|
|
2672
|
+
let expect = call.head.node.parent;
|
|
2673
|
+
while (expect?.type === "MemberExpression") {
|
|
2674
|
+
expect = expect.parent;
|
|
2675
|
+
}
|
|
2676
|
+
if (expect?.type !== "CallExpression") {
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2534
2679
|
const [comparison] = expect.arguments;
|
|
2535
2680
|
const expectCallEnd = expect.range[1];
|
|
2536
2681
|
const [matcherArg] = call.matcherArgs;
|
|
@@ -2549,7 +2694,7 @@ var prefer_comparison_matcher_default = createRule({
|
|
|
2549
2694
|
data: { preferredMatcher },
|
|
2550
2695
|
fix(fixer) {
|
|
2551
2696
|
const [modifier] = call.modifiers;
|
|
2552
|
-
const modifierText = modifier && getStringValue(modifier)
|
|
2697
|
+
const modifierText = modifier && !skipModifiers.has(getStringValue(modifier)) ? `.${getStringValue(modifier)}` : "";
|
|
2553
2698
|
return [
|
|
2554
2699
|
// Replace the comparison argument with the left-hand side of the comparison
|
|
2555
2700
|
fixer.replaceText(comparison, context.sourceCode.getText(comparison.left)),
|
|
@@ -2588,9 +2733,13 @@ var prefer_equality_matcher_default = createRule({
|
|
|
2588
2733
|
return {
|
|
2589
2734
|
CallExpression(node) {
|
|
2590
2735
|
const call = parseFnCall(context, node);
|
|
2591
|
-
if (call?.type !== "expect" || call.matcherArgs.length === 0)
|
|
2736
|
+
if (call?.type !== "expect" || call.matcherArgs.length === 0) {
|
|
2737
|
+
return;
|
|
2738
|
+
}
|
|
2592
2739
|
const expect = call.head.node.parent;
|
|
2593
|
-
if (expect?.type !== "CallExpression")
|
|
2740
|
+
if (expect?.type !== "CallExpression") {
|
|
2741
|
+
return;
|
|
2742
|
+
}
|
|
2594
2743
|
const [comparison] = expect.arguments;
|
|
2595
2744
|
const expectCallEnd = expect.range[1];
|
|
2596
2745
|
const [matcherArg] = call.matcherArgs;
|
|
@@ -2652,7 +2801,9 @@ var prefer_hooks_in_order_default = createRule({
|
|
|
2652
2801
|
let inHook = false;
|
|
2653
2802
|
return {
|
|
2654
2803
|
"CallExpression"(node) {
|
|
2655
|
-
if (inHook)
|
|
2804
|
+
if (inHook) {
|
|
2805
|
+
return;
|
|
2806
|
+
}
|
|
2656
2807
|
const call = parseFnCall(context, node);
|
|
2657
2808
|
if (call?.type !== "hook") {
|
|
2658
2809
|
previousHookIndex = -1;
|
|
@@ -2679,7 +2830,9 @@ var prefer_hooks_in_order_default = createRule({
|
|
|
2679
2830
|
inHook = false;
|
|
2680
2831
|
return;
|
|
2681
2832
|
}
|
|
2682
|
-
if (inHook)
|
|
2833
|
+
if (inHook) {
|
|
2834
|
+
return;
|
|
2835
|
+
}
|
|
2683
2836
|
previousHookIndex = -1;
|
|
2684
2837
|
}
|
|
2685
2838
|
};
|
|
@@ -2756,7 +2909,9 @@ var pageMethods2 = /* @__PURE__ */ new Set([
|
|
|
2756
2909
|
"uncheck"
|
|
2757
2910
|
]);
|
|
2758
2911
|
function isSupportedMethod2(node) {
|
|
2759
|
-
if (node.callee.type !== "MemberExpression")
|
|
2912
|
+
if (node.callee.type !== "MemberExpression") {
|
|
2913
|
+
return false;
|
|
2914
|
+
}
|
|
2760
2915
|
const name = getStringValue(node.callee.property);
|
|
2761
2916
|
return pageMethods2.has(name) && isPageMethod(node, name);
|
|
2762
2917
|
}
|
|
@@ -2764,7 +2919,9 @@ var prefer_locator_default = createRule({
|
|
|
2764
2919
|
create(context) {
|
|
2765
2920
|
return {
|
|
2766
2921
|
CallExpression(node) {
|
|
2767
|
-
if (!isSupportedMethod2(node))
|
|
2922
|
+
if (!isSupportedMethod2(node)) {
|
|
2923
|
+
return;
|
|
2924
|
+
}
|
|
2768
2925
|
context.report({
|
|
2769
2926
|
messageId: "preferLocator",
|
|
2770
2927
|
node
|
|
@@ -2925,9 +3082,13 @@ var prefer_native_locators_default = createRule({
|
|
|
2925
3082
|
const patterns = compilePatterns({ testIdAttribute });
|
|
2926
3083
|
return {
|
|
2927
3084
|
CallExpression(node) {
|
|
2928
|
-
if (node.callee.type !== "MemberExpression")
|
|
3085
|
+
if (node.callee.type !== "MemberExpression") {
|
|
3086
|
+
return;
|
|
3087
|
+
}
|
|
2929
3088
|
const query = getStringValue(node.arguments[0]);
|
|
2930
|
-
if (!isPageMethod(node, "locator"))
|
|
3089
|
+
if (!isPageMethod(node, "locator")) {
|
|
3090
|
+
return;
|
|
3091
|
+
}
|
|
2931
3092
|
for (const pattern of patterns) {
|
|
2932
3093
|
const match = query.match(pattern.pattern);
|
|
2933
3094
|
if (match) {
|
|
@@ -2985,7 +3146,9 @@ var prefer_strict_equal_default = createRule({
|
|
|
2985
3146
|
return {
|
|
2986
3147
|
CallExpression(node) {
|
|
2987
3148
|
const call = parseFnCall(context, node);
|
|
2988
|
-
if (call?.type !== "expect")
|
|
3149
|
+
if (call?.type !== "expect") {
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
2989
3152
|
if (call.matcherName === "toEqual") {
|
|
2990
3153
|
context.report({
|
|
2991
3154
|
messageId: "useToStrictEqual",
|
|
@@ -3053,7 +3216,9 @@ var prefer_to_be_default = createRule({
|
|
|
3053
3216
|
return {
|
|
3054
3217
|
CallExpression(node) {
|
|
3055
3218
|
const call = parseFnCall(context, node);
|
|
3056
|
-
if (call?.type !== "expect")
|
|
3219
|
+
if (call?.type !== "expect") {
|
|
3220
|
+
return;
|
|
3221
|
+
}
|
|
3057
3222
|
const notMatchers = ["toBeUndefined", "toBeDefined"];
|
|
3058
3223
|
const notModifier = call.modifiers.find((node2) => getStringValue(node2) === "not");
|
|
3059
3224
|
if (notModifier && notMatchers.includes(call.matcherName)) {
|
|
@@ -3110,9 +3275,13 @@ var prefer_to_contain_default = createRule({
|
|
|
3110
3275
|
return {
|
|
3111
3276
|
CallExpression(node) {
|
|
3112
3277
|
const call = parseFnCall(context, node);
|
|
3113
|
-
if (call?.type !== "expect" || call.matcherArgs.length === 0)
|
|
3278
|
+
if (call?.type !== "expect" || call.matcherArgs.length === 0) {
|
|
3279
|
+
return;
|
|
3280
|
+
}
|
|
3114
3281
|
const expect = call.head.node.parent;
|
|
3115
|
-
if (expect?.type !== "CallExpression")
|
|
3282
|
+
if (expect?.type !== "CallExpression") {
|
|
3283
|
+
return;
|
|
3284
|
+
}
|
|
3116
3285
|
const [includesCall] = expect.arguments;
|
|
3117
3286
|
const { matcher } = call;
|
|
3118
3287
|
const [matcherArg] = call.matcherArgs;
|
|
@@ -3307,19 +3476,29 @@ var prefer_web_first_assertions_default = createRule({
|
|
|
3307
3476
|
return {
|
|
3308
3477
|
CallExpression(node) {
|
|
3309
3478
|
const fnCall = parseFnCall(context, node);
|
|
3310
|
-
if (fnCall?.type !== "expect")
|
|
3479
|
+
if (fnCall?.type !== "expect") {
|
|
3480
|
+
return;
|
|
3481
|
+
}
|
|
3311
3482
|
const expect = findParent(fnCall.head.node, "CallExpression");
|
|
3312
|
-
if (!expect)
|
|
3483
|
+
if (!expect) {
|
|
3484
|
+
return;
|
|
3485
|
+
}
|
|
3313
3486
|
const arg = dereference(context, fnCall.args[0]);
|
|
3314
|
-
if (!arg)
|
|
3487
|
+
if (!arg) {
|
|
3488
|
+
return;
|
|
3489
|
+
}
|
|
3315
3490
|
const call = arg.type === "AwaitExpression" ? arg.argument : arg;
|
|
3316
3491
|
if (call.type !== "CallExpression" || call.callee.type !== "MemberExpression") {
|
|
3317
3492
|
return;
|
|
3318
3493
|
}
|
|
3319
|
-
if (!supportedMatchers.has(fnCall.matcherName))
|
|
3494
|
+
if (!supportedMatchers.has(fnCall.matcherName)) {
|
|
3495
|
+
return;
|
|
3496
|
+
}
|
|
3320
3497
|
const method = getStringValue(call.callee.property);
|
|
3321
3498
|
const methodConfig = methods3[method];
|
|
3322
|
-
if (!Object.hasOwn(methods3, method))
|
|
3499
|
+
if (!Object.hasOwn(methods3, method)) {
|
|
3500
|
+
return;
|
|
3501
|
+
}
|
|
3323
3502
|
const notModifier = fnCall.modifiers.find((mod) => getStringValue(mod) === "not");
|
|
3324
3503
|
const isFalsy = methodConfig.type === "boolean" && (!!fnCall.matcherArgs.length && isBooleanLiteral(fnCall.matcherArgs[0], false) || fnCall.matcherName === "toBeFalsy");
|
|
3325
3504
|
const isInverse = methodConfig.inverse ? notModifier || isFalsy : notModifier && isFalsy;
|
|
@@ -3519,10 +3698,11 @@ function hasTagInOptions(node) {
|
|
|
3519
3698
|
}
|
|
3520
3699
|
function hasTagInTitle(node) {
|
|
3521
3700
|
const title = node.arguments[0];
|
|
3522
|
-
if (!title
|
|
3701
|
+
if (!title) {
|
|
3523
3702
|
return false;
|
|
3524
3703
|
}
|
|
3525
|
-
|
|
3704
|
+
const value = getStringValue(title);
|
|
3705
|
+
return !!value && tagRegex.test(value);
|
|
3526
3706
|
}
|
|
3527
3707
|
function hasTags(node) {
|
|
3528
3708
|
return hasTagInTitle(node) || hasTagInOptions(node);
|
|
@@ -3617,7 +3797,9 @@ var require_to_throw_message_default = createRule({
|
|
|
3617
3797
|
return {
|
|
3618
3798
|
CallExpression(node) {
|
|
3619
3799
|
const call = parseFnCall(context, node);
|
|
3620
|
-
if (call?.type !== "expect")
|
|
3800
|
+
if (call?.type !== "expect") {
|
|
3801
|
+
return;
|
|
3802
|
+
}
|
|
3621
3803
|
if (call.matcherArgs.length === 0 && ["toThrow", "toThrowError"].includes(call.matcherName) && !call.modifiers.some((nod) => getStringValue(nod) === "not")) {
|
|
3622
3804
|
context.report({
|
|
3623
3805
|
data: { matcherName: call.matcherName },
|
|
@@ -3654,7 +3836,9 @@ var require_top_level_describe_default = createRule({
|
|
|
3654
3836
|
return {
|
|
3655
3837
|
"CallExpression"(node) {
|
|
3656
3838
|
const call = parseFnCall(context, node);
|
|
3657
|
-
if (!call)
|
|
3839
|
+
if (!call) {
|
|
3840
|
+
return;
|
|
3841
|
+
}
|
|
3658
3842
|
if (call.type === "describe") {
|
|
3659
3843
|
describeCount++;
|
|
3660
3844
|
if (describeCount === 1) {
|
|
@@ -3723,7 +3907,9 @@ var valid_describe_callback_default = createRule({
|
|
|
3723
3907
|
return {
|
|
3724
3908
|
CallExpression(node) {
|
|
3725
3909
|
const call = parseFnCall(context, node);
|
|
3726
|
-
if (call?.group !== "describe")
|
|
3910
|
+
if (call?.group !== "describe") {
|
|
3911
|
+
return;
|
|
3912
|
+
}
|
|
3727
3913
|
if (call.members.some((s) => getStringValue(s) === "configure")) {
|
|
3728
3914
|
return;
|
|
3729
3915
|
}
|
|
@@ -3826,7 +4012,9 @@ var isTestCaseCallWithCallbackArg = (context, node) => {
|
|
|
3826
4012
|
};
|
|
3827
4013
|
var isPromiseMethodThatUsesValue = (node, identifier) => {
|
|
3828
4014
|
const name = getStringValue(identifier);
|
|
3829
|
-
if (node.argument == null)
|
|
4015
|
+
if (node.argument == null) {
|
|
4016
|
+
return false;
|
|
4017
|
+
}
|
|
3830
4018
|
if (node.argument.type === "CallExpression" && node.argument.arguments.length > 0) {
|
|
3831
4019
|
const nodeName = getNodeName(node.argument);
|
|
3832
4020
|
if (["Promise.all", "Promise.allSettled"].includes(nodeName)) {
|
|
@@ -4083,7 +4271,9 @@ var valid_expect_default = createRule({
|
|
|
4083
4271
|
return;
|
|
4084
4272
|
}
|
|
4085
4273
|
const { parent: expect } = call.head.node;
|
|
4086
|
-
if (expect?.type !== "CallExpression")
|
|
4274
|
+
if (expect?.type !== "CallExpression") {
|
|
4275
|
+
return;
|
|
4276
|
+
}
|
|
4087
4277
|
if (expect.arguments.length < minArgs) {
|
|
4088
4278
|
const expectLength = getStringValue(call.head.node).length;
|
|
4089
4279
|
const loc = {
|
|
@@ -4132,6 +4322,7 @@ var valid_expect_default = createRule({
|
|
|
4132
4322
|
messages: {
|
|
4133
4323
|
matcherNotCalled: "Matchers must be called to assert.",
|
|
4134
4324
|
matcherNotFound: "Expect must have a corresponding matcher call.",
|
|
4325
|
+
modifierUnknown: "Expect has an unknown modifier.",
|
|
4135
4326
|
notEnoughArgs: "Expect requires at least {{amount}} argument{{s}}.",
|
|
4136
4327
|
tooManyArgs: "Expect takes at most {{amount}} argument{{s}}."
|
|
4137
4328
|
},
|
|
@@ -4209,9 +4400,13 @@ var valid_test_tags_default = createRule({
|
|
|
4209
4400
|
return {
|
|
4210
4401
|
CallExpression(node) {
|
|
4211
4402
|
const call = parseFnCall(context, node);
|
|
4212
|
-
if (!call)
|
|
4403
|
+
if (!call) {
|
|
4404
|
+
return;
|
|
4405
|
+
}
|
|
4213
4406
|
const { type } = call;
|
|
4214
|
-
if (type !== "test" && type !== "describe" && type !== "step")
|
|
4407
|
+
if (type !== "test" && type !== "describe" && type !== "step") {
|
|
4408
|
+
return;
|
|
4409
|
+
}
|
|
4215
4410
|
if (node.arguments.length > 0) {
|
|
4216
4411
|
const titleArg = node.arguments[0];
|
|
4217
4412
|
if (titleArg && titleArg.type === "Literal" && typeof titleArg.value === "string") {
|
|
@@ -4221,14 +4416,20 @@ var valid_test_tags_default = createRule({
|
|
|
4221
4416
|
}
|
|
4222
4417
|
}
|
|
4223
4418
|
}
|
|
4224
|
-
if (node.arguments.length < 2)
|
|
4419
|
+
if (node.arguments.length < 2) {
|
|
4420
|
+
return;
|
|
4421
|
+
}
|
|
4225
4422
|
const optionsArg = node.arguments[1];
|
|
4226
|
-
if (!optionsArg || optionsArg.type !== "ObjectExpression")
|
|
4423
|
+
if (!optionsArg || optionsArg.type !== "ObjectExpression") {
|
|
4424
|
+
return;
|
|
4425
|
+
}
|
|
4227
4426
|
const tagProperty = optionsArg.properties.find(
|
|
4228
4427
|
(prop) => prop.type === "Property" && !("argument" in prop) && // Ensure it's not a spread element
|
|
4229
4428
|
prop.key.type === "Identifier" && prop.key.name === "tag"
|
|
4230
4429
|
);
|
|
4231
|
-
if (!tagProperty)
|
|
4430
|
+
if (!tagProperty) {
|
|
4431
|
+
return;
|
|
4432
|
+
}
|
|
4232
4433
|
const tagValue = tagProperty.value;
|
|
4233
4434
|
if (tagValue.type === "Literal") {
|
|
4234
4435
|
if (typeof tagValue.value !== "string") {
|
|
@@ -4365,7 +4566,9 @@ var valid_title_default = createRule({
|
|
|
4365
4566
|
}
|
|
4366
4567
|
const [argument] = node.arguments;
|
|
4367
4568
|
const title = dereference(context, argument) ?? argument;
|
|
4368
|
-
if (!title)
|
|
4569
|
+
if (!title) {
|
|
4570
|
+
return;
|
|
4571
|
+
}
|
|
4369
4572
|
if (!isStringNode(title)) {
|
|
4370
4573
|
if (title.type === "BinaryExpression" && doesBinaryExpressionContainStringNode(title)) {
|
|
4371
4574
|
return;
|
|
@@ -4414,15 +4617,15 @@ var valid_title_default = createRule({
|
|
|
4414
4617
|
node: title
|
|
4415
4618
|
});
|
|
4416
4619
|
}
|
|
4417
|
-
const [firstWord] = titleString.split(" ");
|
|
4620
|
+
const [firstWord, ...rest] = titleString.split(" ");
|
|
4418
4621
|
if (firstWord.toLowerCase() === functionName) {
|
|
4419
4622
|
context.report({
|
|
4420
|
-
fix: (fixer) => [
|
|
4623
|
+
fix: rest.length > 0 ? (fixer) => [
|
|
4421
4624
|
fixer.replaceTextRange(
|
|
4422
4625
|
title.range,
|
|
4423
4626
|
quoteStringValue(title).replace(/^([`'"]).+? /u, "$1")
|
|
4424
4627
|
)
|
|
4425
|
-
],
|
|
4628
|
+
] : void 0,
|
|
4426
4629
|
messageId: "duplicatePrefix",
|
|
4427
4630
|
node: title
|
|
4428
4631
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-playwright",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "ESLint plugin for Playwright testing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Mark Skelton <mark@mskelton.dev>",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"lint": "eslint .",
|
|
32
32
|
"format": "oxfmt .",
|
|
33
33
|
"format:check": "oxfmt --check .",
|
|
34
|
-
"test": "vitest --
|
|
34
|
+
"test": "vitest --hideSkippedTests",
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
|
36
36
|
"ci": "yarn format:check && yarn lint && yarn typecheck"
|
|
37
37
|
},
|