eslint-plugin-playwright 2.8.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 (3) hide show
  1. package/README.md +9 -9
  2. package/dist/index.cjs +171 -28
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -127,13 +127,13 @@ CLI option\
127
127
  | [no-commented-out-tests](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-commented-out-tests.md) | Disallow commented out tests | | | |
128
128
  | [no-conditional-expect](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-conditional-expect.md) | Disallow calling `expect` conditionally | ✅ | | |
129
129
  | [no-conditional-in-test](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-conditional-in-test.md) | Disallow conditional logic in tests | ✅ | | |
130
- | [no-duplicate-hooks](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-duplicate-hooks.md) | Disallow duplicate setup and teardown hooks | | | |
130
+ | [no-duplicate-hooks](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-duplicate-hooks.md) | Disallow duplicate setup and teardown hooks || | |
131
131
  | [no-duplicate-slow](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-duplicate-slow.md) | Disallow multiple `test.slow()` calls in the same test | ✅ | | |
132
132
  | [no-element-handle](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-element-handle.md) | Disallow usage of element handles | ✅ | | 💡 |
133
133
  | [no-eval](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-eval.md) | Disallow usage of `page.$eval()` and `page.$$eval()` | ✅ | | |
134
134
  | [no-focused-test](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-focused-test.md) | Disallow usage of `.only` annotation | ✅ | | 💡 |
135
135
  | [no-force-option](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-force-option.md) | Disallow usage of the `{ force: true }` option | ✅ | | |
136
- | [no-get-by-title](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-get-by-title.md) | Disallow using `getByTitle()` | | 🔧 | |
136
+ | [no-get-by-title](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-get-by-title.md) | Disallow using `getByTitle()` | | | |
137
137
  | [no-hooks](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-hooks.md) | Disallow setup and teardown hooks | | | |
138
138
  | [no-nested-step](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-nested-step.md) | Disallow nested `test.step()` methods | ✅ | | |
139
139
  | [no-networkidle](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-networkidle.md) | Disallow usage of the `networkidle` option | ✅ | | |
@@ -145,7 +145,7 @@ CLI option\
145
145
  | [no-restricted-roles](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-restricted-roles.md) | Disallow specific roles in `getByRole()` | | | |
146
146
  | [no-skipped-test](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-skipped-test.md) | Disallow usage of the `.skip` annotation | ✅ | | 💡 |
147
147
  | [no-slowed-test](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-slowed-test.md) | Disallow usage of the `.slow` annotation | | | 💡 |
148
- | [no-standalone-expect](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-standalone-expect.md) | Disallow using expect outside of `test` blocks | ✅ | | |
148
+ | [no-standalone-expect](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-standalone-expect.md) | Disallow using expect outside of `test` blocks | ✅ | 🔧 | |
149
149
  | [no-unsafe-references](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-unsafe-references.md) | Prevent unsafe variable references in `page.evaluate()` | ✅ | 🔧 | |
150
150
  | [no-unused-locators](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-unused-locators.md) | Disallow usage of page locators that are not used | ✅ | | |
151
151
  | [no-useless-await](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-useless-await.md) | Disallow unnecessary `await`s for Playwright methods | ✅ | 🔧 | |
@@ -155,16 +155,16 @@ CLI option\
155
155
  | [no-wait-for-timeout](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-timeout.md) | Disallow usage of `page.waitForTimeout()` | ✅ | | 💡 |
156
156
  | [prefer-comparison-matcher](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-comparison-matcher.md) | Suggest using the built-in comparison matchers | | 🔧 | |
157
157
  | [prefer-equality-matcher](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-equality-matcher.md) | Suggest using the built-in equality matchers | | | 💡 |
