-
Notifications
You must be signed in to change notification settings - Fork 96
Expand file tree
/
Copy pathunsafe-to-chain-command.js
More file actions
123 lines (113 loc) · 2.99 KB
/
unsafe-to-chain-command.js
File metadata and controls
123 lines (113 loc) · 2.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
'use strict'
const { basename } = require('path')
const { isRootCypress } = require('./utils/is-root-cypress')
const NAME = basename(__dirname)
const DESCRIPTION = 'disallow actions within chains'
/**
* Commands listed in the documentation with text: 'It is unsafe to chain further commands that rely on the subject after xxx.'
* See {@link https://docs.cypress.io/app/core-concepts/retry-ability#Actions-should-be-at-the-end-of-chains-not-the-middle Actions should be at the end of chains, not the middle}
* for more information.
*
* @type {string[]}
*/
const unsafeToChainActions = [
'blur',
'clear',
'click',
'check',
'dblclick',
'each',
'focus',
'rightclick',
'screenshot',
'scrollIntoView',
'scrollTo',
'select',
'selectFile',
'spread',
'submit',
'type',
'trigger',
'uncheck',
'within',
]
/**
* @type {import('eslint').Rule.RuleMetaData['schema']}
*/
const schema = {
title: NAME,
description: DESCRIPTION,
type: 'object',
properties: {
methods: {
type: 'array',
description:
'An additional list of methods to check for unsafe chaining.',
default: [],
},
},
additionalProperties: false,
}
/**
* @param {import('eslint').Rule.RuleContext} context
* @returns {Record<string, any>}
*/
const getDefaultOptions = (context) => {
return Object.entries(schema.properties).reduce((acc, [key, value]) => {
if (!(value.default in value)) return acc
return {
...acc,
[key]: value.default,
}
}, context.options[0] || {})
}
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
docs: {
description: DESCRIPTION,
category: 'Possible Errors',
recommended: true,
url: 'https://github.com/cypress-io/eslint-plugin-cypress/blob/master/docs/rules/unsafe-to-chain-command.md',
},
schema: [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) {
const { methods } = getDefaultOptions(context)
return {
CallExpression(node) {
if (
isRootCypress(node, context)
&& isActionUnsafeToChain(node, methods)
&& node.parent.type === 'MemberExpression'
) {
context.report({
node,
messageId: 'unexpected',
})
}
},
}
},
}
/**
* @param {import('estree').Node} node
* @param {(string | RegExp)[]} additionalMethods
*/
const isActionUnsafeToChain = (node, additionalMethods = []) => {
const unsafeActionsRegex = new RegExp([
...unsafeToChainActions.map((action) => `^${action}$`),
...additionalMethods.map((method) => method instanceof RegExp ? method.source : method),
].join('|'))
return (
node.callee
&& node.callee.property
&& node.callee.property.type === 'Identifier'
&& unsafeActionsRegex.test(node.callee.property.name)
)
}