Skip to content

Commit 2c911ac

Browse files
duranbejennifer-shehaneMikeMcC399
authored
feat: add no-chained-get rule (#249)
* feat: add rule to disallow chain of get() calls * fix: replace double quotes with single quotes in no-chained-get.js * Update docs/rules/no-chained-get.md Co-authored-by: Mike McCready <[email protected]> * Update lib/rules/no-chained-get.js Co-authored-by: Mike McCready <[email protected]> * feat: add no-chained-get rule * feat: add no-chained-get rule --------- Co-authored-by: Jennifer Shehane <[email protected]> Co-authored-by: Mike McCready <[email protected]>
1 parent 4f062ac commit 2c911ac

File tree

6 files changed

+140
-0
lines changed

6 files changed

+140
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ These rules enforce some of the [best practices recommended for using Cypress](h
5050
| [no-assigning-return-values](docs/rules/no-assigning-return-values.md) | disallow assigning return values of `cy` calls ||
5151
| [no-async-before](docs/rules/no-async-before.md) | disallow using `async`/`await` in Cypress `before` methods | |
5252
| [no-async-tests](docs/rules/no-async-tests.md) | disallow using `async`/`await` in Cypress test cases ||
53+
| [no-chained-get](docs/rules/no-chained-get.md) | disallow chain of `cy.get()` calls | |
5354
| [no-debug](docs/rules/no-debug.md) | disallow using `cy.debug()` calls | |
5455
| [no-force](docs/rules/no-force.md) | disallow using `force: true` with action commands | |
5556
| [no-pause](docs/rules/no-pause.md) | disallow using `cy.pause()` calls | |

docs/rules/no-chained-get.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Disallow chain of `cy.get()` calls (`cypress/no-chained-get`)
2+
3+
<!-- end auto-generated rule header -->
4+
This rule disallows the usage of chained `.get()` calls as `cy.get()` always starts its search from the cy.root element.
5+
6+
## Rule Details
7+
8+
Examples of **incorrect** code for this rule:
9+
10+
```js
11+
cy.get('parent').get('child')
12+
```
13+
14+
Examples of **correct** code for this rule:
15+
16+
```js
17+
cy.get('parent')
18+
.find('child')
19+
```

legacy.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module.exports = {
1313
'no-pause': require('./lib/rules/no-pause'),
1414
'no-debug': require('./lib/rules/no-debug'),
1515
'no-xpath': require('./lib/rules/no-xpath'),
16+
'no-chained-get': require('./lib/rules/no-chained-get'),
1617
},
1718
configs: {
1819
recommended: require('./lib/config/recommended'),

lib/flat.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const plugin = {
1616
'no-pause': require('./rules/no-pause'),
1717
'no-debug': require('./rules/no-debug'),
1818
'no-xpath': require('./rules/no-xpath'),
19+
'no-chained-get': require('./rules/no-chained-get'),
1920
},
2021
}
2122

lib/rules/no-chained-get.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use strict';
2+
3+
//------------------------------------------------------------------------------
4+
// Rule Definition
5+
//------------------------------------------------------------------------------
6+
7+
/** @type {import('eslint').Rule.RuleModule} */
8+
module.exports = {
9+
meta: {
10+
type: 'problem',
11+
docs: {
12+
description: 'disallow chain of `cy.get()` calls',
13+
recommended: false,
14+
url: 'https://github.com/cypress-io/eslint-plugin-cypress/blob/master/docs/rules/no-chained-get.md',
15+
},
16+
fixable: null,
17+
schema: [],
18+
messages: {
19+
unexpected: 'Avoid chaining multiple cy.get() calls',
20+
},
21+
},
22+
23+
create(context) {
24+
const isRootCypress = (node) => {
25+
if (
26+
node.type !== 'CallExpression' ||
27+
node.callee.type !== 'MemberExpression'
28+
) {
29+
return false;
30+
}
31+
32+
if (
33+
node.callee.object.type === 'Identifier' &&
34+
node.callee.object.name === 'cy'
35+
) {
36+
return true;
37+
}
38+
39+
return isRootCypress(node.callee.object);
40+
};
41+
42+
const hasChainedGet = (node) => {
43+
// Check if this node is a get() call
44+
const isGetCall =
45+
node.callee &&
46+
node.callee.type === 'MemberExpression' &&
47+
node.callee.property &&
48+
node.callee.property.type === 'Identifier' &&
49+
node.callee.property.name === 'get';
50+
51+
if (!isGetCall) {
52+
return false;
53+
}
54+
55+
const obj = node.callee.object;
56+
57+
if (obj.type === 'CallExpression') {
58+
const objCallee = obj.callee;
59+
60+
if (
61+
objCallee &&
62+
objCallee.type === 'MemberExpression' &&
63+
objCallee.property &&
64+
objCallee.property.type === 'Identifier' &&
65+
objCallee.property.name === 'get'
66+
) {
67+
return true;
68+
}
69+
70+
return hasChainedGet(obj);
71+
}
72+
73+
return false;
74+
};
75+
76+
return {
77+
CallExpression(node) {
78+
if (isRootCypress(node) && hasChainedGet(node)) {
79+
context.report({
80+
node,
81+
messageId: 'unexpected',
82+
});
83+
}
84+
},
85+
};
86+
},
87+
};

tests/lib/rules/no-chained-get.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @fileoverview disallow chain of `cy.get()` calls
3+
* @author benoit
4+
*/
5+
"use strict";
6+
7+
//------------------------------------------------------------------------------
8+
// Requirements
9+
//------------------------------------------------------------------------------
10+
11+
const rule = require("../../../lib/rules/no-chained-get"),
12+
RuleTester = require("eslint").RuleTester;
13+
14+
//------------------------------------------------------------------------------
15+
// Tests
16+
//------------------------------------------------------------------------------
17+
18+
const ruleTester = new RuleTester();
19+
ruleTester.run("no-chained-get", rule, {
20+
valid: [
21+
{ code: "cy.get('div')" },
22+
{ code: "cy.get('.div').find().get()" },
23+
{ code: "cy.get('input').should('be.disabled')" },
24+
],
25+
invalid: [
26+
{
27+
code: "cy.get('div').get('div')",
28+
errors: [{ messageId: "unexpected" }],
29+
},
30+
],
31+
});

0 commit comments

Comments
 (0)