Skip to content

Commit bd3bc83

Browse files
Merge branch 'master' into no-unnecessary-waiting-support-import-bindings
2 parents 58ae363 + 402c59a commit bd3bc83

File tree

9 files changed

+96
-2
lines changed

9 files changed

+96
-2
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name: 'Handle Comment Workflow'
2+
on:
3+
issue_comment:
4+
types:
5+
- created
6+
jobs:
7+
closed-issue-comment:
8+
uses: cypress-io/cypress/.github/workflows/triage_handle_new_comments.yml@develop
9+
secrets: inherit

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ Disable the `cypress/no-unnecessary-waiting` rule for the entire file by placing
7373
/* eslint-disable cypress/no-unnecessary-waiting */
7474
```
7575

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:
7777

7878
```js
7979
it('waits for a second', () => {
@@ -121,6 +121,7 @@ Rules with a check mark (✅) are enabled by default while using the `plugin:cyp
121121
|| [no-assigning-return-values](./docs/rules/no-assigning-return-values.md) | Prevent assigning return values of cy calls |
122122
|| [no-unnecessary-waiting](./docs/rules/no-unnecessary-waiting.md) | Prevent waiting for arbitrary time periods |
123123
|| [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 |
124125
| | [no-force](./docs/rules/no-force.md) | Disallow using `force: true` with action commands |
125126
| | [assertion-before-screenshot](./docs/rules/assertion-before-screenshot.md) | Ensure screenshots are preceded by an assertion |
126127
| | [require-data-selectors](./docs/rules/require-data-selectors.md) | Only allow data-\* attribute selectors (require-data-selectors) |
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
## No Assigning Return Values
1+
## No Unnecessary Waiting
22

33
See [the Cypress Best Practices guide](https://on.cypress.io/best-practices#Unnecessary-Waiting).
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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).

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const globals = require('globals')
33
module.exports = {
44
rules: {
55
'no-assigning-return-values': require('./lib/rules/no-assigning-return-values'),
6+
'unsafe-to-chain-command': require('./lib/rules/unsafe-to-chain-command'),
67
'no-unnecessary-waiting': require('./lib/rules/no-unnecessary-waiting'),
78
'no-async-tests': require('./lib/rules/no-async-tests'),
89
'assertion-before-screenshot': require('./lib/rules/assertion-before-screenshot'),

lib/config/recommended.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ module.exports = {
99
'cypress/no-assigning-return-values': 'error',
1010
'cypress/no-unnecessary-waiting': 'error',
1111
'cypress/no-async-tests': 'error',
12+
'cypress/unsafe-to-chain-command': 'error',
1213
},
1314
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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', 'within']
45+
46+
return node.callee && node.callee.property && node.callee.property.type === 'Identifier' && unsafeToChainActions.includes(node.callee.property.name)
47+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
})

0 commit comments

Comments
 (0)