eslint-plugin-playwright 2.9.0 → 2.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +175 -21
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -392,8 +392,21 @@ function findParent(node, type) {
|
|
|
392
392
|
function dig(node, identifier) {
|
|
393
393
|
return node.type === "MemberExpression" ? dig(node.property, identifier) : node.type === "CallExpression" ? dig(node.callee, identifier) : node.type === "Identifier" ? isIdentifier(node, identifier) : false;
|
|
394
394
|
}
|
|
395
|
+
var pageFrameFullPattern = /(^(page|frame)|(Page|Frame)$)/;
|
|
396
|
+
var pageFramePrefixPattern = /^(page|frame)/;
|
|
395
397
|
function isPageMethod(node, name) {
|
|
396
|
-
|
|
398
|
+
if (node.callee.type !== "MemberExpression") {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
if (!isPropertyAccessor(node.callee, name)) {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
const obj = node.callee.object;
|
|
405
|
+
if (obj.type === "MemberExpression") {
|
|
406
|
+
const pattern = obj.object.type === "ThisExpression" ? pageFrameFullPattern : pageFramePrefixPattern;
|
|
407
|
+
return isIdentifier(obj.property, pattern);
|
|
408
|
+
}
|
|
409
|
+
return dig(obj, pageFrameFullPattern);
|
|
397
410
|
}
|
|
398
411
|
function isFunction(node) {
|
|
399
412
|
return node?.type === "ArrowFunctionExpression" || node?.type === "FunctionExpression";
|
|
@@ -849,6 +862,9 @@ var max_nested_describe_default = createRule({
|
|
|
849
862
|
|
|
850
863
|
// src/rules/missing-playwright-await.ts
|
|
851
864
|
var validTypes = /* @__PURE__ */ new Set(["AwaitExpression", "ReturnStatement", "ArrowFunctionExpression"]);
|
|
865
|
+
function isArrayLike(node) {
|
|
866
|
+
return node.type === "ArrayExpression" || node.type === "NewExpression" && isIdentifier(node.callee, "Array") || node.type === "CallExpression" && node.callee.type === "MemberExpression" && isIdentifier(node.callee.object, "Array");
|
|
867
|
+
}
|
|
852
868
|
var waitForMethods = [
|
|
853
869
|
"waitForConsoleMessage",
|
|
854
870
|
"waitForDownload",
|
|
@@ -861,6 +877,105 @@ var waitForMethods = [
|
|
|
861
877
|
"waitForWebSocket"
|
|
862
878
|
];
|
|
863
879
|
var waitForMethodsRegex = new RegExp(`^(${waitForMethods.join("|")})$`);
|
|
880
|
+
var pageMethods = /* @__PURE__ */ new Set([
|
|
881
|
+
"addInitScript",
|
|
882
|
+
"addScriptTag",
|
|
883
|
+
"addStyleTag",
|
|
884
|
+
"bringToFront",
|
|
885
|
+
"check",
|
|
886
|
+
"click",
|
|
887
|
+
"close",
|
|
888
|
+
"dblclick",
|
|
889
|
+
"dispatchEvent",
|
|
890
|
+
"dragAndDrop",
|
|
891
|
+
"emulateMedia",
|
|
892
|
+
"evaluate",
|
|
893
|
+
"evaluateHandle",
|
|
894
|
+
"exposeBinding",
|
|
895
|
+
"exposeFunction",
|
|
896
|
+
"fill",
|
|
897
|
+
"focus",
|
|
898
|
+
"getAttribute",
|
|
899
|
+
"goBack",
|
|
900
|
+
"goForward",
|
|
901
|
+
"goto",
|
|
902
|
+
"hover",
|
|
903
|
+
"innerHTML",
|
|
904
|
+
"innerText",
|
|
905
|
+
"inputValue",
|
|
906
|
+
"isChecked",
|
|
907
|
+
"isDisabled",
|
|
908
|
+
"isEditable",
|
|
909
|
+
"isEnabled",
|
|
910
|
+
"isHidden",
|
|
911
|
+
"isVisible",
|
|
912
|
+
"pdf",
|
|
913
|
+
"press",
|
|
914
|
+
"reload",
|
|
915
|
+
"route",
|
|
916
|
+
"routeFromHAR",
|
|
917
|
+
"screenshot",
|
|
918
|
+
"selectOption",
|
|
919
|
+
"setBypassCSP",
|
|
920
|
+
"setContent",
|
|
921
|
+
"setChecked",
|
|
922
|
+
"setExtraHTTPHeaders",
|
|
923
|
+
"setInputFiles",
|
|
924
|
+
"setViewportSize",
|
|
925
|
+
"tap",
|
|
926
|
+
"textContent",
|
|
927
|
+
"title",
|
|
928
|
+
"type",
|
|
929
|
+
"uncheck",
|
|
930
|
+
"unroute",
|
|
931
|
+
"unrouteAll",
|
|
932
|
+
"waitForLoadState",
|
|
933
|
+
"waitForTimeout",
|
|
934
|
+
"waitForURL"
|
|
935
|
+
]);
|
|
936
|
+
var locatorMethods = /* @__PURE__ */ new Set([
|
|
937
|
+
"all",
|
|
938
|
+
"allInnerTexts",
|
|
939
|
+
"allTextContents",
|
|
940
|
+
"blur",
|
|
941
|
+
"boundingBox",
|
|
942
|
+
"check",
|
|
943
|
+
"clear",
|
|
944
|
+
"click",
|
|
945
|
+
"count",
|
|
946
|
+
"dblclick",
|
|
947
|
+
"dispatchEvent",
|
|
948
|
+
"dragTo",
|
|
949
|
+
"evaluate",
|
|
950
|
+
"evaluateAll",
|
|
951
|
+
"evaluateHandle",
|
|
952
|
+
"fill",
|
|
953
|
+
"focus",
|
|
954
|
+
"getAttribute",
|
|
955
|
+
"hover",
|
|
956
|
+
"innerHTML",
|
|
957
|
+
"innerText",
|
|
958
|
+
"inputValue",
|
|
959
|
+
"isChecked",
|
|
960
|
+
"isDisabled",
|
|
961
|
+
"isEditable",
|
|
962
|
+
"isEnabled",
|
|
963
|
+
"isHidden",
|
|
964
|
+
"isVisible",
|
|
965
|
+
"press",
|
|
966
|
+
"pressSequentially",
|
|
967
|
+
"screenshot",
|
|
968
|
+
"scrollIntoViewIfNeeded",
|
|
969
|
+
"selectOption",
|
|
970
|
+
"selectText",
|
|
971
|
+
"setChecked",
|
|
972
|
+
"setInputFiles",
|
|
973
|
+
"tap",
|
|
974
|
+
"textContent",
|
|
975
|
+
"type",
|
|
976
|
+
"uncheck",
|
|
977
|
+
"waitFor"
|
|
978
|
+
]);
|
|
864
979
|
var expectPlaywrightMatchers = [
|
|
865
980
|
"toBeChecked",
|
|
866
981
|
"toBeDisabled",
|
|
@@ -916,14 +1031,18 @@ function getReportNode(node) {
|
|
|
916
1031
|
}
|
|
917
1032
|
function getCallType(call, awaitableMatchers) {
|
|
918
1033
|
if (call.type === "step") {
|
|
919
|
-
return {
|
|
1034
|
+
return {
|
|
1035
|
+
data: { name: "test.step" },
|
|
1036
|
+
messageId: "missingAwait",
|
|
1037
|
+
node: call.head.node
|
|
1038
|
+
};
|
|
920
1039
|
}
|
|
921
1040
|
if (call.type === "expect") {
|
|
922
1041
|
const isPoll = call.modifiers.some((m) => getStringValue(m) === "poll");
|
|
923
1042
|
if (isPoll || awaitableMatchers.has(call.matcherName)) {
|
|
924
1043
|
return {
|
|
925
|
-
data: {
|
|
926
|
-
messageId:
|
|
1044
|
+
data: { name: isPoll ? "expect.poll" : call.matcherName },
|
|
1045
|
+
messageId: "missingAwait",
|
|
927
1046
|
node: call.head.node
|
|
928
1047
|
};
|
|
929
1048
|
}
|
|
@@ -932,6 +1051,7 @@ function getCallType(call, awaitableMatchers) {
|
|
|
932
1051
|
var missing_playwright_await_default = createRule({
|
|
933
1052
|
create(context) {
|
|
934
1053
|
const options = context.options[0] || {};
|
|
1054
|
+
const includePageLocatorMethods = !!options.includePageLocatorMethods;
|
|
935
1055
|
const awaitableMatchers = /* @__PURE__ */ new Set([
|
|
936
1056
|
...expectPlaywrightMatchers,
|
|
937
1057
|
...playwrightTestMatchers,
|
|
@@ -992,9 +1112,18 @@ var missing_playwright_await_default = createRule({
|
|
|
992
1112
|
if (parent.type === "SpreadElement") {
|
|
993
1113
|
return checkValidity(parent, visited);
|
|
994
1114
|
}
|
|
995
|
-
if (parent.type === "CallExpression" && parent.callee.type === "MemberExpression" && isIdentifier(parent.callee.object, "Promise") && isIdentifier(parent.callee.property,
|
|
1115
|
+
if (parent.type === "CallExpression" && parent.callee.type === "MemberExpression" && isIdentifier(parent.callee.object, "Promise") && isIdentifier(parent.callee.property, /^(all|allSettled|race|any)$/)) {
|
|
996
1116
|
return true;
|
|
997
1117
|
}
|
|
1118
|
+
if (parent.type === "MemberExpression" && parent.object === node && getStringValue(parent.property) === "resolves" && node.type === "CallExpression" && isIdentifier(node.callee, "expect")) {
|
|
1119
|
+
return checkValidity(parent, visited);
|
|
1120
|
+
}
|
|
1121
|
+
if (parent.type === "MemberExpression" && parent.object === node) {
|
|
1122
|
+
return checkValidity(parent, visited);
|
|
1123
|
+
}
|
|
1124
|
+
if (parent.type === "CallExpression" && parent.callee === node) {
|
|
1125
|
+
return checkValidity(parent, visited);
|
|
1126
|
+
}
|
|
998
1127
|
if (parent.type === "VariableDeclarator") {
|
|
999
1128
|
return isVariableConsumed(parent, checkValidity, validTypes, visited);
|
|
1000
1129
|
}
|
|
@@ -1006,13 +1135,27 @@ var missing_playwright_await_default = createRule({
|
|
|
1006
1135
|
if (!checkValidity(node, /* @__PURE__ */ new Set())) {
|
|
1007
1136
|
const methodName = getStringValue(node.callee.property);
|
|
1008
1137
|
context.report({
|
|
1009
|
-
data: { methodName },
|
|
1010
|
-
messageId: "
|
|
1138
|
+
data: { name: methodName },
|
|
1139
|
+
messageId: "missingAwait",
|
|
1011
1140
|
node
|
|
1012
1141
|
});
|
|
1013
1142
|
}
|
|
1014
1143
|
return;
|
|
1015
1144
|
}
|
|
1145
|
+
if (includePageLocatorMethods && node.callee.type === "MemberExpression") {
|
|
1146
|
+
const methodName = getStringValue(node.callee.property);
|
|
1147
|
+
const isPlaywrightMethod = !isArrayLike(node.callee.object) && (locatorMethods.has(methodName) || pageMethods.has(methodName) && isPageMethod(node, methodName));
|
|
1148
|
+
if (isPlaywrightMethod) {
|
|
1149
|
+
if (!checkValidity(node, /* @__PURE__ */ new Set())) {
|
|
1150
|
+
context.report({
|
|
1151
|
+
data: { name: methodName },
|
|
1152
|
+
messageId: "missingAwait",
|
|
1153
|
+
node
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1016
1159
|
const call = parseFnCall(context, node);
|
|
1017
1160
|
if (call?.type !== "step" && call?.type !== "expect") {
|
|
1018
1161
|
return;
|
|
@@ -1038,10 +1181,7 @@ var missing_playwright_await_default = createRule({
|
|
|
1038
1181
|
},
|
|
1039
1182
|
fixable: "code",
|
|
1040
1183
|
messages: {
|
|
1041
|
-
|
|
1042
|
-
expectPoll: "'expect.poll' matchers must be awaited or returned.",
|
|
1043
|
-
testStep: "'test.step' must be awaited or returned.",
|
|
1044
|
-
waitFor: "'{{methodName}}' must be awaited or returned."
|
|
1184
|
+
missingAwait: "'{{name}}' must be awaited or returned."
|
|
1045
1185
|
},
|
|
1046
1186
|
schema: [
|
|
1047
1187
|
{
|
|
@@ -1050,6 +1190,9 @@ var missing_playwright_await_default = createRule({
|
|
|
1050
1190
|
customMatchers: {
|
|
1051
1191
|
items: { type: "string" },
|
|
1052
1192
|
type: "array"
|
|
1193
|
+
},
|
|
1194
|
+
includePageLocatorMethods: {
|
|
1195
|
+
type: "boolean"
|
|
1053
1196
|
}
|
|
1054
1197
|
},
|
|
1055
1198
|
type: "object"
|
|
@@ -1962,11 +2105,15 @@ var no_skipped_test_default = createRule({
|
|
|
1962
2105
|
CallExpression(node) {
|
|
1963
2106
|
const options = context.options[0] || {};
|
|
1964
2107
|
const allowConditional = !!options.allowConditional;
|
|
2108
|
+
const disallowFixme = !!options.disallowFixme;
|
|
1965
2109
|
const call = parseFnCall(context, node);
|
|
1966
2110
|
if (call?.group !== "test" && call?.group !== "describe" && call?.group !== "step") {
|
|
1967
2111
|
return;
|
|
1968
2112
|
}
|
|
1969
|
-
const skipNode = call.members.find((
|
|
2113
|
+
const skipNode = call.members.find((member) => {
|
|
2114
|
+
const value = getStringValue(member);
|
|
2115
|
+
return value === "skip" || disallowFixme && value === "fixme";
|
|
2116
|
+
});
|
|
1970
2117
|
if (!skipNode) {
|
|
1971
2118
|
return;
|
|
1972
2119
|
}
|
|
@@ -1974,18 +2121,21 @@ var no_skipped_test_default = createRule({
|
|
|
1974
2121
|
if (isStandalone && allowConditional && (node.arguments.length !== 0 || findParent(node, "BlockStatement")?.parent?.type === "IfStatement" || findParent(node, "SwitchCase") !== void 0)) {
|
|
1975
2122
|
return;
|
|
1976
2123
|
}
|
|
2124
|
+
const annotation = getStringValue(skipNode);
|
|
1977
2125
|
context.report({
|
|
2126
|
+
data: { annotation },
|
|
1978
2127
|
messageId: "noSkippedTest",
|
|
1979
2128
|
node: isStandalone ? node : skipNode,
|
|
1980
2129
|
suggest: [
|
|
1981
2130
|
{
|
|
2131
|
+
data: { annotation: getStringValue(skipNode) },
|
|
1982
2132
|
fix: (fixer) => {
|
|
1983
2133
|
return isStandalone ? fixer.remove(node.parent) : fixer.removeRange([
|
|
1984
2134
|
skipNode.range[0] - 1,
|
|
1985
2135
|
skipNode.range[1] + Number(skipNode.type !== "Identifier")
|
|
1986
2136
|
]);
|
|
1987
2137
|
},
|
|
1988
|
-
messageId: "
|
|
2138
|
+
messageId: "removeAnnotation"
|
|
1989
2139
|
}
|
|
1990
2140
|
]
|
|
1991
2141
|
});
|
|
@@ -2000,8 +2150,8 @@ var no_skipped_test_default = createRule({
|
|
|
2000
2150
|
},
|
|
2001
2151
|
hasSuggestions: true,
|
|
2002
2152
|
messages: {
|
|
2003
|
-
noSkippedTest: "Unexpected use of the `.
|
|
2004
|
-
|
|
2153
|
+
noSkippedTest: "Unexpected use of the `.{{annotation}}()` annotation.",
|
|
2154
|
+
removeAnnotation: "Remove the `.{{annotation}}()` annotation."
|
|
2005
2155
|
},
|
|
2006
2156
|
schema: [
|
|
2007
2157
|
{
|
|
@@ -2010,6 +2160,10 @@ var no_skipped_test_default = createRule({
|
|
|
2010
2160
|
allowConditional: {
|
|
2011
2161
|
default: false,
|
|
2012
2162
|
type: "boolean"
|
|
2163
|
+
},
|
|
2164
|
+
disallowFixme: {
|
|
2165
|
+
default: false,
|
|
2166
|
+
type: "boolean"
|
|
2013
2167
|
}
|
|
2014
2168
|
},
|
|
2015
2169
|
type: "object"
|
|
@@ -2306,7 +2460,7 @@ var no_unused_locators_default = createRule({
|
|
|
2306
2460
|
});
|
|
2307
2461
|
|
|
2308
2462
|
// src/rules/no-useless-await.ts
|
|
2309
|
-
var
|
|
2463
|
+
var locatorMethods2 = /* @__PURE__ */ new Set([
|
|
2310
2464
|
"and",
|
|
2311
2465
|
"first",
|
|
2312
2466
|
"getByAltText",
|
|
@@ -2321,7 +2475,7 @@ var locatorMethods = /* @__PURE__ */ new Set([
|
|
|
2321
2475
|
"nth",
|
|
2322
2476
|
"or"
|
|
2323
2477
|
]);
|
|
2324
|
-
var
|
|
2478
|
+
var pageMethods2 = /* @__PURE__ */ new Set([
|
|
2325
2479
|
"childFrames",
|
|
2326
2480
|
"frame",
|
|
2327
2481
|
"frameLocator",
|
|
@@ -2370,7 +2524,7 @@ function isSupportedMethod(node) {
|
|
|
2370
2524
|
return false;
|
|
2371
2525
|
}
|
|
2372
2526
|
const name = getStringValue(node.callee.property);
|
|
2373
|
-
return
|
|
2527
|
+
return locatorMethods2.has(name) || pageMethods2.has(name) && isPageMethod(node, name);
|
|
2374
2528
|
}
|
|
2375
2529
|
var no_useless_await_default = createRule({
|
|
2376
2530
|
create(context) {
|
|
@@ -2881,7 +3035,7 @@ var prefer_hooks_on_top_default = createRule({
|
|
|
2881
3035
|
});
|
|
2882
3036
|
|
|
2883
3037
|
// src/rules/prefer-locator.ts
|
|
2884
|
-
var
|
|
3038
|
+
var pageMethods3 = /* @__PURE__ */ new Set([
|
|
2885
3039
|
"click",
|
|
2886
3040
|
"dblclick",
|
|
2887
3041
|
"dispatchEvent",
|
|
@@ -2911,7 +3065,7 @@ function isSupportedMethod2(node) {
|
|
|
2911
3065
|
return false;
|
|
2912
3066
|
}
|
|
2913
3067
|
const name = getStringValue(node.callee.property);
|
|
2914
|
-
return
|
|
3068
|
+
return pageMethods3.has(name) && isPageMethod(node, name);
|
|
2915
3069
|
}
|
|
2916
3070
|
var prefer_locator_default = createRule({
|
|
2917
3071
|
create(context) {
|
|
@@ -4015,7 +4169,7 @@ var isPromiseMethodThatUsesValue = (node, identifier) => {
|
|
|
4015
4169
|
}
|
|
4016
4170
|
if (node.argument.type === "CallExpression" && node.argument.arguments.length > 0) {
|
|
4017
4171
|
const nodeName = getNodeName(node.argument);
|
|
4018
|
-
if (
|
|
4172
|
+
if (/^Promise\.(all|allSettled|race|any)$/.test(nodeName)) {
|
|
4019
4173
|
const [firstArg] = node.argument.arguments;
|
|
4020
4174
|
if (firstArg.type === "ArrayExpression" && firstArg.elements.some((nod) => nod && isIdentifier(nod, name))) {
|
|
4021
4175
|
return true;
|