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 +12 -37
- package/dist/index.js +57 -52
- package/dist/index.mjs +63 -59
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -91,45 +91,19 @@ export default [
|
|
|
91
91
|
}
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
### Aliased Playwright Globals
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
"
|
|
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
|
-
|
|
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" &&
|
|
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
|
-
|
|
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" &&
|
|
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,
|
|
133
|
-
return isExpectCall(node) ||
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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" &&
|
|
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
|
-
|
|
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" &&
|
|
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,
|
|
126
|
-
return isExpectCall(node) ||
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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",
|