Skip to content

Commit

Permalink
feat: Add 'unsafe-to-chain-command' rule
Browse files Browse the repository at this point in the history
  • Loading branch information
Erik-Outreach committed Dec 28, 2022
1 parent 874c51f commit bf6a75d
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ Rules with a check mark (✅) are enabled by default while using the `plugin:cyp
|| [no-assigning-return-values](./docs/rules/no-assigning-return-values.md) | Prevent assigning return values of cy calls |
|| [no-unnecessary-waiting](./docs/rules/no-unnecessary-waiting.md) | Prevent waiting for arbitrary time periods |
|| [no-async-tests](./docs/rules/no-async-tests.md) | Prevent using async/await in Cypress test case |
|| [unsafe-to-chain-command](./docs/rules/unsafe-to-chain-command.md) | Prevent chaining from unsafe to chain commands |
| | [no-force](./docs/rules/no-force.md) | Disallow using `force: true` with action commands |
| | [assertion-before-screenshot](./docs/rules/assertion-before-screenshot.md) | Ensure screenshots are preceded by an assertion |
| | [require-data-selectors](./docs/rules/require-data-selectors.md) | Only allow data-\* attribute selectors (require-data-selectors) |
Expand Down
3 changes: 3 additions & 0 deletions docs/rules/unsafe-to-chain-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Unsafe to chain command

See [retry-ability guide](https://docs.cypress.io/guides/core-concepts/retry-ability#Actions-should-be-at-the-end-of-chains-not-the-middle).
47 changes: 47 additions & 0 deletions lib/rules/unsafe-to-chain-command.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict'

module.exports = {
meta: {
docs: {
description: 'Actions should be in the end of chains, not in the middle',
category: 'Possible Errors',
recommended: true,
url: 'https://docs.cypress.io/guides/core-concepts/retry-ability#Actions-should-be-at-the-end-of-chains-not-the-middle',
},
schema: [],
messages: {
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.',
},
},
create (context) {
return {
CallExpression (node) {
if (isRootCypress(node) && isActionUnsafeToChain(node) && node.parent.type === 'MemberExpression') {
context.report({ node, messageId: 'unexpected' })
}
},
}
},
}

function isRootCypress (node) {
while (node.type === 'CallExpression') {
if (node.callee.type !== 'MemberExpression') return false

if (node.callee.object.type === 'Identifier' &&
node.callee.object.name === 'cy') {
return true
}

node = node.callee.object
}

return false
}

function isActionUnsafeToChain (node) {
// commands listed in the documentation with text: 'It is unsafe to chain further commands that rely on the subject after xxx'
const unsafeToChainActions = ['blur', 'clear', 'click', 'check', 'dblclick', 'each', 'focus', 'rightclick', 'screenshot', 'scrollIntoView', 'scrollTo', 'select', 'selectFile', 'spread', 'submit', 'type', 'trigger', 'uncheck', 'wait', 'within']

return node.callee && node.callee.property && node.callee.property.type === 'Identifier' && unsafeToChainActions.includes(node.callee.property.name)
}
20 changes: 20 additions & 0 deletions tests/lib/rules/unsafe-to-chain-command.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict'

const rule = require('../../../lib/rules/unsafe-to-chain-command')
const RuleTester = require('eslint').RuleTester

const ruleTester = new RuleTester()

const errors = [{ messageId: 'unexpected' }]
const parserOptions = { ecmaVersion: 6 }

ruleTester.run('action-ends-chain', rule, {
valid: [
{ 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 },
],

invalid: [
{ code: 'cy.get("new-todo").type("todo A{enter}").should("have.class", "active");', parserOptions, errors },
{ code: 'cy.get("new-todo").type("todo A{enter}").type("todo B{enter}");', parserOptions, errors },
],
})

0 comments on commit bf6a75d

Please sign in to comment.