diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5b262b1..8e8c9b1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,46 +4,47 @@ Thanks for taking the time to contribute! :smile: ## Preparation -* Fork and clone this repository -* Branch from the default `master` branch using a descriptive new branch name -* Install dependencies with `npm ci` +- Fork and clone this repository +- Branch from the default `master` branch using a descriptive new branch name +- Install dependencies with `npm ci` ## Rule references -* Refer to the [ESLint documentation](https://eslint.org/docs/latest/) and the [Custom Rules](https://eslint.org/docs/latest/extend/custom-rules) page +- Refer to the [ESLint documentation](https://eslint.org/docs/latest/) and the [Custom Rules](https://eslint.org/docs/latest/extend/custom-rules) page ## New rule To add a new rule: -* Follow the instructions in the ESLint [generator-eslint](https://www.npmjs.com/package/generator-eslint) documentation to install [Yeoman](https://www.npmjs.com/package/yo) and the generator -* Run the new rule generator `yo eslint:rule` and answer the questions +- Follow the instructions in the ESLint [generator-eslint](https://www.npmjs.com/package/generator-eslint) documentation to install [Yeoman](https://www.npmjs.com/package/yo) and the generator +- Run the new rule generator `yo eslint:rule` and answer the questions - select "ESLint Plugin" - for "Type a short description of this rule" provide text which starts with one of "enforce", "require" or "disallow" (all lower case) -* Yeoman creates three boilerplate files: +- Yeoman creates three boilerplate files: - `docs/rules/.md` - `lib/rules/.js` - `test/rules/.js` -* Run `npm run lint-fix` -* Address the linting errors by editing `lib/rules/.js` +- Run `npm run lint-fix` +- Address the linting errors by editing `lib/rules/.js` - Add a `meta.messages` property (see [MessageIds](https://eslint.org/docs/latest/extend/custom-rules#messageids)) - Select the appropriate `meta.type` property using `problem`, `suggestion`, or `layout` -* Complete the new rule by adding content to the three files previously created -* Run `eslint-doc-generator` to generate automated documentation sections (see [Document generation](#document-generation) below) -* Review documentation changes -* Run `npm run lint` -* Run `npm run format` -* Run `npm test` to run [Vitest](https://vitest.dev/) -* Make sure all tests are passing -* Add the rule to [flat.js](https://github.com/cypress-io/eslint-plugin-cypress/blob/master/lib/flat.js) -* Create a git commit with a commit message similar to: `feat: add rule ` (see [commit message conventions](https://github.com/semantic-release/semantic-release#commit-message-format)) -* Create a PR from your branch +- Complete the new rule by adding content to the three files previously created +- Run `eslint-doc-generator` to generate automated documentation sections (see [Document generation](#document-generation) below) +- Review documentation changes +- Run `npm run lint` +- Run `npm run format` +- Run `npm test` to run [Vitest](https://vitest.dev/) +- Make sure all tests are passing +- Add the rule to [flat.js](https://github.com/cypress-io/eslint-plugin-cypress/blob/master/lib/flat.js) +- Create a git commit with a commit message similar to: `feat: add rule ` (see [commit message conventions](https://github.com/semantic-release/semantic-release#commit-message-format)) +- Create a PR from your branch ## Document generation This plugin uses the ESLint [eslint-doc-generator](https://www.npmjs.com/package/eslint-doc-generator) to generate consistent documentation. -* Install with `npm install eslint-doc-generator -g` -* Run `eslint-doc-generator` in the root directory of the plugin + +- Install with `npm install eslint-doc-generator -g` +- Run `eslint-doc-generator` in the root directory of the plugin ## Merging pull requests @@ -66,7 +67,7 @@ This information is for Cypress.io Members or Collaborators who merge pull reque ``` 1. New versions of this module will be released automatically by the CI pipeline when any PR with a triggering commit message is merged to the `master` branch: see the `release` job of [circle.yml](circle.yml). -This will create a new [GitHub release](https://github.com/cypress-io/eslint-plugin-cypress/releases) and publish it to [eslint-plugin-cypress](https://www.npmjs.com/package/eslint-plugin-cypress) on the [npm registry](https://docs.npmjs.com/about-the-public-npm-registry). + This will create a new [GitHub release](https://github.com/cypress-io/eslint-plugin-cypress/releases) and publish it to [eslint-plugin-cypress](https://www.npmjs.com/package/eslint-plugin-cypress) on the [npm registry](https://docs.npmjs.com/about-the-public-npm-registry). 1. The module's CI is configured to use the [default Angular release rules](https://github.com/semantic-release/commit-analyzer/blob/master/lib/default-release-rules.js). -This means that only `feat:`, `fix:` and `perf:` trigger a new release. -Other Angular commit types listed in the [Angular commit message guidelines](https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md) can be used for documentation purposes, however they are ignored by the currently configured release process. + This means that only `feat:`, `fix:` and `perf:` trigger a new release. + Other Angular commit types listed in the [Angular commit message guidelines](https://github.com/angular/angular/blob/main/contributing-docs/commit-message-guidelines.md) can be used for documentation purposes, however they are ignored by the currently configured release process. diff --git a/FLAT-CONFIG.md b/FLAT-CONFIG.md index be03df6..39e0169 100644 --- a/FLAT-CONFIG.md +++ b/FLAT-CONFIG.md @@ -1,3 +1,3 @@ # Cypress ESLint Plugin - Flat Config - Please refer to the [README](./README.md) document which describes how to use `eslint-plugin-cypress` with an ESLint `v9` (default) [flat configuration](https://eslint.org/docs/latest/use/configure/configuration-files). +Please refer to the [README](./README.md) document which describes how to use `eslint-plugin-cypress` with an ESLint `v9` (default) [flat configuration](https://eslint.org/docs/latest/use/configure/configuration-files). diff --git a/README.md b/README.md index 621454a..a787ab5 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,9 @@ Prerequisites: [ESLint](https://www.npmjs.com/package/eslint) `v9`. Lower versio ```sh npm install eslint eslint-plugin-cypress --save-dev ``` + or + ```sh yarn add eslint eslint-plugin-cypress --dev ``` @@ -36,15 +38,16 @@ import pluginCypress from 'eslint-plugin-cypress/flat' # deprecated There are two specific configurations available: -| Configuration | Content | -| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Configuration | Content | +| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `configs.globals` | defines globals `cy`, `Cypress`, `expect`, `assert` and `chai` used in Cypress test specs as well as `globals.browser` and `globals.mocha` from [globals](https://www.npmjs.com/package/globals). There are no default rules enabled in this configuration. | -| `configs.recommended` | enables [recommended Rules](#rules). It includes also `configs.global` (see above). | +| `configs.recommended` | enables [recommended Rules](#rules). It includes also `configs.global` (see above). | ## Rules These rules enforce some of the [best practices recommended for using Cypress](https://on.cypress.io/best-practices). + 💼 Configurations enabled in.\ @@ -66,6 +69,7 @@ These rules enforce some of the [best practices recommended for using Cypress](h | [unsafe-to-chain-command](docs/rules/unsafe-to-chain-command.md) | disallow actions within chains | ✅ | + ## Usage examples @@ -74,6 +78,7 @@ In the following sections, different examples of possible configuration file con ### Cypress All rules are available by importing from `eslint-plugin-cypress` and can be individually activated. + - [cypress/unsafe-to-chain-command](https://github.com/cypress-io/eslint-plugin-cypress/blob/master/docs/rules/unsafe-to-chain-command.md) is activated and set to `error` ```js @@ -81,18 +86,19 @@ import pluginCypress from 'eslint-plugin-cypress' export default [ { plugins: { - cypress: pluginCypress + cypress: pluginCypress, }, rules: { - 'cypress/unsafe-to-chain-command': 'error' - } - } + 'cypress/unsafe-to-chain-command': 'error', + }, + }, ] ``` ### Cypress recommended The `eslint-plugin-cypress` [recommended rules](#rules) `configs.recommended` are activated, except for + - [cypress/no-unnecessary-waiting](https://github.com/cypress-io/eslint-plugin-cypress/blob/master/docs/rules/no-unnecessary-waiting.md) which is set to `off` ```js @@ -101,9 +107,9 @@ export default [ pluginCypress.configs.recommended, { rules: { - 'cypress/no-unnecessary-waiting': 'off' - } - } + 'cypress/no-unnecessary-waiting': 'off', + }, + }, ] ``` @@ -113,9 +119,7 @@ The `configs.globals` are activated. ```js import pluginCypress from 'eslint-plugin-cypress' -export default [ - pluginCypress.configs.globals -] +export default [pluginCypress.configs.globals] ``` ## Disable rules @@ -174,6 +178,7 @@ During test spec development, [Mocha exclusive tests](https://mochajs.org/#exclu [eslint-plugin-mocha@^11](https://www.npmjs.com/package/eslint-plugin-mocha) is added to the example [Cypress recommended](#cypress-recommended). This version of the plugin supports only flat file configurations with the option `configs.recommended`. The settings for individual `mocha` rules from the `configs.recommended` option are changed. + - [mocha/no-exclusive-tests](https://github.com/lo1tuma/eslint-plugin-mocha/blob/main/docs/rules/no-exclusive-tests.md) and [mocha/no-pending-tests](https://github.com/lo1tuma/eslint-plugin-mocha/blob/main/docs/rules/no-pending-tests.md) are set to `error` instead of `warn` - [mocha/no-mocha-arrows](https://github.com/lo1tuma/eslint-plugin-mocha/blob/main/docs/rules/no-mocha-arrows.md) is set to `off` instead of `error` @@ -192,9 +197,9 @@ export default [ 'mocha/no-exclusive-tests': 'error', 'mocha/no-pending-tests': 'error', 'mocha/no-mocha-arrows': 'off', - 'cypress/no-unnecessary-waiting': 'off' - } - } + 'cypress/no-unnecessary-waiting': 'off', + }, + }, ] ``` @@ -220,7 +225,7 @@ export default [ rules: { 'cypress/no-unnecessary-waiting': 'off', }, - } + }, ] ``` diff --git a/docs/rules/assertion-before-screenshot.md b/docs/rules/assertion-before-screenshot.md index 0e22f66..bedcf01 100644 --- a/docs/rules/assertion-before-screenshot.md +++ b/docs/rules/assertion-before-screenshot.md @@ -1,6 +1,7 @@ # Require screenshots to be preceded by an assertion (`cypress/assertion-before-screenshot`) + If you take screenshots without assertions then you may get different screenshots depending on timing. For example, if clicking a button makes some network calls and upon success, renders something, then the screenshot may sometimes have the new render and sometimes not. @@ -12,14 +13,14 @@ This rule checks there is an assertion making sure your application state is cor Examples of **incorrect** code for this rule: ```js -cy.visit('myUrl'); -cy.screenshot(); +cy.visit('myUrl') +cy.screenshot() ``` Examples of **correct** code for this rule: ```js -cy.visit('myUrl'); -cy.get('[data-test-id="my-element"]').should('be.visible'); -cy.screenshot(); +cy.visit('myUrl') +cy.get('[data-test-id="my-element"]').should('be.visible') +cy.screenshot() ``` diff --git a/docs/rules/no-assigning-return-values.md b/docs/rules/no-assigning-return-values.md index 2c3cf7d..95697b3 100644 --- a/docs/rules/no-assigning-return-values.md +++ b/docs/rules/no-assigning-return-values.md @@ -3,6 +3,7 @@ 💼 This rule is enabled in the ✅ `recommended` config. + ## Further Reading See [the Cypress Best Practices guide](https://on.cypress.io/best-practices#Assigning-Return-Values). diff --git a/docs/rules/no-async-before.md b/docs/rules/no-async-before.md index b69a1b4..df534f0 100644 --- a/docs/rules/no-async-before.md +++ b/docs/rules/no-async-before.md @@ -1,6 +1,7 @@ # Disallow using `async`/`await` in Cypress `before` methods (`cypress/no-async-before`) + Cypress commands that return a promise may cause side effects in `before`/`beforeEach` hooks, possibly causing unexpected behavior. ## Rule Details @@ -11,7 +12,7 @@ Examples of **incorrect** code for this rule: ```js describe('my feature', () => { - before('my test case', async () => { + before('my test case', async () => { await cy.get('.myClass') // other operations }) @@ -20,10 +21,8 @@ describe('my feature', () => { ```js describe('my feature', () => { - before('my test case', async () => { - cy - .get('.myClass') - .click() + before('my test case', async () => { + cy.get('.myClass').click() await someAsyncFunction() }) @@ -34,7 +33,7 @@ Examples of **correct** code for this rule: ```js describe('my feature', () => { - before('my test case', () => { + before('my test case', () => { cy.get('.myClass') // other operations }) diff --git a/docs/rules/no-async-tests.md b/docs/rules/no-async-tests.md index 0d99b82..3d74e74 100644 --- a/docs/rules/no-async-tests.md +++ b/docs/rules/no-async-tests.md @@ -3,6 +3,7 @@ 💼 This rule is enabled in the ✅ `recommended` config. + Cypress tests [that return a promise will error](https://docs.cypress.io/guides/references/error-messages.html#Cypress-detected-that-you-returned-a-promise-from-a-command-while-also-invoking-one-or-more-cy-commands-in-that-promise) and cannot run successfully. An `async` function returns a promise under the hood, so a test using an `async` function will also error. @@ -14,7 +15,7 @@ Examples of **incorrect** code for this rule: ```js describe('my feature', () => { - it('my test case', async () => { + it('my test case', async () => { await cy.get('.myClass') // other operations }) @@ -23,10 +24,8 @@ describe('my feature', () => { ```js describe('my feature', () => { - it('my test case', async () => { - cy - .get('.myClass') - .click() + it('my test case', async () => { + cy.get('.myClass').click() await someAsyncFunction() }) @@ -37,7 +36,7 @@ Examples of **correct** code for this rule: ```js describe('my feature', () => { - it('my test case', () => { + it('my test case', () => { cy.get('.myClass') // other operations }) diff --git a/docs/rules/no-chained-get.md b/docs/rules/no-chained-get.md index 7cc13c6..7c68b19 100644 --- a/docs/rules/no-chained-get.md +++ b/docs/rules/no-chained-get.md @@ -1,6 +1,7 @@ # Disallow chain of `cy.get()` calls (`cypress/no-chained-get`) + This rule disallows the usage of chained `.get()` calls as `cy.get()` always starts its search from the cy.root element. ## Rule Details @@ -14,6 +15,5 @@ cy.get('parent').get('child') Examples of **correct** code for this rule: ```js -cy.get('parent') - .find('child') +cy.get('parent').find('child') ``` diff --git a/docs/rules/no-debug.md b/docs/rules/no-debug.md index 5f2145b..1e90f5c 100644 --- a/docs/rules/no-debug.md +++ b/docs/rules/no-debug.md @@ -1,6 +1,7 @@ # Disallow using `cy.debug()` calls (`cypress/no-debug`) + It is recommended to remove any [cy.debug](https://on.cypress.io/debug) commands before committing specs to avoid other developers getting unexpected results. ## Rule Details @@ -8,8 +9,8 @@ It is recommended to remove any [cy.debug](https://on.cypress.io/debug) commands Examples of **incorrect** code for this rule: ```js -cy.debug(); -cy.get('selector').debug(); +cy.debug() +cy.get('selector').debug() ``` Examples of **correct** code for this rule: diff --git a/docs/rules/no-force.md b/docs/rules/no-force.md index 7aed6a8..2b1bf68 100644 --- a/docs/rules/no-force.md +++ b/docs/rules/no-force.md @@ -1,6 +1,7 @@ # Disallow using `force: true` with action commands (`cypress/no-force`) + 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). @@ -18,26 +19,26 @@ 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}) +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').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').trigger('click', { anyoption: true }) +cy.get('input').rightclick({ anyoption: true }) cy.get('input').check() cy.get('input').select() cy.get('input').focus() diff --git a/docs/rules/no-pause.md b/docs/rules/no-pause.md index d15079d..b5d74ca 100644 --- a/docs/rules/no-pause.md +++ b/docs/rules/no-pause.md @@ -1,6 +1,7 @@ # Disallow using `cy.pause()` calls (`cypress/no-pause`) + It is recommended to remove any [cy.pause](https://on.cypress.io/pause) commands before committing specs to avoid other developers getting unexpected results. ## Rule Details @@ -8,8 +9,8 @@ It is recommended to remove any [cy.pause](https://on.cypress.io/pause) commands Examples of **incorrect** code for this rule: ```js -cy.pause(); -cy.get('selector').pause(); +cy.pause() +cy.get('selector').pause() ``` Examples of **correct** code for this rule: diff --git a/docs/rules/no-unnecessary-waiting.md b/docs/rules/no-unnecessary-waiting.md index 594879d..026e269 100644 --- a/docs/rules/no-unnecessary-waiting.md +++ b/docs/rules/no-unnecessary-waiting.md @@ -3,6 +3,7 @@ 💼 This rule is enabled in the ✅ `recommended` config. + ## Further Reading See [the Cypress Best Practices guide](https://on.cypress.io/best-practices#Unnecessary-Waiting). diff --git a/docs/rules/no-xpath.md b/docs/rules/no-xpath.md index 3e03d6f..c6ecbee 100644 --- a/docs/rules/no-xpath.md +++ b/docs/rules/no-xpath.md @@ -15,7 +15,7 @@ cy.xpath('//div[@class=\"container\"]').click() Examples of **correct** code for this rule: ```js -cy.get('[data-cy="container"]').click(); +cy.get('[data-cy="container"]').click() ``` ## Further Reading diff --git a/docs/rules/require-data-selectors.md b/docs/rules/require-data-selectors.md index 9d32116..c230dcb 100644 --- a/docs/rules/require-data-selectors.md +++ b/docs/rules/require-data-selectors.md @@ -1,6 +1,7 @@ # Require `data-*` attribute selectors (`cypress/require-data-selectors`) + Require `cy.get` to use only selectors that target `data-*` attributes. > Note: If you use this rule, consider only using the `warn` error level, since using `data-*` attribute selectors may not always be possible. @@ -10,11 +11,11 @@ Require `cy.get` to use only selectors that target `data-*` attributes. Examples of **incorrect** code for this rule: ```js -cy.get(".a") +cy.get('.a') cy.get('[daedta-cy=submit]').click() cy.get('[d-cy=submit]') -cy.get(".btn-large").click() -cy.get(".btn-.large").click() +cy.get('.btn-large').click() +cy.get('.btn-.large').click() ``` Examples of **correct** code for this rule: diff --git a/docs/rules/unsafe-to-chain-command.md b/docs/rules/unsafe-to-chain-command.md index 6ae2a4d..749c411 100644 --- a/docs/rules/unsafe-to-chain-command.md +++ b/docs/rules/unsafe-to-chain-command.md @@ -3,6 +3,7 @@ 💼 This rule is enabled in the ✅ `recommended` config. + ### Options