eslint-plugin-playwright 0.22.2 → 1.0.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 CHANGED
@@ -91,45 +91,19 @@ export default [
91
91
  }
92
92
  ```
93
93
 
94
- ## Global Settings
94
+ ### Aliased Playwright Globals
95
95
 
96
- The plugin reads global settings from your ESLint configuration's shared data
97
- under the `playwright` key. It supports the following settings:
98
-
99
- - `additionalAssertFunctionNames`: an array of function names to treat as
100
- assertion functions for the case of rules like `expect-expect`, which enforces
101
- the presence of at least one assertion per test case. This allows such rules
102
- to recognise custom assertion functions as valid assertions. The global
103
- setting applies to all modules. The
104
- [`expect-expect` rule accepts an option by the same name](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/expect-expect.md#additionalassertfunctionnames)
105
- to enable per-module configuration (.e.g, for module-specific custom assert
106
- functions).
107
-
108
- You can configure these settings like so:
109
-
110
- [Flat config](https://eslint.org/docs/latest/use/configure/configuration-files-new)
111
- (**eslint.config.js**)
112
-
113
- ```javascript
114
- export default [
115
- {
116
- settings: {
117
- playwright: {
118
- additionalAssertFunctionNames: ['assertCustomCondition'],
119
- },
120
- },
121
- },
122
- ];
123
- ```
124
-
125
- [Legacy config](https://eslint.org/docs/latest/use/configure/configuration-files)
126
- (**.eslintrc**)
96
+ If you import Playwright globals (e.g. `test`, `expect`) with a custom name, you
97
+ can configure this plugin to be aware of these additional names.
127
98
 
128
99
  ```json
129
100
  {
130
101
  "settings": {
131
102
  "playwright": {
132
- "additionalAssertFunctionNames": ["assertCustomCondition"]
103
+ "globalAliases": {
104
+ "test": ["myTest"],
105
+ "expect": ["myExpect"]
106
+ }
133
107
  }
134
108
  }
135
109
  }
@@ -150,20 +124,21 @@ command line option.\
150
124
  | ✔ | 🔧 | | [missing-playwright-await](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/missing-playwright-await.md) | Enforce Playwright APIs to be awaited |
151
125
  | ✔ | | | [no-conditional-in-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-conditional-in-test.md) | Disallow conditional logic in tests |
152
126
  | ✔ | | 💡 | [no-element-handle](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-element-handle.md) | Disallow usage of element handles |
153
- | ✔ | | | [no-eval](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-eval.md) | Disallow usage of `page.$eval` and `page.$$eval` |
127
+ | ✔ | | | [no-eval](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-eval.md) | Disallow usage of `page.$eval()` and `page.$$eval()` |
154
128
  | ✔ | | 💡 | [no-focused-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-focused-test.md) | Disallow usage of `.only` annotation |
155
129
  | ✔ | | | [no-force-option](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-force-option.md) | Disallow usage of the `{ force: true }` option |
156
130
  | ✔ | | | [no-nested-step](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-nested-step.md) | Disallow nested `test.step()` methods |
157
131
  | ✔ | | | [no-networkidle](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-networkidle.md) | Disallow usage of the `networkidle` option |
158
132
  | | | | [no-nth-methods](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-nth-methods.md) | Disallow usage of `first()`, `last()`, and `nth()` methods |
159
- | ✔ | | | [no-page-pause](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-page-pause.md) | Disallow using `page.pause` |
133
+ | ✔ | | | [no-page-pause](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-page-pause.md) | Disallow using `page.pause()` |
134
+ | | 🔧 | | [no-get-by-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-get-by-title.md) | Disallow using `getByTitle()` |
160
135
  | | | | [no-raw-locators](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-raw-locators.md) | Disallow using raw locators |
161
136
  | ✔ | 🔧 | | [no-useless-await](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-useless-await.md) | Disallow unnecessary `await`s for Playwright methods |
162
137
  | | | | [no-restricted-matchers](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-restricted-matchers.md) | Disallow specific matchers & modifiers |
163
138
  | ✔ | | 💡 | [no-skipped-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-skipped-test.md) | Disallow usage of the `.skip` annotation |
164
139
  | ✔ | 🔧 | | [no-useless-not](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-useless-not.md) | Disallow usage of `not` matchers when a specific matcher exists |
