Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ These rules enforce some of the [best practices recommended for using Cypress](h
| [no-assigning-return-values](docs/rules/no-assigning-return-values.md) | disallow assigning return values of `cy` calls | ✅ |
| [no-async-before](docs/rules/no-async-before.md) | disallow using `async`/`await` in Cypress `before` methods | |
| [no-async-tests](docs/rules/no-async-tests.md) | disallow using `async`/`await` in Cypress test cases | ✅ |
| [no-chained-get](docs/rules/no-chained-get.md) | disallow chain of `cy.get()` calls | |
| [no-debug](docs/rules/no-debug.md) | disallow using `cy.debug()` calls | |
| [no-force](docs/rules/no-force.md) | disallow using `force: true` with action commands | |
| [no-pause](docs/rules/no-pause.md) | disallow using `cy.pause()` calls | |
Expand Down
19 changes: 19 additions & 0 deletions docs/rules/no-chained-get.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Disallow chain of `cy.get()` calls (`cypress/no-chained-get`)

<!-- end auto-generated rule header -->
This rule disallows the usage of chained `.get()` calls as `cy.get()` always starts its search from the cy.root element.

## Rule Details

Examples of **incorrect** code for this rule:

```js
cy.get('parent').get('child')
```

Examples of **correct** code for this rule:

```js
cy.get('parent')
.find('child')
```
1 change: 1 addition & 0 deletions legacy.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = {
'no-pause': require('./lib/rules/no-pause'),
'no-debug': require('./lib/rules/no-debug'),
'no-xpath': require('./lib/rules/no-xpath'),
'no-chained-get': require('./lib/rules/no-chained-get'),
},
configs: {
recommended: require('./lib/config/recommended'),
Expand Down
1 change: 1 addition & 0 deletions lib/flat.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const plugin = {
'no-pause': require('./rules/no-pause'),
'no-debug': require('./rules/no-debug'),
'no-xpath': require('./rules/no-xpath'),
'no-chained-get': require('./rules/no-chained-get'),
},
}

Expand Down
87 changes: 87 additions & 0 deletions lib/rules/no-chained-get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use strict';

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'disallow chain of `cy.get()` calls',
recommended: false,
url: 'https://github.com/cypress-io/eslint-plugin-cypress/blob/master/docs/rules/no-chained-get.md',
},
fixable: null,
schema: [],
messages: {
unexpected: 'Avoid chaining multiple cy.get() calls',
},
},

create(context) {
const isRootCypress = (node) => {
if (
node.type !== 'CallExpression' ||
node.callee.type !== 'MemberExpression'
) {
return false;
}

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

return isRootCypress(node.callee.object);
};

const hasChainedGet = (node) => {
// Check if this node is a get() call
const isGetCall =
node.callee &&
node.callee.type === 'MemberExpression' &&
node.callee.property &&
node.callee.property.type === 'Identifier' &&
node.callee.property.name === 'get';

if (!isGetCall) {
return false;
}

const obj = node.callee.object;

if (obj.type === 'CallExpression') {
const objCallee = obj.callee;

if (
objCallee &&
objCallee.type === 'MemberExpression' &&
objCallee.property &&
objCallee.property.type === 'Identifier' &&
objCallee.property.name === 'get'
) {
return true;
}

return hasChainedGet(obj);
}

return false;
};

return {
CallExpression(node) {
if (isRootCypress(node) && hasChainedGet(node)) {
context.report({
node,
messageId: 'unexpected',
});
}
},
};
},
};
31 changes: 31 additions & 0 deletions tests/lib/rules/no-chained-get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @fileoverview disallow chain of `cy.get()` calls
* @author benoit
*/
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const rule = require("../../../lib/rules/no-chained-get"),
RuleTester = require("eslint").RuleTester;

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const ruleTester = new RuleTester();
ruleTester.run("no-chained-get", rule, {
valid: [
{ code: "cy.get('div')" },
{ code: "cy.get('.div').find().get()" },
{ code: "cy.get('input').should('be.disabled')" },
],
invalid: [
{
code: "cy.get('div').get('div')",
errors: [{ messageId: "unexpected" }],
},
],
});