eslint-plugin-cypress 2.11.1 → 2.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@ npm install eslint-plugin-cypress --save-dev
11
11
  ```
12
12
  or
13
13
  ```sh
14
- yarn add eslint-plugin-cypress --save-dev
14
+ yarn add eslint-plugin-cypress --dev
15
15
  ```
16
16
 
17
17
  ## Usage
@@ -35,12 +35,13 @@ You can add rules:
35
35
  "cypress/no-unnecessary-waiting": "error",
36
36
  "cypress/assertion-before-screenshot": "warn",
37
37
  "cypress/no-force": "warn",
38
- "cypress/no-async-tests": "error"
38
+ "cypress/no-async-tests": "error",
39
+ "cypress/no-pause": "error"
39
40
  }
40
41
  }
41
42
  ```
42
43
 
43
- You can whitelist globals provided by Cypress:
44
+ You can allow certain globals provided by Cypress:
44
45
 
45
46
  ```json
46
47
  {
@@ -62,6 +63,51 @@ Use the recommended configuration and you can forego configuring _plugins_, _rul
62
63
  }
63
64
  ```
64
65
 
66
+ ## Disable rules
67
+
68
+ You can disable specific rules per file, for a portion of a file, or for a single line.
69
+
70
+ Disable the `cypress/no-unnecessary-waiting` rule for the entire file by placing this at the start of the file:
71
+
72
+ ```js
73
+ /* eslint-disable cypress/no-unnecessary-waiting */
74
+ ```
75
+
76
+ Disable the `cypress/no-unnecessary-waiting` rule for a portion of the file:
77
+
78
+ ```js
79
+ it('waits for a second', () => {
80
+ ...
81
+ /* eslint-disable cypress/no-unnecessary-waiting */
82
+ cy.wait(1000)
83
+ /* eslint-enable cypress/no-unnecessary-waiting */
84
+ ...
85
+ })
86
+ ```
87
+
88
+ Disable the `cypress/no-unnecessary-waiting` rule for a specific line:
89
+
90
+ ```js
91
+ it('waits for a second', () => {
92
+ ...
93
+ cy.wait(1000) // eslint-disable-line cypress/no-unnecessary-waiting
94
+ ...
95
+ })
96
+ ```
97
+
98
+ You can also disable a rule for the next line:
99
+
100
+ ```js
101
+ it('waits for a second', () => {
102
+ ...
103
+ // eslint-disable-next-line cypress/no-unnecessary-waiting
104
+ cy.wait(1000)
105
+ ...
106
+ })
107
+ ```
108
+
109
+ For more, see the [ESLint rules](https://eslint.org/docs/user-guide/configuring/rules) documentation.
110
+
65
111
  ## Rules
66
112
 
67
113
  These rules enforce some of the [best practices recommended for using Cypress](https://on.cypress.io/best-practices).
@@ -78,6 +124,7 @@ Rules with a check mark (✅) are enabled by default while using the `plugin:cyp
78
124
  | | [no-force](./docs/rules/no-force.md) | Disallow using `force: true` with action commands |
79
125
  | | [assertion-before-screenshot](./docs/rules/assertion-before-screenshot.md) | Ensure screenshots are preceded by an assertion |
80
126
  | | [require-data-selectors](./docs/rules/require-data-selectors.md) | Only allow data-\* attribute selectors (require-data-selectors) |
127
+ | | [no-pause](./docs/rules/no-pause.md) | Disallow `cy.pause()` parent command |
81
128
 
82
129
  ## Chai and `no-unused-expressions`
83
130
 
@@ -0,0 +1,16 @@
1
+ ## Do not use `cy.pause` command
2
+
3
+ It is recommended to remove [cy.pause](https://on.cypress.io/pause) command before committing the specs to avoid other developers getting unexpected results.
4
+
5
+ Invalid:
6
+
7
+ ```js
8
+ cy.pause();
9
+ ```
10
+
11
+ Valid:
12
+
13
+ ```js
14
+ // only the parent cy.pause command is detected
15
+ cy.get('selector').pause();
16
+ ```
package/index.js CHANGED
@@ -8,6 +8,7 @@ module.exports = {
8
8
  'assertion-before-screenshot': require('./lib/rules/assertion-before-screenshot'),
9
9
  'require-data-selectors': require('./lib/rules/require-data-selectors'),
10
10
  'no-force': require('./lib/rules/no-force'),
11
+ 'no-pause': require('./lib/rules/no-pause'),
11
12
  },
12
13
  configs: {
13
14
  recommended: require('./lib/config/recommended'),
@@ -39,7 +39,7 @@ module.exports = {
39
39
  },
40
40
  }
41
41
 
42
- const whitelistedCommands = {
42
+ const allowedCommands = {
43
43
  now: true,
44
44
  spy: true,
45
45
  state: true,
@@ -61,7 +61,7 @@ function isCypressCommandDeclaration (declarator) {
61
61
 
62
62
  const parent = get(object, 'parent.property.name') || get(declarator, 'id.name')
63
63
 
64
- if (commandName && (whitelistedCommands[commandName] || whitelistedCommands[parent])) return
64
+ if (commandName && (allowedCommands[commandName] || allowedCommands[parent])) return
65
65
 
66
66
  return object.name === 'cy'
67
67
  }
@@ -42,7 +42,7 @@ module.exports = {
42
42
 
43
43
  return node.arguments && node.arguments.length &&
44
44
  node.arguments.some((arg) => {
45
- return arg.type === 'ObjectExpression' && arg.properties.some((propNode) => propNode.key.name === 'force')
45
+ return arg.type === 'ObjectExpression' && arg.properties.some((propNode) => propNode.key && propNode.key.name === 'force')
46
46
  })
47
47
  }
48
48
 
@@ -0,0 +1,56 @@
1
+ 'use strict'
2
+
3
+ //------------------------------------------------------------------------------
4
+ // Rule Definition
5
+ //------------------------------------------------------------------------------
6
+
7
+ module.exports = {
8
+ meta: {
9
+ docs: {
10
+ description: 'Disallow using of \'cy.pause\' calls',
11
+ category: 'Possible Errors',
12
+ recommended: false,
13
+ },
14
+ fixable: null, // or "code" or "whitespace"
15
+ schema: [],
16
+ messages: {
17
+ unexpected: 'Do not use cy.pause command',
18
+ },
19
+ },
20
+
21
+ create (context) {
22
+
23
+ // variables should be defined here
24
+
25
+ //----------------------------------------------------------------------
26
+ // Helpers
27
+ //----------------------------------------------------------------------
28
+ function isCallingPause (node) {
29
+ return node.callee &&
30
+ node.callee.property &&
31
+ node.callee.property.type === 'Identifier' &&
32
+ node.callee.property.name === 'pause'
33
+ }
34
+
35
+ function isCypressCall (node) {
36
+ return node.callee &&
37
+ node.callee.type === 'MemberExpression' &&
38
+ node.callee.object.type === 'Identifier' &&
39
+ node.callee.object.name === 'cy'
40
+ }
41
+
42
+ //----------------------------------------------------------------------
43
+ // Public
44
+ //----------------------------------------------------------------------
45
+
46
+ return {
47
+
48
+ CallExpression (node) {
49
+ if (isCypressCall(node) && isCallingPause(node)) {
50
+ context.report({ node, messageId: 'unexpected' })
51
+ }
52
+ },
53
+
54
+ }
55
+ },
56
+ }
@@ -56,7 +56,8 @@ function isIdentifierNumberConstArgument (node, scope) {
56
56
 
57
57
  if (node.arguments[0].type !== 'Identifier') return false
58
58
 
59
- const resolvedIdentifier = scope.resolve(node.arguments[0]).resolved
59
+ const identifier = node.arguments[0]
60
+ const resolvedIdentifier = scope.references.find((ref) => ref.identifier === identifier).resolved
60
61
  const definition = resolvedIdentifier.defs[0]
61
62
  const isVariable = definition.type === 'Variable'
62
63
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-cypress",
3
- "version": "2.11.1",
3
+ "version": "2.12.1",
4
4
  "description": "An ESLint plugin for projects using Cypress",
5
5
  "main": "index.js",
6
6
  "author": "Chris Breiding (chris@cypress.io)",
@@ -9,7 +9,7 @@ const rule = require('../../../lib/rules/no-force')
9
9
  const RuleTester = require('eslint').RuleTester
10
10
 
11
11
  const errors = [{ messageId: 'unexpected' }]
12
- const parserOptions = { ecmaVersion: 6 }
12
+ const parserOptions = { ecmaVersion: 2018 }
13
13
 
14
14
  //------------------------------------------------------------------------------
15
15
  // Tests
@@ -24,12 +24,13 @@ ruleTester.run('no-force', rule, {
24
24
  { code: `cy.get('button').click({multiple: true})`, parserOptions },
25
25
  { code: `cy.get('button').dblclick()`, parserOptions },
26
26
  { code: `cy.get('input').type('somth')`, parserOptions },
27
- { code: `cy.get('input').type('somth', {anyoption: true})`, parserOptions, errors },
28
- { code: `cy.get('input').trigger('click', {anyoption: true})`, parserOptions, errors },
29
- { code: `cy.get('input').rightclick({anyoption: true})`, parserOptions, errors },
30
- { code: `cy.get('input').check()`, parserOptions, errors },
31
- { code: `cy.get('input').select()`, parserOptions, errors },
32
- { code: `cy.get('input').focus()`, parserOptions, errors },
27
+ { code: `cy.get('input').type('somth', {anyoption: true})`, parserOptions },
28
+ { code: `cy.get('input').trigger('click', {anyoption: true})`, parserOptions },
29
+ { code: `cy.get('input').rightclick({anyoption: true})`, parserOptions },
30
+ { code: `cy.get('input').check()`, parserOptions },
31
+ { code: `cy.get('input').select()`, parserOptions },
32
+ { code: `cy.get('input').focus()`, parserOptions },
33
+ { code: `cy.document().trigger("keydown", { ...event })`, parserOptions },
33
34
  ],
34
35
 
35
36
  invalid: [
@@ -0,0 +1,33 @@
1
+ 'use strict'
2
+
3
+ //------------------------------------------------------------------------------
4
+ // Requirements
5
+ //------------------------------------------------------------------------------
6
+
7
+ const rule = require('../../../lib/rules/no-pause')
8
+
9
+ const RuleTester = require('eslint').RuleTester
10
+
11
+ const errors = [{ messageId: 'unexpected' }]
12
+ const parserOptions = { ecmaVersion: 2018 }
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Tests
16
+ //------------------------------------------------------------------------------
17
+
18
+ const ruleTester = new RuleTester()
19
+
20
+ ruleTester.run('no-pause', rule, {
21
+
22
+ valid: [
23
+ // for now, we do not detect .pause() child command
24
+ { code: `cy.get('button').pause()`, parserOptions },
25
+ { code: `pause()`, parserOptions },
26
+ { code: `cy.get('button').dblclick()`, parserOptions },
27
+ ],
28
+
29
+ invalid: [
30
+ { code: `cy.pause()`, parserOptions, errors },
31
+ { code: `cy.pause({ log: false })`, parserOptions, errors },
32
+ ],
33
+ })
@@ -26,6 +26,29 @@ ruleTester.run('no-unnecessary-waiting', rule, {
26
26
  { code: 'const customWait = (alias = "@someRequest") => { cy.wait(alias) }', parserOptions, errors },
27
27
  { code: 'function customWait (ms) { cy.wait(ms) }', parserOptions, errors },
28
28
  { code: 'const customWait = (ms) => { cy.wait(ms) }', parserOptions, errors },
29
+
30
+ // disable the eslint rule
31
+ {
32
+ code: `
33
+ cy.wait(100); // eslint-disable-line no-unnecessary-waiting
34
+ `,
35
+ parserOptions,
36
+ },
37
+ {
38
+ code: `
39
+ /* eslint-disable-next-line no-unnecessary-waiting */
40
+ cy.wait(100)
41
+ `,
42
+ parserOptions,
43
+ },
44
+ {
45
+ code: `
46
+ /* eslint-disable no-unnecessary-waiting */
47
+ cy.wait(100)
48
+ /* eslint-enable no-unnecessary-waiting */
49
+ `,
50
+ parserOptions,
51
+ },
29
52
  ],
30
53
 
31
54
  invalid: [