eslint-plugin-playwright 1.2.0 → 1.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 +53 -49
- package/dist/index.d.mts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +326 -16
- package/dist/index.mjs +364 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -109,52 +109,56 @@ can configure this plugin to be aware of these additional names.
|
|
|
109
109
|
}
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
##
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
[suggestions](https://eslint.org/docs/latest/developer-guide/working-with-rules#providing-suggestions)
|
|
119
|
-
|
|
120
|
-
|
|
|
121
|
-
|
|
|
122
|
-
|
|
|
123
|
-
|
|
|
124
|
-
|
|
|
125
|
-
|
|
|
126
|
-
|
|
|
127
|
-
|
|
|
128
|
-
|
|
|
129
|
-
|
|
|
130
|
-
|
|
|
131
|
-
|
|
|
132
|
-
|
|
|
133
|
-
|
|
|
134
|
-
|
|
|
135
|
-
|
|
|
136
|
-
|
|
|
137
|
-
|
|
|
138
|
-
|
|
|
139
|
-
|
|
|
140
|
-
|
|
|
141
|
-
|
|
|
142
|
-
|
|
|
143
|
-
|
|
|
144
|
-
|
|
|
145
|
-
|
|
|
146
|
-
|
|
|
147
|
-
|
|
|
148
|
-
|
|
|
149
|
-
|
|
|
150
|
-
|
|
|
151
|
-
|
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
|
|
|
155
|
-
|
|
|
156
|
-
|
|
|
157
|
-
|
|
|
158
|
-
|
|
|
159
|
-
|
|
|
160
|
-
|
|
|
112
|
+
## Rules
|
|
113
|
+
|
|
114
|
+
✅ Set in the `recommended` configuration\
|
|
115
|
+
🔧 Automatically fixable by the [`--fix`](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix)
|
|
116
|
+
CLI option\
|
|
117
|
+
💡 Manually fixable by
|
|
118
|
+
[editor suggestions](https://eslint.org/docs/latest/developer-guide/working-with-rules#providing-suggestions)
|
|
119
|
+
|
|
120
|
+
| Rule | Description | ✅ | 🔧 | 💡 |
|
|
121
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | :-: | :-: | :-: |
|
|
122
|
+
| [expect-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/expect-expect.md) | Enforce assertion to be made in a test body | ✅ | | |
|
|
123
|
+
| [max-expects](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/max-expects.md) | Enforces a maximum number assertion calls in a test body | ✅ | | |
|
|
124
|
+
| [max-nested-describe](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/max-nested-describe.md) | Enforces a maximum depth to nested describe calls | ✅ | | |
|
|
125
|
+
| [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 | ✅ | 🔧 | |
|
|
126
|
+
| [no-commented-out-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-commented-out-test.md) | Disallow commented out tests | | | |
|
|
127
|
+
| [no-conditional-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-conditional-expect.md) | Disallow calling `expect` conditionally | ✅ | | |
|
|
128
|
+
| [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 | ✅ | | |
|
|
129
|
+
| [no-duplicate-hooks](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-duplicate-hooks.md) | Disallow duplicate setup and teardown hooks | | | |
|
|
130
|
+
| [no-element-handle](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-element-handle.md) | Disallow usage of element handles | ✅ | | 💡 |
|
|
131
|
+
| [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()` | ✅ | | |
|
|
132
|
+
| [no-focused-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-focused-test.md) | Disallow usage of `.only` annotation | ✅ | | 💡 |
|
|
133
|
+
| [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 | ✅ | | |
|
|
134
|
+
| [no-hooks](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-hooks.md) | Disallow setup and teardown hooks | | | |
|
|
135
|
+
| [no-nested-step](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-nested-step.md) | Disallow nested `test.step()` methods | ✅ | | |
|
|
136
|
+
| [no-networkidle](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-networkidle.md) | Disallow usage of the `networkidle` option | ✅ | | |
|
|
137
|
+
| [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 | | | |
|
|
138
|
+
| [no-page-pause](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-page-pause.md) | Disallow using `page.pause()` | ✅ | | |
|
|
139
|
+
| [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()` | ✅ | 🔧 | |
|
|
140
|
+
| [no-get-by-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-get-by-title.md) | Disallow using `getByTitle()` | | 🔧 | |
|
|
141
|
+
| [no-raw-locators](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-raw-locators.md) | Disallow using raw locators | | | |
|
|
142
|
+
| [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 | ✅ | 🔧 | |
|
|
143
|
+
| [no-restricted-matchers](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-restricted-matchers.md) | Disallow specific matchers & modifiers | | | |
|
|
144
|
+
| [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 | ✅ | | 💡 |
|
|
145
|
+
| [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 | ✅ | | |
|
|
146
|
+
| [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
|
+
| [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()` | ✅ | | 💡 |
|
|
148
|
+
| [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()` | ✅ | | 💡 |
|
|
149
|
+
| [prefer-comparison-matcher](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-comparison-matcher.md) | Suggest using the built-in comparison matchers | | 🔧 | |
|
|
150
|
+
| [prefer-equality-matcher](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-equality-matcher.md) | Suggest using the built-in equality matchers | | | 💡 |
|
|
151
|
+
| [prefer-hooks-in-order](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-hooks-in-order.md) | Prefer having hooks in a consistent order | | | |
|
|
152
|
+
| [prefer-hooks-on-top](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | | |
|
|
153
|
+
| [prefer-strict-equal](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | | 💡 |
|
|
154
|
+
| [prefer-lowercase-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names | | 🔧 | |
|
|
155
|
+
| [prefer-to-be](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-be.md) | Suggest using `toBe()` | | 🔧 | |
|
|
156
|
+
| [prefer-to-contain](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-contain.md) | Suggest using `toContain()` | | 🔧 | |
|
|
157
|
+
| [prefer-to-have-count](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-count.md) | Suggest using `toHaveCount()` | | 🔧 | |
|
|
158
|
+
| [prefer-to-have-length](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` | | 🔧 | |
|
|
159
|
+
| [prefer-web-first-assertions](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-web-first-assertions.md) | Suggest using web first assertions | ✅ | 🔧 | |
|
|
160
|
+
| [require-hook](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/require-hook.md) | Require setup and teardown code to be within a hook | | | |
|
|
161
|
+
| [require-top-level-describe](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/require-top-level-describe.md) | Require test cases and hooks to be inside a `test.describe` block | | | |
|
|
162
|
+
| [require-soft-assertions](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/require-soft-assertions.md) | Require assertions to use `expect.soft()` | | 🔧 | |
|
|
163
|
+
| [valid-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-expect.md) | Enforce valid `expect()` usage | ✅ | | |
|
|
164
|
+
| [valid-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-title.md) | Enforce valid titles | ✅ | 🔧 | |
|
package/dist/index.d.mts
CHANGED
|
@@ -23,6 +23,7 @@ declare const _default: {
|
|
|
23
23
|
'no-focused-test': eslint.Rule.RuleModule;
|
|
24
24
|
'no-force-option': eslint.Rule.RuleModule;
|
|
25
25
|
'no-get-by-title': eslint.Rule.RuleModule;
|
|
26
|
+
'no-hooks': eslint.Rule.RuleModule;
|
|
26
27
|
'no-nested-step': eslint.Rule.RuleModule;
|
|
27
28
|
'no-networkidle': eslint.Rule.RuleModule;
|
|
28
29
|
'no-nth-methods': eslint.Rule.RuleModule;
|
|
@@ -36,6 +37,8 @@ declare const _default: {
|
|
|
36
37
|
'no-useless-not': eslint.Rule.RuleModule;
|
|
37
38
|
'no-wait-for-selector': eslint.Rule.RuleModule;
|
|
38
39
|
'no-wait-for-timeout': eslint.Rule.RuleModule;
|
|
40
|
+
'prefer-comparison-matcher': eslint.Rule.RuleModule;
|
|
41
|
+
'prefer-equality-matcher': eslint.Rule.RuleModule;
|
|
39
42
|
'prefer-hooks-in-order': eslint.Rule.RuleModule;
|
|
40
43
|
'prefer-hooks-on-top': eslint.Rule.RuleModule;
|
|
41
44
|
'prefer-lowercase-title': eslint.Rule.RuleModule;
|
|
@@ -45,6 +48,7 @@ declare const _default: {
|
|
|
45
48
|
'prefer-to-have-count': eslint.Rule.RuleModule;
|
|
46
49
|
'prefer-to-have-length': eslint.Rule.RuleModule;
|
|
47
50
|
'prefer-web-first-assertions': eslint.Rule.RuleModule;
|
|
51
|
+
'require-hook': eslint.Rule.RuleModule;
|
|
48
52
|
'require-soft-assertions': eslint.Rule.RuleModule;
|
|
49
53
|
'require-top-level-describe': eslint.Rule.RuleModule;
|
|
50
54
|
'valid-expect': eslint.Rule.RuleModule;
|
|
@@ -81,6 +85,7 @@ declare const _default: {
|
|
|
81
85
|
'no-focused-test': eslint.Rule.RuleModule;
|
|
82
86
|
'no-force-option': eslint.Rule.RuleModule;
|
|
83
87
|
'no-get-by-title': eslint.Rule.RuleModule;
|
|
88
|
+
'no-hooks': eslint.Rule.RuleModule;
|
|
84
89
|
'no-nested-step': eslint.Rule.RuleModule;
|
|
85
90
|
'no-networkidle': eslint.Rule.RuleModule;
|
|
86
91
|
'no-nth-methods': eslint.Rule.RuleModule;
|
|
@@ -94,6 +99,8 @@ declare const _default: {
|
|
|
94
99
|
'no-useless-not': eslint.Rule.RuleModule;
|
|
95
100
|
'no-wait-for-selector': eslint.Rule.RuleModule;
|
|
96
101
|
'no-wait-for-timeout': eslint.Rule.RuleModule;
|
|
102
|
+
'prefer-comparison-matcher': eslint.Rule.RuleModule;
|
|
103
|
+
'prefer-equality-matcher': eslint.Rule.RuleModule;
|
|
97
104
|
'prefer-hooks-in-order': eslint.Rule.RuleModule;
|
|
98
105
|
'prefer-hooks-on-top': eslint.Rule.RuleModule;
|
|
99
106
|
'prefer-lowercase-title': eslint.Rule.RuleModule;
|
|
@@ -103,6 +110,7 @@ declare const _default: {
|
|
|
103
110
|
'prefer-to-have-count': eslint.Rule.RuleModule;
|
|
104
111
|
'prefer-to-have-length': eslint.Rule.RuleModule;
|
|
105
112
|
'prefer-web-first-assertions': eslint.Rule.RuleModule;
|
|
113
|
+
'require-hook': eslint.Rule.RuleModule;
|
|
106
114
|
'require-soft-assertions': eslint.Rule.RuleModule;
|
|
107
115
|
'require-top-level-describe': eslint.Rule.RuleModule;
|
|
108
116
|
'valid-expect': eslint.Rule.RuleModule;
|
|
@@ -235,6 +243,7 @@ declare const _default: {
|
|
|
235
243
|
'no-focused-test': eslint.Rule.RuleModule;
|
|
236
244
|
'no-force-option': eslint.Rule.RuleModule;
|
|
237
245
|
'no-get-by-title': eslint.Rule.RuleModule;
|
|
246
|
+
'no-hooks': eslint.Rule.RuleModule;
|
|
238
247
|
'no-nested-step': eslint.Rule.RuleModule;
|
|
239
248
|
'no-networkidle': eslint.Rule.RuleModule;
|
|
240
249
|
'no-nth-methods': eslint.Rule.RuleModule;
|
|
@@ -248,6 +257,8 @@ declare const _default: {
|
|
|
248
257
|
'no-useless-not': eslint.Rule.RuleModule;
|
|
249
258
|
'no-wait-for-selector': eslint.Rule.RuleModule;
|
|
250
259
|
'no-wait-for-timeout': eslint.Rule.RuleModule;
|
|
260
|
+
'prefer-comparison-matcher': eslint.Rule.RuleModule;
|
|
261
|
+
'prefer-equality-matcher': eslint.Rule.RuleModule;
|
|
251
262
|
'prefer-hooks-in-order': eslint.Rule.RuleModule;
|
|
252
263
|
'prefer-hooks-on-top': eslint.Rule.RuleModule;
|
|
253
264
|
'prefer-lowercase-title': eslint.Rule.RuleModule;
|
|
@@ -257,6 +268,7 @@ declare const _default: {
|
|
|
257
268
|
'prefer-to-have-count': eslint.Rule.RuleModule;
|
|
258
269
|
'prefer-to-have-length': eslint.Rule.RuleModule;
|
|
259
270
|
'prefer-web-first-assertions': eslint.Rule.RuleModule;
|
|
271
|
+
'require-hook': eslint.Rule.RuleModule;
|
|
260
272
|
'require-soft-assertions': eslint.Rule.RuleModule;
|
|
261
273
|
'require-top-level-describe': eslint.Rule.RuleModule;
|
|
262
274
|
'valid-expect': eslint.Rule.RuleModule;
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ declare const _default: {
|
|
|
23
23
|
'no-focused-test': eslint.Rule.RuleModule;
|
|
24
24
|
'no-force-option': eslint.Rule.RuleModule;
|
|
25
25
|
'no-get-by-title': eslint.Rule.RuleModule;
|
|
26
|
+
'no-hooks': eslint.Rule.RuleModule;
|
|
26
27
|
'no-nested-step': eslint.Rule.RuleModule;
|
|
27
28
|
'no-networkidle': eslint.Rule.RuleModule;
|
|
28
29
|
'no-nth-methods': eslint.Rule.RuleModule;
|
|
@@ -36,6 +37,8 @@ declare const _default: {
|
|
|
36
37
|
'no-useless-not': eslint.Rule.RuleModule;
|
|
37
38
|
'no-wait-for-selector': eslint.Rule.RuleModule;
|
|
38
39
|
'no-wait-for-timeout': eslint.Rule.RuleModule;
|
|
40
|
+
'prefer-comparison-matcher': eslint.Rule.RuleModule;
|
|
41
|
+
'prefer-equality-matcher': eslint.Rule.RuleModule;
|
|
39
42
|
'prefer-hooks-in-order': eslint.Rule.RuleModule;
|
|
40
43
|
'prefer-hooks-on-top': eslint.Rule.RuleModule;
|
|
41
44
|
'prefer-lowercase-title': eslint.Rule.RuleModule;
|
|
@@ -45,6 +48,7 @@ declare const _default: {
|
|
|
45
48
|
'prefer-to-have-count': eslint.Rule.RuleModule;
|
|
46
49
|
'prefer-to-have-length': eslint.Rule.RuleModule;
|
|
47
50
|
'prefer-web-first-assertions': eslint.Rule.RuleModule;
|
|
51
|
+
'require-hook': eslint.Rule.RuleModule;
|
|
48
52
|
'require-soft-assertions': eslint.Rule.RuleModule;
|
|
49
53
|
'require-top-level-describe': eslint.Rule.RuleModule;
|
|
50
54
|
'valid-expect': eslint.Rule.RuleModule;
|
|
@@ -81,6 +85,7 @@ declare const _default: {
|
|
|
81
85
|
'no-focused-test': eslint.Rule.RuleModule;
|
|
82
86
|
'no-force-option': eslint.Rule.RuleModule;
|
|
83
87
|
'no-get-by-title': eslint.Rule.RuleModule;
|
|
88
|
+
'no-hooks': eslint.Rule.RuleModule;
|
|
84
89
|
'no-nested-step': eslint.Rule.RuleModule;
|
|
85
90
|
'no-networkidle': eslint.Rule.RuleModule;
|
|
86
91
|
'no-nth-methods': eslint.Rule.RuleModule;
|
|
@@ -94,6 +99,8 @@ declare const _default: {
|
|
|
94
99
|
'no-useless-not': eslint.Rule.RuleModule;
|
|
95
100
|
'no-wait-for-selector': eslint.Rule.RuleModule;
|
|
96
101
|
'no-wait-for-timeout': eslint.Rule.RuleModule;
|
|
102
|
+
'prefer-comparison-matcher': eslint.Rule.RuleModule;
|
|
103
|
+
'prefer-equality-matcher': eslint.Rule.RuleModule;
|
|
97
104
|
'prefer-hooks-in-order': eslint.Rule.RuleModule;
|
|
98
105
|
'prefer-hooks-on-top': eslint.Rule.RuleModule;
|
|
99
106
|
'prefer-lowercase-title': eslint.Rule.RuleModule;
|
|
@@ -103,6 +110,7 @@ declare const _default: {
|
|
|
103
110
|
'prefer-to-have-count': eslint.Rule.RuleModule;
|
|
104
111
|
'prefer-to-have-length': eslint.Rule.RuleModule;
|
|
105
112
|
'prefer-web-first-assertions': eslint.Rule.RuleModule;
|
|
113
|
+
'require-hook': eslint.Rule.RuleModule;
|
|
106
114
|
'require-soft-assertions': eslint.Rule.RuleModule;
|
|
107
115
|
'require-top-level-describe': eslint.Rule.RuleModule;
|
|
108
116
|
'valid-expect': eslint.Rule.RuleModule;
|
|
@@ -235,6 +243,7 @@ declare const _default: {
|
|
|
235
243
|
'no-focused-test': eslint.Rule.RuleModule;
|
|
236
244
|
'no-force-option': eslint.Rule.RuleModule;
|
|
237
245
|
'no-get-by-title': eslint.Rule.RuleModule;
|
|
246
|
+
'no-hooks': eslint.Rule.RuleModule;
|
|
238
247
|
'no-nested-step': eslint.Rule.RuleModule;
|
|
239
248
|
'no-networkidle': eslint.Rule.RuleModule;
|
|
240
249
|
'no-nth-methods': eslint.Rule.RuleModule;
|
|
@@ -248,6 +257,8 @@ declare const _default: {
|
|
|
248
257
|
'no-useless-not': eslint.Rule.RuleModule;
|
|
249
258
|
'no-wait-for-selector': eslint.Rule.RuleModule;
|
|
250
259
|
'no-wait-for-timeout': eslint.Rule.RuleModule;
|
|
260
|
+
'prefer-comparison-matcher': eslint.Rule.RuleModule;
|
|
261
|
+
'prefer-equality-matcher': eslint.Rule.RuleModule;
|
|
251
262
|
'prefer-hooks-in-order': eslint.Rule.RuleModule;
|
|
252
263
|
'prefer-hooks-on-top': eslint.Rule.RuleModule;
|
|
253
264
|
'prefer-lowercase-title': eslint.Rule.RuleModule;
|
|
@@ -257,6 +268,7 @@ declare const _default: {
|
|
|
257
268
|
'prefer-to-have-count': eslint.Rule.RuleModule;
|
|
258
269
|
'prefer-to-have-length': eslint.Rule.RuleModule;
|
|
259
270
|
'prefer-web-first-assertions': eslint.Rule.RuleModule;
|
|
271
|
+
'require-hook': eslint.Rule.RuleModule;
|
|
260
272
|
'require-soft-assertions': eslint.Rule.RuleModule;
|
|
261
273
|
'require-top-level-describe': eslint.Rule.RuleModule;
|
|
262
274
|
'valid-expect': eslint.Rule.RuleModule;
|
package/dist/index.js
CHANGED
|
@@ -95,6 +95,28 @@ var testHooks = /* @__PURE__ */ new Set(["afterAll", "afterEach", "beforeAll", "
|
|
|
95
95
|
function isTestHook(context, node) {
|
|
96
96
|
return node.callee.type === "MemberExpression" && isTestIdentifier(context, node.callee.object) && testHooks.has(getStringValue(node.callee.property));
|
|
97
97
|
}
|
|
98
|
+
function parseFnCall(context, node) {
|
|
99
|
+
if (isTestCall(context, node)) {
|
|
100
|
+
return {
|
|
101
|
+
fn: node.arguments[1],
|
|
102
|
+
name: getStringValue(node.callee),
|
|
103
|
+
type: "test"
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (node.callee.type === "MemberExpression" && isTestIdentifier(context, node.callee.object) && testHooks.has(getStringValue(node.callee.property))) {
|
|
107
|
+
return {
|
|
108
|
+
fn: node.arguments[0],
|
|
109
|
+
name: getStringValue(node.callee.property),
|
|
110
|
+
type: "hook"
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (isDescribeCall(node)) {
|
|
114
|
+
return {
|
|
115
|
+
name: getStringValue(node.callee),
|
|
116
|
+
type: "describe"
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
98
120
|
var expectSubCommands = /* @__PURE__ */ new Set(["soft", "poll"]);
|
|
99
121
|
function getExpectType(context, node) {
|
|
100
122
|
const aliases = context.settings.playwright?.globalAliases?.expect ?? [];
|
|
@@ -130,6 +152,7 @@ function isPageMethod(node, name) {
|
|
|
130
152
|
function isFunction(node) {
|
|
131
153
|
return node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression";
|
|
132
154
|
}
|
|
155
|
+
var equalityMatchers = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
|
|
133
156
|
|
|
134
157
|
// src/rules/expect-expect.ts
|
|
135
158
|
function isAssertionCall(context, node, assertFunctionNames) {
|
|
@@ -865,6 +888,52 @@ var no_get_by_title_default = {
|
|
|
865
888
|
}
|
|
866
889
|
};
|
|
867
890
|
|
|
891
|
+
// src/rules/no-hooks.ts
|
|
892
|
+
var no_hooks_default = {
|
|
893
|
+
create(context) {
|
|
894
|
+
const options = {
|
|
895
|
+
allow: [],
|
|
896
|
+
...context.options?.[0] ?? {}
|
|
897
|
+
};
|
|
898
|
+
return {
|
|
899
|
+
CallExpression(node) {
|
|
900
|
+
const call = parseFnCall(context, node);
|
|
901
|
+
if (call?.type === "hook" && !options.allow.includes(call.name)) {
|
|
902
|
+
context.report({
|
|
903
|
+
data: { hookName: call.name },
|
|
904
|
+
messageId: "unexpectedHook",
|
|
905
|
+
node
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
},
|
|
911
|
+
meta: {
|
|
912
|
+
docs: {
|
|
913
|
+
category: "Best Practices",
|
|
914
|
+
description: "Disallow setup and teardown hooks",
|
|
915
|
+
recommended: false,
|
|
916
|
+
url: "https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-hooks.md"
|
|
917
|
+
},
|
|
918
|
+
messages: {
|
|
919
|
+
unexpectedHook: "Unexpected '{{ hookName }}' hook"
|
|
920
|
+
},
|
|
921
|
+
schema: [
|
|
922
|
+
{
|
|
923
|
+
additionalProperties: false,
|
|
924
|
+
properties: {
|
|
925
|
+
allow: {
|
|
926
|
+
contains: ["beforeAll", "beforeEach", "afterAll", "afterEach"],
|
|
927
|
+
type: "array"
|
|
928
|
+
}
|
|
929
|
+
},
|
|
930
|
+
type: "object"
|
|
931
|
+
}
|
|
932
|
+
],
|
|
933
|
+
type: "suggestion"
|
|
934
|
+
}
|
|
935
|
+
};
|
|
936
|
+
|
|
868
937
|
// src/rules/no-nested-step.ts
|
|
869
938
|
function isStepCall(node) {
|
|
870
939
|
const inner = node.type === "CallExpression" ? node.callee : node;
|
|
@@ -1237,7 +1306,7 @@ var getBlockType = (statement) => {
|
|
|
1237
1306
|
const func = getParent(statement);
|
|
1238
1307
|
if (!func) {
|
|
1239
1308
|
throw new Error(
|
|
1240
|
-
`Unexpected BlockStatement. No parent defined. - please file a github issue at https://github.com/
|
|
1309
|
+
`Unexpected BlockStatement. No parent defined. - please file a github issue at https://github.com/playwright-community/eslint-plugin-playwright`
|
|
1241
1310
|
);
|
|
1242
1311
|
}
|
|
1243
1312
|
if (func.type === "FunctionDeclaration") {
|
|
@@ -1245,7 +1314,7 @@ var getBlockType = (statement) => {
|
|
|
1245
1314
|
}
|
|
1246
1315
|
if (isFunction(func) && func.parent) {
|
|
1247
1316
|
const expr = func.parent;
|
|
1248
|
-
if (expr.type === "VariableDeclarator") {
|
|
1317
|
+
if (expr.type === "VariableDeclarator" || expr.type === "MethodDefinition") {
|
|
1249
1318
|
return "function";
|
|
1250
1319
|
}
|
|
1251
1320
|
if (expr.type === "CallExpression" && isDescribeCall(expr)) {
|
|
@@ -1311,7 +1380,7 @@ var no_standalone_expect_default = {
|
|
|
1311
1380
|
category: "Best Practices",
|
|
1312
1381
|
description: "Disallow using `expect` outside of `test` blocks",
|
|
1313
1382
|
recommended: false,
|
|
1314
|
-
url: "https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/
|
|
1383
|
+
url: "https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-standalone-expect.md"
|
|
1315
1384
|
},
|
|
1316
1385
|
fixable: "code",
|
|
1317
1386
|
messages: {
|
|
@@ -1643,6 +1712,167 @@ var no_wait_for_timeout_default = {
|
|
|
1643
1712
|
}
|
|
1644
1713
|
};
|
|
1645
1714
|
|
|
1715
|
+
// src/rules/prefer-comparison-matcher.ts
|
|
1716
|
+
var isString = (node) => {
|
|
1717
|
+
return isStringLiteral(node) || node.type === "TemplateLiteral";
|
|
1718
|
+
};
|
|
1719
|
+
var isComparingToString = (expression) => {
|
|
1720
|
+
return isString(expression.left) || isString(expression.right);
|
|
1721
|
+
};
|
|
1722
|
+
var invertedOperators = {
|
|
1723
|
+
"<": ">=",
|
|
1724
|
+
"<=": ">",
|
|
1725
|
+
">": "<=",
|
|
1726
|
+
">=": "<"
|
|
1727
|
+
};
|
|
1728
|
+
var operatorMatcher = {
|
|
1729
|
+
"<": "toBeLessThan",
|
|
1730
|
+
"<=": "toBeLessThanOrEqual",
|
|
1731
|
+
">": "toBeGreaterThan",
|
|
1732
|
+
">=": "toBeGreaterThanOrEqual"
|
|
1733
|
+
};
|
|
1734
|
+
var determineMatcher = (operator, negated) => {
|
|
1735
|
+
const op = negated ? invertedOperators[operator] : operator;
|
|
1736
|
+
return operatorMatcher[op] ?? null;
|
|
1737
|
+
};
|
|
1738
|
+
var prefer_comparison_matcher_default = {
|
|
1739
|
+
create(context) {
|
|
1740
|
+
return {
|
|
1741
|
+
CallExpression(node) {
|
|
1742
|
+
const expectCall = parseExpectCall(context, node);
|
|
1743
|
+
if (!expectCall || expectCall.args.length === 0)
|
|
1744
|
+
return;
|
|
1745
|
+
const { args, matcher } = expectCall;
|
|
1746
|
+
const [comparison] = node.arguments;
|
|
1747
|
+
const expectCallEnd = node.range[1];
|
|
1748
|
+
const [matcherArg] = args;
|
|
1749
|
+
if (comparison?.type !== "BinaryExpression" || isComparingToString(comparison) || !equalityMatchers.has(getStringValue(matcher)) || !isBooleanLiteral(matcherArg)) {
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
const hasNot = expectCall.modifiers.some(
|
|
1753
|
+
(node2) => getStringValue(node2) === "not"
|
|
1754
|
+
);
|
|
1755
|
+
const preferredMatcher = determineMatcher(
|
|
1756
|
+
comparison.operator,
|
|
1757
|
+
getRawValue(matcherArg) === hasNot.toString()
|
|
1758
|
+
);
|
|
1759
|
+
if (!preferredMatcher) {
|
|
1760
|
+
return;
|
|
1761
|
+
}
|
|
1762
|
+
context.report({
|
|
1763
|
+
data: { preferredMatcher },
|
|
1764
|
+
fix(fixer) {
|
|
1765
|
+
const [modifier] = expectCall.modifiers;
|
|
1766
|
+
const modifierText = modifier && getStringValue(modifier) !== "not" ? `.${getStringValue(modifier)}` : "";
|
|
1767
|
+
return [
|
|
1768
|
+
// Replace the comparison argument with the left-hand side of the comparison
|
|
1769
|
+
fixer.replaceText(
|
|
1770
|
+
comparison,
|
|
1771
|
+
context.sourceCode.getText(comparison.left)
|
|
1772
|
+
),
|
|
1773
|
+
// Replace the current matcher & modifier with the preferred matcher
|
|
1774
|
+
fixer.replaceTextRange(
|
|
1775
|
+
[expectCallEnd, getParent(matcher).range[1]],
|
|
1776
|
+
`${modifierText}.${preferredMatcher}`
|
|
1777
|
+
),
|
|
1778
|
+
// Replace the matcher argument with the right-hand side of the comparison
|
|
1779
|
+
fixer.replaceText(
|
|
1780
|
+
matcherArg,
|
|
1781
|
+
context.sourceCode.getText(comparison.right)
|
|
1782
|
+
)
|
|
1783
|
+
];
|
|
1784
|
+
},
|
|
1785
|
+
messageId: "useToBeComparison",
|
|
1786
|
+
node: matcher
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1790
|
+
},
|
|
1791
|
+
meta: {
|
|
1792
|
+
docs: {
|
|
1793
|
+
category: "Best Practices",
|
|
1794
|
+
description: "Suggest using the built-in comparison matchers",
|
|
1795
|
+
recommended: false
|
|
1796
|
+
},
|
|
1797
|
+
fixable: "code",
|
|
1798
|
+
messages: {
|
|
1799
|
+
useToBeComparison: "Prefer using `{{ preferredMatcher }}` instead"
|
|
1800
|
+
},
|
|
1801
|
+
type: "suggestion"
|
|
1802
|
+
}
|
|
1803
|
+
};
|
|
1804
|
+
|
|
1805
|
+
// src/rules/prefer-equality-matcher.ts
|
|
1806
|
+
var prefer_equality_matcher_default = {
|
|
1807
|
+
create(context) {
|
|
1808
|
+
return {
|
|
1809
|
+
CallExpression(node) {
|
|
1810
|
+
const expectCall = parseExpectCall(context, node);
|
|
1811
|
+
if (!expectCall || expectCall.args.length === 0)
|
|
1812
|
+
return;
|
|
1813
|
+
const { args, matcher } = expectCall;
|
|
1814
|
+
const [comparison] = node.arguments;
|
|
1815
|
+
const expectCallEnd = node.range[1];
|
|
1816
|
+
const [matcherArg] = args;
|
|
1817
|
+
if (comparison?.type !== "BinaryExpression" || comparison.operator !== "===" && comparison.operator !== "!==" || !equalityMatchers.has(getStringValue(matcher)) || !isBooleanLiteral(matcherArg)) {
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
const matcherValue = getRawValue(matcherArg) === "true";
|
|
1821
|
+
const [modifier] = expectCall.modifiers;
|
|
1822
|
+
const hasNot = expectCall.modifiers.some(
|
|
1823
|
+
(node2) => getStringValue(node2) === "not"
|
|
1824
|
+
);
|
|
1825
|
+
const addNotModifier = (comparison.operator === "!==" ? !matcherValue : matcherValue) === hasNot;
|
|
1826
|
+
context.report({
|
|
1827
|
+
messageId: "useEqualityMatcher",
|
|
1828
|
+
node: matcher,
|
|
1829
|
+
suggest: [...equalityMatchers.keys()].map((equalityMatcher) => ({
|
|
1830
|
+
data: { matcher: equalityMatcher },
|
|
1831
|
+
fix(fixer) {
|
|
1832
|
+
let modifierText = modifier && getStringValue(modifier) !== "not" ? `.${getStringValue(modifier)}` : "";
|
|
1833
|
+
if (addNotModifier) {
|
|
1834
|
+
modifierText += `.not`;
|
|
1835
|
+
}
|
|
1836
|
+
return [
|
|
1837
|
+
// replace the comparison argument with the left-hand side of the comparison
|
|
1838
|
+
fixer.replaceText(
|
|
1839
|
+
comparison,
|
|
1840
|
+
context.sourceCode.getText(comparison.left)
|
|
1841
|
+
),
|
|
1842
|
+
// replace the current matcher & modifier with the preferred matcher
|
|
1843
|
+
fixer.replaceTextRange(
|
|
1844
|
+
[expectCallEnd, getParent(matcher).range[1]],
|
|
1845
|
+
`${modifierText}.${equalityMatcher}`
|
|
1846
|
+
),
|
|
1847
|
+
// replace the matcher argument with the right-hand side of the comparison
|
|
1848
|
+
fixer.replaceText(
|
|
1849
|
+
matcherArg,
|
|
1850
|
+
context.sourceCode.getText(comparison.right)
|
|
1851
|
+
)
|
|
1852
|
+
];
|
|
1853
|
+
},
|
|
1854
|
+
messageId: "suggestEqualityMatcher"
|
|
1855
|
+
}))
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
};
|
|
1859
|
+
},
|
|
1860
|
+
meta: {
|
|
1861
|
+
docs: {
|
|
1862
|
+
category: "Best Practices",
|
|
1863
|
+
description: "Suggest using the built-in equality matchers",
|
|
1864
|
+
recommended: false,
|
|
1865
|
+
url: "https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-equality-matcher.md"
|
|
1866
|
+
},
|
|
1867
|
+
hasSuggestions: true,
|
|
1868
|
+
messages: {
|
|
1869
|
+
suggestEqualityMatcher: "Use `{{ matcher }}`",
|
|
1870
|
+
useEqualityMatcher: "Prefer using one of the equality matchers instead"
|
|
1871
|
+
},
|
|
1872
|
+
type: "suggestion"
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
1875
|
+
|
|
1646
1876
|
// src/rules/prefer-hooks-in-order.ts
|
|
1647
1877
|
var HooksOrder = ["beforeAll", "beforeEach", "afterEach", "afterAll"];
|
|
1648
1878
|
var prefer_hooks_in_order_default = {
|
|
@@ -1921,9 +2151,8 @@ var prefer_to_be_default = {
|
|
|
1921
2151
|
notModifier
|
|
1922
2152
|
);
|
|
1923
2153
|
}
|
|
1924
|
-
const argumentMatchers = ["toBe", "toEqual", "toStrictEqual"];
|
|
1925
2154
|
const firstArg = expectCall.args[0];
|
|
1926
|
-
if (!
|
|
2155
|
+
if (!equalityMatchers.has(expectCall.matcherName) || !firstArg) {
|
|
1927
2156
|
return;
|
|
1928
2157
|
}
|
|
1929
2158
|
if (firstArg.type === "Literal" && firstArg.value === null) {
|
|
@@ -1963,7 +2192,6 @@ var prefer_to_be_default = {
|
|
|
1963
2192
|
};
|
|
1964
2193
|
|
|
1965
2194
|
// src/rules/prefer-to-contain.ts
|
|
1966
|
-
var matchers = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
|
|
1967
2195
|
var isFixableIncludesCallExpression = (node) => node.type === "CallExpression" && node.callee.type === "MemberExpression" && isPropertyAccessor(node.callee, "includes") && node.arguments.length === 1 && node.arguments[0].type !== "SpreadElement";
|
|
1968
2196
|
var prefer_to_contain_default = {
|
|
1969
2197
|
create(context) {
|
|
@@ -1975,7 +2203,7 @@ var prefer_to_contain_default = {
|
|
|
1975
2203
|
const { args, matcher, matcherName } = expectCall;
|
|
1976
2204
|
const [includesCall] = node.arguments;
|
|
1977
2205
|
const [matcherArg] = args;
|
|
1978
|
-
if (!includesCall || matcherArg.type === "SpreadElement" || !
|
|
2206
|
+
if (!includesCall || matcherArg.type === "SpreadElement" || !equalityMatchers.has(matcherName) || !isBooleanLiteral(matcherArg) || !isFixableIncludesCallExpression(includesCall)) {
|
|
1979
2207
|
return;
|
|
1980
2208
|
}
|
|
1981
2209
|
const notModifier = expectCall.modifiers.find(
|
|
@@ -2033,13 +2261,12 @@ var prefer_to_contain_default = {
|
|
|
2033
2261
|
};
|
|
2034
2262
|
|
|
2035
2263
|
// src/rules/prefer-to-have-count.ts
|
|
2036
|
-
var matchers2 = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
|
|
2037
2264
|
var prefer_to_have_count_default = {
|
|
2038
2265
|
create(context) {
|
|
2039
2266
|
return {
|
|
2040
2267
|
CallExpression(node) {
|
|
2041
2268
|
const expectCall = parseExpectCall(context, node);
|
|
2042
|
-
if (!expectCall || !
|
|
2269
|
+
if (!expectCall || !equalityMatchers.has(expectCall.matcherName)) {
|
|
2043
2270
|
return;
|
|
2044
2271
|
}
|
|
2045
2272
|
const [argument] = node.arguments;
|
|
@@ -2089,13 +2316,12 @@ var prefer_to_have_count_default = {
|
|
|
2089
2316
|
};
|
|
2090
2317
|
|
|
2091
2318
|
// src/rules/prefer-to-have-length.ts
|
|
2092
|
-
var lengthMatchers = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
|
|
2093
2319
|
var prefer_to_have_length_default = {
|
|
2094
2320
|
create(context) {
|
|
2095
2321
|
return {
|
|
2096
2322
|
CallExpression(node) {
|
|
2097
2323
|
const expectCall = parseExpectCall(context, node);
|
|
2098
|
-
if (!expectCall || !
|
|
2324
|
+
if (!expectCall || !equalityMatchers.has(expectCall.matcherName)) {
|
|
2099
2325
|
return;
|
|
2100
2326
|
}
|
|
2101
2327
|
const [argument] = node.arguments;
|
|
@@ -2290,6 +2516,86 @@ var prefer_web_first_assertions_default = {
|
|
|
2290
2516
|
}
|
|
2291
2517
|
};
|
|
2292
2518
|
|
|
2519
|
+
// src/rules/require-hook.ts
|
|
2520
|
+
var isNullOrUndefined = (node) => {
|
|
2521
|
+
return node.type === "Literal" && node.value === null || isIdentifier(node, "undefined");
|
|
2522
|
+
};
|
|
2523
|
+
var shouldBeInHook = (context, node, allowedFunctionCalls = []) => {
|
|
2524
|
+
switch (node.type) {
|
|
2525
|
+
case "ExpressionStatement":
|
|
2526
|
+
return shouldBeInHook(context, node.expression, allowedFunctionCalls);
|
|
2527
|
+
case "CallExpression":
|
|
2528
|
+
return !(parseFnCall(context, node) || allowedFunctionCalls.includes(getStringValue(node.callee)));
|
|
2529
|
+
case "VariableDeclaration": {
|
|
2530
|
+
if (node.kind === "const") {
|
|
2531
|
+
return false;
|
|
2532
|
+
}
|
|
2533
|
+
return node.declarations.some(
|
|
2534
|
+
({ init }) => init != null && !isNullOrUndefined(init)
|
|
2535
|
+
);
|
|
2536
|
+
}
|
|
2537
|
+
default:
|
|
2538
|
+
return false;
|
|
2539
|
+
}
|
|
2540
|
+
};
|
|
2541
|
+
var require_hook_default = {
|
|
2542
|
+
create(context) {
|
|
2543
|
+
const options = {
|
|
2544
|
+
allowedFunctionCalls: [],
|
|
2545
|
+
...context.options?.[0] ?? {}
|
|
2546
|
+
};
|
|
2547
|
+
const checkBlockBody = (body) => {
|
|
2548
|
+
for (const statement of body) {
|
|
2549
|
+
if (shouldBeInHook(context, statement, options.allowedFunctionCalls)) {
|
|
2550
|
+
context.report({
|
|
2551
|
+
messageId: "useHook",
|
|
2552
|
+
node: statement
|
|
2553
|
+
});
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
};
|
|
2557
|
+
return {
|
|
2558
|
+
CallExpression(node) {
|
|
2559
|
+
if (!isDescribeCall(node) || node.arguments.length < 2) {
|
|
2560
|
+
return;
|
|
2561
|
+
}
|
|
2562
|
+
const [, testFn] = node.arguments;
|
|
2563
|
+
if (!isFunction(testFn) || testFn.body.type !== "BlockStatement") {
|
|
2564
|
+
return;
|
|
2565
|
+
}
|
|
2566
|
+
checkBlockBody(testFn.body.body);
|
|
2567
|
+
},
|
|
2568
|
+
Program(program) {
|
|
2569
|
+
checkBlockBody(program.body);
|
|
2570
|
+
}
|
|
2571
|
+
};
|
|
2572
|
+
},
|
|
2573
|
+
meta: {
|
|
2574
|
+
docs: {
|
|
2575
|
+
category: "Best Practices",
|
|
2576
|
+
description: "Require setup and teardown code to be within a hook",
|
|
2577
|
+
recommended: false,
|
|
2578
|
+
url: "https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/require-hook.md"
|
|
2579
|
+
},
|
|
2580
|
+
messages: {
|
|
2581
|
+
useHook: "This should be done within a hook"
|
|
2582
|
+
},
|
|
2583
|
+
schema: [
|
|
2584
|
+
{
|
|
2585
|
+
additionalProperties: false,
|
|
2586
|
+
properties: {
|
|
2587
|
+
allowedFunctionCalls: {
|
|
2588
|
+
items: { type: "string" },
|
|
2589
|
+
type: "array"
|
|
2590
|
+
}
|
|
2591
|
+
},
|
|
2592
|
+
type: "object"
|
|
2593
|
+
}
|
|
2594
|
+
],
|
|
2595
|
+
type: "suggestion"
|
|
2596
|
+
}
|
|
2597
|
+
};
|
|
2598
|
+
|
|
2293
2599
|
// src/rules/require-soft-assertions.ts
|
|
2294
2600
|
var require_soft_assertions_default = {
|
|
2295
2601
|
create(context) {
|
|
@@ -2486,17 +2792,17 @@ var compileMatcherPattern = (matcherMaybeWithMessage) => {
|
|
|
2486
2792
|
const [matcher, message] = Array.isArray(matcherMaybeWithMessage) ? matcherMaybeWithMessage : [matcherMaybeWithMessage];
|
|
2487
2793
|
return [new RegExp(matcher, "u"), message];
|
|
2488
2794
|
};
|
|
2489
|
-
var compileMatcherPatterns = (
|
|
2490
|
-
if (typeof
|
|
2491
|
-
const compiledMatcher = compileMatcherPattern(
|
|
2795
|
+
var compileMatcherPatterns = (matchers) => {
|
|
2796
|
+
if (typeof matchers === "string" || Array.isArray(matchers)) {
|
|
2797
|
+
const compiledMatcher = compileMatcherPattern(matchers);
|
|
2492
2798
|
return {
|
|
2493
2799
|
describe: compiledMatcher,
|
|
2494
2800
|
test: compiledMatcher
|
|
2495
2801
|
};
|
|
2496
2802
|
}
|
|
2497
2803
|
return {
|
|
2498
|
-
describe:
|
|
2499
|
-
test:
|
|
2804
|
+
describe: matchers.describe ? compileMatcherPattern(matchers.describe) : null,
|
|
2805
|
+
test: matchers.test ? compileMatcherPattern(matchers.test) : null
|
|
2500
2806
|
};
|
|
2501
2807
|
};
|
|
2502
2808
|
var MatcherAndMessageSchema = {
|
|
@@ -2700,6 +3006,7 @@ var index = {
|
|
|
2700
3006
|
"no-focused-test": no_focused_test_default,
|
|
2701
3007
|
"no-force-option": no_force_option_default,
|
|
2702
3008
|
"no-get-by-title": no_get_by_title_default,
|
|
3009
|
+
"no-hooks": no_hooks_default,
|
|
2703
3010
|
"no-nested-step": no_nested_step_default,
|
|
2704
3011
|
"no-networkidle": no_networkidle_default,
|
|
2705
3012
|
"no-nth-methods": no_nth_methods_default,
|
|
@@ -2713,6 +3020,8 @@ var index = {
|
|
|
2713
3020
|
"no-useless-not": no_useless_not_default,
|
|
2714
3021
|
"no-wait-for-selector": no_wait_for_selector_default,
|
|
2715
3022
|
"no-wait-for-timeout": no_wait_for_timeout_default,
|
|
3023
|
+
"prefer-comparison-matcher": prefer_comparison_matcher_default,
|
|
3024
|
+
"prefer-equality-matcher": prefer_equality_matcher_default,
|
|
2716
3025
|
"prefer-hooks-in-order": prefer_hooks_in_order_default,
|
|
2717
3026
|
"prefer-hooks-on-top": prefer_hooks_on_top_default,
|
|
2718
3027
|
"prefer-lowercase-title": prefer_lowercase_title_default,
|
|
@@ -2722,6 +3031,7 @@ var index = {
|
|
|
2722
3031
|
"prefer-to-have-count": prefer_to_have_count_default,
|
|
2723
3032
|
"prefer-to-have-length": prefer_to_have_length_default,
|
|
2724
3033
|
"prefer-web-first-assertions": prefer_web_first_assertions_default,
|
|
3034
|
+
"require-hook": require_hook_default,
|
|
2725
3035
|
"require-soft-assertions": require_soft_assertions_default,
|
|
2726
3036
|
"require-top-level-describe": require_top_level_describe_default,
|
|
2727
3037
|
"valid-expect": valid_expect_default,
|
package/dist/index.mjs
CHANGED
|
@@ -66,6 +66,28 @@ function isTestCall(context, node, modifiers) {
|
|
|
66
66
|
function isTestHook(context, node) {
|
|
67
67
|
return node.callee.type === "MemberExpression" && isTestIdentifier(context, node.callee.object) && testHooks.has(getStringValue(node.callee.property));
|
|
68
68
|
}
|
|
69
|
+
function parseFnCall(context, node) {
|
|
70
|
+
if (isTestCall(context, node)) {
|
|
71
|
+
return {
|
|
72
|
+
fn: node.arguments[1],
|
|
73
|
+
name: getStringValue(node.callee),
|
|
74
|
+
type: "test"
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (node.callee.type === "MemberExpression" && isTestIdentifier(context, node.callee.object) && testHooks.has(getStringValue(node.callee.property))) {
|
|
78
|
+
return {
|
|
79
|
+
fn: node.arguments[0],
|
|
80
|
+
name: getStringValue(node.callee.property),
|
|
81
|
+
type: "hook"
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (isDescribeCall(node)) {
|
|
85
|
+
return {
|
|
86
|
+
name: getStringValue(node.callee),
|
|
87
|
+
type: "describe"
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
69
91
|
function getExpectType(context, node) {
|
|
70
92
|
const aliases = context.settings.playwright?.globalAliases?.expect ?? [];
|
|
71
93
|
const expectNames = ["expect", ...aliases];
|
|
@@ -100,7 +122,7 @@ function isPageMethod(node, name) {
|
|
|
100
122
|
function isFunction(node) {
|
|
101
123
|
return node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression";
|
|
102
124
|
}
|
|
103
|
-
var isTemplateLiteral, describeProperties, testHooks, expectSubCommands;
|
|
125
|
+
var isTemplateLiteral, describeProperties, testHooks, expectSubCommands, equalityMatchers;
|
|
104
126
|
var init_ast = __esm({
|
|
105
127
|
"src/utils/ast.ts"() {
|
|
106
128
|
"use strict";
|
|
@@ -115,6 +137,7 @@ var init_ast = __esm({
|
|
|
115
137
|
]);
|
|
116
138
|
testHooks = /* @__PURE__ */ new Set(["afterAll", "afterEach", "beforeAll", "beforeEach"]);
|
|
117
139
|
expectSubCommands = /* @__PURE__ */ new Set(["soft", "poll"]);
|
|
140
|
+
equalityMatchers = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
|
|
118
141
|
}
|
|
119
142
|
});
|
|
120
143
|
|
|
@@ -943,6 +966,59 @@ var init_no_get_by_title = __esm({
|
|
|
943
966
|
}
|
|
944
967
|
});
|
|
945
968
|
|
|
969
|
+
// src/rules/no-hooks.ts
|
|
970
|
+
var no_hooks_default;
|
|
971
|
+
var init_no_hooks = __esm({
|
|
972
|
+
"src/rules/no-hooks.ts"() {
|
|
973
|
+
"use strict";
|
|
974
|
+
init_ast();
|
|
975
|
+
no_hooks_default = {
|
|
976
|
+
create(context) {
|
|
977
|
+
const options = {
|
|
978
|
+
allow: [],
|
|
979
|
+
...context.options?.[0] ?? {}
|
|
980
|
+
};
|
|
981
|
+
return {
|
|
982
|
+
CallExpression(node) {
|
|
983
|
+
const call = parseFnCall(context, node);
|
|
984
|
+
if (call?.type === "hook" && !options.allow.includes(call.name)) {
|
|
985
|
+
context.report({
|
|
986
|
+
data: { hookName: call.name },
|
|
987
|
+
messageId: "unexpectedHook",
|
|
988
|
+
node
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
},
|
|
994
|
+
meta: {
|
|
995
|
+
docs: {
|
|
996
|
+
category: "Best Practices",
|
|
997
|
+
description: "Disallow setup and teardown hooks",
|
|
998
|
+
recommended: false,
|
|
999
|
+
url: "https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-hooks.md"
|
|
1000
|
+
},
|
|
1001
|
+
messages: {
|
|
1002
|
+
unexpectedHook: "Unexpected '{{ hookName }}' hook"
|
|
1003
|
+
},
|
|
1004
|
+
schema: [
|
|
1005
|
+
{
|
|
1006
|
+
additionalProperties: false,
|
|
1007
|
+
properties: {
|
|
1008
|
+
allow: {
|
|
1009
|
+
contains: ["beforeAll", "beforeEach", "afterAll", "afterEach"],
|
|
1010
|
+
type: "array"
|
|
1011
|
+
}
|
|
1012
|
+
},
|
|
1013
|
+
type: "object"
|
|
1014
|
+
}
|
|
1015
|
+
],
|
|
1016
|
+
type: "suggestion"
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1021
|
+
|
|
946
1022
|
// src/rules/no-nested-step.ts
|
|
947
1023
|
function isStepCall(node) {
|
|
948
1024
|
const inner = node.type === "CallExpression" ? node.callee : node;
|
|
@@ -1377,7 +1453,7 @@ var init_no_standalone_expect = __esm({
|
|
|
1377
1453
|
const func = getParent(statement);
|
|
1378
1454
|
if (!func) {
|
|
1379
1455
|
throw new Error(
|
|
1380
|
-
`Unexpected BlockStatement. No parent defined. - please file a github issue at https://github.com/
|
|
1456
|
+
`Unexpected BlockStatement. No parent defined. - please file a github issue at https://github.com/playwright-community/eslint-plugin-playwright`
|
|
1381
1457
|
);
|
|
1382
1458
|
}
|
|
1383
1459
|
if (func.type === "FunctionDeclaration") {
|
|
@@ -1385,7 +1461,7 @@ var init_no_standalone_expect = __esm({
|
|
|
1385
1461
|
}
|
|
1386
1462
|
if (isFunction(func) && func.parent) {
|
|
1387
1463
|
const expr = func.parent;
|
|
1388
|
-
if (expr.type === "VariableDeclarator") {
|
|
1464
|
+
if (expr.type === "VariableDeclarator" || expr.type === "MethodDefinition") {
|
|
1389
1465
|
return "function";
|
|
1390
1466
|
}
|
|
1391
1467
|
if (expr.type === "CallExpression" && isDescribeCall(expr)) {
|
|
@@ -1451,7 +1527,7 @@ var init_no_standalone_expect = __esm({
|
|
|
1451
1527
|
category: "Best Practices",
|
|
1452
1528
|
description: "Disallow using `expect` outside of `test` blocks",
|
|
1453
1529
|
recommended: false,
|
|
1454
|
-
url: "https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/
|
|
1530
|
+
url: "https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-standalone-expect.md"
|
|
1455
1531
|
},
|
|
1456
1532
|
fixable: "code",
|
|
1457
1533
|
messages: {
|
|
@@ -1835,6 +1911,183 @@ var init_no_wait_for_timeout = __esm({
|
|
|
1835
1911
|
}
|
|
1836
1912
|
});
|
|
1837
1913
|
|
|
1914
|
+
// src/rules/prefer-comparison-matcher.ts
|
|
1915
|
+
var isString, isComparingToString, invertedOperators, operatorMatcher, determineMatcher, prefer_comparison_matcher_default;
|
|
1916
|
+
var init_prefer_comparison_matcher = __esm({
|
|
1917
|
+
"src/rules/prefer-comparison-matcher.ts"() {
|
|
1918
|
+
"use strict";
|
|
1919
|
+
init_ast();
|
|
1920
|
+
init_parseExpectCall();
|
|
1921
|
+
isString = (node) => {
|
|
1922
|
+
return isStringLiteral(node) || node.type === "TemplateLiteral";
|
|
1923
|
+
};
|
|
1924
|
+
isComparingToString = (expression) => {
|
|
1925
|
+
return isString(expression.left) || isString(expression.right);
|
|
1926
|
+
};
|
|
1927
|
+
invertedOperators = {
|
|
1928
|
+
"<": ">=",
|
|
1929
|
+
"<=": ">",
|
|
1930
|
+
">": "<=",
|
|
1931
|
+
">=": "<"
|
|
1932
|
+
};
|
|
1933
|
+
operatorMatcher = {
|
|
1934
|
+
"<": "toBeLessThan",
|
|
1935
|
+
"<=": "toBeLessThanOrEqual",
|
|
1936
|
+
">": "toBeGreaterThan",
|
|
1937
|
+
">=": "toBeGreaterThanOrEqual"
|
|
1938
|
+
};
|
|
1939
|
+
determineMatcher = (operator, negated) => {
|
|
1940
|
+
const op = negated ? invertedOperators[operator] : operator;
|
|
1941
|
+
return operatorMatcher[op] ?? null;
|
|
1942
|
+
};
|
|
1943
|
+
prefer_comparison_matcher_default = {
|
|
1944
|
+
create(context) {
|
|
1945
|
+
return {
|
|
1946
|
+
CallExpression(node) {
|
|
1947
|
+
const expectCall = parseExpectCall(context, node);
|
|
1948
|
+
if (!expectCall || expectCall.args.length === 0)
|
|
1949
|
+
return;
|
|
1950
|
+
const { args, matcher } = expectCall;
|
|
1951
|
+
const [comparison] = node.arguments;
|
|
1952
|
+
const expectCallEnd = node.range[1];
|
|
1953
|
+
const [matcherArg] = args;
|
|
1954
|
+
if (comparison?.type !== "BinaryExpression" || isComparingToString(comparison) || !equalityMatchers.has(getStringValue(matcher)) || !isBooleanLiteral(matcherArg)) {
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1957
|
+
const hasNot = expectCall.modifiers.some(
|
|
1958
|
+
(node2) => getStringValue(node2) === "not"
|
|
1959
|
+
);
|
|
1960
|
+
const preferredMatcher = determineMatcher(
|
|
1961
|
+
comparison.operator,
|
|
1962
|
+
getRawValue(matcherArg) === hasNot.toString()
|
|
1963
|
+
);
|
|
1964
|
+
if (!preferredMatcher) {
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
context.report({
|
|
1968
|
+
data: { preferredMatcher },
|
|
1969
|
+
fix(fixer) {
|
|
1970
|
+
const [modifier] = expectCall.modifiers;
|
|
1971
|
+
const modifierText = modifier && getStringValue(modifier) !== "not" ? `.${getStringValue(modifier)}` : "";
|
|
1972
|
+
return [
|
|
1973
|
+
// Replace the comparison argument with the left-hand side of the comparison
|
|
1974
|
+
fixer.replaceText(
|
|
1975
|
+
comparison,
|
|
1976
|
+
context.sourceCode.getText(comparison.left)
|
|
1977
|
+
),
|
|
1978
|
+
// Replace the current matcher & modifier with the preferred matcher
|
|
1979
|
+
fixer.replaceTextRange(
|
|
1980
|
+
[expectCallEnd, getParent(matcher).range[1]],
|
|
1981
|
+
`${modifierText}.${preferredMatcher}`
|
|
1982
|
+
),
|
|
1983
|
+
// Replace the matcher argument with the right-hand side of the comparison
|
|
1984
|
+
fixer.replaceText(
|
|
1985
|
+
matcherArg,
|
|
1986
|
+
context.sourceCode.getText(comparison.right)
|
|
1987
|
+
)
|
|
1988
|
+
];
|
|
1989
|
+
},
|
|
1990
|
+
messageId: "useToBeComparison",
|
|
1991
|
+
node: matcher
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
},
|
|
1996
|
+
meta: {
|
|
1997
|
+
docs: {
|
|
1998
|
+
category: "Best Practices",
|
|
1999
|
+
description: "Suggest using the built-in comparison matchers",
|
|
2000
|
+
recommended: false
|
|
2001
|
+
},
|
|
2002
|
+
fixable: "code",
|
|
2003
|
+
messages: {
|
|
2004
|
+
useToBeComparison: "Prefer using `{{ preferredMatcher }}` instead"
|
|
2005
|
+
},
|
|
2006
|
+
type: "suggestion"
|
|
2007
|
+
}
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
});
|
|
2011
|
+
|
|
2012
|
+
// src/rules/prefer-equality-matcher.ts
|
|
2013
|
+
var prefer_equality_matcher_default;
|
|
2014
|
+
var init_prefer_equality_matcher = __esm({
|
|
2015
|
+
"src/rules/prefer-equality-matcher.ts"() {
|
|
2016
|
+
"use strict";
|
|
2017
|
+
init_ast();
|
|
2018
|
+
init_parseExpectCall();
|
|
2019
|
+
prefer_equality_matcher_default = {
|
|
2020
|
+
create(context) {
|
|
2021
|
+
return {
|
|
2022
|
+
CallExpression(node) {
|
|
2023
|
+
const expectCall = parseExpectCall(context, node);
|
|
2024
|
+
if (!expectCall || expectCall.args.length === 0)
|
|
2025
|
+
return;
|
|
2026
|
+
const { args, matcher } = expectCall;
|
|
2027
|
+
const [comparison] = node.arguments;
|
|
2028
|
+
const expectCallEnd = node.range[1];
|
|
2029
|
+
const [matcherArg] = args;
|
|
2030
|
+
if (comparison?.type !== "BinaryExpression" || comparison.operator !== "===" && comparison.operator !== "!==" || !equalityMatchers.has(getStringValue(matcher)) || !isBooleanLiteral(matcherArg)) {
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
const matcherValue = getRawValue(matcherArg) === "true";
|
|
2034
|
+
const [modifier] = expectCall.modifiers;
|
|
2035
|
+
const hasNot = expectCall.modifiers.some(
|
|
2036
|
+
(node2) => getStringValue(node2) === "not"
|
|
2037
|
+
);
|
|
2038
|
+
const addNotModifier = (comparison.operator === "!==" ? !matcherValue : matcherValue) === hasNot;
|
|
2039
|
+
context.report({
|
|
2040
|
+
messageId: "useEqualityMatcher",
|
|
2041
|
+
node: matcher,
|
|
2042
|
+
suggest: [...equalityMatchers.keys()].map((equalityMatcher) => ({
|
|
2043
|
+
data: { matcher: equalityMatcher },
|
|
2044
|
+
fix(fixer) {
|
|
2045
|
+
let modifierText = modifier && getStringValue(modifier) !== "not" ? `.${getStringValue(modifier)}` : "";
|
|
2046
|
+
if (addNotModifier) {
|
|
2047
|
+
modifierText += `.not`;
|
|
2048
|
+
}
|
|
2049
|
+
return [
|
|
2050
|
+
// replace the comparison argument with the left-hand side of the comparison
|
|
2051
|
+
fixer.replaceText(
|
|
2052
|
+
comparison,
|
|
2053
|
+
context.sourceCode.getText(comparison.left)
|
|
2054
|
+
),
|
|
2055
|
+
// replace the current matcher & modifier with the preferred matcher
|
|
2056
|
+
fixer.replaceTextRange(
|
|
2057
|
+
[expectCallEnd, getParent(matcher).range[1]],
|
|
2058
|
+
`${modifierText}.${equalityMatcher}`
|
|
2059
|
+
),
|
|
2060
|
+
// replace the matcher argument with the right-hand side of the comparison
|
|
2061
|
+
fixer.replaceText(
|
|
2062
|
+
matcherArg,
|
|
2063
|
+
context.sourceCode.getText(comparison.right)
|
|
2064
|
+
)
|
|
2065
|
+
];
|
|
2066
|
+
},
|
|
2067
|
+
messageId: "suggestEqualityMatcher"
|
|
2068
|
+
}))
|
|
2069
|
+
});
|
|
2070
|
+
}
|
|
2071
|
+
};
|
|
2072
|
+
},
|
|
2073
|
+
meta: {
|
|
2074
|
+
docs: {
|
|
2075
|
+
category: "Best Practices",
|
|
2076
|
+
description: "Suggest using the built-in equality matchers",
|
|
2077
|
+
recommended: false,
|
|
2078
|
+
url: "https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-equality-matcher.md"
|
|
2079
|
+
},
|
|
2080
|
+
hasSuggestions: true,
|
|
2081
|
+
messages: {
|
|
2082
|
+
suggestEqualityMatcher: "Use `{{ matcher }}`",
|
|
2083
|
+
useEqualityMatcher: "Prefer using one of the equality matchers instead"
|
|
2084
|
+
},
|
|
2085
|
+
type: "suggestion"
|
|
2086
|
+
}
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
});
|
|
2090
|
+
|
|
1838
2091
|
// src/rules/prefer-hooks-in-order.ts
|
|
1839
2092
|
var HooksOrder, prefer_hooks_in_order_default;
|
|
1840
2093
|
var init_prefer_hooks_in_order = __esm({
|
|
@@ -2149,9 +2402,8 @@ var init_prefer_to_be = __esm({
|
|
|
2149
2402
|
notModifier
|
|
2150
2403
|
);
|
|
2151
2404
|
}
|
|
2152
|
-
const argumentMatchers = ["toBe", "toEqual", "toStrictEqual"];
|
|
2153
2405
|
const firstArg = expectCall.args[0];
|
|
2154
|
-
if (!
|
|
2406
|
+
if (!equalityMatchers.has(expectCall.matcherName) || !firstArg) {
|
|
2155
2407
|
return;
|
|
2156
2408
|
}
|
|
2157
2409
|
if (firstArg.type === "Literal" && firstArg.value === null) {
|
|
@@ -2193,13 +2445,12 @@ var init_prefer_to_be = __esm({
|
|
|
2193
2445
|
});
|
|
2194
2446
|
|
|
2195
2447
|
// src/rules/prefer-to-contain.ts
|
|
2196
|
-
var
|
|
2448
|
+
var isFixableIncludesCallExpression, prefer_to_contain_default;
|
|
2197
2449
|
var init_prefer_to_contain = __esm({
|
|
2198
2450
|
"src/rules/prefer-to-contain.ts"() {
|
|
2199
2451
|
"use strict";
|
|
2200
2452
|
init_ast();
|
|
2201
2453
|
init_parseExpectCall();
|
|
2202
|
-
matchers = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
|
|
2203
2454
|
isFixableIncludesCallExpression = (node) => node.type === "CallExpression" && node.callee.type === "MemberExpression" && isPropertyAccessor(node.callee, "includes") && node.arguments.length === 1 && node.arguments[0].type !== "SpreadElement";
|
|
2204
2455
|
prefer_to_contain_default = {
|
|
2205
2456
|
create(context) {
|
|
@@ -2211,7 +2462,7 @@ var init_prefer_to_contain = __esm({
|
|
|
2211
2462
|
const { args, matcher, matcherName } = expectCall;
|
|
2212
2463
|
const [includesCall] = node.arguments;
|
|
2213
2464
|
const [matcherArg] = args;
|
|
2214
|
-
if (!includesCall || matcherArg.type === "SpreadElement" || !
|
|
2465
|
+
if (!includesCall || matcherArg.type === "SpreadElement" || !equalityMatchers.has(matcherName) || !isBooleanLiteral(matcherArg) || !isFixableIncludesCallExpression(includesCall)) {
|
|
2215
2466
|
return;
|
|
2216
2467
|
}
|
|
2217
2468
|
const notModifier = expectCall.modifiers.find(
|
|
@@ -2271,20 +2522,19 @@ var init_prefer_to_contain = __esm({
|
|
|
2271
2522
|
});
|
|
2272
2523
|
|
|
2273
2524
|
// src/rules/prefer-to-have-count.ts
|
|
2274
|
-
var
|
|
2525
|
+
var prefer_to_have_count_default;
|
|
2275
2526
|
var init_prefer_to_have_count = __esm({
|
|
2276
2527
|
"src/rules/prefer-to-have-count.ts"() {
|
|
2277
2528
|
"use strict";
|
|
2278
2529
|
init_ast();
|
|
2279
2530
|
init_fixer();
|
|
2280
2531
|
init_parseExpectCall();
|
|
2281
|
-
matchers2 = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
|
|
2282
2532
|
prefer_to_have_count_default = {
|
|
2283
2533
|
create(context) {
|
|
2284
2534
|
return {
|
|
2285
2535
|
CallExpression(node) {
|
|
2286
2536
|
const expectCall = parseExpectCall(context, node);
|
|
2287
|
-
if (!expectCall || !
|
|
2537
|
+
if (!expectCall || !equalityMatchers.has(expectCall.matcherName)) {
|
|
2288
2538
|
return;
|
|
2289
2539
|
}
|
|
2290
2540
|
const [argument] = node.arguments;
|
|
@@ -2336,20 +2586,19 @@ var init_prefer_to_have_count = __esm({
|
|
|
2336
2586
|
});
|
|
2337
2587
|
|
|
2338
2588
|
// src/rules/prefer-to-have-length.ts
|
|
2339
|
-
var
|
|
2589
|
+
var prefer_to_have_length_default;
|
|
2340
2590
|
var init_prefer_to_have_length = __esm({
|
|
2341
2591
|
"src/rules/prefer-to-have-length.ts"() {
|
|
2342
2592
|
"use strict";
|
|
2343
2593
|
init_ast();
|
|
2344
2594
|
init_fixer();
|
|
2345
2595
|
init_parseExpectCall();
|
|
2346
|
-
lengthMatchers = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
|
|
2347
2596
|
prefer_to_have_length_default = {
|
|
2348
2597
|
create(context) {
|
|
2349
2598
|
return {
|
|
2350
2599
|
CallExpression(node) {
|
|
2351
2600
|
const expectCall = parseExpectCall(context, node);
|
|
2352
|
-
if (!expectCall || !
|
|
2601
|
+
if (!expectCall || !equalityMatchers.has(expectCall.matcherName)) {
|
|
2353
2602
|
return;
|
|
2354
2603
|
}
|
|
2355
2604
|
const [argument] = node.arguments;
|
|
@@ -2554,6 +2803,93 @@ var init_prefer_web_first_assertions = __esm({
|
|
|
2554
2803
|
}
|
|
2555
2804
|
});
|
|
2556
2805
|
|
|
2806
|
+
// src/rules/require-hook.ts
|
|
2807
|
+
var isNullOrUndefined, shouldBeInHook, require_hook_default;
|
|
2808
|
+
var init_require_hook = __esm({
|
|
2809
|
+
"src/rules/require-hook.ts"() {
|
|
2810
|
+
"use strict";
|
|
2811
|
+
init_ast();
|
|
2812
|
+
isNullOrUndefined = (node) => {
|
|
2813
|
+
return node.type === "Literal" && node.value === null || isIdentifier(node, "undefined");
|
|
2814
|
+
};
|
|
2815
|
+
shouldBeInHook = (context, node, allowedFunctionCalls = []) => {
|
|
2816
|
+
switch (node.type) {
|
|
2817
|
+
case "ExpressionStatement":
|
|
2818
|
+
return shouldBeInHook(context, node.expression, allowedFunctionCalls);
|
|
2819
|
+
case "CallExpression":
|
|
2820
|
+
return !(parseFnCall(context, node) || allowedFunctionCalls.includes(getStringValue(node.callee)));
|
|
2821
|
+
case "VariableDeclaration": {
|
|
2822
|
+
if (node.kind === "const") {
|
|
2823
|
+
return false;
|
|
2824
|
+
}
|
|
2825
|
+
return node.declarations.some(
|
|
2826
|
+
({ init }) => init != null && !isNullOrUndefined(init)
|
|
2827
|
+
);
|
|
2828
|
+
}
|
|
2829
|
+
default:
|
|
2830
|
+
return false;
|
|
2831
|
+
}
|
|
2832
|
+
};
|
|
2833
|
+
require_hook_default = {
|
|
2834
|
+
create(context) {
|
|
2835
|
+
const options = {
|
|
2836
|
+
allowedFunctionCalls: [],
|
|
2837
|
+
...context.options?.[0] ?? {}
|
|
2838
|
+
};
|
|
2839
|
+
const checkBlockBody = (body) => {
|
|
2840
|
+
for (const statement of body) {
|
|
2841
|
+
if (shouldBeInHook(context, statement, options.allowedFunctionCalls)) {
|
|
2842
|
+
context.report({
|
|
2843
|
+
messageId: "useHook",
|
|
2844
|
+
node: statement
|
|
2845
|
+
});
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
};
|
|
2849
|
+
return {
|
|
2850
|
+
CallExpression(node) {
|
|
2851
|
+
if (!isDescribeCall(node) || node.arguments.length < 2) {
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
const [, testFn] = node.arguments;
|
|
2855
|
+
if (!isFunction(testFn) || testFn.body.type !== "BlockStatement") {
|
|
2856
|
+
return;
|
|
2857
|
+
}
|
|
2858
|
+
checkBlockBody(testFn.body.body);
|
|
2859
|
+
},
|
|
2860
|
+
Program(program) {
|
|
2861
|
+
checkBlockBody(program.body);
|
|
2862
|
+
}
|
|
2863
|
+
};
|
|
2864
|
+
},
|
|
2865
|
+
meta: {
|
|
2866
|
+
docs: {
|
|
2867
|
+
category: "Best Practices",
|
|
2868
|
+
description: "Require setup and teardown code to be within a hook",
|
|
2869
|
+
recommended: false,
|
|
2870
|
+
url: "https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/require-hook.md"
|
|
2871
|
+
},
|
|
2872
|
+
messages: {
|
|
2873
|
+
useHook: "This should be done within a hook"
|
|
2874
|
+
},
|
|
2875
|
+
schema: [
|
|
2876
|
+
{
|
|
2877
|
+
additionalProperties: false,
|
|
2878
|
+
properties: {
|
|
2879
|
+
allowedFunctionCalls: {
|
|
2880
|
+
items: { type: "string" },
|
|
2881
|
+
type: "array"
|
|
2882
|
+
}
|
|
2883
|
+
},
|
|
2884
|
+
type: "object"
|
|
2885
|
+
}
|
|
2886
|
+
],
|
|
2887
|
+
type: "suggestion"
|
|
2888
|
+
}
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
});
|
|
2892
|
+
|
|
2557
2893
|
// src/rules/require-soft-assertions.ts
|
|
2558
2894
|
var require_soft_assertions_default;
|
|
2559
2895
|
var init_require_soft_assertions = __esm({
|
|
@@ -2779,17 +3115,17 @@ var init_valid_title = __esm({
|
|
|
2779
3115
|
const [matcher, message] = Array.isArray(matcherMaybeWithMessage) ? matcherMaybeWithMessage : [matcherMaybeWithMessage];
|
|
2780
3116
|
return [new RegExp(matcher, "u"), message];
|
|
2781
3117
|
};
|
|
2782
|
-
compileMatcherPatterns = (
|
|
2783
|
-
if (typeof
|
|
2784
|
-
const compiledMatcher = compileMatcherPattern(
|
|
3118
|
+
compileMatcherPatterns = (matchers) => {
|
|
3119
|
+
if (typeof matchers === "string" || Array.isArray(matchers)) {
|
|
3120
|
+
const compiledMatcher = compileMatcherPattern(matchers);
|
|
2785
3121
|
return {
|
|
2786
3122
|
describe: compiledMatcher,
|
|
2787
3123
|
test: compiledMatcher
|
|
2788
3124
|
};
|
|
2789
3125
|
}
|
|
2790
3126
|
return {
|
|
2791
|
-
describe:
|
|
2792
|
-
test:
|
|
3127
|
+
describe: matchers.describe ? compileMatcherPattern(matchers.describe) : null,
|
|
3128
|
+
test: matchers.test ? compileMatcherPattern(matchers.test) : null
|
|
2793
3129
|
};
|
|
2794
3130
|
};
|
|
2795
3131
|
MatcherAndMessageSchema = {
|
|
@@ -2995,6 +3331,7 @@ var require_src = __commonJS({
|
|
|
2995
3331
|
init_no_focused_test();
|
|
2996
3332
|
init_no_force_option();
|
|
2997
3333
|
init_no_get_by_title();
|
|
3334
|
+
init_no_hooks();
|
|
2998
3335
|
init_no_nested_step();
|
|
2999
3336
|
init_no_networkidle();
|
|
3000
3337
|
init_no_nth_methods();
|
|
@@ -3008,6 +3345,8 @@ var require_src = __commonJS({
|
|
|
3008
3345
|
init_no_useless_not();
|
|
3009
3346
|
init_no_wait_for_selector();
|
|
3010
3347
|
init_no_wait_for_timeout();
|
|
3348
|
+
init_prefer_comparison_matcher();
|
|
3349
|
+
init_prefer_equality_matcher();
|
|
3011
3350
|
init_prefer_hooks_in_order();
|
|
3012
3351
|
init_prefer_hooks_on_top();
|
|
3013
3352
|
init_prefer_lowercase_title();
|
|
@@ -3017,6 +3356,7 @@ var require_src = __commonJS({
|
|
|
3017
3356
|
init_prefer_to_have_count();
|
|
3018
3357
|
init_prefer_to_have_length();
|
|
3019
3358
|
init_prefer_web_first_assertions();
|
|
3359
|
+
init_require_hook();
|
|
3020
3360
|
init_require_soft_assertions();
|
|
3021
3361
|
init_require_top_level_describe();
|
|
3022
3362
|
init_valid_expect();
|
|
@@ -3037,6 +3377,7 @@ var require_src = __commonJS({
|
|
|
3037
3377
|
"no-focused-test": no_focused_test_default,
|
|
3038
3378
|
"no-force-option": no_force_option_default,
|
|
3039
3379
|
"no-get-by-title": no_get_by_title_default,
|
|
3380
|
+
"no-hooks": no_hooks_default,
|
|
3040
3381
|
"no-nested-step": no_nested_step_default,
|
|
3041
3382
|
"no-networkidle": no_networkidle_default,
|
|
3042
3383
|
"no-nth-methods": no_nth_methods_default,
|
|
@@ -3050,6 +3391,8 @@ var require_src = __commonJS({
|
|
|
3050
3391
|
"no-useless-not": no_useless_not_default,
|
|
3051
3392
|
"no-wait-for-selector": no_wait_for_selector_default,
|
|
3052
3393
|
"no-wait-for-timeout": no_wait_for_timeout_default,
|
|
3394
|
+
"prefer-comparison-matcher": prefer_comparison_matcher_default,
|
|
3395
|
+
"prefer-equality-matcher": prefer_equality_matcher_default,
|
|
3053
3396
|
"prefer-hooks-in-order": prefer_hooks_in_order_default,
|
|
3054
3397
|
"prefer-hooks-on-top": prefer_hooks_on_top_default,
|
|
3055
3398
|
"prefer-lowercase-title": prefer_lowercase_title_default,
|
|
@@ -3059,6 +3402,7 @@ var require_src = __commonJS({
|
|
|
3059
3402
|
"prefer-to-have-count": prefer_to_have_count_default,
|
|
3060
3403
|
"prefer-to-have-length": prefer_to_have_length_default,
|
|
3061
3404
|
"prefer-web-first-assertions": prefer_web_first_assertions_default,
|
|
3405
|
+
"require-hook": require_hook_default,
|
|
3062
3406
|
"require-soft-assertions": require_soft_assertions_default,
|
|
3063
3407
|
"require-top-level-describe": require_top_level_describe_default,
|
|
3064
3408
|
"valid-expect": valid_expect_default,
|
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": "1.
|
|
4
|
+
"version": "1.3.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",
|