165
- | ✔ | | 💡 | [no-wait-for-selector](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-selector.md) | Disallow usage of `page.waitForSelector` |
166
- | ✔ | | 💡 | [no-wait-for-timeout](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-timeout.md) | Disallow usage of `page.waitForTimeout` |
140
+ | ✔ | | 💡 | [no-wait-for-selector](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-selector.md) | Disallow usage of `page.waitForSelector()` |
141
+ | ✔ | | 💡 | [no-wait-for-timeout](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-timeout.md) | Disallow usage of `page.waitForTimeout()` |
167
142
  | | | 💡 | [prefer-strict-equal](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` |
168
143
  | | 🔧 | | [prefer-lowercase-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names |
169
144
  | | 🔧 | | [prefer-to-be](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-be.md) | Suggest using `toBe()` |
package/dist/index.js CHANGED
@@ -54,8 +54,11 @@ function isStringNode(node) {
54
54
  function isPropertyAccessor(node, name) {
55
55
  return getStringValue(node.property) === name;
56
56
  }
57
- function isTestIdentifier(node) {
58
- return isIdentifier(node, "test") || node.type === "MemberExpression" && isIdentifier(node.object, "test");
57
+ function isTestIdentifier(context, node) {
58
+ const aliases = context.settings.playwright?.globalAliases?.test ?? [];
59
+ const testNames = ["test", ...aliases];
60
+ const regex = new RegExp(`^(${testNames.join("|")})$`);
61
+ return isIdentifier(node, regex) || node.type === "MemberExpression" && isIdentifier(node.object, regex);
59
62
  }
60
63
  var describeProperties = /* @__PURE__ */ new Set([
61
64
  "parallel",
@@ -79,27 +82,31 @@ function findParent(node, type) {
79
82
  return;
80
83
  return node.parent.type === type ? node.parent : findParent(node.parent, type);
81
84
  }
82
- function isTestCall(node, modifiers) {
83
- return isTestIdentifier(node.callee) && !isDescribeCall(node) && (node.callee.type !== "MemberExpression" || !modifiers || modifiers?.includes(getStringValue(node.callee.property))) && node.arguments.length === 2 && ["ArrowFunctionExpression", "FunctionExpression"].includes(
85
+ function isTestCall(context, node, modifiers) {
86
+ return isTestIdentifier(context, node.callee) && !isDescribeCall(node) && (node.callee.type !== "MemberExpression" || !modifiers || modifiers?.includes(getStringValue(node.callee.property))) && node.arguments.length === 2 && ["ArrowFunctionExpression", "FunctionExpression"].includes(
84
87
  node.arguments[1].type
85
88
  );
86
89
  }
87
90
  var testHooks = /* @__PURE__ */ new Set(["afterAll", "afterEach", "beforeAll", "beforeEach"]);
88
- function isTestHook(node) {
89
- return node.callee.type === "MemberExpression" && isIdentifier(node.callee.object, "test") && testHooks.has(getStringValue(node.callee.property));
91
+ function isTestHook(context, node) {
92
+ return node.callee.type === "MemberExpression" && isTestIdentifier(context, node.callee.object) && testHooks.has(getStringValue(node.callee.property));
90
93
  }
91
94
  var expectSubCommands = /* @__PURE__ */ new Set(["soft", "poll"]);
92
- function getExpectType(node) {
93
- if (isIdentifier(node.callee, /(^expect|Expect)$/)) {
95
+ function getExpectType(context, node) {
96
+ const aliases = context.settings.playwright?.globalAliases?.expect ?? [];
97
+ const expectNames = ["expect", ...aliases];
98
+ const regex = new RegExp(`(^(${expectNames.join("|")})|Expect)$`);
99
+ if (isIdentifier(node.callee, regex)) {
94
100
  return "standalone";
95
101
  }
96
- if (node.callee.type === "MemberExpression" && isIdentifier(node.callee.object, "expect")) {
102
+ if (node.callee.type === "MemberExpression" && // TODO: Maybe
103
+ isIdentifier(node.callee.object, "expect")) {
97
104
  const type = getStringValue(node.callee.property);
98
105
  return expectSubCommands.has(type) ? type : void 0;
99
106
  }
100
107
  }
101
- function isExpectCall(node) {
102
- return !!getExpectType(node);
108
+ function isExpectCall(context, node) {
109
+ return !!getExpectType(context, node);
103
110
  }
104
111
  function getMatchers(node, chain = []) {
105
112
  if (node.parent.type === "MemberExpression" && node.parent.object === node) {
@@ -117,26 +124,18 @@ function isPageMethod(node, name) {
117
124
  return node.callee.type === "MemberExpression" && dig(node.callee.object, /(^(page|frame)|(Page|Frame)$)/) && isPropertyAccessor(node.callee, name);
118
125
  }
119
126
 
120
- // src/utils/misc.ts
121
- var getAmountData = (amount) => ({
122
- amount: amount.toString(),
123
- s: amount === 1 ? "" : "s"
124
- });
125
- function getAdditionalAssertFunctionNames(context) {
126
- const globalSettings = context.settings.playwright?.additionalAssertFunctionNames ?? [];
127
- const ruleSettings = context.options[0]?.additionalAssertFunctionNames ?? [];
128
- return [...globalSettings, ...ruleSettings];
129
- }
130
-
131
127
  // src/rules/expect-expect.ts
132
- function isAssertionCall(node, additionalAssertFunctionNames) {
133
- return isExpectCall(node) || additionalAssertFunctionNames.find((name) => dig(node.callee, name));
128
+ function isAssertionCall(context, node, assertFunctionNames) {
129
+ return isExpectCall(context, node) || assertFunctionNames.find((name) => dig(node.callee, name));
134
130
  }
135
131
  var expect_expect_default = {
136
132
  create(context) {
133
+ const options = {
134
+ assertFunctionNames: [],
135
+ ...context.options?.[0] ?? {}
136
+ };
137
137
  const sourceCode = context.sourceCode ?? context.getSourceCode();
138
138
  const unchecked = [];
139
- const additionalAssertFunctionNames = getAdditionalAssertFunctionNames(context);
140
139
  function checkExpressions(nodes) {
141
140
  for (const node of nodes) {
142
141
  const index2 = node.type === "CallExpression" ? unchecked.indexOf(node) : -1;
@@ -148,9 +147,9 @@ var expect_expect_default = {
148
147
  }
149
148
  return {
150
149
  CallExpression(node) {
151
- if (isTestCall(node, ["fixme", "only", "skip"])) {
150
+ if (isTestCall(context, node, ["fixme", "only", "skip"])) {
152
151
  unchecked.push(node);
153
- } else if (isAssertionCall(node, additionalAssertFunctionNames)) {
152
+ } else if (isAssertionCall(context, node, options.assertFunctionNames)) {
154
153
  const ancestors = sourceCode.getAncestors ? sourceCode.getAncestors(node) : context.getAncestors();
155
154
  checkExpressions(ancestors);
156
155
  }
@@ -176,7 +175,7 @@ var expect_expect_default = {
176
175
  {
177
176
  additionalProperties: false,
178
177
  properties: {
179
- additionalAssertFunctionNames: {
178
+ assertFunctionNames: {
180
179
  items: [{ type: "string" }],
181
180
  type: "array"
182
181
  }
@@ -302,11 +301,11 @@ var playwrightTestMatchers = [
302
301
  "toBeAttached",
303
302
  "toBeInViewport"
304
303
  ];
305
- function getCallType(node, awaitableMatchers) {
304
+ function getCallType(context, node, awaitableMatchers) {
306
305
  if (node.callee.type === "MemberExpression" && isIdentifier(node.callee.object, "test") && isPropertyAccessor(node.callee, "step")) {
307
306
  return { messageId: "testStep", node };
308
307
  }
309
- const expectType = getExpectType(node);
308
+ const expectType = getExpectType(context, node);
310
309
  if (!expectType)
311
310
  return;
312
311
  const [lastMatcher] = getMatchers(node).slice(-1);
@@ -355,7 +354,7 @@ var missing_playwright_await_default = {
355
354
  }
356
355
  return {
357
356
  CallExpression(node) {
358
- const result = getCallType(node, awaitableMatchers);
357
+ const result = getCallType(context, node, awaitableMatchers);
359
358
  const isValid = result ? checkValidity(result.node) : false;
360
359
  if (result && !isValid) {
361
360
  context.report({
@@ -401,7 +400,7 @@ var no_conditional_in_test_default = {
401
400
  create(context) {
402
401
  function checkConditional(node) {
403
402
  const call = findParent(node, "CallExpression");
404
- if (call && isTestCall(call)) {
403
+ if (call && isTestCall(context, call)) {
405
404
  context.report({ messageId: "conditionalInTest", node });
406
405
  }
407
406
  }
@@ -519,7 +518,7 @@ var no_focused_test_default = {
519
518
  create(context) {
520
519
  return {
521
520
  CallExpression(node) {
522
- if ((isTestCall(node) || isDescribeCall(node)) && node.callee.type === "MemberExpression" && isPropertyAccessor(node.callee, "only")) {
521
+ if ((isTestCall(context, node) || isDescribeCall(node)) && node.callee.type === "MemberExpression" && isPropertyAccessor(node.callee, "only")) {
523
522
  const { callee } = node;
524
523
  context.report({
525
524
  messageId: "noFocusedTest",
@@ -824,8 +823,8 @@ function getExpectArguments(node) {
824
823
  const grandparent = node.parent.parent;
825
824
  return grandparent.type === "CallExpression" ? grandparent.arguments : [];
826
825
  }
827
- function parseExpectCall(node) {
828
- if (!isExpectCall(node)) {
826
+ function parseExpectCall(context, node) {
827
+ if (!isExpectCall(context, node)) {
829
828
  return;
830
829
  }
831
830
  const members = getMatchers(node);
@@ -856,7 +855,7 @@ var no_restricted_matchers_default = {
856
855
  const restrictedChains = context.options?.[0] ?? {};
857
856
  return {
858
857
  CallExpression(node) {
859
- const expectCall = parseExpectCall(node);
858
+ const expectCall = parseExpectCall(context, node);
860
859
  if (!expectCall)
861
860
  return;
862
861
  Object.entries(restrictedChains).map(([restriction, message]) => {
@@ -917,8 +916,8 @@ var no_skipped_test_default = {
917
916
  const options = context.options[0] || {};
918
917
  const allowConditional = !!options.allowConditional;
919
918
  const { callee } = node;
920
- if ((isTestIdentifier(callee) || isDescribeCall(node)) && callee.type === "MemberExpression" && isPropertyAccessor(callee, "skip")) {
921
- const isHook = isTestCall(node) || isDescribeCall(node);
919
+ if ((isTestIdentifier(context, callee) || isDescribeCall(node)) && callee.type === "MemberExpression" && isPropertyAccessor(callee, "skip")) {
920
+ const isHook = isTestCall(context, node) || isDescribeCall(node);
922
921
  if (!isHook && allowConditional && node.arguments.length) {
923
922
  return;
924
923
  }
@@ -1072,7 +1071,7 @@ var no_useless_not_default = {
1072
1071
  create(context) {
1073
1072
  return {
1074
1073
  CallExpression(node) {
1075
- const expectCall = parseExpectCall(node);
1074
+ const expectCall = parseExpectCall(context, node);
1076
1075
  if (!expectCall)
1077
1076
  return;
1078
1077
  const notModifier = expectCall.modifiers.find(
@@ -1204,7 +1203,7 @@ var prefer_lowercase_title_default = {
1204
1203
  let describeCount = 0;
1205
1204
  return {
1206
1205
  CallExpression(node) {
1207
- const method = isDescribeCall(node) ? "test.describe" : isTestCall(node) ? "test" : null;
1206
+ const method = isDescribeCall(node) ? "test.describe" : isTestCall(context, node) ? "test" : null;
1208
1207
  if (method === "test.describe") {
1209
1208
  describeCount++;
1210
1209
  if (ignoreTopLevelDescribe && describeCount === 1) {
@@ -1290,7 +1289,7 @@ var prefer_strict_equal_default = {
1290
1289
  create(context) {
1291
1290
  return {
1292
1291
  CallExpression(node) {
1293
- const expectCall = parseExpectCall(node);
1292
+ const expectCall = parseExpectCall(context, node);
1294
1293
  if (expectCall?.matcherName === "toEqual") {
1295
1294
  context.report({
1296
1295
  messageId: "useToStrictEqual",
@@ -1364,7 +1363,7 @@ var prefer_to_be_default = {
1364
1363
  create(context) {
1365
1364
  return {
1366
1365
  CallExpression(node) {
1367
- const expectCall = parseExpectCall(node);
1366
+ const expectCall = parseExpectCall(context, node);
1368
1367
  if (!expectCall)
1369
1368
  return;
1370
1369
  const notMatchers = ["toBeUndefined", "toBeDefined"];
@@ -1427,7 +1426,7 @@ var prefer_to_contain_default = {
1427
1426
  create(context) {
1428
1427
  return {
1429
1428
  CallExpression(node) {
1430
- const expectCall = parseExpectCall(node);
1429
+ const expectCall = parseExpectCall(context, node);
1431
1430
  if (!expectCall || expectCall.args.length === 0)
1432
1431
  return;
1433
1432
  const { args, matcher, matcherName } = expectCall;
@@ -1497,7 +1496,7 @@ var prefer_to_have_count_default = {
1497
1496
  create(context) {
1498
1497
  return {
1499
1498
  CallExpression(node) {
1500
- const expectCall = parseExpectCall(node);
1499
+ const expectCall = parseExpectCall(context, node);
1501
1500
  if (!expectCall || !matchers2.has(expectCall.matcherName)) {
1502
1501
  return;
1503
1502
  }
@@ -1553,7 +1552,7 @@ var prefer_to_have_length_default = {
1553
1552
  create(context) {
1554
1553
  return {
1555
1554
  CallExpression(node) {
1556
- const expectCall = parseExpectCall(node);
1555
+ const expectCall = parseExpectCall(context, node);
1557
1556
  if (!expectCall || !lengthMatchers.has(expectCall.matcherName)) {
1558
1557
  return;
1559
1558
  }
@@ -1641,7 +1640,7 @@ var prefer_web_first_assertions_default = {
1641
1640
  create(context) {
1642
1641
  return {
1643
1642
  CallExpression(node) {
1644
- const expectCall = parseExpectCall(node);
1643
+ const expectCall = parseExpectCall(context, node);
1645
1644
  if (!expectCall)
1646
1645
  return;
1647
1646
  const [arg] = node.arguments;
@@ -1742,7 +1741,7 @@ var require_soft_assertions_default = {
1742
1741
  create(context) {
1743
1742
  return {
1744
1743
  CallExpression(node) {
1745
- if (getExpectType(node) === "standalone") {
1744
+ if (getExpectType(context, node) === "standalone") {
1746
1745
  context.report({
1747
1746
  fix: (fixer) => fixer.insertTextAfter(node.callee, ".soft"),
1748
1747
  messageId: "requireSoft",
@@ -1767,6 +1766,12 @@ var require_soft_assertions_default = {
1767
1766
  }
1768
1767
  };
1769
1768
 
1769
+ // src/utils/misc.ts
1770
+ var getAmountData = (amount) => ({
1771
+ amount: amount.toString(),
1772
+ s: amount === 1 ? "" : "s"
1773
+ });
1774
+
1770
1775
  // src/rules/require-top-level-describe.ts
1771
1776
  var require_top_level_describe_default = {
1772
1777
  create(context) {
@@ -1791,9 +1796,9 @@ var require_top_level_describe_default = {
1791
1796
  }
1792
1797
  }
1793
1798
  } else if (!describeCount) {
1794
- if (isTestCall(node)) {
1799
+ if (isTestCall(context, node)) {
1795
1800
  context.report({ messageId: "unexpectedTest", node: node.callee });
1796
- } else if (isTestHook(node)) {
1801
+ } else if (isTestHook(context, node)) {
1797
1802
  context.report({ messageId: "unexpectedHook", node: node.callee });
1798
1803
  }
1799
1804
  }
@@ -1854,9 +1859,9 @@ var valid_expect_default = {
1854
1859
  const maxArgs = Math.max(options.minArgs, options.maxArgs);
1855
1860
  return {
1856
1861
  CallExpression(node) {
1857
- if (!isExpectCall(node))
1862
+ if (!isExpectCall(context, node))
1858
1863
  return;
1859
- const expectCall = parseExpectCall(node);
1864
+ const expectCall = parseExpectCall(context, node);
1860
1865
  if (!expectCall) {
1861
1866
  context.report({ messageId: "matcherNotFound", node });
1862
1867
  } else {
@@ -1973,7 +1978,7 @@ var valid_title_default = {
1973
1978
  return {
1974
1979
  CallExpression(node) {
1975
1980
  const isDescribe = isDescribeCall(node);
1976
- const isTest = isTestCall(node);
1981
+ const isTest = isTestCall(context, node);
1977
1982
  if (!isDescribe && !isTest) {
1978
1983
  return;
1979
1984
  }
package/dist/index.mjs CHANGED
@@ -33,8 +33,11 @@ function isStringNode(node) {
33
33
  function isPropertyAccessor(node, name) {
34
34
  return getStringValue(node.property) === name;
35
35
  }
36
- function isTestIdentifier(node) {
37
- return isIdentifier(node, "test") || node.type === "MemberExpression" && isIdentifier(node.object, "test");
36
+ function isTestIdentifier(context, node) {
37
+ const aliases = context.settings.playwright?.globalAliases?.test ?? [];
38
+ const testNames = ["test", ...aliases];
39
+ const regex = new RegExp(`^(${testNames.join("|")})$`);
40
+ return isIdentifier(node, regex) || node.type === "MemberExpression" && isIdentifier(node.object, regex);
38
41
  }
39
42
  function isDescribeCall(node) {
40
43
  const inner = node.type === "CallExpression" ? node.callee : node;
@@ -51,25 +54,29 @@ function findParent(node, type) {
51
54
  return;
52
55
  return node.parent.type === type ? node.parent : findParent(node.parent, type);
53
56
  }
54
- function isTestCall(node, modifiers) {
55
- return isTestIdentifier(node.callee) && !isDescribeCall(node) && (node.callee.type !== "MemberExpression" || !modifiers || modifiers?.includes(getStringValue(node.callee.property))) && node.arguments.length === 2 && ["ArrowFunctionExpression", "FunctionExpression"].includes(
57
+ function isTestCall(context, node, modifiers) {
58
+ return isTestIdentifier(context, node.callee) && !isDescribeCall(node) && (node.callee.type !== "MemberExpression" || !modifiers || modifiers?.includes(getStringValue(node.callee.property))) && node.arguments.length === 2 && ["ArrowFunctionExpression", "FunctionExpression"].includes(
56
59
  node.arguments[1].type
57
60
  );
58
61
  }
59
- function isTestHook(node) {
60
- return node.callee.type === "MemberExpression" && isIdentifier(node.callee.object, "test") && testHooks.has(getStringValue(node.callee.property));
62
+ function isTestHook(context, node) {
63
+ return node.callee.type === "MemberExpression" && isTestIdentifier(context, node.callee.object) && testHooks.has(getStringValue(node.callee.property));
61
64
  }
62
- function getExpectType(node) {
63
- if (isIdentifier(node.callee, /(^expect|Expect)$/)) {
65
+ function getExpectType(context, node) {
66
+ const aliases = context.settings.playwright?.globalAliases?.expect ?? [];
67
+ const expectNames = ["expect", ...aliases];
68
+ const regex = new RegExp(`(^(${expectNames.join("|")})|Expect)$`);
69
+ if (isIdentifier(node.callee, regex)) {
64
70
  return "standalone";
65
71
  }
66
- if (node.callee.type === "MemberExpression" && isIdentifier(node.callee.object, "expect")) {
72
+ if (node.callee.type === "MemberExpression" && // TODO: Maybe
73
+ isIdentifier(node.callee.object, "expect")) {
67
74
  const type = getStringValue(node.callee.property);
68
75
  return expectSubCommands.has(type) ? type : void 0;
69
76
  }
70
77
  }
71
- function isExpectCall(node) {
72
- return !!getExpectType(node);
78
+ function isExpectCall(context, node) {
79
+ return !!getExpectType(context, node);
73
80
  }
74
81
  function getMatchers(node, chain = []) {
75
82
  if (node.parent.type === "MemberExpression" && node.parent.object === node) {
@@ -104,38 +111,23 @@ var init_ast = __esm({
104
111
  }
105
112
  });
106
113
 
107
- // src/utils/misc.ts
108
- function getAdditionalAssertFunctionNames(context) {
109
- const globalSettings = context.settings.playwright?.additionalAssertFunctionNames ?? [];
110
- const ruleSettings = context.options[0]?.additionalAssertFunctionNames ?? [];
111
- return [...globalSettings, ...ruleSettings];
112
- }
113
- var getAmountData;
114
- var init_misc = __esm({
115
- "src/utils/misc.ts"() {
116
- "use strict";
117
- getAmountData = (amount) => ({
118
- amount: amount.toString(),
119
- s: amount === 1 ? "" : "s"
120
- });
121
- }
122
- });
123
-
124
114
  // src/rules/expect-expect.ts
125
- function isAssertionCall(node, additionalAssertFunctionNames) {
126
- return isExpectCall(node) || additionalAssertFunctionNames.find((name) => dig(node.callee, name));
115
+ function isAssertionCall(context, node, assertFunctionNames) {
116
+ return isExpectCall(context, node) || assertFunctionNames.find((name) => dig(node.callee, name));
127
117
  }
128
118
  var expect_expect_default;
129
119
  var init_expect_expect = __esm({
130
120
  "src/rules/expect-expect.ts"() {
131
121
  "use strict";
132
122
  init_ast();
133
- init_misc();
134
123
  expect_expect_default = {
135
124
  create(context) {
125
+ const options = {
126
+ assertFunctionNames: [],
127
+ ...context.options?.[0] ?? {}
128
+ };
136
129
  const sourceCode = context.sourceCode ?? context.getSourceCode();
137
130
  const unchecked = [];
138
- const additionalAssertFunctionNames = getAdditionalAssertFunctionNames(context);
139
131
  function checkExpressions(nodes) {
140
132
  for (const node of nodes) {
141
133
  const index = node.type === "CallExpression" ? unchecked.indexOf(node) : -1;
@@ -147,9 +139,9 @@ var init_expect_expect = __esm({
147
139
  }
148
140
  return {
149
141
  CallExpression(node) {
150
- if (isTestCall(node, ["fixme", "only", "skip"])) {
142
+ if (isTestCall(context, node, ["fixme", "only", "skip"])) {
151
143
  unchecked.push(node);
152
- } else if (isAssertionCall(node, additionalAssertFunctionNames)) {
144
+ } else if (isAssertionCall(context, node, options.assertFunctionNames)) {
153
145
  const ancestors = sourceCode.getAncestors ? sourceCode.getAncestors(node) : context.getAncestors();
154
146
  checkExpressions(ancestors);
155
147
  }
@@ -175,7 +167,7 @@ var init_expect_expect = __esm({
175
167
  {
176
168
  additionalProperties: false,
177
169
  properties: {
178
- additionalAssertFunctionNames: {
170
+ assertFunctionNames: {
179
171
  items: [{ type: "string" }],
180
172
  type: "array"
181
173
  }
@@ -258,11 +250,11 @@ var init_max_nested_describe = __esm({
258
250
  });
259
251
 
260
252
  // src/rules/missing-playwright-await.ts
261
- function getCallType(node, awaitableMatchers) {
253
+ function getCallType(context, node, awaitableMatchers) {
262
254
  if (node.callee.type === "MemberExpression" && isIdentifier(node.callee.object, "test") && isPropertyAccessor(node.callee, "step")) {
263
255
  return { messageId: "testStep", node };
264
256
  }
265
- const expectType = getExpectType(node);
257
+ const expectType = getExpectType(context, node);
266
258
  if (!expectType)
267
259
  return;
268
260
  const [lastMatcher] = getMatchers(node).slice(-1);
@@ -368,7 +360,7 @@ var init_missing_playwright_await = __esm({
368
360
  }
369
361
  return {
370
362
  CallExpression(node) {
371
- const result = getCallType(node, awaitableMatchers);
363
+ const result = getCallType(context, node, awaitableMatchers);
372
364
  const isValid = result ? checkValidity(result.node) : false;
373
365
  if (result && !isValid) {
374
366
  context.report({
@@ -421,7 +413,7 @@ var init_no_conditional_in_test = __esm({
421
413
  create(context) {
422
414
  function checkConditional(node) {
423
415
  const call = findParent(node, "CallExpression");
424
- if (call && isTestCall(call)) {
416
+ if (call && isTestCall(context, call)) {
425
417
  context.report({ messageId: "conditionalInTest", node });
426
418
  }
427
419
  }
@@ -560,7 +552,7 @@ var init_no_focused_test = __esm({
560
552
  create(context) {
561
553
  return {
562
554
  CallExpression(node) {
563
- if ((isTestCall(node) || isDescribeCall(node)) && node.callee.type === "MemberExpression" && isPropertyAccessor(node.callee, "only")) {
555
+ if ((isTestCall(context, node) || isDescribeCall(node)) && node.callee.type === "MemberExpression" && isPropertyAccessor(node.callee, "only")) {
564
556
  const { callee } = node;
565
557
  context.report({
566
558
  messageId: "noFocusedTest",
@@ -908,8 +900,8 @@ function getExpectArguments(node) {
908
900
  const grandparent = node.parent.parent;
909
901
  return grandparent.type === "CallExpression" ? grandparent.arguments : [];
910
902
  }
911
- function parseExpectCall(node) {
912
- if (!isExpectCall(node)) {
903
+ function parseExpectCall(context, node) {
904
+ if (!isExpectCall(context, node)) {
913
905
  return;
914
906
  }
915
907
  const members = getMatchers(node);
@@ -954,7 +946,7 @@ var init_no_restricted_matchers = __esm({
954
946
  const restrictedChains = context.options?.[0] ?? {};
955
947
  return {
956
948
  CallExpression(node) {
957
- const expectCall = parseExpectCall(node);
949
+ const expectCall = parseExpectCall(context, node);
958
950
  if (!expectCall)
959
951
  return;
960
952
  Object.entries(restrictedChains).map(([restriction, message]) => {
@@ -1022,8 +1014,8 @@ var init_no_skipped_test = __esm({
1022
1014
  const options = context.options[0] || {};
1023
1015
  const allowConditional = !!options.allowConditional;
1024
1016
  const { callee } = node;
1025
- if ((isTestIdentifier(callee) || isDescribeCall(node)) && callee.type === "MemberExpression" && isPropertyAccessor(callee, "skip")) {
1026
- const isHook = isTestCall(node) || isDescribeCall(node);
1017
+ if ((isTestIdentifier(context, callee) || isDescribeCall(node)) && callee.type === "MemberExpression" && isPropertyAccessor(callee, "skip")) {
1018
+ const isHook = isTestCall(context, node) || isDescribeCall(node);
1027
1019
  if (!isHook && allowConditional && node.arguments.length) {
1028
1020
  return;
1029
1021
  }
@@ -1199,7 +1191,7 @@ var init_no_useless_not = __esm({
1199
1191
  create(context) {
1200
1192
  return {
1201
1193
  CallExpression(node) {
1202
- const expectCall = parseExpectCall(node);
1194
+ const expectCall = parseExpectCall(context, node);
1203
1195
  if (!expectCall)
1204
1196
  return;
1205
1197
  const notModifier = expectCall.modifiers.find(
@@ -1352,7 +1344,7 @@ var init_prefer_lowercase_title = __esm({
1352
1344
  let describeCount = 0;
1353
1345
  return {
1354
1346
  CallExpression(node) {
1355
- const method = isDescribeCall(node) ? "test.describe" : isTestCall(node) ? "test" : null;
1347
+ const method = isDescribeCall(node) ? "test.describe" : isTestCall(context, node) ? "test" : null;
1356
1348
  if (method === "test.describe") {
1357
1349
  describeCount++;
1358
1350
  if (ignoreTopLevelDescribe && describeCount === 1) {
@@ -1446,7 +1438,7 @@ var init_prefer_strict_equal = __esm({
1446
1438
  create(context) {
1447
1439
  return {
1448
1440
  CallExpression(node) {
1449
- const expectCall = parseExpectCall(node);
1441
+ const expectCall = parseExpectCall(context, node);
1450
1442
  if (expectCall?.matcherName === "toEqual") {
1451
1443
  context.report({
1452
1444
  messageId: "useToStrictEqual",
@@ -1529,7 +1521,7 @@ var init_prefer_to_be = __esm({
1529
1521
  create(context) {
1530
1522
  return {
1531
1523
  CallExpression(node) {
1532
- const expectCall = parseExpectCall(node);
1524
+ const expectCall = parseExpectCall(context, node);
1533
1525
  if (!expectCall)
1534
1526
  return;
1535
1527
  const notMatchers = ["toBeUndefined", "toBeDefined"];
@@ -1600,7 +1592,7 @@ var init_prefer_to_contain = __esm({
1600
1592
  create(context) {
1601
1593
  return {
1602
1594
  CallExpression(node) {
1603
- const expectCall = parseExpectCall(node);
1595
+ const expectCall = parseExpectCall(context, node);
1604
1596
  if (!expectCall || expectCall.args.length === 0)
1605
1597
  return;
1606
1598
  const { args, matcher, matcherName } = expectCall;
@@ -1679,7 +1671,7 @@ var init_prefer_to_have_count = __esm({
1679
1671
  create(context) {
1680
1672
  return {
1681
1673
  CallExpression(node) {
1682
- const expectCall = parseExpectCall(node);
1674
+ const expectCall = parseExpectCall(context, node);
1683
1675
  if (!expectCall || !matchers2.has(expectCall.matcherName)) {
1684
1676
  return;
1685
1677
  }
@@ -1744,7 +1736,7 @@ var init_prefer_to_have_length = __esm({
1744
1736
  create(context) {
1745
1737
  return {
1746
1738
  CallExpression(node) {
1747
- const expectCall = parseExpectCall(node);
1739
+ const expectCall = parseExpectCall(context, node);
1748
1740
  if (!expectCall || !lengthMatchers.has(expectCall.matcherName)) {
1749
1741
  return;
1750
1742
  }
@@ -1840,7 +1832,7 @@ var init_prefer_web_first_assertions = __esm({
1840
1832
  create(context) {
1841
1833
  return {
1842
1834
  CallExpression(node) {
1843
- const expectCall = parseExpectCall(node);
1835
+ const expectCall = parseExpectCall(context, node);
1844
1836
  if (!expectCall)
1845
1837
  return;
1846
1838
  const [arg] = node.arguments;
@@ -1948,7 +1940,7 @@ var init_require_soft_assertions = __esm({
1948
1940
  create(context) {
1949
1941
  return {
1950
1942
  CallExpression(node) {
1951
- if (getExpectType(node) === "standalone") {
1943
+ if (getExpectType(context, node) === "standalone") {
1952
1944
  context.report({
1953
1945
  fix: (fixer) => fixer.insertTextAfter(node.callee, ".soft"),
1954
1946
  messageId: "requireSoft",
@@ -1975,6 +1967,18 @@ var init_require_soft_assertions = __esm({
1975
1967
  }
1976
1968
  });
1977
1969
 
1970
+ // src/utils/misc.ts
1971
+ var getAmountData;
1972
+ var init_misc = __esm({
1973
+ "src/utils/misc.ts"() {
1974
+ "use strict";
1975
+ getAmountData = (amount) => ({
1976
+ amount: amount.toString(),
1977
+ s: amount === 1 ? "" : "s"
1978
+ });
1979
+ }
1980
+ });
1981
+
1978
1982
  // src/rules/require-top-level-describe.ts
1979
1983
  var require_top_level_describe_default;
1980
1984
  var init_require_top_level_describe = __esm({
@@ -2005,9 +2009,9 @@ var init_require_top_level_describe = __esm({
2005
2009
  }
2006
2010
  }
2007
2011
  } else if (!describeCount) {
2008
- if (isTestCall(node)) {
2012
+ if (isTestCall(context, node)) {
2009
2013
  context.report({ messageId: "unexpectedTest", node: node.callee });
2010
- } else if (isTestHook(node)) {
2014
+ } else if (isTestHook(context, node)) {
2011
2015
  context.report({ messageId: "unexpectedHook", node: node.callee });
2012
2016
  }
2013
2017
  }
@@ -2077,9 +2081,9 @@ var init_valid_expect = __esm({
2077
2081
  const maxArgs = Math.max(options.minArgs, options.maxArgs);
2078
2082
  return {
2079
2083
  CallExpression(node) {
2080
- if (!isExpectCall(node))
2084
+ if (!isExpectCall(context, node))
2081
2085
  return;
2082
- const expectCall = parseExpectCall(node);
2086
+ const expectCall = parseExpectCall(context, node);
2083
2087
  if (!expectCall) {
2084
2088
  context.report({ messageId: "matcherNotFound", node });
2085
2089
  } else {
@@ -2203,7 +2207,7 @@ var init_valid_title = __esm({
2203
2207
  return {
2204
2208
  CallExpression(node) {
2205
2209
  const isDescribe = isDescribeCall(node);
2206
- const isTest = isTestCall(node);
2210
+ const isTest = isTestCall(context, node);
2207
2211
  if (!isDescribe && !isTest) {
2208
2212
  return;
2209
2213
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eslint-plugin-playwright",
3
3
  "description": "ESLint plugin for Playwright testing.",
4
- "version": "0.22.2",
4
+ "version": "1.0.0",
5
5
  "repository": "https://github.com/playwright-community/eslint-plugin-playwright",
6
6
  "author": "Mark Skelton <mark@mskelton.dev>",
7
7
  "packageManager": "pnpm@8.12.0",