Skip to content

Commit 9f2bf50

Browse files
authored
Merge pull request #118 from Erik-Outreach/unsafe-to-chain-command
feat: Add 'unsafe-to-chain-command' rule
2 parents 874c51f + bf6a75d commit 9f2bf50

File tree

4 files changed

+71
-0
lines changed

4 files changed

+71
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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: 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).
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', 'wait', '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)