eslint-plugin-cypress 2.12.1 → 2.13.1

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.
@@ -0,0 +1,12 @@
1
+ name: 'Add issue/PR to Triage Board'
2
+ on:
3
+ issues:
4
+ types:
5
+ - opened
6
+ pull_request_target:
7
+ types:
8
+ - opened
9
+ jobs:
10
+ add-to-triage-project-board:
11
+ uses: cypress-io/cypress/.github/workflows/triage_add_to_project.yml@develop
12
+ secrets: inherit
@@ -0,0 +1,9 @@
1
+ name: 'Closed Issue Comment'
2
+ on:
3
+ issue_comment:
4
+ types:
5
+ - created
6
+ jobs:
7
+ closed-issue-comment:
8
+ uses: cypress-io/cypress/.github/workflows/triage_closed_issue_comment.yml@develop
9
+ secrets: inherit
package/README.md CHANGED
@@ -73,7 +73,7 @@ Disable the `cypress/no-unnecessary-waiting` rule for the entire file by placing
73
73
  /* eslint-disable cypress/no-unnecessary-waiting */
74
74
  ```
75
75
 
76
- Disable the `cypress/no-unnecessary-waiting` rule for a portion of the file:
76
+ Disable the `cypress/no-unnecessary-waiting` rule for only a portion of the file:
77
77
 
78
78
  ```js
79
79
  it('waits for a second', () => {
@@ -121,6 +121,7 @@ Rules with a check mark (✅) are enabled by default while using the `plugin:cyp
121
121
  | ✅ | [no-assigning-return-values](./docs/rules/no-assigning-return-values.md) | Prevent assigning return values of cy calls |
122
122
  | ✅ | [no-unnecessary-waiting](./docs/rules/no-unnecessary-waiting.md) | Prevent waiting for arbitrary time periods |
123
123
  | ✅ | [no-async-tests](./docs/rules/no-async-tests.md) | Prevent using async/await in Cypress test case |
124
+ | ✅ | [unsafe-to-chain-command](./docs/rules/unsafe-to-chain-command.md) | Prevent chaining from unsafe to chain commands |
124
125
  | | [no-force](./docs/rules/no-force.md) | Disallow using `force: true` with action commands |
125
126
  | | [assertion-before-screenshot](./docs/rules/assertion-before-screenshot.md) | Ensure screenshots are preceded by an assertion |
126
127
  | | [require-data-selectors](./docs/rules/require-data-selectors.md) | Only allow data-\* attribute selectors (require-data-selectors) |
@@ -1,3 +1,3 @@
1
- ## No Assigning Return Values
1
+ ## No Unnecessary Waiting
2
2
 
3
3
  See [the Cypress Best Practices guide](https://on.cypress.io/best-practices#Unnecessary-Waiting).
@@ -0,0 +1,3 @@
1
+ ## Unsafe to chain command
2
+
3
+ See [retry-ability guide](https://docs.cypress.io/guides/core-concepts/retry-ability#Actions-should-be-at-the-end-of-chains-not-the-middle).
package/index.js CHANGED
@@ -3,6 +3,7 @@ const globals = require('globals')
3
3
  module.exports = {
4
4
  rules: {
5
5
  'no-assigning-return-values': require('./lib/rules/no-assigning-return-values'),
6
+ 'unsafe-to-chain-command': require('./lib/rules/unsafe-to-chain-command'),
6
7
  'no-unnecessary-waiting': require('./lib/rules/no-unnecessary-waiting'),
7
8
  'no-async-tests': require('./lib/rules/no-async-tests'),
8
9
  'assertion-before-screenshot': require('./lib/rules/assertion-before-screenshot'),
@@ -9,5 +9,6 @@ module.exports = {
9
9
  'cypress/no-assigning-return-values': 'error',
10
10
  'cypress/no-unnecessary-waiting': 'error',
11
11
  'cypress/no-async-tests': 'error',
12
+ 'cypress/unsafe-to-chain-command': 'error',
12
13
  },
13
14
  }
@@ -0,0 +1,47 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ meta: {
5
+ docs: {
6
+ description: 'Actions should be in the end of chains, not in the middle',
7
+ category: 'Possible Errors',
8
+ recommended: true,
9
+ url: 'https://docs.cypress.io/guides/core-concepts/retry-ability#Actions-should-be-at-the-end-of-chains-not-the-middle',
10
+ },
11
+ schema: [],
12
+ messages: {
13
+ unexpected: 'It is unsafe to chain further commands that rely on the subject after this command. It is best to split the chain, chaining again from `cy.` in a next command line.',
14
+ },
15
+ },
16
+ create (context) {
17
+ return {
18
+ CallExpression (node) {
19
+ if (isRootCypress(node) && isActionUnsafeToChain(node) && node.parent.type === 'MemberExpression') {
20
+ context.report({ node, messageId: 'unexpected' })
21
+ }
22
+ },
23
+ }
24
+ },
25
+ }
26
+
27
+ function isRootCypress (node) {
28
+ while (node.type === 'CallExpression') {
29
+ if (node.callee.type !== 'MemberExpression') return false
30
+
31
+ if (node.callee.object.type === 'Identifier' &&
32
+ node.callee.object.name === 'cy') {
33
+ return true
34
+ }
35
+
36
+ node = node.callee.object
37
+ }
38
+
39
+ return false
40
+ }
41
+
42
+ function isActionUnsafeToChain (node) {
43
+ // commands listed in the documentation with text: 'It is unsafe to chain further commands that rely on the subject after xxx'
44
+ const unsafeToChainActions = ['blur', 'clear', 'click', 'check', 'dblclick', 'each', 'focus', 'rightclick', 'screenshot', 'scrollIntoView', 'scrollTo', 'select', 'selectFile', 'spread', 'submit', 'type', 'trigger', 'uncheck', 'wait', 'within']
45
+
46
+ return node.callee && node.callee.property && node.callee.property.type === 'Identifier' && unsafeToChainActions.includes(node.callee.property.name)
47
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-cypress",
3
- "version": "2.12.1",
3
+ "version": "2.13.1",
4
4
  "description": "An ESLint plugin for projects using Cypress",
5
5
  "main": "index.js",
6
6
  "author": "Chris Breiding (chris@cypress.io)",
@@ -0,0 +1,20 @@
1
+ 'use strict'
2
+
3
+ const rule = require('../../../lib/rules/unsafe-to-chain-command')
4
+ const RuleTester = require('eslint').RuleTester
5
+
6
+ const ruleTester = new RuleTester()
7
+
8
+ const errors = [{ messageId: 'unexpected' }]
9
+ const parserOptions = { ecmaVersion: 6 }
10
+
11
+ ruleTester.run('action-ends-chain', rule, {
12
+ valid: [
13
+ { code: 'cy.get("new-todo").type("todo A{enter}"); cy.get("new-todo").type("todo B{enter}"); cy.get("new-todo").should("have.class", "active");', parserOptions },
14
+ ],
15
+
16
+ invalid: [
17
+ { code: 'cy.get("new-todo").type("todo A{enter}").should("have.class", "active");', parserOptions, errors },
18
+ { code: 'cy.get("new-todo").type("todo A{enter}").type("todo B{enter}");', parserOptions, errors },
19
+ ],
20
+ })