Skip to content

Commit 74f592b

Browse files
committed
Adds a recommended config with new no-assigning-return-values rule
Adds an opinionated rule as suggested in #4. Tries to help with the "Assigning Return Values" Best Practice described in the guide: <https://docs.cypress.io/guides/references/best-practices.html#Assigning-Return-Values>
1 parent f02931b commit 74f592b

File tree

7 files changed

+134
-1
lines changed

7 files changed

+134
-1
lines changed

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,32 @@ Add an `.eslintrc.json` file to your `cypress` directory with the following:
2121
"plugins": [
2222
"cypress"
2323
],
24+
"extends": [
25+
"plugin:cypress/recommended"
26+
],
2427
"env": {
2528
"cypress/globals": true
2629
}
2730
}
2831
```
2932

33+
## Rules
34+
35+
Rules are grouped by category to help you understand their purpose.
36+
37+
Rules with a check mark (✅) are enabled by default while using
38+
the `plugin:cypress/recommended` config.
39+
40+
The --fix option on the command line automatically fixes problems reported by
41+
rules which have a wrench (🔧) below.
42+
43+
44+
### Possible Errors
45+
46+
| | Rule ID | Description |
47+
|:---|:--------|:------------|
48+
|| [no-assigning-return-values](./docs/rules/no-assigning-return-values.md) | Prevent assigning return values of cy calls |
49+
3050
## Chai and `no-unused-expressions`
3151

3252
Using an assertion such as `expect(value).to.be.true` can fail the ESLint rule `no-unused-expressions` even though it's not an error in this case. To fix this, you can install and use [eslint-plugin-chai-friendly](https://www.npmjs.com/package/eslint-plugin-chai-friendly).
@@ -49,3 +69,15 @@ In your `.eslintrc.json`:
4969
}
5070
}
5171
```
72+
73+
## Contribution Guide
74+
75+
To add a new rule:
76+
* Fork and clone this repository
77+
* Generate a new rule (a [yeoman generator](https://github.com/eslint/generator-eslint) is available)
78+
* Run `yarn start` or `npm start`
79+
* Write test scenarios then implement logic
80+
* Describe the rule in the generated `docs` file
81+
* Make sure all tests are passing
82+
* Add the rule to this README
83+
* Create a PR
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## No Assigning Return Values
2+
3+
See [the Cypress Best Practices guide](https://docs.cypress.io/guides/references/best-practices.html#Assigning-Return-Values).

index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
const globals = require('globals')
22

33
module.exports = {
4+
rules: {
5+
'no-assigning-return-values': require('./lib/rules/no-assigning-return-values')
6+
},
7+
configs: {
8+
recommended: require('./lib/config/recommended')
9+
},
410
environments: {
511
globals: {
612
globals: Object.assign(globals.browser, globals.mocha, {

lib/config/recommended.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use strict';
2+
3+
module.exports = {
4+
plugins: ['cypress'],
5+
rules: {
6+
'cypress/no-assigning-return-values': 'error'
7+
}
8+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @fileoverview Prevent assigning return value of cy calls
3+
* @author Elad Shahar
4+
*/
5+
6+
'use strict';
7+
8+
module.exports = {
9+
meta: {
10+
docs: {
11+
description: 'Prevent assigning return values of cy calls',
12+
category: 'Possible Errors',
13+
recommended: true,
14+
url: 'https://docs.cypress.io/guides/references/best-practices.html#Assigning-Return-Values'
15+
},
16+
schema: [],
17+
messages: {
18+
unexpected: 'Do not assign the return value of a Cypress command'
19+
}
20+
},
21+
create(context) {
22+
return {
23+
VariableDeclaration(node) {
24+
if (node.declarations.some(isCypressCommandDeclaration)) {
25+
context.report({ node, messageId: 'unexpected' });
26+
}
27+
}
28+
};
29+
}
30+
};
31+
32+
function isCypressCommandDeclaration(declarator) {
33+
if (!declarator) { return; }
34+
if (!declarator.init) { return; }
35+
if (!declarator.init.callee) { return; }
36+
37+
let object = declarator.init.callee.object;
38+
while (object.callee) {
39+
object = object.callee.object;
40+
}
41+
42+
return object.name === 'cy';
43+
}

package.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,28 @@
2626
},
2727
"devDependencies": {
2828
"condition-circle": "1.5.0",
29+
"eslint": "^5.7.0",
2930
"github-post-release": "1.13.1",
3031
"husky": "^0.14.3",
32+
"jest": "^23.6.0",
3133
"semantic-release": "8.2.0",
3234
"simple-commit-message": "3.3.1"
3335
},
3436
"scripts": {
3537
"precommit": "eslint *.js",
36-
"semantic-release": "semantic-release pre && npm publish --access public && semantic-release post"
38+
"semantic-release": "semantic-release pre && npm publish --access public && semantic-release post",
39+
"start": "yarn run test:watch",
40+
"test": "jest",
41+
"test-watch": "jest --watchAll"
3742
},
3843
"release": {
3944
"verifyConditions": "condition-circle",
4045
"analyzeCommits": "simple-commit-message",
4146
"generateNotes": "github-post-release"
47+
},
48+
"jest": {
49+
"testMatch": [
50+
"**/tests/**/*.js"
51+
]
4252
}
4353
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"use strict";
2+
3+
const rule = require('../../../lib/rules/no-assigning-return-values');
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('no-assigning-return-values', rule, {
12+
valid: [
13+
{ code: 'var foo = true;', parserOptions },
14+
{ code: 'let foo = true;', parserOptions },
15+
{ code: 'const foo = true;', parserOptions },
16+
{ code: 'cy.get("foo");', parserOptions },
17+
{ code: 'cy.contains("foo").click();', parserOptions }
18+
],
19+
20+
invalid: [
21+
{ code: 'let a = cy.get("foo")', parserOptions, errors },
22+
{ code: 'const a = cy.get("foo")', parserOptions, errors },
23+
{ code: 'var a = cy.get("foo")', parserOptions, errors },
24+
25+
{ code: 'let a = cy.contains("foo")', parserOptions, errors },
26+
{ code: 'let a = cy.window()', parserOptions, errors },
27+
{ code: 'let a = cy.wait("@something")', parserOptions, errors },
28+
29+
{ code: 'let a = cy.contains("foo").click()', parserOptions, errors }
30+
]
31+
});

0 commit comments

Comments
 (0)