eslint-plugin-playwright 2.2.2 → 2.3.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 +1 -0
- package/dist/index.cjs +69 -53
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -142,6 +142,7 @@ CLI option\
|
|
|
142
142
|
| [no-slowed-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-slowed-test.md) | Disallow usage of the `.slow` annotation | | | 💡 |
|
|
143
143
|
| [no-standalone-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-standalone-expect.md) | Disallow using expect outside of `test` blocks | ✅ | | |
|
|
144
144
|
| [no-unsafe-references](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-unsafe-references.md) | Prevent unsafe variable references in `page.evaluate()` | ✅ | 🔧 | |
|
|
145
|
+
| [no-unused-locators](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-unused-locators.md) | Disallow usage of page locators that are not used | ✅ | | |
|
|
145
146
|
| [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 | ✅ | 🔧 | |
|
|
146
147
|
| [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 | ✅ | 🔧 | |
|
|
147
148
|
| [no-wait-for-navigation](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-navigation.md) | Disallow usage of `page.waitForNavigation()` | ✅ | | 💡 |
|
package/dist/index.cjs
CHANGED
|
@@ -21,28 +21,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
22
|
mod
|
|
23
23
|
));
|
|
24
|
-
var __accessCheck = (obj, member, msg) => {
|
|
25
|
-
if (!member.has(obj))
|
|
26
|
-
throw TypeError("Cannot " + msg);
|
|
27
|
-
};
|
|
28
|
-
var __privateGet = (obj, member, getter) => {
|
|
29
|
-
__accessCheck(obj, member, "read from private field");
|
|
30
|
-
return getter ? getter.call(obj) : member.get(obj);
|
|
31
|
-
};
|
|
32
|
-
var __privateAdd = (obj, member, value) => {
|
|
33
|
-
if (member.has(obj))
|
|
34
|
-
throw TypeError("Cannot add the same private member more than once");
|
|
35
|
-
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
36
|
-
};
|
|
37
|
-
var __privateSet = (obj, member, value, setter) => {
|
|
38
|
-
__accessCheck(obj, member, "write to private field");
|
|
39
|
-
setter ? setter.call(obj, value) : member.set(obj, value);
|
|
40
|
-
return value;
|
|
41
|
-
};
|
|
42
|
-
var __privateMethod = (obj, member, method) => {
|
|
43
|
-
__accessCheck(obj, member, "access private method");
|
|
44
|
-
return method;
|
|
45
|
-
};
|
|
46
24
|
|
|
47
25
|
// src/index.ts
|
|
48
26
|
var import_globals = __toESM(require("globals"), 1);
|
|
@@ -106,43 +84,38 @@ var VALID_CHAINS = /* @__PURE__ */ new Set([
|
|
|
106
84
|
]);
|
|
107
85
|
var joinChains = (a, b) => a && b ? [...a, ...b] : null;
|
|
108
86
|
var isSupportedAccessor = (node, value) => isIdentifier(node, value) || isStringNode(node, value);
|
|
109
|
-
var _nodes, _leaves, _buildChain, buildChain_fn;
|
|
110
87
|
var Chain = class {
|
|
88
|
+
#nodes = null;
|
|
89
|
+
#leaves = /* @__PURE__ */ new WeakSet();
|
|
111
90
|
constructor(node) {
|
|
112
|
-
|
|
113
|
-
__privateAdd(this, _nodes, null);
|
|
114
|
-
__privateAdd(this, _leaves, /* @__PURE__ */ new WeakSet());
|
|
115
|
-
__privateSet(this, _nodes, __privateMethod(this, _buildChain, buildChain_fn).call(this, node));
|
|
91
|
+
this.#nodes = this.#buildChain(node);
|
|
116
92
|
}
|
|
117
93
|
isLeaf(node) {
|
|
118
|
-
return
|
|
94
|
+
return this.#leaves.has(node);
|
|
119
95
|
}
|
|
120
96
|
get nodes() {
|
|
121
|
-
return
|
|
97
|
+
return this.#nodes;
|
|
122
98
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
99
|
+
#buildChain(node, insideCall = false) {
|
|
100
|
+
if (isSupportedAccessor(node)) {
|
|
101
|
+
if (insideCall) {
|
|
102
|
+
this.#leaves.add(node);
|
|
103
|
+
}
|
|
104
|
+
return [node];
|
|
105
|
+
}
|
|
106
|
+
switch (node.type) {
|
|
107
|
+
case "TaggedTemplateExpression":
|
|
108
|
+
return this.#buildChain(node.tag);
|
|
109
|
+
case "MemberExpression":
|
|
110
|
+
return joinChains(
|
|
111
|
+
this.#buildChain(node.object),
|
|
112
|
+
this.#buildChain(node.property, insideCall)
|
|
113
|
+
);
|
|
114
|
+
case "CallExpression":
|
|
115
|
+
return this.#buildChain(node.callee, true);
|
|
116
|
+
default:
|
|
117
|
+
return null;
|
|
131
118
|
}
|
|
132
|
-
return [node];
|
|
133
|
-
}
|
|
134
|
-
switch (node.type) {
|
|
135
|
-
case "TaggedTemplateExpression":
|
|
136
|
-
return __privateMethod(this, _buildChain, buildChain_fn).call(this, node.tag);
|
|
137
|
-
case "MemberExpression":
|
|
138
|
-
return joinChains(
|
|
139
|
-
__privateMethod(this, _buildChain, buildChain_fn).call(this, node.object),
|
|
140
|
-
__privateMethod(this, _buildChain, buildChain_fn).call(this, node.property, insideCall)
|
|
141
|
-
);
|
|
142
|
-
case "CallExpression":
|
|
143
|
-
return __privateMethod(this, _buildChain, buildChain_fn).call(this, node.callee, true);
|
|
144
|
-
default:
|
|
145
|
-
return null;
|
|
146
119
|
}
|
|
147
120
|
};
|
|
148
121
|
var resolvePossibleAliasedGlobal = (context, global) => {
|
|
@@ -982,7 +955,7 @@ var no_duplicate_hooks_default = createRule({
|
|
|
982
955
|
}
|
|
983
956
|
const currentLayer = hookContexts[hookContexts.length - 1];
|
|
984
957
|
const name = node.callee.type === "MemberExpression" ? getStringValue(node.callee.property) : "";
|
|
985
|
-
currentLayer[name]
|
|
958
|
+
currentLayer[name] ||= 0;
|
|
986
959
|
currentLayer[name] += 1;
|
|
987
960
|
if (currentLayer[name] > 1) {
|
|
988
961
|
context.report({
|
|
@@ -1856,6 +1829,35 @@ var no_unsafe_references_default = createRule({
|
|
|
1856
1829
|
}
|
|
1857
1830
|
});
|
|
1858
1831
|
|
|
1832
|
+
// src/rules/no-unused-locators.ts
|
|
1833
|
+
var LOCATOR_REGEX = /locator|getBy(Role|Text|Label|Placeholder|AltText|Title|TestId)/;
|
|
1834
|
+
var no_unused_locators_default = createRule({
|
|
1835
|
+
create(context) {
|
|
1836
|
+
return {
|
|
1837
|
+
CallExpression(node) {
|
|
1838
|
+
if (!isPageMethod(node, LOCATOR_REGEX)) {
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
if (node.parent.type === "ExpressionStatement" || node.parent.type === "AwaitExpression") {
|
|
1842
|
+
context.report({ messageId: "noUnusedLocator", node });
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
};
|
|
1846
|
+
},
|
|
1847
|
+
meta: {
|
|
1848
|
+
docs: {
|
|
1849
|
+
category: "Possible Errors",
|
|
1850
|
+
description: `Disallow usage of page locators that are not used`,
|
|
1851
|
+
recommended: true,
|
|
1852
|
+
url: "https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-unused-locators.md"
|
|
1853
|
+
},
|
|
1854
|
+
messages: {
|
|
1855
|
+
noUnusedLocator: "Unused locator"
|
|
1856
|
+
},
|
|
1857
|
+
type: "problem"
|
|
1858
|
+
}
|
|
1859
|
+
});
|
|
1860
|
+
|
|
1859
1861
|
// src/rules/no-useless-await.ts
|
|
1860
1862
|
var locatorMethods = /* @__PURE__ */ new Set([
|
|
1861
1863
|
"and",
|
|
@@ -3857,6 +3859,9 @@ var valid_test_tags_default = createRule({
|
|
|
3857
3859
|
);
|
|
3858
3860
|
}
|
|
3859
3861
|
}
|
|
3862
|
+
const extractTagsFromTitle = (title) => {
|
|
3863
|
+
return title.match(/@[\S]+/g) || [];
|
|
3864
|
+
};
|
|
3860
3865
|
const validateTag = (tag, node) => {
|
|
3861
3866
|
if (!tag.startsWith("@")) {
|
|
3862
3867
|
context.report({
|
|
@@ -3899,6 +3904,15 @@ var valid_test_tags_default = createRule({
|
|
|
3899
3904
|
const { type } = call;
|
|
3900
3905
|
if (type !== "test" && type !== "describe" && type !== "step")
|
|
3901
3906
|
return;
|
|
3907
|
+
if (node.arguments.length > 0) {
|
|
3908
|
+
const titleArg = node.arguments[0];
|
|
3909
|
+
if (titleArg && titleArg.type === "Literal" && typeof titleArg.value === "string") {
|
|
3910
|
+
const titleTags = extractTagsFromTitle(titleArg.value);
|
|
3911
|
+
for (const tag of titleTags) {
|
|
3912
|
+
validateTag(tag, node);
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3902
3916
|
if (node.arguments.length < 2)
|
|
3903
3917
|
return;
|
|
3904
3918
|
const optionsArg = node.arguments[1];
|
|
@@ -3938,7 +3952,7 @@ var valid_test_tags_default = createRule({
|
|
|
3938
3952
|
},
|
|
3939
3953
|
meta: {
|
|
3940
3954
|
docs: {
|
|
3941
|
-
description: "Enforce valid tag format in Playwright test blocks",
|
|
3955
|
+
description: "Enforce valid tag format in Playwright test blocks and titles",
|
|
3942
3956
|
recommended: true
|
|
3943
3957
|
},
|
|
3944
3958
|
messages: {
|
|
@@ -4231,6 +4245,7 @@ var index = {
|
|
|
4231
4245
|
"no-slowed-test": no_slowed_test_default,
|
|
4232
4246
|
"no-standalone-expect": no_standalone_expect_default,
|
|
4233
4247
|
"no-unsafe-references": no_unsafe_references_default,
|
|
4248
|
+
"no-unused-locators": no_unused_locators_default,
|
|
4234
4249
|
"no-useless-await": no_useless_await_default,
|
|
4235
4250
|
"no-useless-not": no_useless_not_default,
|
|
4236
4251
|
"no-wait-for-navigation": no_wait_for_navigation_default,
|
|
@@ -4278,6 +4293,7 @@ var sharedConfig = {
|
|
|
4278
4293
|
"playwright/no-skipped-test": "warn",
|
|
4279
4294
|
"playwright/no-standalone-expect": "error",
|
|
4280
4295
|
"playwright/no-unsafe-references": "error",
|
|
4296
|
+
"playwright/no-unused-locators": "error",
|
|
4281
4297
|
"playwright/no-useless-await": "warn",
|
|
4282
4298
|
"playwright/no-useless-not": "warn",
|
|
4283
4299
|
"playwright/no-wait-for-navigation": "error",
|
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": "2.
|
|
4
|
+
"version": "2.3.0",
|
|
5
5
|
"repository": "https://github.com/playwright-community/eslint-plugin-playwright",
|
|
6
6
|
"author": "Mark Skelton <mark@mskelton.dev>",
|
|
7
7
|
"contributors": [
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"engines": {
|
|
12
|
-
"node": ">=16.
|
|
12
|
+
"node": ">=16.9.0"
|
|
13
13
|
},
|
|
14
14
|
"type": "module",
|
|
15
15
|
"types": "./index.d.ts",
|
|
@@ -30,6 +30,6 @@
|
|
|
30
30
|
"eslint": ">=8.40.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"globals": "^
|
|
33
|
+
"globals": "^16.4.0"
|
|
34
34
|
}
|
|
35
35
|
}
|