Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
9 changes: 9 additions & 0 deletions docs/rules/require-data-selectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,22 @@ cy.get('[daedta-cy=submit]').click()
cy.get('[d-cy=submit]')
cy.get('.btn-large').click()
cy.get('.btn-.large').click()

const CLASS_SELECTOR = ".my-class";
cy.get(CLASS_SELECTOR)
```

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

```js
cy.get('[data-cy=submit]').click()
cy.get('[data-QA=submit]')
cy.get(`[data-QA=submit]`)
```

```js
const ASSESSMENT_SUBMIT = "[data-cy=assessment-submit]"
cy.get(ASSESSMENT_SUBMIT).click()
```

## Further Reading
Expand Down
47 changes: 39 additions & 8 deletions lib/rules/require-data-selectors.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
'use strict'

module.exports = {
meta: {
type: 'suggestion',
Expand All @@ -16,9 +15,29 @@ module.exports = {
},

create(context) {
const variablesSet = new Set()
return {
VariableDeclarator(node) {
if (node.init && node.id && node.id.type === 'Identifier') {
let selectorValue = null

if (node.init.type === 'Literal' && typeof node.init.value === 'string') {
selectorValue = node.init.value
}
else if (node.init.type === 'TemplateLiteral'
&& node.init.expressions.length === 0
&& node.init.quasis.length === 1) {
selectorValue = node.init.quasis[0].value.cooked
}

if (selectorValue && isAliasOrDataSelector(selectorValue)) {
variablesSet.add(node.id.name, selectorValue)
}
}
},

CallExpression(node) {
if (isCallingCyGet(node) && !isDataArgument(node)) {
if (isCallingCyGet(node) && !isDataArgument(node, variablesSet)) {
context.report({ node, messageId: 'unexpected' })
}
},
Expand All @@ -34,12 +53,24 @@ function isCallingCyGet(node) {
&& node.callee.property.name === 'get'
}

function isDataArgument(node) {
return node.arguments.length > 0
&& (
(node.arguments[0].type === 'Literal' && isAliasOrDataSelector(String(node.arguments[0].value)))
|| (node.arguments[0].type === 'TemplateLiteral' && isAliasOrDataSelector(String(node.arguments[0].quasis[0].value.cooked)))
)
function isDataArgument(node, dataVariables) {
if (node.arguments.length === 0) return false

const firstArg = node.arguments[0]

if (firstArg.type === 'Literal') {
return isAliasOrDataSelector(String(firstArg.value))
}

if (firstArg.type === 'TemplateLiteral') {
return isAliasOrDataSelector(String(firstArg.quasis[0].value.cooked))
}

if (firstArg.type === 'Identifier') {
return dataVariables.has(firstArg.name)
}

return false
}

function isAliasOrDataSelector(selector) {
Expand Down
4 changes: 4 additions & 0 deletions tests/lib/rules/require-data-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ ruleTester.run('require-data-selectors', rule, {
{ code: 'cy.get(\`[data-cy=${1}]\`)' }, // eslint-disable-line no-useless-escape
{ code: 'cy.get("@my-alias")' },
{ code: 'cy.get(`@my-alias`)' },
{ code: 'const ASSESSMENT_SUBMIT = "[data-cy=assessment-submit]"; cy.get(ASSESSMENT_SUBMIT)' },
{ code: 'const ALIAS_TEMPLATE = `@my-alias`; cy.get(ALIAS_TEMPLATE)' },
],

invalid: [
Expand All @@ -26,5 +28,7 @@ ruleTester.run('require-data-selectors', rule, {
{ code: 'cy.get(".btn-.large").click()', errors },
{ code: 'cy.get(".a")', errors },
{ code: 'cy.get(\`[daedta-cy=${1}]\`)', errors }, // eslint-disable-line no-useless-escape
{ code: 'const BAD_SELECTOR = ".my-class"; cy.get(BAD_SELECTOR)', errors },
{ code: 'const GOOD = "[data-cy=good]"; const BAD = ".bad"; cy.get(GOOD); cy.get(BAD)', errors },
],
})