-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rule): add 'no-force-true' rule
- Loading branch information
Showing
4 changed files
with
184 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# disallow using of 'force: true' option (no-force-true) | ||
|
||
Using force:true on inputs appears to be confusing rather than helpful. | ||
It usually silences the actual problem instead of providing a way to overcome it. | ||
See [Cypress Core Concepts](https://docs.cypress.io/guides/core-concepts/interacting-with-elements.html#Forcing). | ||
|
||
|
||
## Rule Details | ||
|
||
This rule aims to disallow using of the `force` option on [`.click()`](https://on.cypress.io/click), | ||
[`.dblclick()`](https://on.cypress.io/dblclick), [`.type()`](https://on.cypress.io/type), | ||
[`.rightclick()`](https://on.cypress.io/rightclick), [`.select()`](https://on.cypress.io/select), | ||
[`.focus()`](https://on.cypress.io/focus), [`.check()`](https://on.cypress.io/check), | ||
and [`.trigger()`](https://on.cypress.io/trigger). | ||
Examples of **incorrect** code for this rule: | ||
|
||
```js | ||
|
||
cy.get('button').click({force: true}) | ||
cy.get('button').dblclick({force: true}) | ||
cy.get('input').type('somth', {force: true}) | ||
cy.get('div').find('.foo').find('.bar').trigger('change', {force: true}) | ||
cy.get('input').trigger('click', {force: true}) | ||
cy.get('input').rightclick({force: true}) | ||
cy.get('input').check({force: true}) | ||
cy.get('input').select({force: true}) | ||
cy.get('input').focus({force: true}) | ||
|
||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```js | ||
|
||
cy.get('button').click() | ||
cy.get('button').click({multiple: true}) | ||
cy.get('button').dblclick() | ||
cy.get('input').type('somth') | ||
cy.get('input').trigger('click', {anyoption: true}) | ||
cy.get('input').rightclick({anyoption: true}) | ||
cy.get('input').check() | ||
cy.get('input').select() | ||
cy.get('input').focus() | ||
|
||
``` | ||
|
||
|
||
## When Not To Use It | ||
|
||
If you really need to use. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/** | ||
* @fileoverview Disallow using of \'force: true\' option for click and type calls | ||
* @author Alex Kuznetsov | ||
*/ | ||
|
||
'use strict' | ||
|
||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
//------------------------------------------------------------------------------ | ||
|
||
module.exports = { | ||
meta: { | ||
docs: { | ||
description: 'Disallow using of \'force: true\' option for click and type calls', | ||
category: 'Possible Errors', | ||
recommended: false, | ||
}, | ||
fixable: null, // or "code" or "whitespace" | ||
schema: [], | ||
messages: { | ||
unexpected: 'Do not use force on click and type calls', | ||
}, | ||
}, | ||
|
||
create (context) { | ||
|
||
// variables should be defined here | ||
|
||
//---------------------------------------------------------------------- | ||
// Helpers | ||
//---------------------------------------------------------------------- | ||
function isCallingClickOrType (node) { | ||
const allowedMethods = ['click', 'dblclick', 'type', 'trigger', 'check', 'rightclick', 'focus', 'select'] | ||
|
||
return node.property && node.property.type === 'Identifier' && | ||
allowedMethods.includes(node.property.name) | ||
} | ||
|
||
function isCypressCall (node) { | ||
return node.callee.type === 'MemberExpression' && | ||
node.callee.object.type === 'Identifier' && | ||
node.callee.object.name === 'cy' | ||
} | ||
|
||
function hasOptionForce (node) { | ||
|
||
return node.arguments && node.arguments.length && | ||
node.arguments.some((arg) => { | ||
return arg.type === 'ObjectExpression' && arg.properties.some((propNode) => propNode.key.name === 'force') | ||
}) | ||
} | ||
|
||
function deepCheck (node, checkFunc) { | ||
let currentNode = node | ||
|
||
while (currentNode.parent) { | ||
|
||
if (checkFunc(currentNode.parent)) { | ||
return true | ||
} | ||
|
||
currentNode = currentNode.parent | ||
} | ||
|
||
return false | ||
} | ||
|
||
//---------------------------------------------------------------------- | ||
// Public | ||
//---------------------------------------------------------------------- | ||
|
||
return { | ||
|
||
CallExpression (node) { | ||
if (isCypressCall(node) && deepCheck(node, isCallingClickOrType) && deepCheck(node, hasOptionForce)) { | ||
context.report({ node, messageId: 'unexpected' }) | ||
} | ||
}, | ||
|
||
} | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
'use strict' | ||
|
||
//------------------------------------------------------------------------------ | ||
// Requirements | ||
//------------------------------------------------------------------------------ | ||
|
||
const rule = require('../../../lib/rules/no-force-true') | ||
|
||
const RuleTester = require('eslint').RuleTester | ||
|
||
const errors = [{ messageId: 'unexpected' }] | ||
const parserOptions = { ecmaVersion: 6 } | ||
|
||
//------------------------------------------------------------------------------ | ||
// Tests | ||
//------------------------------------------------------------------------------ | ||
|
||
let ruleTester = new RuleTester() | ||
|
||
ruleTester.run('no-force-true', rule, { | ||
|
||
valid: [ | ||
{ code: `cy.get('button').click()`, parserOptions }, | ||
{ code: `cy.get('button').click({multiple: true})`, parserOptions }, | ||
{ code: `cy.get('button').dblclick()`, parserOptions }, | ||
{ code: `cy.get('input').type('somth')`, parserOptions }, | ||
{ code: `cy.get('input').type('somth', {anyoption: true})`, parserOptions, errors }, | ||
{ code: `cy.get('input').trigger('click', {anyoption: true})`, parserOptions, errors }, | ||
{ code: `cy.get('input').rightclick({anyoption: true})`, parserOptions, errors }, | ||
{ code: `cy.get('input').check()`, parserOptions, errors }, | ||
{ code: `cy.get('input').select()`, parserOptions, errors }, | ||
{ code: `cy.get('input').focus()`, parserOptions, errors }, | ||
], | ||
|
||
invalid: [ | ||
{ code: `cy.get('button').click({force: true})`, parserOptions, errors }, | ||
{ code: `cy.get('button').dblclick({force: true})`, parserOptions, errors }, | ||
{ code: `cy.get('input').type('somth', {force: true})`, parserOptions, errors }, | ||
{ code: `cy.get('div').find('.foo').type('somth', {force: true})`, parserOptions, errors }, | ||
{ code: `cy.get('div').find('.foo').find('.bar').click({force: true})`, parserOptions, errors }, | ||
{ code: `cy.get('div').find('.foo').find('.bar').trigger('change', {force: true})`, parserOptions, errors }, | ||
{ code: `cy.get('input').trigger('click', {force: true})`, parserOptions, errors }, | ||
{ code: `cy.get('input').rightclick({force: true})`, parserOptions, errors }, | ||
{ code: `cy.get('input').check({force: true})`, parserOptions, errors }, | ||
{ code: `cy.get('input').select({force: true})`, parserOptions, errors }, | ||
{ code: `cy.get('input').focus({force: true})`, parserOptions, errors }, | ||
], | ||
}) |