158
- | [prefer-hooks-in-order](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-hooks-in-order.md) | Prefer having hooks in a consistent order | | | |
159
- | [prefer-hooks-on-top](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | | |
158
+ | [prefer-hooks-in-order](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-hooks-in-order.md) | Prefer having hooks in a consistent order || | |
159
+ | [prefer-hooks-on-top](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases || | |
160
160
  | [prefer-lowercase-title](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names | | 🔧 | |
161
161
  | [prefer-native-locators](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-native-locators.md) | Suggest built-in locators over `page.locator()` | | 🔧 | |
162
- | [prefer-locator](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-locator.md) | Suggest locators over page methods | | | |
163
- | [prefer-strict-equal](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | | 💡 |
162
+ | [prefer-locator](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-locator.md) | Suggest locators over page methods || | |
163
+ | [prefer-strict-equal](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | 🔧 | 💡 |
164
164
  | [prefer-to-be](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-be.md) | Suggest using `toBe()` | | 🔧 | |
165
165
  | [prefer-to-contain](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-contain.md) | Suggest using `toContain()` | | 🔧 | |
166
- | [prefer-to-have-count](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-count.md) | Suggest using `toHaveCount()` | | 🔧 | |
167
- | [prefer-to-have-length](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` | | 🔧 | |
166
+ | [prefer-to-have-count](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-count.md) | Suggest using `toHaveCount()` || 🔧 | |
167
+ | [prefer-to-have-length](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` || 🔧 | |
168
168
  | [prefer-web-first-assertions](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/prefer-web-first-assertions.md) | Suggest using web first assertions | ✅ | 🔧 | |
169
169
  | [require-hook](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/require-hook.md) | Require setup and teardown code to be within a hook | | | |
170
170
  | [require-soft-assertions](https://github.com/mskelton/eslint-plugin-playwright/tree/main/docs/rules/require-soft-assertions.md) | Require assertions to use `expect.soft()` | | 🔧 | |
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"
@@ -1720,14 +1847,12 @@ var no_raw_locators_default = createRule({
1720
1847
  }
1721
1848
  return {
1722
1849
  CallExpression(node) {
1723
- if (node.callee.type !== "MemberExpression" || node.arguments[0]?.type === "Identifier") {
1850
+ if (node.callee.type !== "MemberExpression" || !isPropertyAccessor(node.callee, "locator")) {
1724
1851
  return;
1725
1852
  }
1726
- const method = getStringValue(node.callee.property);
1727
- const arg = getStringValue(node.arguments[0]);
1728
- const isLocator = isPageMethod(node, "locator") || method === "locator";
1729
- if (isLocator && !isAllowed(arg)) {
1853
+ if ((node.arguments.length === 0 || isStringNode(node.arguments[0])) && !isAllowed(getStringValue(node.arguments[0]))) {
1730
1854
  context.report({ messageId: "noRawLocator", node });
1855
+ return;
1731
1856
  }
1732
1857
  }
1733
1858
  };
@@ -1906,7 +2031,7 @@ var no_restricted_roles_default = createRule({
1906
2031
  }
1907
2032
  return {
1908
2033
  CallExpression(node) {
1909
- if (!isPageMethod(node, "getByRole")) {
2034
+ if (node.callee.type !== "MemberExpression" || !isPropertyAccessor(node.callee, "getByRole")) {
1910
2035
  return;
1911
2036
  }
1912
2037
  const role = getStringValue(node.arguments[0]);
@@ -1964,11 +2089,15 @@ var no_skipped_test_default = createRule({
1964
2089
  CallExpression(node) {
1965
2090
  const options = context.options[0] || {};
1966
2091
  const allowConditional = !!options.allowConditional;
2092
+ const disallowFixme = !!options.disallowFixme;
1967
2093
  const call = parseFnCall(context, node);
1968
2094
  if (call?.group !== "test" && call?.group !== "describe" && call?.group !== "step") {
1969
2095
  return;
1970
2096
  }
1971
- 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
+ });
1972
2101
  if (!skipNode) {
1973
2102
  return;
1974
2103
  }
@@ -1976,18 +2105,21 @@ var no_skipped_test_default = createRule({
1976
2105
  if (isStandalone && allowConditional && (node.arguments.length !== 0 || findParent(node, "BlockStatement")?.parent?.type === "IfStatement" || findParent(node, "SwitchCase") !== void 0)) {
1977
2106
  return;
1978
2107
  }
2108
+ const annotation = getStringValue(skipNode);
1979
2109
  context.report({
2110
+ data: { annotation },
1980
2111
  messageId: "noSkippedTest",
1981
2112
  node: isStandalone ? node : skipNode,
1982
2113
  suggest: [
1983
2114
  {
2115
+ data: { annotation: getStringValue(skipNode) },
1984
2116
  fix: (fixer) => {
1985
2117
  return isStandalone ? fixer.remove(node.parent) : fixer.removeRange([
1986
2118
  skipNode.range[0] - 1,
1987
2119
  skipNode.range[1] + Number(skipNode.type !== "Identifier")
1988
2120
  ]);
1989
2121
  },
1990
- messageId: "removeSkippedTestAnnotation"
2122
+ messageId: "removeAnnotation"
1991
2123
  }
1992
2124
  ]
1993
2125
  });
@@ -2002,8 +2134,8 @@ var no_skipped_test_default = createRule({
2002
2134
  },
2003
2135
  hasSuggestions: true,
2004
2136
  messages: {
2005
- noSkippedTest: "Unexpected use of the `.skip()` annotation.",
2006
- removeSkippedTestAnnotation: "Remove the `.skip()` annotation."
2137
+ noSkippedTest: "Unexpected use of the `.{{annotation}}()` annotation.",
2138
+ removeAnnotation: "Remove the `.{{annotation}}()` annotation."
2007
2139
  },
2008
2140
  schema: [
2009
2141
  {
@@ -2012,6 +2144,10 @@ var no_skipped_test_default = createRule({
2012
2144
  allowConditional: {
2013
2145
  default: false,
2014
2146
  type: "boolean"
2147
+ },
2148
+ disallowFixme: {
2149
+ default: false,
2150
+ type: "boolean"
2015
2151
  }
2016
2152
  },
2017
2153
  type: "object"
@@ -2285,7 +2421,7 @@ var no_unused_locators_default = createRule({
2285
2421
  create(context) {
2286
2422
  return {
2287
2423
  CallExpression(node) {
2288
- if (!isPageMethod(node, LOCATOR_REGEX)) {
2424
+ if (node.callee.type !== "MemberExpression" || !isPropertyAccessor(node.callee, LOCATOR_REGEX)) {
2289
2425
  return;
2290
2426
  }
2291
2427
  if (node.parent.type === "ExpressionStatement" || node.parent.type === "AwaitExpression") {
@@ -2308,7 +2444,7 @@ var no_unused_locators_default = createRule({
2308
2444
  });
2309
2445
 
2310
2446
  // src/rules/no-useless-await.ts
2311
- var locatorMethods = /* @__PURE__ */ new Set([
2447
+ var locatorMethods2 = /* @__PURE__ */ new Set([
2312
2448
  "and",
2313
2449
  "first",
2314
2450
  "getByAltText",
@@ -2323,7 +2459,7 @@ var locatorMethods = /* @__PURE__ */ new Set([
2323
2459
  "nth",
2324
2460
  "or"
2325
2461
  ]);
2326
- var pageMethods = /* @__PURE__ */ new Set([
2462
+ var pageMethods2 = /* @__PURE__ */ new Set([
2327
2463
  "childFrames",
2328
2464
  "frame",
2329
2465
  "frameLocator",
@@ -2372,7 +2508,7 @@ function isSupportedMethod(node) {
2372
2508
  return false;
2373
2509
  }
2374
2510
  const name = getStringValue(node.callee.property);
2375
- return locatorMethods.has(name) || pageMethods.has(name) && isPageMethod(node, name);
2511
+ return locatorMethods2.has(name) || pageMethods2.has(name) && isPageMethod(node, name);
2376
2512
  }
2377
2513
  var no_useless_await_default = createRule({
2378
2514
  create(context) {
@@ -2883,7 +3019,7 @@ var prefer_hooks_on_top_default = createRule({
2883
3019
  });
2884
3020
 
2885
3021
  // src/rules/prefer-locator.ts
2886
- var pageMethods2 = /* @__PURE__ */ new Set([
3022
+ var pageMethods3 = /* @__PURE__ */ new Set([
2887
3023
  "click",
2888
3024
  "dblclick",
2889
3025
  "dispatchEvent",
@@ -2913,7 +3049,7 @@ function isSupportedMethod2(node) {
2913
3049
  return false;
2914
3050
  }
2915
3051
  const name = getStringValue(node.callee.property);
2916
- return pageMethods2.has(name) && isPageMethod(node, name);
3052
+ return pageMethods3.has(name) && isPageMethod(node, name);
2917
3053
  }
2918
3054
  var prefer_locator_default = createRule({
2919
3055
  create(context) {
@@ -3086,7 +3222,7 @@ var prefer_native_locators_default = createRule({
3086
3222
  return;
3087
3223
  }
3088
3224
  const query = getStringValue(node.arguments[0]);
3089
- if (!isPageMethod(node, "locator")) {
3225
+ if (!isPropertyAccessor(node.callee, "locator")) {
3090
3226
  return;
3091
3227
  }
3092
3228
  for (const pattern of patterns) {
@@ -4017,7 +4153,7 @@ var isPromiseMethodThatUsesValue = (node, identifier) => {
4017
4153
  }
4018
4154
  if (node.argument.type === "CallExpression" && node.argument.arguments.length > 0) {
4019
4155
  const nodeName = getNodeName(node.argument);
4020
- if (["Promise.all", "Promise.allSettled"].includes(nodeName)) {
4156
+ if (/^Promise\.(all|allSettled|race|any)$/.test(nodeName)) {
4021
4157
  const [firstArg] = node.argument.arguments;
4022
4158
  if (firstArg.type === "ArrayExpression" && firstArg.elements.some((nod) => nod && isIdentifier(nod, name))) {
4023
4159
  return true;
@@ -4797,6 +4933,8 @@ var sharedConfig = {
4797
4933
  "playwright/missing-playwright-await": "error",
4798
4934
  "playwright/no-conditional-expect": "warn",
4799
4935
  "playwright/no-conditional-in-test": "warn",
4936
+ "playwright/no-duplicate-hooks": "warn",
4937
+ "playwright/no-duplicate-slow": "warn",
4800
4938
  "playwright/no-element-handle": "warn",
4801
4939
  "playwright/no-eval": "warn",
4802
4940
  "playwright/no-focused-test": "error",
@@ -4813,6 +4951,11 @@ var sharedConfig = {
4813
4951
  "playwright/no-wait-for-navigation": "error",
4814
4952
  "playwright/no-wait-for-selector": "warn",
4815
4953
  "playwright/no-wait-for-timeout": "warn",
4954
+ "playwright/prefer-hooks-in-order": "warn",
4955
+ "playwright/prefer-hooks-on-top": "warn",
4956
+ "playwright/prefer-locator": "warn",
4957
+ "playwright/prefer-to-have-count": "warn",
4958
+ "playwright/prefer-to-have-length": "warn",
4816
4959
  "playwright/prefer-web-first-assertions": "error",
4817
4960
  "playwright/valid-describe-callback": "error",
4818
4961
  "playwright/valid-expect": "error",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-playwright",
3
- "version": "2.8.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>",