eslint-plugin-cypress 5.1.0 → 5.2.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/CONTRIBUTING.md +25 -23
- package/README.md +58 -30
- package/docs/rules/assertion-before-screenshot.md +6 -5
- package/docs/rules/no-assigning-return-values.md +1 -0
- package/docs/rules/no-async-before.md +7 -8
- package/docs/rules/no-async-tests.md +8 -9
- package/docs/rules/no-chained-get.md +2 -2
- package/docs/rules/no-debug.md +3 -2
- package/docs/rules/no-force.md +14 -13
- package/docs/rules/no-pause.md +3 -2
- package/docs/rules/no-unnecessary-waiting.md +1 -0
- package/docs/rules/no-xpath.md +2 -2
- package/docs/rules/require-data-selectors.md +14 -4
- package/docs/rules/unsafe-to-chain-command.md +2 -1
- package/lib/flat.d.ts +12 -0
- package/lib/rules/require-data-selectors.js +39 -8
- package/lib/rules/unsafe-to-chain-command.js +1 -1
- package/package.json +9 -6
package/CONTRIBUTING.md
CHANGED
|
@@ -4,45 +4,47 @@ Thanks for taking the time to contribute! :smile:
|
|
|
4
4
|
|
|
5
5
|
## Preparation
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
- Fork and clone this repository
|
|
8
|
+
- Branch from the default `master` branch using a descriptive new branch name
|
|
9
|
+
- Install dependencies with `npm ci`
|
|
10
10
|
|
|
11
11
|
## Rule references
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
- Refer to the [ESLint documentation](https://eslint.org/docs/latest/) and the [Custom Rules](https://eslint.org/docs/latest/extend/custom-rules) page
|
|
14
14
|
|
|
15
15
|
## New rule
|
|
16
16
|
|
|
17
17
|
To add a new rule:
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
- Follow the instructions in the ESLint [generator-eslint](https://www.npmjs.com/package/generator-eslint) documentation to install [Yeoman](https://www.npmjs.com/package/yo) and the generator
|
|
20
|
+
- Run the new rule generator `yo eslint:rule` and answer the questions
|
|
21
21
|
- select "ESLint Plugin"
|
|
22
22
|
- for "Type a short description of this rule" provide text which starts with one of "enforce", "require" or "disallow" (all lower case)
|
|
23
|
-
|
|
23
|
+
- Yeoman creates three boilerplate files:
|
|
24
24
|
- `docs/rules/<rule-id>.md`
|
|
25
25
|
- `lib/rules/<rule-id>.js`
|
|
26
26
|
- `test/rules/<rule-id>.js`
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
- Run `npm run lint-fix`
|
|
28
|
+
- Address the linting errors by editing `lib/rules/<rule-id>.js`
|
|
29
29
|
- Add a `meta.messages` property (see [MessageIds](https://eslint.org/docs/latest/extend/custom-rules#messageids))
|
|
30
30
|
- Select the appropriate `meta.type` property using `problem`, `suggestion`, or `layout`
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
- Complete the new rule by adding content to the three files previously created
|
|
32
|
+
- Run `eslint-doc-generator` to generate automated documentation sections (see [Document generation](#document-generation) below)
|
|
33
|
+
- Review documentation changes
|
|
34
|
+
- Run `npm run lint`
|
|
35
|
+
- Run `npm run format`
|
|
36
|
+
- Run `npm test` to run [Vitest](https://vitest.dev/)
|
|
37
|
+
- Make sure all tests are passing
|
|
38
|
+
- Add the rule to [flat.js](https://github.com/cypress-io/eslint-plugin-cypress/blob/master/lib/flat.js)
|
|
39
|
+
- Create a git commit with a commit message similar to: `feat: add rule <description>` (see [commit message conventions](https://github.com/semantic-release/semantic-release#commit-message-format))
|
|
40
|
+
- Create a PR from your branch
|
|
40
41
|
|
|
41
42
|
## Document generation
|
|
42
43
|
|
|
43
44
|
This plugin uses the ESLint [eslint-doc-generator](https://www.npmjs.com/package/eslint-doc-generator) to generate consistent documentation.
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
- Install with `npm install eslint-doc-generator -g`
|
|
47
|
+
- Run `eslint-doc-generator` in the root directory of the plugin
|
|
46
48
|
|
|
47
49
|
## Merging pull requests
|
|
48
50
|
|
|
@@ -65,7 +67,7 @@ This information is for Cypress.io Members or Collaborators who merge pull reque
|
|
|
65
67
|
```
|
|
66
68
|
|
|
67
69
|
1. New versions of this module will be released automatically by the CI pipeline when any PR with a triggering commit message is merged to the `master` branch: see the `release` job of [circle.yml](circle.yml).
|
|
68
|
-
This will create a new [GitHub release](https://github.com/cypress-io/eslint-plugin-cypress/releases) and publish it to [eslint-plugin-cypress](https://www.npmjs.com/package/eslint-plugin-cypress) on the [npm registry](https://docs.npmjs.com/about-the-public-npm-registry).
|
|
70
|
+
This will create a new [GitHub release](https://github.com/cypress-io/eslint-plugin-cypress/releases) and publish it to [eslint-plugin-cypress](https://www.npmjs.com/package/eslint-plugin-cypress) on the [npm registry](https://docs.npmjs.com/about-the-public-npm-registry).
|
|
69
71
|
1. The module's CI is configured to use the [default Angular release rules](https://github.com/semantic-release/commit-analyzer/blob/master/lib/default-release-rules.js).
|
|
70
|
-
This means that only `feat:`, `fix:` and `perf:` trigger a new release.
|
|
71
|
-
Other Angular commit types listed in the [Angular commit message guidelines](https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md) can be used for documentation purposes, however they are ignored by the currently configured release process.
|
|
72
|
+
This means that only `feat:`, `fix:` and `perf:` trigger a new release.
|
|
73
|
+
Other Angular commit types listed in the [Angular commit message guidelines](https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md) can be used for documentation purposes, however they are ignored by the currently configured release process.
|
package/README.md
CHANGED
|
@@ -11,7 +11,9 @@ Prerequisites: [ESLint](https://www.npmjs.com/package/eslint) `v9`. Lower versio
|
|
|
11
11
|
```sh
|
|
12
12
|
npm install eslint eslint-plugin-cypress --save-dev
|
|
13
13
|
```
|
|
14
|
+
|
|
14
15
|
or
|
|
16
|
+
|
|
15
17
|
```sh
|
|
16
18
|
yarn add eslint eslint-plugin-cypress --dev
|
|
17
19
|
```
|
|
@@ -36,15 +38,16 @@ import pluginCypress from 'eslint-plugin-cypress/flat' # deprecated
|
|
|
36
38
|
|
|
37
39
|
There are two specific configurations available:
|
|
38
40
|
|
|
39
|
-
| Configuration | Content
|
|
40
|
-
| --------------------- |
|
|
41
|
+
| Configuration | Content |
|
|
42
|
+
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
41
43
|
| `configs.globals` | defines globals `cy`, `Cypress`, `expect`, `assert` and `chai` used in Cypress test specs as well as `globals.browser` and `globals.mocha` from [globals](https://www.npmjs.com/package/globals). There are no default rules enabled in this configuration. |
|
|
42
|
-
| `configs.recommended` | enables [recommended Rules](#rules). It includes also `configs.global` (see above).
|
|
44
|
+
| `configs.recommended` | enables [recommended Rules](#rules). It includes also `configs.global` (see above). |
|
|
43
45
|
|
|
44
46
|
## Rules
|
|
45
47
|
|
|
46
48
|
These rules enforce some of the [best practices recommended for using Cypress](https://on.cypress.io/best-practices).
|
|
47
49
|
|
|
50
|
+
<!-- prettier-ignore-start -->
|
|
48
51
|
<!-- begin auto-generated rules list -->
|
|
49
52
|
|
|
50
53
|
💼 Configurations enabled in.\
|
|
@@ -66,45 +69,55 @@ These rules enforce some of the [best practices recommended for using Cypress](h
|
|
|
66
69
|
| [unsafe-to-chain-command](docs/rules/unsafe-to-chain-command.md) | disallow actions within chains | ✅ |
|
|
67
70
|
|
|
68
71
|
<!-- end auto-generated rules list -->
|
|
72
|
+
<!-- prettier-ignore-end -->
|
|
69
73
|
|
|
70
74
|
## Usage examples
|
|
71
75
|
|
|
72
76
|
In the following sections, different examples of possible configuration file contents are given, together with some brief explanations. Adapt these examples according to your needs.
|
|
73
77
|
|
|
78
|
+
The examples use the `defineConfig()` helper, introduced with ESLint [9.22.0](https://eslint.org/blog/2025/03/eslint-v9.22.0-released/). Refer to the blog article [Evolving flat config with extends](https://eslint.org/blog/2025/03/flat-config-extends-define-config-global-ignores/) for background information. If you are using ESLint `<9.22.0`, import `defineConfig` from [@eslint/config-helpers](https://www.npmjs.com/package/@eslint/config-helpers) instead of from `eslint/config`.
|
|
79
|
+
|
|
74
80
|
### Cypress
|
|
75
81
|
|
|
76
82
|
All rules are available by importing from `eslint-plugin-cypress` and can be individually activated.
|
|
83
|
+
|
|
77
84
|
- [cypress/unsafe-to-chain-command](https://github.com/cypress-io/eslint-plugin-cypress/blob/master/docs/rules/unsafe-to-chain-command.md) is activated and set to `error`
|
|
78
85
|
|
|
79
86
|
```js
|
|
87
|
+
import { defineConfig } from 'eslint/config'
|
|
80
88
|
import pluginCypress from 'eslint-plugin-cypress'
|
|
81
|
-
export default [
|
|
89
|
+
export default defineConfig([
|
|
82
90
|
{
|
|
83
91
|
plugins: {
|
|
84
|
-
cypress: pluginCypress
|
|
92
|
+
cypress: pluginCypress,
|
|
85
93
|
},
|
|
86
94
|
rules: {
|
|
87
|
-
'cypress/unsafe-to-chain-command': 'error'
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
]
|
|
95
|
+
'cypress/unsafe-to-chain-command': 'error',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
])
|
|
91
99
|
```
|
|
92
100
|
|
|
93
101
|
### Cypress recommended
|
|
94
102
|
|
|
95
103
|
The `eslint-plugin-cypress` [recommended rules](#rules) `configs.recommended` are activated, except for
|
|
104
|
+
|
|
96
105
|
- [cypress/no-unnecessary-waiting](https://github.com/cypress-io/eslint-plugin-cypress/blob/master/docs/rules/no-unnecessary-waiting.md) which is set to `off`
|
|
97
106
|
|
|
98
107
|
```js
|
|
108
|
+
import { defineConfig } from 'eslint/config'
|
|
99
109
|
import pluginCypress from 'eslint-plugin-cypress'
|
|
100
|
-
export default [
|
|
101
|
-
pluginCypress.configs.recommended,
|
|
110
|
+
export default defineConfig([
|
|
102
111
|
{
|
|
112
|
+
files: ['cypress/**/*.js'],
|
|
113
|
+
extends: [
|
|
114
|
+
pluginCypress.configs.recommended,
|
|
115
|
+
],
|
|
103
116
|
rules: {
|
|
104
|
-
'cypress/no-unnecessary-waiting': 'off'
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
]
|
|
117
|
+
'cypress/no-unnecessary-waiting': 'off',
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
])
|
|
108
121
|
```
|
|
109
122
|
|
|
110
123
|
### Cypress globals
|
|
@@ -112,10 +125,16 @@ export default [
|
|
|
112
125
|
The `configs.globals` are activated.
|
|
113
126
|
|
|
114
127
|
```js
|
|
128
|
+
import { defineConfig } from 'eslint/config'
|
|
115
129
|
import pluginCypress from 'eslint-plugin-cypress'
|
|
116
|
-
export default [
|
|
117
|
-
|
|
118
|
-
]
|
|
130
|
+
export default defineConfig([
|
|
131
|
+
{
|
|
132
|
+
files: ['cypress/**/*.js'],
|
|
133
|
+
extends: [
|
|
134
|
+
pluginCypress.configs.globals,
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
])
|
|
119
138
|
```
|
|
120
139
|
|
|
121
140
|
## Disable rules
|
|
@@ -174,6 +193,7 @@ During test spec development, [Mocha exclusive tests](https://mochajs.org/#exclu
|
|
|
174
193
|
[eslint-plugin-mocha@^11](https://www.npmjs.com/package/eslint-plugin-mocha) is added to the example [Cypress recommended](#cypress-recommended). This version of the plugin supports only flat file configurations with the option `configs.recommended`.
|
|
175
194
|
|
|
176
195
|
The settings for individual `mocha` rules from the `configs.recommended` option are changed.
|
|
196
|
+
|
|
177
197
|
- [mocha/no-exclusive-tests](https://github.com/lo1tuma/eslint-plugin-mocha/blob/main/docs/rules/no-exclusive-tests.md) and [mocha/no-pending-tests](https://github.com/lo1tuma/eslint-plugin-mocha/blob/main/docs/rules/no-pending-tests.md) are set to `error` instead of `warn`
|
|
178
198
|
- [mocha/no-mocha-arrows](https://github.com/lo1tuma/eslint-plugin-mocha/blob/main/docs/rules/no-mocha-arrows.md) is set to `off` instead of `error`
|
|
179
199
|
|
|
@@ -182,20 +202,24 @@ npm install eslint-plugin-mocha@^11 --save-dev
|
|
|
182
202
|
```
|
|
183
203
|
|
|
184
204
|
```js
|
|
205
|
+
import { defineConfig } from 'eslint/config'
|
|
185
206
|
import pluginMocha from 'eslint-plugin-mocha'
|
|
186
207
|
import pluginCypress from 'eslint-plugin-cypress'
|
|
187
|
-
export default [
|
|
188
|
-
pluginMocha.configs.recommended,
|
|
189
|
-
pluginCypress.configs.recommended,
|
|
208
|
+
export default defineConfig([
|
|
190
209
|
{
|
|
210
|
+
files: ['cypress/**/*.js'],
|
|
211
|
+
extends: [
|
|
212
|
+
pluginMocha.configs.recommended,
|
|
213
|
+
pluginCypress.configs.recommended,
|
|
214
|
+
],
|
|
191
215
|
rules: {
|
|
192
216
|
'mocha/no-exclusive-tests': 'error',
|
|
193
217
|
'mocha/no-pending-tests': 'error',
|
|
194
218
|
'mocha/no-mocha-arrows': 'off',
|
|
195
|
-
'cypress/no-unnecessary-waiting': 'off'
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
]
|
|
219
|
+
'cypress/no-unnecessary-waiting': 'off',
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
])
|
|
199
223
|
```
|
|
200
224
|
|
|
201
225
|
### Cypress and Chai recommended
|
|
@@ -211,17 +235,21 @@ npm install eslint-plugin-chai-friendly@^1.0.1 --save-dev
|
|
|
211
235
|
```
|
|
212
236
|
|
|
213
237
|
```js
|
|
238
|
+
import { defineConfig } from 'eslint/config'
|
|
214
239
|
import pluginCypress from 'eslint-plugin-cypress'
|
|
215
240
|
import pluginChaiFriendly from 'eslint-plugin-chai-friendly'
|
|
216
|
-
export default [
|
|
217
|
-
pluginCypress.configs.recommended,
|
|
218
|
-
pluginChaiFriendly.configs.recommendedFlat,
|
|
241
|
+
export default defineConfig([
|
|
219
242
|
{
|
|
243
|
+
files: ['cypress/**/*.js'],
|
|
244
|
+
extends: [
|
|
245
|
+
pluginCypress.configs.recommended,
|
|
246
|
+
pluginChaiFriendly.configs.recommendedFlat,
|
|
247
|
+
],
|
|
220
248
|
rules: {
|
|
221
249
|
'cypress/no-unnecessary-waiting': 'off',
|
|
222
250
|
},
|
|
223
|
-
}
|
|
224
|
-
]
|
|
251
|
+
},
|
|
252
|
+
])
|
|
225
253
|
```
|
|
226
254
|
|
|
227
255
|
## Contributing
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Require screenshots to be preceded by an assertion (`cypress/assertion-before-screenshot`)
|
|
2
2
|
|
|
3
3
|
<!-- end auto-generated rule header -->
|
|
4
|
+
|
|
4
5
|
If you take screenshots without assertions then you may get different screenshots depending on timing.
|
|
5
6
|
|
|
6
7
|
For example, if clicking a button makes some network calls and upon success, renders something, then the screenshot may sometimes have the new render and sometimes not.
|
|
@@ -12,14 +13,14 @@ This rule checks there is an assertion making sure your application state is cor
|
|
|
12
13
|
Examples of **incorrect** code for this rule:
|
|
13
14
|
|
|
14
15
|
```js
|
|
15
|
-
cy.visit('myUrl')
|
|
16
|
-
cy.screenshot()
|
|
16
|
+
cy.visit('myUrl')
|
|
17
|
+
cy.screenshot()
|
|
17
18
|
```
|
|
18
19
|
|
|
19
20
|
Examples of **correct** code for this rule:
|
|
20
21
|
|
|
21
22
|
```js
|
|
22
|
-
cy.visit('myUrl')
|
|
23
|
-
cy.get('[data-test-id="my-element"]').should('be.visible')
|
|
24
|
-
cy.screenshot()
|
|
23
|
+
cy.visit('myUrl')
|
|
24
|
+
cy.get('[data-test-id="my-element"]').should('be.visible')
|
|
25
|
+
cy.screenshot()
|
|
25
26
|
```
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Disallow using `async`/`await` in Cypress `before` methods (`cypress/no-async-before`)
|
|
2
2
|
|
|
3
3
|
<!-- end auto-generated rule header -->
|
|
4
|
+
|
|
4
5
|
Cypress commands that return a promise may cause side effects in `before`/`beforeEach` hooks, possibly causing unexpected behavior.
|
|
5
6
|
|
|
6
7
|
## Rule Details
|
|
@@ -11,7 +12,7 @@ Examples of **incorrect** code for this rule:
|
|
|
11
12
|
|
|
12
13
|
```js
|
|
13
14
|
describe('my feature', () => {
|
|
14
|
-
before('my test case', async ()
|
|
15
|
+
before('my test case', async () => {
|
|
15
16
|
await cy.get('.myClass')
|
|
16
17
|
// other operations
|
|
17
18
|
})
|
|
@@ -20,10 +21,8 @@ describe('my feature', () => {
|
|
|
20
21
|
|
|
21
22
|
```js
|
|
22
23
|
describe('my feature', () => {
|
|
23
|
-
before('my test case', async ()
|
|
24
|
-
cy
|
|
25
|
-
.get('.myClass')
|
|
26
|
-
.click()
|
|
24
|
+
before('my test case', async () => {
|
|
25
|
+
cy.get('.myClass').click()
|
|
27
26
|
|
|
28
27
|
await someAsyncFunction()
|
|
29
28
|
})
|
|
@@ -34,7 +33,7 @@ Examples of **correct** code for this rule:
|
|
|
34
33
|
|
|
35
34
|
```js
|
|
36
35
|
describe('my feature', () => {
|
|
37
|
-
before('my test case', ()
|
|
36
|
+
before('my test case', () => {
|
|
38
37
|
cy.get('.myClass')
|
|
39
38
|
// other operations
|
|
40
39
|
})
|
|
@@ -47,5 +46,5 @@ If there are genuine use-cases for using `async/await` in your `before` hooks th
|
|
|
47
46
|
|
|
48
47
|
## Further Reading
|
|
49
48
|
|
|
50
|
-
- [Mixing Async and Sync code](https://on.cypress.io/
|
|
51
|
-
- [Commands Are Asynchronous](https://on.cypress.io/
|
|
49
|
+
- [Mixing Async and Sync code](https://on.cypress.io/app/core-concepts/introduction-to-cypress#Mixing-Async-and-Sync-code)
|
|
50
|
+
- [Commands Are Asynchronous](https://on.cypress.io/app/core-concepts/introduction-to-cypress.html#Commands-Are-Asynchronous)
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
💼 This rule is enabled in the ✅ `recommended` config.
|
|
4
4
|
|
|
5
5
|
<!-- end auto-generated rule header -->
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
Cypress tests [that return a promise will error](https://docs.cypress.io/app/references/error-messages.html#Cypress-detected-that-you-returned-a-promise-from-a-command-while-also-invoking-one-or-more-cy-commands-in-that-promise) and cannot run successfully.
|
|
7
8
|
An `async` function returns a promise under the hood, so a test using an `async` function will also error.
|
|
8
9
|
|
|
9
10
|
## Rule Details
|
|
@@ -14,7 +15,7 @@ Examples of **incorrect** code for this rule:
|
|
|
14
15
|
|
|
15
16
|
```js
|
|
16
17
|
describe('my feature', () => {
|
|
17
|
-
it('my test case', async ()
|
|
18
|
+
it('my test case', async () => {
|
|
18
19
|
await cy.get('.myClass')
|
|
19
20
|
// other operations
|
|
20
21
|
})
|
|
@@ -23,10 +24,8 @@ describe('my feature', () => {
|
|
|
23
24
|
|
|
24
25
|
```js
|
|
25
26
|
describe('my feature', () => {
|
|
26
|
-
it('my test case', async ()
|
|
27
|
-
cy
|
|
28
|
-
.get('.myClass')
|
|
29
|
-
.click()
|
|
27
|
+
it('my test case', async () => {
|
|
28
|
+
cy.get('.myClass').click()
|
|
30
29
|
|
|
31
30
|
await someAsyncFunction()
|
|
32
31
|
})
|
|
@@ -37,7 +36,7 @@ Examples of **correct** code for this rule:
|
|
|
37
36
|
|
|
38
37
|
```js
|
|
39
38
|
describe('my feature', () => {
|
|
40
|
-
it('my test case', ()
|
|
39
|
+
it('my test case', () => {
|
|
41
40
|
cy.get('.myClass')
|
|
42
41
|
// other operations
|
|
43
42
|
})
|
|
@@ -50,5 +49,5 @@ If there are genuine use-cases for using `async/await` in your test cases then y
|
|
|
50
49
|
|
|
51
50
|
## Further Reading
|
|
52
51
|
|
|
53
|
-
- [Mixing Async and Sync code](https://on.cypress.io/
|
|
54
|
-
- [Commands Are Asynchronous](https://on.cypress.io/
|
|
52
|
+
- [Mixing Async and Sync code](https://on.cypress.io/app/core-concepts/introduction-to-cypress#Mixing-Async-and-Sync-code)
|
|
53
|
+
- [Commands Are Asynchronous](https://on.cypress.io/app/core-concepts/introduction-to-cypress.html#Commands-Are-Asynchronous)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Disallow chain of `cy.get()` calls (`cypress/no-chained-get`)
|
|
2
2
|
|
|
3
3
|
<!-- end auto-generated rule header -->
|
|
4
|
+
|
|
4
5
|
This rule disallows the usage of chained `.get()` calls as `cy.get()` always starts its search from the cy.root element.
|
|
5
6
|
|
|
6
7
|
## Rule Details
|
|
@@ -14,6 +15,5 @@ cy.get('parent').get('child')
|
|
|
14
15
|
Examples of **correct** code for this rule:
|
|
15
16
|
|
|
16
17
|
```js
|
|
17
|
-
cy.get('parent')
|
|
18
|
-
.find('child')
|
|
18
|
+
cy.get('parent').find('child')
|
|
19
19
|
```
|
package/docs/rules/no-debug.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Disallow using `cy.debug()` calls (`cypress/no-debug`)
|
|
2
2
|
|
|
3
3
|
<!-- end auto-generated rule header -->
|
|
4
|
+
|
|
4
5
|
It is recommended to remove any [cy.debug](https://on.cypress.io/debug) commands before committing specs to avoid other developers getting unexpected results.
|
|
5
6
|
|
|
6
7
|
## Rule Details
|
|
@@ -8,8 +9,8 @@ It is recommended to remove any [cy.debug](https://on.cypress.io/debug) commands
|
|
|
8
9
|
Examples of **incorrect** code for this rule:
|
|
9
10
|
|
|
10
11
|
```js
|
|
11
|
-
cy.debug()
|
|
12
|
-
cy.get('selector').debug()
|
|
12
|
+
cy.debug()
|
|
13
|
+
cy.get('selector').debug()
|
|
13
14
|
```
|
|
14
15
|
|
|
15
16
|
Examples of **correct** code for this rule:
|
package/docs/rules/no-force.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# Disallow using `force: true` with action commands (`cypress/no-force`)
|
|
2
2
|
|
|
3
3
|
<!-- end auto-generated rule header -->
|
|
4
|
+
|
|
4
5
|
Using `force: true` on inputs appears to be confusing rather than helpful.
|
|
5
6
|
It usually silences the actual problem instead of providing a way to overcome it.
|
|
6
|
-
See [Cypress Core Concepts](https://docs.cypress.io/
|
|
7
|
+
See [Cypress Core Concepts](https://docs.cypress.io/app/core-concepts/interacting-with-elements.html#Forcing).
|
|
7
8
|
|
|
8
9
|
If enabling this rule, it's recommended to set the severity to `warn`.
|
|
9
10
|
|
|
@@ -18,26 +19,26 @@ and [`.trigger()`](https://on.cypress.io/trigger).
|
|
|
18
19
|
Examples of **incorrect** code for this rule:
|
|
19
20
|
|
|
20
21
|
```js
|
|
21
|
-
cy.get('button').click({force: true})
|
|
22
|
-
cy.get('button').dblclick({force: true})
|
|
23
|
-
cy.get('input').type('somth', {force: true})
|
|
24
|
-
cy.get('div').find('.foo').find('.bar').trigger('change', {force: true})
|
|
25
|
-
cy.get('input').trigger('click', {force: true})
|
|
26
|
-
cy.get('input').rightclick({force: true})
|
|
27
|
-
cy.get('input').check({force: true})
|
|
28
|
-
cy.get('input').select({force: true})
|
|
29
|
-
cy.get('input').focus({force: true})
|
|
22
|
+
cy.get('button').click({ force: true })
|
|
23
|
+
cy.get('button').dblclick({ force: true })
|
|
24
|
+
cy.get('input').type('somth', { force: true })
|
|
25
|
+
cy.get('div').find('.foo').find('.bar').trigger('change', { force: true })
|
|
26
|
+
cy.get('input').trigger('click', { force: true })
|
|
27
|
+
cy.get('input').rightclick({ force: true })
|
|
28
|
+
cy.get('input').check({ force: true })
|
|
29
|
+
cy.get('input').select({ force: true })
|
|
30
|
+
cy.get('input').focus({ force: true })
|
|
30
31
|
```
|
|
31
32
|
|
|
32
33
|
Examples of **correct** code for this rule:
|
|
33
34
|
|
|
34
35
|
```js
|
|
35
36
|
cy.get('button').click()
|
|
36
|
-
cy.get('button').click({multiple: true})
|
|
37
|
+
cy.get('button').click({ multiple: true })
|
|
37
38
|
cy.get('button').dblclick()
|
|
38
39
|
cy.get('input').type('somth')
|
|
39
|
-
cy.get('input').trigger('click', {anyoption: true})
|
|
40
|
-
cy.get('input').rightclick({anyoption: true})
|
|
40
|
+
cy.get('input').trigger('click', { anyoption: true })
|
|
41
|
+
cy.get('input').rightclick({ anyoption: true })
|
|
41
42
|
cy.get('input').check()
|
|
42
43
|
cy.get('input').select()
|
|
43
44
|
cy.get('input').focus()
|
package/docs/rules/no-pause.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Disallow using `cy.pause()` calls (`cypress/no-pause`)
|
|
2
2
|
|
|
3
3
|
<!-- end auto-generated rule header -->
|
|
4
|
+
|
|
4
5
|
It is recommended to remove any [cy.pause](https://on.cypress.io/pause) commands before committing specs to avoid other developers getting unexpected results.
|
|
5
6
|
|
|
6
7
|
## Rule Details
|
|
@@ -8,8 +9,8 @@ It is recommended to remove any [cy.pause](https://on.cypress.io/pause) commands
|
|
|
8
9
|
Examples of **incorrect** code for this rule:
|
|
9
10
|
|
|
10
11
|
```js
|
|
11
|
-
cy.pause()
|
|
12
|
-
cy.get('selector').pause()
|
|
12
|
+
cy.pause()
|
|
13
|
+
cy.get('selector').pause()
|
|
13
14
|
```
|
|
14
15
|
|
|
15
16
|
Examples of **correct** code for this rule:
|
package/docs/rules/no-xpath.md
CHANGED
|
@@ -15,11 +15,11 @@ cy.xpath('//div[@class=\"container\"]').click()
|
|
|
15
15
|
Examples of **correct** code for this rule:
|
|
16
16
|
|
|
17
17
|
```js
|
|
18
|
-
cy.get('[data-cy="container"]').click()
|
|
18
|
+
cy.get('[data-cy="container"]').click()
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Further Reading
|
|
22
22
|
|
|
23
23
|
Both `@cypress/xpath` and `cypress-xpath` are deprecated.
|
|
24
24
|
|
|
25
|
-
See [the Cypress Best Practices guide](https://docs.cypress.io/
|
|
25
|
+
See [the Cypress Best Practices guide](https://docs.cypress.io/app/core-concepts/best-practices.html#Selecting-Elements).
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Require `data-*` attribute selectors (`cypress/require-data-selectors`)
|
|
2
2
|
|
|
3
3
|
<!-- end auto-generated rule header -->
|
|
4
|
+
|
|
4
5
|
Require `cy.get` to use only selectors that target `data-*` attributes.
|
|
5
6
|
|
|
6
7
|
> Note: If you use this rule, consider only using the `warn` error level, since using `data-*` attribute selectors may not always be possible.
|
|
@@ -10,11 +11,14 @@ Require `cy.get` to use only selectors that target `data-*` attributes.
|
|
|
10
11
|
Examples of **incorrect** code for this rule:
|
|
11
12
|
|
|
12
13
|
```js
|
|
13
|
-
cy.get(
|
|
14
|
+
cy.get('.a')
|
|
14
15
|
cy.get('[daedta-cy=submit]').click()
|
|
15
16
|
cy.get('[d-cy=submit]')
|
|
16
|
-
cy.get(
|
|
17
|
-
cy.get(
|
|
17
|
+
cy.get('.btn-large').click()
|
|
18
|
+
cy.get('.btn-.large').click()
|
|
19
|
+
|
|
20
|
+
const CLASS_SELECTOR = ".my-class";
|
|
21
|
+
cy.get(CLASS_SELECTOR)
|
|
18
22
|
```
|
|
19
23
|
|
|
20
24
|
Examples of **correct** code for this rule:
|
|
@@ -22,8 +26,14 @@ Examples of **correct** code for this rule:
|
|
|
22
26
|
```js
|
|
23
27
|
cy.get('[data-cy=submit]').click()
|
|
24
28
|
cy.get('[data-QA=submit]')
|
|
29
|
+
cy.get(`[data-QA=submit]`)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
const ASSESSMENT_SUBMIT = "[data-cy=assessment-submit]"
|
|
34
|
+
cy.get(ASSESSMENT_SUBMIT).click()
|
|
25
35
|
```
|
|
26
36
|
|
|
27
37
|
## Further Reading
|
|
28
38
|
|
|
29
|
-
See [the Cypress Best Practices guide](https://docs.cypress.io/
|
|
39
|
+
See [the Cypress Best Practices guide](https://docs.cypress.io/app/core-concepts/best-practices.html#Selecting-Elements).
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
💼 This rule is enabled in the ✅ `recommended` config.
|
|
4
4
|
|
|
5
5
|
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
6
7
|
### Options
|
|
7
8
|
|
|
8
9
|
<!-- begin auto-generated rule options list -->
|
|
@@ -15,4 +16,4 @@
|
|
|
15
16
|
|
|
16
17
|
## Further Reading
|
|
17
18
|
|
|
18
|
-
See [retry-ability guide](https://docs.cypress.io/
|
|
19
|
+
See [retry-ability guide](https://docs.cypress.io/app/core-concepts/retry-ability#Actions-should-be-at-the-end-of-chains-not-the-middle).
|
package/lib/flat.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ESLint, Linter } from 'eslint'
|
|
2
|
+
|
|
3
|
+
declare const plugin: {
|
|
4
|
+
meta: { name: string, version: string }
|
|
5
|
+
configs: {
|
|
6
|
+
globals: Linter.Config<Linter.RulesRecord>
|
|
7
|
+
recommended: Linter.Config<Linter.RulesRecord>
|
|
8
|
+
}
|
|
9
|
+
rules: NonNullable<ESLint.Plugin['rules']>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export = plugin
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
|
|
3
2
|
module.exports = {
|
|
4
3
|
meta: {
|
|
5
4
|
type: 'suggestion',
|
|
@@ -16,9 +15,29 @@ module.exports = {
|
|
|
16
15
|
},
|
|
17
16
|
|
|
18
17
|
create(context) {
|
|
18
|
+
const variablesSet = new Set()
|
|
19
19
|
return {
|
|
20
|
+
VariableDeclarator(node) {
|
|
21
|
+
if (node.init && node.id && node.id.type === 'Identifier') {
|
|
22
|
+
let selectorValue = null
|
|
23
|
+
|
|
24
|
+
if (node.init.type === 'Literal' && typeof node.init.value === 'string') {
|
|
25
|
+
selectorValue = node.init.value
|
|
26
|
+
}
|
|
27
|
+
else if (node.init.type === 'TemplateLiteral'
|
|
28
|
+
&& node.init.expressions.length === 0
|
|
29
|
+
&& node.init.quasis.length === 1) {
|
|
30
|
+
selectorValue = node.init.quasis[0].value.cooked
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (selectorValue && isAliasOrDataSelector(selectorValue)) {
|
|
34
|
+
variablesSet.add(node.id.name)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
20
39
|
CallExpression(node) {
|
|
21
|
-
if (isCallingCyGet(node) && !isDataArgument(node)) {
|
|
40
|
+
if (isCallingCyGet(node) && !isDataArgument(node, variablesSet)) {
|
|
22
41
|
context.report({ node, messageId: 'unexpected' })
|
|
23
42
|
}
|
|
24
43
|
},
|
|
@@ -34,12 +53,24 @@ function isCallingCyGet(node) {
|
|
|
34
53
|
&& node.callee.property.name === 'get'
|
|
35
54
|
}
|
|
36
55
|
|
|
37
|
-
function isDataArgument(node) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
56
|
+
function isDataArgument(node, dataVariables) {
|
|
57
|
+
if (node.arguments.length === 0) return false
|
|
58
|
+
|
|
59
|
+
const firstArg = node.arguments[0]
|
|
60
|
+
|
|
61
|
+
if (firstArg.type === 'Literal') {
|
|
62
|
+
return isAliasOrDataSelector(String(firstArg.value))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (firstArg.type === 'TemplateLiteral') {
|
|
66
|
+
return isAliasOrDataSelector(String(firstArg.quasis[0].value.cooked))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (firstArg.type === 'Identifier') {
|
|
70
|
+
return dataVariables.has(firstArg.name)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return false
|
|
43
74
|
}
|
|
44
75
|
|
|
45
76
|
function isAliasOrDataSelector(selector) {
|
|
@@ -7,7 +7,7 @@ const DESCRIPTION = 'disallow actions within chains'
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Commands listed in the documentation with text: 'It is unsafe to chain further commands that rely on the subject after xxx.'
|
|
10
|
-
* See {@link https://docs.cypress.io/
|
|
10
|
+
* See {@link https://docs.cypress.io/app/core-concepts/retry-ability#Actions-should-be-at-the-end-of-chains-not-the-middle Actions should be at the end of chains, not the middle}
|
|
11
11
|
* for more information.
|
|
12
12
|
*
|
|
13
13
|
* @type {string[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-cypress",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "An ESLint plugin for projects using Cypress",
|
|
5
5
|
"main": "./lib/flat.js",
|
|
6
6
|
"exports": {
|
|
@@ -34,17 +34,20 @@
|
|
|
34
34
|
"globals": "^16.2.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@stylistic/eslint-plugin": "^
|
|
38
|
-
"eslint": "^9.
|
|
39
|
-
"eslint-plugin-eslint-plugin": "^
|
|
37
|
+
"@stylistic/eslint-plugin": "^5.3.1",
|
|
38
|
+
"eslint": "^9.35.0",
|
|
39
|
+
"eslint-plugin-eslint-plugin": "^7.0.0",
|
|
40
40
|
"eslint-plugin-mocha": "^11.1.0",
|
|
41
41
|
"husky": "^9.1.7",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
42
|
+
"prettier": "^3.6.2",
|
|
43
|
+
"semantic-release": "24.2.7",
|
|
44
|
+
"vitest": "^3.2.4"
|
|
44
45
|
},
|
|
45
46
|
"scripts": {
|
|
46
47
|
"lint": "eslint",
|
|
47
48
|
"lint-fix": "eslint --fix",
|
|
49
|
+
"format": "prettier --write .",
|
|
50
|
+
"format:check": "prettier --check .",
|
|
48
51
|
"semantic-release": "semantic-release",
|
|
49
52
|
"test": "vitest run",
|
|
50
53
|
"prepare": "husky"
|