diff --git a/.node-version b/.node-version index 7af24b7d..5b540673 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -22.11.0 +22.16.0 diff --git a/.nvmrc b/.nvmrc index 7af24b7d..5b540673 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.11.0 +22.16.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f12516a6..23541754 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,7 +34,7 @@ To add a new rule: * Run `npm run lint` * Run `npm test` to run [Jest](https://jestjs.io/) (or run `npm start` to run [Jest](https://jestjs.io/) in [watchAll](https://jestjs.io/docs/cli#--watchall) mode where it remains active and reruns when source changes are made) * Make sure all tests are passing -* Add the rule to [legacy.js](https://github.com/cypress-io/eslint-plugin-cypress/blob/master/legacy.js) and to [flat.js](https://github.com/cypress-io/eslint-plugin-cypress/blob/master/lib/flat.js) +* 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 @@ -44,11 +44,6 @@ This plugin uses the ESLint [eslint-doc-generator](https://www.npmjs.com/package * Install with `npm install eslint-doc-generator -g` * Run `eslint-doc-generator` in the root directory of the plugin -## Legacy tests - -* The directory [tests-legacy](https://github.com/cypress-io/eslint-plugin-cypress/tree/master/tests-legacy) contains tests which are compatible with the legacy [ESLint v8 RuleTester](https://eslint.org/docs/v8.x/integrate/nodejs-api#ruletester) utility. It is not expected to add new rules to this set of tests. -* The directory [tests](https://github.com/cypress-io/eslint-plugin-cypress/tree/master/tests) is for tests compatible with the current [ESLint RuleTester](https://eslint.org/docs/latest/integrate/nodejs-api#ruletester). - ## Merging pull requests This information is for Cypress.io Members or Collaborators who merge pull requests: diff --git a/ESLINTRC-CONFIG.md b/ESLINTRC-CONFIG.md index 34c39e4e..a1bf7400 100644 --- a/ESLINTRC-CONFIG.md +++ b/ESLINTRC-CONFIG.md @@ -1,147 +1,7 @@ # Cypress ESLint Plugin - Legacy Config -This document supplements the [README](./README.md) document and describes how to use the Cypress ESLint Plugin (`eslint-plugin-cypress`) in a [deprecated ESLint legacy config environment](https://eslint.org/docs/latest/use/configure/configuration-files-deprecated). The use of flat configurations with this plugin is described in the [README](./README.md) document. +This document previously described how to use the Cypress ESLint Plugin (`eslint-plugin-cypress`) with an [ESLint legacy config environment](https://eslint.org/docs/latest/use/configure/configuration-files-deprecated). -Usage with ESLint `9.x` is described. +This form of configuration was deprecated with the release of ESLint `v9` and its use with `eslint-plugin-cypress` is no longer supported. -## Deprecations - -The use of `eslintrc` configurations with `eslint-plugin-cypress` is deprecated and support will be removed in a future version of this plugin. This is tied in to the ESLint announcement in the blog post [Flat config rollout plans](https://eslint.org/blog/2023/10/flat-config-rollout-plans/) from October 2023 which describes that the `eslintrc` configuration system is planned to be removed in the future ESLint `v10.0.0`. Users are encouraged to migrate to using a [flat configuration](https://eslint.org/docs/latest/use/configure/configuration-files). - -## Installation - -Use a minimum ESLint `9.x`. - -```shell -npm install eslint eslint-plugin-cypress --save-dev -``` - -or - -```shell -yarn add eslint eslint-plugin-cypress --dev -``` - -## Usage - -To use a deprecated configuration with ESLint `v9`, such as `.eslintrc.json`, you must set the `ESLINT_USE_FLAT_CONFIG` environment variable to `false` (see [ESLint v9 > Configuration Files (Deprecated)](https://eslint.org/docs/latest/use/configure/configuration-files-deprecated)). The following examples use `json` format for the content of the configuration file: - -```json -{ - "plugins": [ - "cypress" - ] -} -``` - -You can add rules - see [Rules](./README.md#rules) for a list of the available rules: - -```json -{ - "rules": { - "cypress/no-assigning-return-values": "error", - "cypress/no-unnecessary-waiting": "error", - "cypress/assertion-before-screenshot": "warn", - "cypress/no-force": "warn", - "cypress/no-async-tests": "error", - "cypress/no-async-before": "error", - "cypress/no-pause": "error", - "cypress/no-debug": "error" - } -} -``` - -You can allow certain globals provided by Cypress: - -```json -{ - "env": { - "cypress/globals": true - } -} -``` - -## Recommended configuration - -Use the recommended configuration and you can forego configuring _plugins_, _rules_, and _env_ individually. See [Rules](./README.md#rules) for which rules are included in the recommended configuration. - -```json -{ - "extends": [ - "plugin:cypress/recommended" - ] -} -``` - -## Rules - -See the [Rules](./README.md#rules) list in the main [README](./README.md) document. - -## Mocha and Chai - -Cypress is built on top of [Mocha](https://on.cypress.io/app/references/bundled-libraries#Mocha) and [Chai](https://on.cypress.io/app/references/bundled-libraries#Chai). See the following sections for information on using ESLint plugins [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-chai-friendly](https://www.npmjs.com/package/eslint-plugin-chai-friendly) together with `eslint-plugin-cypress`. - -## Mocha `.only` and `.skip` - -During test spec development, [Mocha exclusive tests](https://mochajs.org/#exclusive-tests) `.only` or [Mocha inclusive tests](https://mochajs.org/#inclusive-tests) `.skip` may be used to control which tests are executed, as described in the Cypress documentation [Excluding and Including Tests](https://on.cypress.io/app/core-concepts/writing-and-organizing-tests#Excluding-and-Including-Tests). To apply corresponding rules, you can install and use [eslint-plugin-mocha@^10](https://www.npmjs.com/package/eslint-plugin-mocha). This is the highest plugin version that still supports legacy configurations. The rule [mocha/no-exclusive-tests](https://github.com/lo1tuma/eslint-plugin-mocha/blob/main/docs/rules/no-exclusive-tests.md) detects the use of `.only` and the [mocha/no-skipped-tests](https://github.com/lo1tuma/eslint-plugin-mocha/blob/v10.5.0/docs/rules/no-skipped-tests.md) rule detects the use of `.skip`: - -```sh -npm install --save-dev eslint-plugin-mocha@^10 -``` - -In your `.eslintrc.json`: - -```json -{ - "plugins": [ - "cypress", - "mocha" - ], - "rules": { - "mocha/no-exclusive-tests": "warn", - "mocha/no-skipped-tests": "warn" - } -} -``` - -Or you can simply use the `cypress/recommended` and `mocha/recommended` configurations together, for example: - -```json -{ - "extends": [ - "plugin:cypress/recommended", - "plugin:mocha/recommended" - ] -} -``` - -## Chai and `no-unused-expressions` - -Using an assertion such as `expect(value).to.be.true` can fail the ESLint rule `no-unused-expressions` even though it's not an error in this case. To fix this, you can install and use [eslint-plugin-chai-friendly](https://www.npmjs.com/package/eslint-plugin-chai-friendly). - -```sh -npm install --save-dev eslint-plugin-chai-friendly -``` - -In your `.eslintrc.json`: - -```json -{ - "plugins": [ - "cypress", - "chai-friendly" - ], - "rules": { - "no-unused-expressions": 0, - "chai-friendly/no-unused-expressions": 2 - } -} -``` - -Or you can simply add its `recommended` config: - -```json -{ - "extends": ["plugin:chai-friendly/recommended"] -} -``` +Users who were previously using a deprecated configuration environment should migrate to a [flat configuration](https://eslint.org/docs/latest/use/configure/configuration-files). diff --git a/FLAT-CONFIG.md b/FLAT-CONFIG.md index 2fdb16bc..be03df65 100644 --- a/FLAT-CONFIG.md +++ b/FLAT-CONFIG.md @@ -1,5 +1,3 @@ # Cypress ESLint Plugin - Flat Config - Please refer to the [README](./README.md) document where the previous contents of this document, describing how to use `eslint-plugin-cypress` with an ESLint `v9` (default) [flat configuration](https://eslint.org/docs/latest/use/configure/configuration-files), can now be found. - -For instructions on using a deprecated [eslintrc-type](https://eslint.org/docs/latest/use/configure/configuration-files-deprecated) config file from previous ESLint `v8` versions and below, please refer to the [ESLINTRC-CONFIG](./ESLINTRC-CONFIG.md) document. + 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 d84ba8b1..621454a6 100644 --- a/README.md +++ b/README.md @@ -18,21 +18,27 @@ yarn add eslint eslint-plugin-cypress --dev ## Usage -ESLint `v9` uses a [Flat config file](https://eslint.org/docs/latest/use/configure/configuration-files) format with filename `eslint.config.*js` by default. For instructions on using a deprecated [eslintrc-type](https://eslint.org/docs/latest/use/configure/configuration-files-deprecated) config file from previous ESLint versions, please refer to the [ESLINTRC-CONFIG](./ESLINTRC-CONFIG.md) document. +ESLint `v9` uses a [Flat config file](https://eslint.org/docs/latest/use/configure/configuration-files) format with filename `eslint.config.*js` by default. This plugin no longer supports the use of a deprecated [eslintrc-type](https://eslint.org/docs/latest/use/configure/configuration-files-deprecated) config file from previous ESLint versions. -To set up a flat configuration, add a file `eslint.config.mjs` to the root directory of your Cypress project and include the following instructions to import the available flat configurations using: +To set up a configuration, add a file `eslint.config.mjs` to the root directory of your Cypress project and include the following instructions to import the available configurations using: ```shell -import pluginCypress from 'eslint-plugin-cypress/flat' +import pluginCypress from 'eslint-plugin-cypress' +``` + +For backwards compatibility with previous plugin versions `3.3.0` - `4.3.0`, the following equivalent deprecated form is also supported. This is planned to be removed in a future major version: + +```shell +import pluginCypress from 'eslint-plugin-cypress/flat' # deprecated ``` ## Configurations -There are two specific flat configurations available: +There are two specific configurations available: | 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). This version no longer specifies `languageOptions` for `ecmaVersion` and `sourceType` - see ESLint [JavaScript languageOptions](https://eslint.org/docs/latest/use/configure/language-options#specifying-javascript-options). There are no default rules enabled in this configuration. | +| `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). | ## Rules @@ -67,11 +73,11 @@ In the following sections, different examples of possible configuration file con ### Cypress -All rules from `eslint-plugin-cypress` are available through `eslint-plugin-cypress/flat` and can be individually activated. +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 -import pluginCypress from 'eslint-plugin-cypress/flat' +import pluginCypress from 'eslint-plugin-cypress' export default [ { plugins: { @@ -90,7 +96,7 @@ The `eslint-plugin-cypress` [recommended rules](#rules) `configs.recommended` ar - [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 -import pluginCypress from 'eslint-plugin-cypress/flat' +import pluginCypress from 'eslint-plugin-cypress' export default [ pluginCypress.configs.recommended, { @@ -106,7 +112,7 @@ export default [ The `configs.globals` are activated. ```js -import pluginCypress from 'eslint-plugin-cypress/flat' +import pluginCypress from 'eslint-plugin-cypress' export default [ pluginCypress.configs.globals ] @@ -177,7 +183,7 @@ npm install eslint-plugin-mocha@^11 --save-dev ```js import pluginMocha from 'eslint-plugin-mocha' -import pluginCypress from 'eslint-plugin-cypress/flat' +import pluginCypress from 'eslint-plugin-cypress' export default [ pluginMocha.configs.recommended, pluginCypress.configs.recommended, @@ -205,7 +211,7 @@ npm install eslint-plugin-chai-friendly@^1.0.1 --save-dev ``` ```js -import pluginCypress from 'eslint-plugin-cypress/flat' +import pluginCypress from 'eslint-plugin-cypress' import pluginChaiFriendly from 'eslint-plugin-chai-friendly' export default [ pluginCypress.configs.recommended, diff --git a/circle.yml b/circle.yml index 32e3059b..0f3f4351 100644 --- a/circle.yml +++ b/circle.yml @@ -3,14 +3,13 @@ version: 2.1 executors: docker-executor: docker: - - image: cimg/node:22.14.0 + - image: cimg/node:22.16.0 resource_class: medium workflows: main: jobs: - lint - - test-v8 - test-v9 - build-test-project @@ -21,6 +20,7 @@ workflows: config-file: [ # configurations correspond to examples in README 'default', + 'default-deprecated', # using deprecated /flat 'recommended', 'globals', ] @@ -29,7 +29,6 @@ workflows: - release: requires: - lint - - test-v8 - test-v9 - test-test-project filters: @@ -52,23 +51,6 @@ jobs: name: Lint code command: npm run lint - test-v8: - executor: docker-executor - steps: - - checkout - - run: - name: Install dependencies - command: npm ci - - run: - name: Install ESLint 8 - command: npm install eslint@8 - - run: - name: Show ESLint version - command: npx eslint --version - - run: - name: Test ESLint 8 - command: npm run test:legacy - test-v9: executor: docker-executor steps: diff --git a/jest.config-legacy.js b/jest.config-legacy.js deleted file mode 100644 index 989d74c0..00000000 --- a/jest.config-legacy.js +++ /dev/null @@ -1,6 +0,0 @@ -const { defaults } = require('jest-config') - -module.exports = { - testMatch: ['**/tests-legacy/**/*.[jt]s?(x)'], - testPathIgnorePatterns: [...defaults.testPathIgnorePatterns, '.history'], -} diff --git a/legacy.js b/legacy.js deleted file mode 100644 index 03a1ee64..00000000 --- a/legacy.js +++ /dev/null @@ -1,36 +0,0 @@ -const globals = require('globals') - -module.exports = { - rules: { - 'no-assigning-return-values': require('./lib/rules/no-assigning-return-values'), - 'unsafe-to-chain-command': require('./lib/rules/unsafe-to-chain-command'), - 'no-unnecessary-waiting': require('./lib/rules/no-unnecessary-waiting'), - 'no-async-before': require('./lib/rules/no-async-before'), - 'no-async-tests': require('./lib/rules/no-async-tests'), - 'assertion-before-screenshot': require('./lib/rules/assertion-before-screenshot'), - 'require-data-selectors': require('./lib/rules/require-data-selectors'), - 'no-force': require('./lib/rules/no-force'), - 'no-pause': require('./lib/rules/no-pause'), - 'no-debug': require('./lib/rules/no-debug'), - 'no-xpath': require('./lib/rules/no-xpath'), - 'no-chained-get': require('./lib/rules/no-chained-get'), - }, - configs: { - recommended: require('./lib/config/recommended'), - }, - environments: { - globals: { - globals: Object.assign({ - cy: false, - Cypress: false, - expect: false, - assert: false, - chai: false, - }, globals.browser, globals.mocha), - parserOptions: { - ecmaVersion: 2019, - sourceType: 'module', - }, - }, - }, -} diff --git a/lib/flat.js b/lib/flat.js index e2f82db6..a3e2d4c0 100644 --- a/lib/flat.js +++ b/lib/flat.js @@ -5,18 +5,18 @@ const plugin = { meta: { name, version }, configs: {}, rules: { + 'assertion-before-screenshot': require('./rules/assertion-before-screenshot'), 'no-assigning-return-values': require('./rules/no-assigning-return-values'), - 'unsafe-to-chain-command': require('./rules/unsafe-to-chain-command'), - 'no-unnecessary-waiting': require('./rules/no-unnecessary-waiting'), 'no-async-before': require('./rules/no-async-before'), 'no-async-tests': require('./rules/no-async-tests'), - 'assertion-before-screenshot': require('./rules/assertion-before-screenshot'), - 'require-data-selectors': require('./rules/require-data-selectors'), + 'no-chained-get': require('./rules/no-chained-get'), + 'no-debug': require('./rules/no-debug'), 'no-force': require('./rules/no-force'), 'no-pause': require('./rules/no-pause'), - 'no-debug': require('./rules/no-debug'), + 'no-unnecessary-waiting': require('./rules/no-unnecessary-waiting'), 'no-xpath': require('./rules/no-xpath'), - 'no-chained-get': require('./rules/no-chained-get'), + 'require-data-selectors': require('./rules/require-data-selectors'), + 'unsafe-to-chain-command': require('./rules/unsafe-to-chain-command'), }, } diff --git a/package-lock.json b/package-lock.json index f1e83368..3d2e47cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0-development", "license": "MIT", "dependencies": { - "globals": "^15.15.0" + "globals": "^16.2.0" }, "devDependencies": { "eslint": "^9.27.0", @@ -17,7 +17,7 @@ "eslint-plugin-mocha": "^11.1.0", "husky": "^9.1.7", "jest": "^29.7.0", - "semantic-release": "24.2.3" + "semantic-release": "24.2.5" }, "peerDependencies": { "eslint": ">=9" @@ -3234,6 +3234,19 @@ "eslint": ">=9.0.0" } }, + "node_modules/eslint-plugin-mocha/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-scope": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", @@ -3755,9 +3768,9 @@ } }, "node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz", + "integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==", "license": "MIT", "engines": { "node": ">=18" @@ -5276,9 +5289,9 @@ } }, "node_modules/marked": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", - "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "dev": true, "license": "MIT", "bin": { @@ -9224,9 +9237,9 @@ "license": "MIT" }, "node_modules/semantic-release": { - "version": "24.2.3", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.3.tgz", - "integrity": "sha512-KRhQG9cUazPavJiJEFIJ3XAMjgfd0fcK3B+T26qOl8L0UG5aZUjeRfREO0KM5InGtYwxqiiytkJrbcYoLDEv0A==", + "version": "24.2.5", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.5.tgz", + "integrity": "sha512-9xV49HNY8C0/WmPWxTlaNleiXhWb//qfMzG2c5X8/k7tuWcu8RssbuS+sujb/h7PiWSXv53mrQvV9hrO9b7vuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9248,8 +9261,8 @@ "hosted-git-info": "^8.0.0", "import-from-esm": "^2.0.0", "lodash-es": "^4.17.21", - "marked": "^12.0.0", - "marked-terminal": "^7.0.0", + "marked": "^15.0.0", + "marked-terminal": "^7.3.0", "micromatch": "^4.0.2", "p-each-series": "^3.0.0", "p-reduce": "^3.0.0", diff --git a/package.json b/package.json index d8f8ebd0..db6c6209 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "name": "eslint-plugin-cypress", "version": "0.0.0-development", "description": "An ESLint plugin for projects using Cypress", - "main": "legacy.js", + "main": "./lib/flat.js", "exports": { - ".": "./legacy.js", + ".": "./lib/flat.js", "./flat": "./lib/flat.js" }, "author": "Cypress-io", @@ -26,7 +26,7 @@ "eslint": ">=9" }, "dependencies": { - "globals": "^15.15.0" + "globals": "^16.2.0" }, "devDependencies": { "eslint": "^9.27.0", @@ -34,15 +34,14 @@ "eslint-plugin-mocha": "^11.1.0", "husky": "^9.1.7", "jest": "^29.7.0", - "semantic-release": "24.2.3" + "semantic-release": "24.2.5" }, "scripts": { - "lint": "eslint \"*.js\" \"**/**/*.js\"", - "lint-fix": "npm run lint -- --fix", + "lint": "eslint", + "lint-fix": "eslint --fix", "semantic-release": "semantic-release", "start": "npm run test-watch", "test": "jest", - "test:legacy": "jest --config jest.config-legacy.js", "test-watch": "jest --watchAll", "prepare": "husky" } diff --git a/test-project/eslint-configs/eslint.default-deprecated.mjs b/test-project/eslint-configs/eslint.default-deprecated.mjs new file mode 100644 index 00000000..1b0d9fca --- /dev/null +++ b/test-project/eslint-configs/eslint.default-deprecated.mjs @@ -0,0 +1,11 @@ +// eslint-plugin-cypress/flat is deprecated and is identical to +// eslint-plugin-cypress +import pluginCypress from 'eslint-plugin-cypress/flat' +export default [ + pluginCypress.configs.recommended, + { + rules: { + 'cypress/no-unnecessary-waiting': 'off' + } + } +] diff --git a/test-project/eslint-configs/eslint.default.mjs b/test-project/eslint-configs/eslint.default.mjs index c2f74df6..f48f6312 100644 --- a/test-project/eslint-configs/eslint.default.mjs +++ b/test-project/eslint-configs/eslint.default.mjs @@ -1,4 +1,4 @@ -import pluginCypress from 'eslint-plugin-cypress/flat' +import pluginCypress from 'eslint-plugin-cypress' export default [ pluginCypress.configs.recommended, { diff --git a/test-project/eslint-configs/eslint.globals.mjs b/test-project/eslint-configs/eslint.globals.mjs index 25f9f97f..51a66e7d 100644 --- a/test-project/eslint-configs/eslint.globals.mjs +++ b/test-project/eslint-configs/eslint.globals.mjs @@ -1,4 +1,4 @@ -import pluginCypress from 'eslint-plugin-cypress/flat' +import pluginCypress from 'eslint-plugin-cypress' export default [ pluginCypress.configs.globals ] diff --git a/test-project/eslint-configs/eslint.recommended.mjs b/test-project/eslint-configs/eslint.recommended.mjs index c2f74df6..f48f6312 100644 --- a/test-project/eslint-configs/eslint.recommended.mjs +++ b/test-project/eslint-configs/eslint.recommended.mjs @@ -1,4 +1,4 @@ -import pluginCypress from 'eslint-plugin-cypress/flat' +import pluginCypress from 'eslint-plugin-cypress' export default [ pluginCypress.configs.recommended, { diff --git a/tests-legacy/config.js b/tests-legacy/config.js deleted file mode 100644 index e335fa03..00000000 --- a/tests-legacy/config.js +++ /dev/null @@ -1,26 +0,0 @@ -/* global describe, it, expect */ -'use strict' - -const globals = require('globals') -const config = require('../legacy.js') - -describe('environments globals', () => { - const env = config.environments.globals - - it('should not mutate globals', () => { - expect(globals.browser).not.toHaveProperty('cy') - expect(globals.mocha).not.toHaveProperty('cy') - }) - - it('should include other globals', () => { - expect(env.globals).toEqual(expect.objectContaining(globals.browser)) - expect(env.globals).toEqual(expect.objectContaining(globals.mocha)) - }) - - it('should include cypress globals', () => { - expect(env.globals).toEqual(expect.objectContaining({ - cy: false, - Cypress: false, - })) - }) -}) diff --git a/tests-legacy/lib/rules/assertion-before-screenshot.js b/tests-legacy/lib/rules/assertion-before-screenshot.js deleted file mode 100644 index 1532badd..00000000 --- a/tests-legacy/lib/rules/assertion-before-screenshot.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict' - -const rule = require('../../../lib/rules/assertion-before-screenshot') -const RuleTester = require('eslint').RuleTester - -const ruleTester = new RuleTester() - -const errors = [{ messageId: 'unexpected' }] -const parserOptions = { ecmaVersion: 6 } - -ruleTester.run('assertion-before-screenshot', rule, { - valid: [ - { code: 'cy.get(".some-element"); cy.screenshot();', parserOptions }, - { code: 'cy.get(".some-element").should("exist").screenshot();', parserOptions }, - { code: 'cy.get(".some-element").should("exist").screenshot().click()', parserOptions, errors }, - { code: 'cy.get(".some-element").should("exist"); if(true) cy.screenshot();', parserOptions }, - { code: 'if(true) { cy.get(".some-element").should("exist"); cy.screenshot(); }', parserOptions }, - { code: 'cy.get(".some-element").should("exist"); if(true) { cy.screenshot(); }', parserOptions }, - { code: 'const a = () => { cy.get(".some-element").should("exist"); cy.screenshot(); }', parserOptions, errors }, - { code: 'cy.get(".some-element").should("exist").and("be.visible"); cy.screenshot();', parserOptions }, - { code: 'cy.get(".some-element").contains("Text"); cy.screenshot();', parserOptions }, - ], - - invalid: [ - { code: 'cy.screenshot()', parserOptions, errors }, - { code: 'cy.visit("somepage"); cy.screenshot();', parserOptions, errors }, - { code: 'cy.custom(); cy.screenshot()', parserOptions, errors }, - { code: 'cy.get(".some-element").click(); cy.screenshot()', parserOptions, errors }, - { code: 'cy.get(".some-element").click().screenshot()', parserOptions, errors }, - { code: 'if(true) { cy.get(".some-element").click(); cy.screenshot(); }', parserOptions, errors }, - { code: 'cy.get(".some-element").click(); if(true) { cy.screenshot(); }', parserOptions, errors }, - { code: 'cy.get(".some-element"); function a() { cy.screenshot(); }', parserOptions, errors }, - { code: 'cy.get(".some-element"); const a = () => { cy.screenshot(); }', parserOptions, errors }, - ], -}) diff --git a/tests-legacy/lib/rules/no-assigning-return-values.js b/tests-legacy/lib/rules/no-assigning-return-values.js deleted file mode 100644 index 9d574911..00000000 --- a/tests-legacy/lib/rules/no-assigning-return-values.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict' - -const rule = require('../../../lib/rules/no-assigning-return-values') -const RuleTester = require('eslint').RuleTester - -const ruleTester = new RuleTester() - -const errors = [{ messageId: 'unexpected' }] -const parserOptions = { ecmaVersion: 6 } - -ruleTester.run('no-assigning-return-values', rule, { - valid: [ - { code: 'var foo = true;', parserOptions }, - { code: 'let foo = true;', parserOptions }, - { code: 'const foo = true;', parserOptions }, - { code: 'const foo = bar();', parserOptions }, - { code: 'const foo = bar().baz();', parserOptions }, - { code: 'const spy = cy.spy();', parserOptions }, - { code: 'const spy = cy.spy().as();', parserOptions }, - { code: 'const stub = cy.stub();', parserOptions }, - { code: 'const result = cy.now();', parserOptions }, - { code: 'const state = cy.state();', parserOptions }, - { code: 'cy.get("foo");', parserOptions }, - { code: 'cy.contains("foo").click();', parserOptions }, - ], - - invalid: [ - { code: 'let a = cy.get("foo")', parserOptions, errors }, - { code: 'const a = cy.get("foo")', parserOptions, errors }, - { code: 'var a = cy.get("foo")', parserOptions, errors }, - - { code: 'let a = cy.contains("foo")', parserOptions, errors }, - { code: 'let a = cy.window()', parserOptions, errors }, - { code: 'let a = cy.wait("@something")', parserOptions, errors }, - - { code: 'let a = cy.contains("foo").click()', parserOptions, errors }, - ], -}) diff --git a/tests-legacy/lib/rules/no-async-before.js b/tests-legacy/lib/rules/no-async-before.js deleted file mode 100644 index 6c485319..00000000 --- a/tests-legacy/lib/rules/no-async-before.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict' - -const rule = require('../../../lib/rules/no-async-before') -const RuleTester = require('eslint').RuleTester - -const ruleTester = new RuleTester() - -const errors = [{ messageId: 'unexpected' }] -// async functions are an ES2017 feature -const parserOptions = { ecmaVersion: 8 } - -ruleTester.run('no-async-before', rule, { - valid: [ - { code: 'before(\'a before case\', () => { cy.get(\'.someClass\'); })', parserOptions }, - { code: 'before(\'a before case\', async () => { await somethingAsync(); })', parserOptions }, - { code: 'async function nonTestFn () { return await somethingAsync(); }', parserOptions }, - { code: 'const nonTestArrowFn = async () => { await somethingAsync(); }', parserOptions }, - ], - invalid: [ - { code: 'before(\'a test case\', async () => { cy.get(\'.someClass\'); })', parserOptions, errors }, - { code: 'beforeEach(\'a test case\', async () => { cy.get(\'.someClass\'); })', parserOptions, errors }, - { code: 'before(\'a test case\', async function () { cy.get(\'.someClass\'); })', parserOptions, errors }, - { code: 'beforeEach(\'a test case\', async function () { cy.get(\'.someClass\'); })', parserOptions, errors }, - ], -}) diff --git a/tests-legacy/lib/rules/no-async-tests.js b/tests-legacy/lib/rules/no-async-tests.js deleted file mode 100644 index e255856b..00000000 --- a/tests-legacy/lib/rules/no-async-tests.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict' - -const rule = require('../../../lib/rules/no-async-tests') -const RuleTester = require('eslint').RuleTester - -const ruleTester = new RuleTester() - -const errors = [{ messageId: 'unexpected' }] -// async functions are an ES2017 feature -const parserOptions = { ecmaVersion: 8 } - -ruleTester.run('no-async-tests', rule, { - valid: [ - { code: 'it(\'a test case\', () => { cy.get(\'.someClass\'); })', parserOptions }, - { code: 'it(\'a test case\', async () => { await somethingAsync(); })', parserOptions }, - { code: 'async function nonTestFn () { return await somethingAsync(); }', parserOptions }, - { code: 'const nonTestArrowFn = async () => { await somethingAsync(); }', parserOptions }, - ], - invalid: [ - { code: 'it(\'a test case\', async () => { cy.get(\'.someClass\'); })', parserOptions, errors }, - { code: 'test(\'a test case\', async () => { cy.get(\'.someClass\'); })', parserOptions, errors }, - { code: 'it(\'a test case\', async function () { cy.get(\'.someClass\'); })', parserOptions, errors }, - { code: 'test(\'a test case\', async function () { cy.get(\'.someClass\'); })', parserOptions, errors }, - ], -}) diff --git a/tests-legacy/lib/rules/no-force.js b/tests-legacy/lib/rules/no-force.js deleted file mode 100644 index 86439258..00000000 --- a/tests-legacy/lib/rules/no-force.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict' - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-force') - -const RuleTester = require('eslint').RuleTester - -const errors = [{ messageId: 'unexpected' }] -const parserOptions = { ecmaVersion: 2018 } - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -let ruleTester = new RuleTester() - -ruleTester.run('no-force', 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 }, - { code: `cy.get('input').trigger('click', {anyoption: true})`, parserOptions }, - { code: `cy.get('input').rightclick({anyoption: true})`, parserOptions }, - { code: `cy.get('input').check()`, parserOptions }, - { code: `cy.get('input').select()`, parserOptions }, - { code: `cy.get('input').focus()`, parserOptions }, - { code: `cy.document().trigger("keydown", { ...event })`, parserOptions }, - ], - - 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 }, - ], -}) diff --git a/tests-legacy/lib/rules/no-pause.js b/tests-legacy/lib/rules/no-pause.js deleted file mode 100644 index 5d296d1c..00000000 --- a/tests-legacy/lib/rules/no-pause.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict' - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const rule = require('../../../lib/rules/no-pause') - -const RuleTester = require('eslint').RuleTester - -const errors = [{ messageId: 'unexpected' }] -const parserOptions = { ecmaVersion: 2018 } - -//------------------------------------------------------------------------------ -// Tests -//------------------------------------------------------------------------------ - -const ruleTester = new RuleTester() - -ruleTester.run('no-pause', rule, { - - valid: [ - { code: `pause()`, parserOptions }, - { code: `cy.get('button').dblclick()`, parserOptions }, - ], - - invalid: [ - { code: `cy.pause()`, parserOptions, errors }, - { code: `cy.pause({ log: false })`, parserOptions, errors }, - { code: `cy.get('button').pause()`, parserOptions, errors }, - { - code: `cy.get('a').should('have.attr', 'href').and('match', /dashboard/).pause()`, - parserOptions, - errors - } - ], -}) diff --git a/tests-legacy/lib/rules/no-unnecessary-waiting.js b/tests-legacy/lib/rules/no-unnecessary-waiting.js deleted file mode 100644 index a788b2b5..00000000 --- a/tests-legacy/lib/rules/no-unnecessary-waiting.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict' - -const rule = require('../../../lib/rules/no-unnecessary-waiting') -const RuleTester = require('eslint').RuleTester - -const ruleTester = new RuleTester() - -const errors = [{ messageId: 'unexpected' }] -const parserOptions = { ecmaVersion: 6, sourceType: 'module' } - -ruleTester.run('no-unnecessary-waiting', rule, { - valid: [ - { code: 'foo.wait(10)', parserOptions }, - - { code: 'cy.wait("@someRequest")', parserOptions }, - { code: 'cy.wait("@someRequest", { log: false })', parserOptions }, - { code: 'cy.wait("@someRequest").then((xhr) => xhr)', parserOptions }, - { code: 'cy.wait(["@someRequest", "@anotherRequest"])', parserOptions }, - - { code: 'cy.clock(5000)', parserOptions }, - { code: 'cy.scrollTo(0, 10)', parserOptions }, - { code: 'cy.tick(500)', parserOptions }, - - { code: 'const someRequest="@someRequest"; cy.wait(someRequest)', parserOptions, errors }, - { code: 'function customWait (alias = "@someRequest") { cy.wait(alias) }', parserOptions, errors }, - { code: 'const customWait = (alias = "@someRequest") => { cy.wait(alias) }', parserOptions, errors }, - { code: 'function customWait (ms) { cy.wait(ms) }', parserOptions, errors }, - { code: 'const customWait = (ms) => { cy.wait(ms) }', parserOptions, errors }, - - { code: 'import BAR_BAZ from "bar-baz"; cy.wait(BAR_BAZ)', parserOptions }, - { code: 'import { FOO_BAR } from "foo-bar"; cy.wait(FOO_BAR)', parserOptions }, - { code: 'import * as wildcard from "wildcard"; cy.wait(wildcard.value)', parserOptions }, - { code: 'import { NAME as OTHER_NAME } from "rename"; cy.wait(OTHER_NAME)', parserOptions }, - - // disable the eslint rule - { - code: ` - cy.wait(100); // eslint-disable-line no-unnecessary-waiting - `, - parserOptions, - }, - { - code: ` - /* eslint-disable-next-line no-unnecessary-waiting */ - cy.wait(100) - `, - parserOptions, - }, - { - code: ` - /* eslint-disable no-unnecessary-waiting */ - cy.wait(100) - /* eslint-enable no-unnecessary-waiting */ - `, - parserOptions, - }, - ], - - invalid: [ - { code: 'cy.wait(0)', parserOptions, errors }, - { code: 'cy.wait(100)', parserOptions, errors }, - { code: 'cy.wait(5000)', parserOptions, errors }, - { code: 'const someNumber=500; cy.wait(someNumber)', parserOptions, errors }, - { code: 'function customWait (ms = 1) { cy.wait(ms) }', parserOptions, errors }, - { code: 'const customWait = (ms = 1) => { cy.wait(ms) }', parserOptions, errors }, - - { code: 'cy.get(".some-element").wait(10)', parserOptions, errors }, - { code: 'cy.get(".some-element").contains("foo").wait(10)', parserOptions, errors }, - { code: 'const customWait = (ms = 1) => { cy.get(".some-element").wait(ms) }', parserOptions, errors }, - ], -}) diff --git a/tests-legacy/lib/rules/require-data-selectors.js b/tests-legacy/lib/rules/require-data-selectors.js deleted file mode 100644 index 1deda1be..00000000 --- a/tests-legacy/lib/rules/require-data-selectors.js +++ /dev/null @@ -1,31 +0,0 @@ -'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('require-data-selectors', 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 }, - { code: 'cy.get(\`[data-cy=${1}]\`)', parserOptions }, // eslint-disable-line no-useless-escape - { code: 'cy.get("@my-alias")', parserOptions, errors }, - { code: 'cy.get(`@my-alias`)', parserOptions, errors }, - ], - - 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 }, - { code: 'cy.get(\`[daedta-cy=${1}]\`)', parserOptions, errors }, // eslint-disable-line no-useless-escape - ], -}) diff --git a/tests-legacy/lib/rules/unsafe-to-chain-command.js b/tests-legacy/lib/rules/unsafe-to-chain-command.js deleted file mode 100644 index 901d8788..00000000 --- a/tests-legacy/lib/rules/unsafe-to-chain-command.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict' - -const rule = require('../../../lib/rules/unsafe-to-chain-command') -const RuleTester = require('eslint').RuleTester - -const ruleTester = new RuleTester() - -const errors = [{ messageId: 'unexpected' }] -const parserOptions = { ecmaVersion: 6 } - -ruleTester.run('action-ends-chain', rule, { - valid: [ - { - code: 'cy.get("new-todo").type("todo A{enter}"); cy.get("new-todo").type("todo B{enter}"); cy.get("new-todo").should("have.class", "active");', - parserOptions, - }, - { - code: 'cy.focused().should("be.visible");', - parserOptions, - }, - { - code: 'cy.submitBtn().click();', - parserOptions, - }, - ], - - invalid: [ - { - code: 'cy.get("new-todo").type("todo A{enter}").should("have.class", "active");', - parserOptions, - errors, - }, - { - code: 'cy.get("new-todo").type("todo A{enter}").type("todo B{enter}");', - parserOptions, - errors, - }, - { - code: 'cy.get("new-todo").focus().should("have.class", "active");', - parserOptions, - errors, - }, - { - code: 'cy.get("new-todo").customType("todo A{enter}").customClick();', - options: [{ methods: ['customType', 'customClick'] }], - parserOptions, - errors, - }, - { - code: 'cy.get("new-todo").customPress("Enter").customScroll();', - options: [{ methods: [/customPress/, /customScroll/] }], - parserOptions, - errors, - }, - ], -})