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 CHANGED
@@ -109,52 +109,56 @@ can configure this plugin to be aware of these additional names.
109
109
  }
110
110
  ```
111
111
 
112
- ## List of Supported Rules
113
-
114
- ✔: Enabled in the recommended configuration.\
115
- 🔧: Some problems reported by this rule are automatically fixable by the [`--fix`](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix)
116
- command line option.\
117
- 💡: Some problems reported by this rule are manually fixable by editor
118
- [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-nested-step](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-nested-step.md) | Disallow nested `test.step()` methods |
135
- | ✔ | | | [no-networkidle](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-networkidle.md) | Disallow usage of the `networkidle` option |
136
- | | | | [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 |
137
- | ✔ | | | [no-page-pause](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-page-pause.md) | Disallow using `page.pause()` |
138
- | ✔ | 🔧 | | [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()` |
139
- | | 🔧 | | [no-get-by-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-get-by-title.md) | Disallow using `getByTitle()` |
140
- | | | | [no-raw-locators](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-raw-locators.md) | Disallow using raw locators |
141
- | ✔ | 🔧 | | [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 |
142
- | | | | [no-restricted-matchers](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-restricted-matchers.md) | Disallow specific matchers & modifiers |
143
- | ✔ | | 💡 | [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 |
144
- | ✔ | | | [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 |
145
- | ✔ | 🔧 | | [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 |
146
- | ✔ | | 💡 | [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()` |
147
- | ✔ | | 💡 | [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()` |
148
- | | | | [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 |
149
- | | | | [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 |
150
- | | | 💡 | [prefer-strict-equal](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` |
151
- | | 🔧 | | [prefer-lowercase-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names |
152
- | | 🔧 | | [prefer-to-be](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-be.md) | Suggest using `toBe()` |
153
- | | 🔧 | | [prefer-to-contain](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-contain.md) | Suggest using `toContain()` |
154
- | | 🔧 | | [prefer-to-have-count](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-count.md) | Suggest using `toHaveCount()` |
155
- | | 🔧 | | [prefer-to-have-length](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` |
156
- | ✔ | 🔧 | | [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 |
157
- | | | | [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 |
158
- | | 🔧 | | [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()` |
159
- | ✔ | | | [valid-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-expect.md) | Enforce valid `expect()` usage |
160
- | ✔ | 🔧 | | [valid-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-title.md) | Enforce valid titles |
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/jest-community/eslint-plugin-jest`
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/prefer-to-contain.md"
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 (!argumentMatchers.includes(expectCall.matcherName) || !firstArg) {
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" || !matchers.has(matcherName) || !isBooleanLiteral(matcherArg) || !isFixableIncludesCallExpression(includesCall)) {
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 || !matchers2.has(expectCall.matcherName)) {
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 || !lengthMatchers.has(expectCall.matcherName)) {
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 = (matchers3) => {
2490
- if (typeof matchers3 === "string" || Array.isArray(matchers3)) {
2491
- const compiledMatcher = compileMatcherPattern(matchers3);
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: matchers3.describe ? compileMatcherPattern(matchers3.describe) : null,
2499
- test: matchers3.test ? compileMatcherPattern(matchers3.test) : null
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/jest-community/eslint-plugin-jest`
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/prefer-to-contain.md"
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 (!argumentMatchers.includes(expectCall.matcherName) || !firstArg) {
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 matchers, isFixableIncludesCallExpression, prefer_to_contain_default;
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" || !matchers.has(matcherName) || !isBooleanLiteral(matcherArg) || !isFixableIncludesCallExpression(includesCall)) {
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 matchers2, prefer_to_have_count_default;
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 || !matchers2.has(expectCall.matcherName)) {
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 lengthMatchers, prefer_to_have_length_default;
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 || !lengthMatchers.has(expectCall.matcherName)) {
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 = (matchers3) => {
2783
- if (typeof matchers3 === "string" || Array.isArray(matchers3)) {
2784
- const compiledMatcher = compileMatcherPattern(matchers3);
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: matchers3.describe ? compileMatcherPattern(matchers3.describe) : null,
2792
- test: matchers3.test ? compileMatcherPattern(matchers3.test) : null
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.2.0",
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",