Skip to content

Commit

Permalink
feat(rule): add 'require-data-selectors' rule (#30)
Browse files Browse the repository at this point in the history
Only allow `[data-*]` attribute selectors on `cy.get`
  • Loading branch information
sandeepbaldawa authored and kuceb committed Sep 26, 2019
1 parent 7fabf07 commit a4a0e8e
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 0 deletions.
23 changes: 23 additions & 0 deletions docs/rules/require-data-selectors.md
@@ -0,0 +1,23 @@
## Only allow `data-*` attribute selectors (require-data-selectors)
only allow `cy.get` to allow selectors that target `data-*` attributes

See [the Cypress Best Practices guide](https://docs.cypress.io/guides/references/best-practices.html#Selecting-Elements).

> Note: If you use this rule, consider only using the `warn` error level, since using `data-*` attribute selectors may not always be possible.
### Rule Details

examples of **incorrect** code with `require-data-selectors`:
```js
cy.get(".a")
cy.get('[daedta-cy=submit]').click()
cy.get('[d-cy=submit]')
cy.get(".btn-large").click()
cy.get(".btn-.large").click()
```

examples of **correct** code with `require-data-selectors`:
```js
cy.get('[data-cy=submit]').click()
cy.get('[data-QA=submit]')
```
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -5,6 +5,7 @@ module.exports = {
'no-assigning-return-values': require('./lib/rules/no-assigning-return-values'),
'no-unnecessary-waiting': require('./lib/rules/no-unnecessary-waiting'),
'assertion-before-screenshot': require('./lib/rules/assertion-before-screenshot'),
'require-data-selectors': require('./lib/rules/require-data-selectors'),
},
configs: {
recommended: require('./lib/config/recommended'),
Expand Down
45 changes: 45 additions & 0 deletions lib/rules/require-data-selectors.js
@@ -0,0 +1,45 @@
/**
* @fileoverview Use data-* attribute selectors instead of classes or tag names
* @author Sandeep Baldawa
*/
'use strict'

module.exports = {
meta: {
docs: {
description: 'Use data-* attributes to provide context to your selectors and insulate them from CSS or JS changes https://docs.cypress.io/guides/references/best-practices.html#Selecting-Elements',
category: 'Possible Errors',
recommended: false,
url: 'https://docs.cypress.io/guides/references/best-practices.html#Selecting-Elements',
},
schema: [],
messages: {
unexpected: 'use data-* attribute selectors instead of classes or tag names',
},
},

create (context) {
return {
CallExpression (node) {
if (isCallingCyGet(node) && !isDataArgument(node)) {
context.report({ node, messageId: 'unexpected' })
}
},
}
},
}

function isCallingCyGet (node) {
return node.callee.type === 'MemberExpression' &&
node.callee.object.type === 'Identifier' &&
node.callee.object.name === 'cy' &&
node.callee.property.type === 'Identifier' &&
node.callee.property.name === 'get'
}

function isDataArgument (node) {
return node.arguments.length > 0 &&
node.arguments[0].type === 'Literal' &&
String(node.arguments[0].value).startsWith('[data-')

}
27 changes: 27 additions & 0 deletions tests/lib/rules/require-data-selectors.js
@@ -0,0 +1,27 @@
'use strict'

const rule = require('../../../lib/rules/require-data-selectors')
const RuleTester = require('eslint').RuleTester

const ruleTester = new RuleTester()

const errors = [{ messageId: 'unexpected' }]
const parserOptions = { ecmaVersion: 6 }

ruleTester.run('no-dynamic-id-classes', rule, {
valid: [
{ code: 'cy.get(\'[data-cy=submit]\').click()', parserOptions },
{ code: 'cy.get(\'[data-QA=submit]\')', parserOptions },
{ code: 'cy.clock(5000)', parserOptions },
{ code: 'cy.scrollTo(0, 10)', parserOptions },
{ code: 'cy.tick(500)', parserOptions },
],

invalid: [
{ code: 'cy.get(\'[daedta-cy=submit]\').click()', parserOptions, errors },
{ code: 'cy.get(\'[d-cy=submit]\')', parserOptions, errors },
{ code: 'cy.get(".btn-large").click()', parserOptions, errors },
{ code: 'cy.get(".btn-.large").click()', parserOptions, errors },
{ code: 'cy.get(".a")', parserOptions, errors },
],
})

0 comments on commit a4a0e8e

Please sign in to comment.