eslint-plugin-playwright 2.9.0 → 2.10.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.
Files changed (2) hide show
  1. package/dist/index.cjs +158 -20
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -861,6 +861,105 @@ var waitForMethods = [
861
861
  "waitForWebSocket"
862
862
  ];
863
863
  var waitForMethodsRegex = new RegExp(`^(${waitForMethods.join("|")})$`);
864
+ var pageMethods = /* @__PURE__ */ new Set([
865
+ "addInitScript",
866
+ "addScriptTag",
867
+ "addStyleTag",
868
+ "bringToFront",
869
+ "check",
870
+ "click",
871
+ "close",
872
+ "dblclick",
873
+ "dispatchEvent",
874
+ "dragAndDrop",
875
+ "emulateMedia",
876
+ "evaluate",
877
+ "evaluateHandle",
878
+ "exposeBinding",
879
+ "exposeFunction",
880
+ "fill",
881
+ "focus",
882
+ "getAttribute",
883
+ "goBack",
884
+ "goForward",
885
+ "goto",
886
+ "hover",
887
+ "innerHTML",
888
+ "innerText",
889
+ "inputValue",
890
+ "isChecked",
891
+ "isDisabled",
892
+ "isEditable",
893
+ "isEnabled",
894
+ "isHidden",
895
+ "isVisible",
896
+ "pdf",
897
+ "press",
898
+ "reload",
899
+ "route",
900
+ "routeFromHAR",
901
+ "screenshot",
902
+ "selectOption",
903
+ "setBypassCSP",
904
+ "setContent",
905
+ "setChecked",
906
+ "setExtraHTTPHeaders",
907
+ "setInputFiles",
908
+ "setViewportSize",
909
+ "tap",
910
+ "textContent",
911
+ "title",
912
+ "type",
913
+ "uncheck",
914
+ "unroute",
915
+ "unrouteAll",
916
+ "waitForLoadState",
917
+ "waitForTimeout",
918
+ "waitForURL"
919
+ ]);
920
+ var locatorMethods = /* @__PURE__ */ new Set([
921
+ "all",
922
+ "allInnerTexts",
923
+ "allTextContents",
924
+ "blur",
925
+ "boundingBox",
926
+ "check",
927
+ "clear",
928
+ "click",
929
+ "count",
930
+ "dblclick",
931
+ "dispatchEvent",
932
+ "dragTo",
933
+ "evaluate",
934
+ "evaluateAll",
935
+ "evaluateHandle",
936
+ "fill",
937
+ "focus",
938
+ "getAttribute",
939
+ "hover",
940
+ "innerHTML",
941
+ "innerText",
942
+ "inputValue",
943
+ "isChecked",
944
+ "isDisabled",
945
+ "isEditable",
946
+ "isEnabled",
947
+ "isHidden",
948
+ "isVisible",
949
+ "press",
950
+ "pressSequentially",
951
+ "screenshot",
952
+ "scrollIntoViewIfNeeded",
953
+ "selectOption",
954
+ "selectText",
955
+ "setChecked",
956
+ "setInputFiles",
957
+ "tap",
958
+ "textContent",
959
+ "type",
960
+ "uncheck",
961
+ "waitFor"
962
+ ]);
864
963
  var expectPlaywrightMatchers = [
865
964
  "toBeChecked",
866
965
  "toBeDisabled",
@@ -916,14 +1015,18 @@ function getReportNode(node) {
916
1015
  }
917
1016
  function getCallType(call, awaitableMatchers) {
918
1017
  if (call.type === "step") {
919
- return { messageId: "testStep", node: call.head.node };
1018
+ return {
1019
+ data: { name: "test.step" },
1020
+ messageId: "missingAwait",
1021
+ node: call.head.node
1022
+ };
920
1023
  }
921
1024
  if (call.type === "expect") {
922
1025
  const isPoll = call.modifiers.some((m) => getStringValue(m) === "poll");
923
1026
  if (isPoll || awaitableMatchers.has(call.matcherName)) {
924
1027
  return {
925
- data: { matcherName: call.matcherName },
926
- messageId: isPoll ? "expectPoll" : "expect",
1028
+ data: { name: isPoll ? "expect.poll" : call.matcherName },
1029
+ messageId: "missingAwait",
927
1030
  node: call.head.node
928
1031
  };
929
1032
  }
@@ -932,6 +1035,7 @@ function getCallType(call, awaitableMatchers) {
932
1035
  var missing_playwright_await_default = createRule({
933
1036
  create(context) {
934
1037
  const options = context.options[0] || {};
1038
+ const includePageLocatorMethods = !!options.includePageLocatorMethods;
935
1039
  const awaitableMatchers = /* @__PURE__ */ new Set([
936
1040
  ...expectPlaywrightMatchers,
937
1041
  ...playwrightTestMatchers,
@@ -992,9 +1096,18 @@ var missing_playwright_await_default = createRule({
992
1096
  if (parent.type === "SpreadElement") {
993
1097
  return checkValidity(parent, visited);
994
1098
  }
995
- if (parent.type === "CallExpression" && parent.callee.type === "MemberExpression" && isIdentifier(parent.callee.object, "Promise") && isIdentifier(parent.callee.property, "all")) {
1099
+ if (parent.type === "CallExpression" && parent.callee.type === "MemberExpression" && isIdentifier(parent.callee.object, "Promise") && isIdentifier(parent.callee.property, /^(all|allSettled|race|any)$/)) {
996
1100
  return true;
997
1101
  }
1102
+ if (parent.type === "MemberExpression" && parent.object === node && getStringValue(parent.property) === "resolves" && node.type === "CallExpression" && isIdentifier(node.callee, "expect")) {
1103
+ return checkValidity(parent, visited);
1104
+ }
1105
+ if (parent.type === "MemberExpression" && parent.object === node) {
1106
+ return checkValidity(parent, visited);
1107
+ }
1108
+ if (parent.type === "CallExpression" && parent.callee === node) {
1109
+ return checkValidity(parent, visited);
1110
+ }
998
1111
  if (parent.type === "VariableDeclarator") {
999
1112
  return isVariableConsumed(parent, checkValidity, validTypes, visited);
1000
1113
  }
@@ -1006,13 +1119,27 @@ var missing_playwright_await_default = createRule({
1006
1119
  if (!checkValidity(node, /* @__PURE__ */ new Set())) {
1007
1120
  const methodName = getStringValue(node.callee.property);
1008
1121
  context.report({
1009
- data: { methodName },
1010
- messageId: "waitFor",
1122
+ data: { name: methodName },
1123
+ messageId: "missingAwait",
1011
1124
  node
1012
1125
  });
1013
1126
  }
1014
1127
  return;
1015
1128
  }
1129
+ if (includePageLocatorMethods && node.callee.type === "MemberExpression") {
1130
+ const methodName = getStringValue(node.callee.property);
1131
+ const isPlaywrightMethod = locatorMethods.has(methodName) || pageMethods.has(methodName) && isPageMethod(node, methodName);
1132
+ if (isPlaywrightMethod) {
1133
+ if (!checkValidity(node, /* @__PURE__ */ new Set())) {
1134
+ context.report({
1135
+ data: { name: methodName },
1136
+ messageId: "missingAwait",
1137
+ node
1138
+ });
1139
+ }
1140
+ return;
1141
+ }
1142
+ }
1016
1143
  const call = parseFnCall(context, node);
1017
1144
  if (call?.type !== "step" && call?.type !== "expect") {
1018
1145
  return;
@@ -1038,10 +1165,7 @@ var missing_playwright_await_default = createRule({
1038
1165
  },
1039
1166
  fixable: "code",
1040
1167
  messages: {
1041
- expect: "'{{matcherName}}' must be awaited or returned.",
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."
1168
+ missingAwait: "'{{name}}' must be awaited or returned."
1045
1169
  },
1046
1170
  schema: [
1047
1171
  {
@@ -1050,6 +1174,9 @@ var missing_playwright_await_default = createRule({
1050
1174
  customMatchers: {
1051
1175
  items: { type: "string" },
1052
1176
  type: "array"
1177
+ },
1178
+ includePageLocatorMethods: {
1179
+ type: "boolean"
1053
1180
  }
1054
1181
  },
1055
1182
  type: "object"
@@ -1962,11 +2089,15 @@ var no_skipped_test_default = createRule({
1962
2089
  CallExpression(node) {
1963
2090
  const options = context.options[0] || {};
1964
2091
  const allowConditional = !!options.allowConditional;
2092
+ const disallowFixme = !!options.disallowFixme;
1965
2093
  const call = parseFnCall(context, node);
1966
2094
  if (call?.group !== "test" && call?.group !== "describe" && call?.group !== "step") {
1967
2095
  return;
1968
2096
  }
1969
- const skipNode = call.members.find((s) => getStringValue(s) === "skip");
2097
+ const skipNode = call.members.find((member) => {
2098
+ const value = getStringValue(member);
2099
+ return value === "skip" || disallowFixme && value === "fixme";
2100
+ });
1970
2101
  if (!skipNode) {
1971
2102
  return;
1972
2103
  }
@@ -1974,18 +2105,21 @@ var no_skipped_test_default = createRule({
1974
2105
  if (isStandalone && allowConditional && (node.arguments.length !== 0 || findParent(node, "BlockStatement")?.parent?.type === "IfStatement" || findParent(node, "SwitchCase") !== void 0)) {
1975
2106
  return;
1976
2107
  }
2108
+ const annotation = getStringValue(skipNode);
1977
2109
  context.report({
2110
+ data: { annotation },
1978
2111
  messageId: "noSkippedTest",
1979
2112
  node: isStandalone ? node : skipNode,
1980
2113
  suggest: [
1981
2114
  {
2115
+ data: { annotation: getStringValue(skipNode) },
1982
2116
  fix: (fixer) => {
1983
2117
  return isStandalone ? fixer.remove(node.parent) : fixer.removeRange([
1984
2118
  skipNode.range[0] - 1,
1985
2119
  skipNode.range[1] + Number(skipNode.type !== "Identifier")
1986
2120
  ]);
1987
2121
  },
1988
- messageId: "removeSkippedTestAnnotation"
2122
+ messageId: "removeAnnotation"
1989
2123
  }
1990
2124
  ]
1991
2125
  });
@@ -2000,8 +2134,8 @@ var no_skipped_test_default = createRule({
2000
2134
  },
2001
2135
  hasSuggestions: true,
2002
2136
  messages: {
2003
- noSkippedTest: "Unexpected use of the `.skip()` annotation.",
2004
- removeSkippedTestAnnotation: "Remove the `.skip()` annotation."
2137
+ noSkippedTest: "Unexpected use of the `.{{annotation}}()` annotation.",
2138
+ removeAnnotation: "Remove the `.{{annotation}}()` annotation."
2005
2139
  },
2006
2140
  schema: [
2007
2141
  {
@@ -2010,6 +2144,10 @@ var no_skipped_test_default = createRule({
2010
2144
  allowConditional: {
2011
2145
  default: false,
2012
2146
  type: "boolean"
2147
+ },
2148
+ disallowFixme: {
2149
+ default: false,
2150
+ type: "boolean"
2013
2151
  }
2014
2152
  },
2015
2153
  type: "object"
@@ -2306,7 +2444,7 @@ var no_unused_locators_default = createRule({
2306
2444
  });
2307
2445
 
2308
2446
  // src/rules/no-useless-await.ts
2309
- var locatorMethods = /* @__PURE__ */ new Set([
2447
+ var locatorMethods2 = /* @__PURE__ */ new Set([
2310
2448
  "and",
2311
2449
  "first",
2312
2450
  "getByAltText",
@@ -2321,7 +2459,7 @@ var locatorMethods = /* @__PURE__ */ new Set([
2321
2459
  "nth",
2322
2460
  "or"
2323
2461
  ]);
2324
- var pageMethods = /* @__PURE__ */ new Set([
2462
+ var pageMethods2 = /* @__PURE__ */ new Set([
2325
2463
  "childFrames",
2326
2464
  "frame",
2327
2465
  "frameLocator",
@@ -2370,7 +2508,7 @@ function isSupportedMethod(node) {
2370
2508
  return false;
2371
2509
  }
2372
2510
  const name = getStringValue(node.callee.property);
2373
- return locatorMethods.has(name) || pageMethods.has(name) && isPageMethod(node, name);
2511
+ return locatorMethods2.has(name) || pageMethods2.has(name) && isPageMethod(node, name);
2374
2512
  }
2375
2513
  var no_useless_await_default = createRule({
2376
2514
  create(context) {
@@ -2881,7 +3019,7 @@ var prefer_hooks_on_top_default = createRule({
2881
3019
  });
2882
3020
 
2883
3021
  // src/rules/prefer-locator.ts
2884
- var pageMethods2 = /* @__PURE__ */ new Set([
3022
+ var pageMethods3 = /* @__PURE__ */ new Set([
2885
3023
  "click",
2886
3024
  "dblclick",
2887
3025
  "dispatchEvent",
@@ -2911,7 +3049,7 @@ function isSupportedMethod2(node) {
2911
3049
  return false;
2912
3050
  }
2913
3051
  const name = getStringValue(node.callee.property);
2914
- return pageMethods2.has(name) && isPageMethod(node, name);
3052
+ return pageMethods3.has(name) && isPageMethod(node, name);
2915
3053
  }
2916
3054
  var prefer_locator_default = createRule({
2917
3055
  create(context) {
@@ -4015,7 +4153,7 @@ var isPromiseMethodThatUsesValue = (node, identifier) => {
4015
4153
  }
4016
4154
  if (node.argument.type === "CallExpression" && node.argument.arguments.length > 0) {
4017
4155
  const nodeName = getNodeName(node.argument);
4018
- if (["Promise.all", "Promise.allSettled"].includes(nodeName)) {
4156
+ if (/^Promise\.(all|allSettled|race|any)$/.test(nodeName)) {
4019
4157
  const [firstArg] = node.argument.arguments;
4020
4158
  if (firstArg.type === "ArrayExpression" && firstArg.elements.some((nod) => nod && isIdentifier(nod, name))) {
4021
4159
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-playwright",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "description": "ESLint plugin for Playwright testing.",
5
5
  "license": "MIT",
6
6
  "author": "Mark Skelton <mark@mskelton.dev>",