From 61906ba4c9454ce485ec923beb2bbe04b81ec24d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katka=20Pil=C3=A1tov=C3=A1?= Date: Tue, 31 Dec 2024 15:24:24 +0100 Subject: [PATCH 1/2] Add Playwright plugin config --- .github/ISSUE_TEMPLATE/bug-report.yml | 1 + .github/ISSUE_TEMPLATE/change-request.yml | 1 + .github/ISSUE_TEMPLATE/feature-request.yml | 1 + README.md | 2 + docs/icons/material/playwright.png | Bin 0 -> 786 bytes docs/playwright.md | 73 +++++++++++++++++++++ package-lock.json | 33 +++++++++- package.json | 5 ++ scripts/helpers/configs.js | 5 +- scripts/helpers/plugins.js | 3 + scripts/helpers/types.d.ts | 1 + src/configs/playwright.js | 40 +++++++++++ src/lib/patterns.js | 6 ++ tests/configs/playwright.spec.js | 47 +++++++++++++ 14 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 docs/icons/material/playwright.png create mode 100644 docs/playwright.md create mode 100644 src/configs/playwright.js create mode 100644 tests/configs/playwright.spec.js diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index acbbad9..88d8f44 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -49,6 +49,7 @@ body: - Jest - Vitest - Cypress + - Playwright - Storybook validations: required: true diff --git a/.github/ISSUE_TEMPLATE/change-request.yml b/.github/ISSUE_TEMPLATE/change-request.yml index 2c7c123..7ce4cbd 100644 --- a/.github/ISSUE_TEMPLATE/change-request.yml +++ b/.github/ISSUE_TEMPLATE/change-request.yml @@ -47,6 +47,7 @@ body: - Jest - Vitest - Cypress + - Playwright - Storybook validations: required: true diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index 9bbd9a5..d60d659 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -47,6 +47,7 @@ body: - Jest - Vitest - Cypress + - Playwright - Storybook validations: required: true diff --git a/README.md b/README.md index a08aac8..e16c034 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Recommended ESLint presets by [Code PushUp](https://github.com/code-pushup/cli/t | ![jest](./docs/icons/material/jest.png) | [jest](./docs/jest.md) | Config for projects using **Jest** for testing. | | ![vitest](./docs/icons/material/vitest.png) | [vitest](./docs/vitest.md) | Config for projects using **Vitest** for testing. | | ![cypress](./docs/icons/material/cypress.png) | [cypress](./docs/cypress.md) | Config for projects using **Cypress** for testing. | +| ![playwright](./docs/icons/material/playwright.png) | [playwright](./docs/playwright.md) | Config for projects using **Playwright** for testing. | | ![storybook](./docs/icons/material/storybook.png) | [storybook](./docs/storybook.md) | Config for projects using **Storybook** for UI components. | Some configs extend other configs, as illustrated below. So, for example, extending `angular` config implicitly extends `typescript` and `javascript` configs as well. @@ -89,6 +90,7 @@ All peer dependencies used by `@code-pushup/eslint-config` are listed below, alo | ![jest](./docs/icons/material/jest.png) | [eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) | `^28.8.0` | | | ![test](./docs/icons/icons8/test.png) | [eslint-plugin-jest-formatting](https://www.npmjs.com/package/eslint-plugin-jest-formatting) | `^3.0.0` | | | ![nodejs](./docs/icons/material/nodejs.png) | [eslint-plugin-n](https://www.npmjs.com/package/eslint-plugin-n) | `>=17.0.0` | | +| ![playwright](./docs/icons/material/playwright.png) | [eslint-plugin-playwright](https://www.npmjs.com/package/eslint-plugin-playwright) | `>=2.1.0` | | | ![rxjs](./docs/icons/other/rxjs.png) | [eslint-plugin-rxjs-x](https://www.npmjs.com/package/eslint-plugin-rxjs-x) | `>=0.6.0` | | | ![storybook](./docs/icons/material/storybook.png) | [eslint-plugin-storybook](https://www.npmjs.com/package/eslint-plugin-storybook) | `>=0.10.0` | | | ![vitest](./docs/icons/material/vitest.png) | [eslint-plugin-vitest](https://www.npmjs.com/package/eslint-plugin-vitest) | `>=0.5.0` | | diff --git a/docs/icons/material/playwright.png b/docs/icons/material/playwright.png new file mode 100644 index 0000000000000000000000000000000000000000..6a8991784dc37735e30d322db99970b9eebb226f GIT binary patch literal 786 zcmV+t1MU2YP)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10-s4l zK~zYI-IYyj%uy7@fA`IdmW60hA0e?|QxahzG}Xic5zUyHDhn)VLd1?(kS;7NY$PPL z5RnKQX-4&9TCpQkCJ|q&N=1zji4?8xRA=6?Ff$#~qLp;PUEX`nJ@?%A-v4isn9OjK z+Wo%}2#yC7PK0ME<2o!8IM5!ua(^NM<8t6Lt^wIE^`d#K2z(e2T>?(x&bG(>Z)7%tG0iux}=lOudjhWYeR9(jd?VJOg+!BZs#>lcfXBpEua|+vb40 zOO6P@t*n#lX}L-nXNBegd??Lxb_uR}@7@Rw<_9%(I_H-os{-!j`XhHEFHjb5yA3=u z*cdeb%0!o8r2y%`sNtpnRryI}^SyWVa^5Tx*qKcy6R~*ThyzJr7m6BrLG!>=J^s0zv>^LvSbpA4{?X7?$iBrn^81umM<=r9YTD99rF6-~8>D z0s!x}MJk=Q{lGGy|4g-gOfJ%%QNW+$wU^a&Mq7FdlYjgc<)XGNa@=G`f0!5*y^=)| z(UzX#!ng$h!Yy?xW!ev{!TFg$O0u{w8EWd<)HO5~XF>vh6aW4%bJ7LA1Ei1@7-sp( Q0000007*qoM6N<$f?k1Cg#Z8m literal 0 HcmV?d00001 diff --git a/docs/playwright.md b/docs/playwright.md new file mode 100644 index 0000000..8525072 --- /dev/null +++ b/docs/playwright.md @@ -0,0 +1,73 @@ +# `playwright` config + +Config for projects using **Playwright** for testing. + +## 🏗️ Setup + +1. If you haven't already, make sure to [install `@code-pushup/eslint-config` and its required peer dependencies](../README.md#🏗️-setup). +2. Since this plugin requires additional peer dependencies, you have to install them as well: + + ```sh + npm install -D eslint-plugin-playwright + ``` + +3. Add to your `eslint.config.js` file: + + ```js + import playwright from '@code-pushup/eslint-config/playwright.js'; + import tseslint from 'typescript-eslint'; + + export default tseslint.config(...playwright); + ``` + +## 📏 Rules (38) + +> 🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).
💡 Manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). + +### 🚨 Errors (10) + +| Plugin | Rule | Options | Autofix | Overrides | +| :-----------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------ | :-----: | :-------: | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [missing-playwright-await](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/missing-playwright-await.md)
Identify false positives when async Playwright APIs are not properly awaited. | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-focused-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-focused-test.md)
Prevent usage of `.only()` focus test annotation | | 💡 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-networkidle](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-networkidle.md)
Prevent usage of the networkidle option | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-standalone-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-standalone-expect.md)
Disallow using `expect` outside of `test` blocks | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-unsafe-references](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-unsafe-references.md)
Prevent unsafe variable references in page.evaluate() | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-web-first-assertions](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-web-first-assertions.md)
Prefer web first assertions | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [valid-describe-callback](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-describe-callback.md)
Enforce valid `describe()` callback | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [valid-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-expect.md)
Enforce valid `expect()` usage | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [valid-expect-in-promise](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-expect-in-promise.md)
Require promises that have expectations in their chain to be valid | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [valid-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-title.md)
Enforce valid titles | | 🔧 | | + +### ⚠️ Warnings (28) + +| Plugin | Rule | Options | Autofix | Overrides | +| :-----------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------ | :-----: | :-------: | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [expect-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/expect-expect.md)
Enforce assertion to be made in a test body | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [max-nested-describe](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/max-nested-describe.md)
Enforces a maximum depth to nested describe calls | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-commented-out-tests](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-commented-out-tests.md)
Disallow commented out tests | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-conditional-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-conditional-expect.md)
Disallow calling `expect` conditionally | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-conditional-in-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-conditional-in-test.md)
Disallow conditional logic in tests | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-duplicate-hooks](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-duplicate-hooks.md)
Disallow duplicate setup and teardown hooks | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-element-handle](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-element-handle.md)
The use of ElementHandle is discouraged, use Locator instead | | 💡 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-eval](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-eval.md)
The use of `page.$eval` and `page.$$eval` are discouraged, use `locator.evaluate` or `locator.evaluateAll` instead | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-force-option](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-force-option.md)
Prevent usage of `{ force: true }` option. | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-nested-step](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-nested-step.md)
Disallow nested `test.step()` methods | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-page-pause](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-page-pause.md)
Prevent usage of page.pause() | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-skipped-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-skipped-test.md)
Prevent usage of the `.skip()` skip test annotation. | | 💡 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-useless-await](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-useless-await.md)
Disallow unnecessary awaits for Playwright methods | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-useless-not](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-useless-not.md)
Disallow usage of 'not' matchers when a more specific matcher exists | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-wait-for-selector](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-selector.md)
Prevent usage of page.waitForSelector() | | 💡 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-wait-for-timeout](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-timeout.md)
Prevent usage of page.waitForTimeout() | | 💡 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-comparison-matcher](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-comparision-matcher.md)
Suggest using the built-in comparison matchers | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-equality-matcher](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-equality-matcher.md)
Suggest using the built-in equality matchers | | 💡 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-hooks-in-order](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-hooks-in-order.md)
Prefer having hooks in a consistent order | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-hooks-on-top](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-hooks-on-top.md)
Suggest having hooks before any test cases | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-locator](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-locator.md)
Suggest locators over page methods | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-native-locators](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-native-locators.md)
Prefer native locator functions | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-to-be](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-be.md)
Suggest using `toBe()` for primitive literals | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-to-contain](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-contain.md)
Suggest using toContain() | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-to-have-count](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-count.md)
Suggest using `toHaveCount()` | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-to-have-length](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-length.md)
Suggest using `toHaveLength()` | | 🔧 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [require-hook](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/require-hook.md)
Require setup and teardown code to be within a hook | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [require-to-throw-message](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/require-to-throw-message.md)
Require a message for `toThrow()` | | | | diff --git a/package-lock.json b/package-lock.json index fb5beca..9f648ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "eslint-plugin-jest": "^28.8.0", "eslint-plugin-jest-formatting": "^3.1.0", "eslint-plugin-n": "^17.0.0", + "eslint-plugin-playwright": "^2.1.0", "eslint-plugin-promise": "^6.4.0", "eslint-plugin-rxjs-x": "^0.6.1", "eslint-plugin-sonarjs": "^1.0.4", @@ -54,7 +55,7 @@ "eslint-plugin-jest-formatting": "^3.0.0", "eslint-plugin-n": ">=17.0.0", "eslint-plugin-promise": ">=6.4.0", - "eslint-plugin-rxjs-x": ">=0.2.4", + "eslint-plugin-rxjs-x": ">=0.6.0", "eslint-plugin-sonarjs": "^1.0.4", "eslint-plugin-storybook": ">=0.10.0", "eslint-plugin-unicorn": ">=50.0.0", @@ -5555,6 +5556,36 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/eslint-plugin-playwright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.1.0.tgz", + "integrity": "sha512-wMbHOehofSB1cBdzz2CLaCYaKNLeTQ0YnOW+7AHa281TJqlpEJUBgTHbRUYOUxiXphfWwOyTPvgr6vvEmArbSA==", + "dev": true, + "dependencies": { + "globals": "^13.23.0" + }, + "engines": { + "node": ">=16.6.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, + "node_modules/eslint-plugin-playwright/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-plugin-promise": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.4.0.tgz", diff --git a/package.json b/package.json index 2317081..e51613d 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "eslint-plugin-jest": "^28.8.0", "eslint-plugin-jest-formatting": "^3.0.0", "eslint-plugin-n": ">=17.0.0", + "eslint-plugin-playwright": ">=2.1.0", "eslint-plugin-promise": ">=6.4.0", "eslint-plugin-rxjs-x": ">=0.6.0", "eslint-plugin-sonarjs": "^1.0.4", @@ -84,6 +85,9 @@ "eslint-plugin-n": { "optional": true }, + "eslint-plugin-playwright": { + "optional": true + }, "eslint-plugin-rxjs-x": { "optional": true }, @@ -111,6 +115,7 @@ "eslint-plugin-jest": "^28.8.0", "eslint-plugin-jest-formatting": "^3.1.0", "eslint-plugin-n": "^17.0.0", + "eslint-plugin-playwright": "^2.1.0", "eslint-plugin-promise": "^6.4.0", "eslint-plugin-rxjs-x": "^0.6.1", "eslint-plugin-sonarjs": "^1.0.4", diff --git a/scripts/helpers/configs.js b/scripts/helpers/configs.js index 9609935..4ff9101 100644 --- a/scripts/helpers/configs.js +++ b/scripts/helpers/configs.js @@ -13,6 +13,7 @@ const configDescriptions = { jest: md`Config for projects using ${md.bold('Jest')} for testing.`, vitest: md`Config for projects using ${md.bold('Vitest')} for testing.`, cypress: md`Config for projects using ${md.bold('Cypress')} for testing.`, + playwright: md`Config for projects using ${md.bold('Playwright')} for testing.`, storybook: md`Config for projects using ${md.bold('Storybook')} for UI components.`, }; @@ -31,6 +32,7 @@ const configIcons = { jest: 'material/jest', vitest: 'material/vitest', cypress: 'material/cypress', + playwright: 'material/playwright', storybook: 'material/storybook', }; @@ -40,6 +42,7 @@ const configPatterns = { jest: '*.test.ts', vitest: '*.test.ts', cypress: '*.cy.ts', + playwright: '*.spec.ts', storybook: '*.stories.ts', }; @@ -50,7 +53,7 @@ const configExtraPatterns = { }; /** @type {(keyof typeof configDescriptions)[]} */ -const testConfigs = ['jest', 'vitest', 'cypress']; +const testConfigs = ['jest', 'vitest', 'cypress', 'playwright']; const tsConfigDocsReference = md`Refer to ${md.link('./typescript.md#🏗️-setup', "step 3 in TypeScript config's setup docs")} for how to set up tsconfig properly.`; diff --git a/scripts/helpers/plugins.js b/scripts/helpers/plugins.js index 9b47391..0b6fef8 100644 --- a/scripts/helpers/plugins.js +++ b/scripts/helpers/plugins.js @@ -16,6 +16,7 @@ const pluginIcons = { 'jest-formatting': 'icons8/test', n: 'material/nodejs', 'no-secrets': 'icons8/secure', + playwright: 'material/playwright', promise: 'icons8/promise', react: 'material/react', 'react-hooks': 'material/react_ts', @@ -47,6 +48,8 @@ const pluginDocsUrls = { 'https://github.com/dangreenisrael/eslint-plugin-jest-formatting#readme', n: 'https://github.com/eslint-community/eslint-plugin-n#readme', 'no-secrets': 'https://github.com/nickdeis/eslint-plugin-no-secrets#readme', + playwright: + 'https://github.com/playwright-community/eslint-plugin-playwright#readme', promise: 'https://github.com/eslint-community/eslint-plugin-promise#readme', react: 'https://github.com/jsx-eslint/eslint-plugin-react#readme', 'react-hooks': diff --git a/scripts/helpers/types.d.ts b/scripts/helpers/types.d.ts index dc9537a..b5b26bc 100644 --- a/scripts/helpers/types.d.ts +++ b/scripts/helpers/types.d.ts @@ -46,6 +46,7 @@ export type Icon = | 'material/javascript' | 'material/jest' | 'material/nodejs' + | 'material/playwright' | 'material/react_ts' | 'material/react' | 'material/storybook' diff --git a/src/configs/playwright.js b/src/configs/playwright.js new file mode 100644 index 0000000..faba554 --- /dev/null +++ b/src/configs/playwright.js @@ -0,0 +1,40 @@ +// @ts-check + +import playwright from 'eslint-plugin-playwright'; +import tseslint from 'typescript-eslint'; +import { PLAYWRIGHT_FILE_PATTERNS } from '../lib/patterns.js'; + +export default tseslint.config({ + files: PLAYWRIGHT_FILE_PATTERNS, + extends: [ + playwright.configs['flat/recommended'], + { + name: 'code-pushup/playwright/customized', + rules: { + 'playwright/no-conditional-expect': 'warn', + 'playwright/no-conditional-in-test': 'warn', + }, + }, + { + name: 'code-pushup/playwright/additional', + rules: { + 'playwright/no-commented-out-tests': 'warn', + 'playwright/no-duplicate-hooks': 'warn', + 'playwright/no-focused-test': 'warn', + 'playwright/no-nested-step': 'warn', + 'playwright/prefer-comparison-matcher': 'warn', + 'playwright/prefer-equality-matcher': 'warn', + 'playwright/prefer-hooks-in-order': 'warn', + 'playwright/prefer-hooks-on-top': 'warn', + 'playwright/prefer-native-locators': 'warn', + 'playwright/prefer-locator': 'warn', + 'playwright/prefer-to-be': 'warn', + 'playwright/prefer-to-contain': 'warn', + 'playwright/prefer-to-have-count': 'warn', + 'playwright/prefer-to-have-length': 'warn', + 'playwright/require-hook': 'warn', + 'playwright/require-to-throw-message': 'warn', + }, + }, + ], +}); diff --git a/src/lib/patterns.js b/src/lib/patterns.js index 967bf40..fe9e5f1 100644 --- a/src/lib/patterns.js +++ b/src/lib/patterns.js @@ -72,6 +72,12 @@ export const CYPRESS_FILE_PATTERNS = [ '**/e2e/**/*.[jt]s?(x)', ]; +export const PLAYWRIGHT_FILE_PATTERNS = [ + '**/*.+(test|spec).[jt]s?(x)', + '**/*.po.[jt]s?(x)', + '**/e2e/**/*.[jt]s?(x)', +]; + export const NODE_FILE_PATTERNS = ['**/*.ts', '**/*.js']; export const GRAPHQL_FILE_PATTERNS = ['**/*.graphql']; diff --git a/tests/configs/playwright.spec.js b/tests/configs/playwright.spec.js new file mode 100644 index 0000000..d2a1114 --- /dev/null +++ b/tests/configs/playwright.spec.js @@ -0,0 +1,47 @@ +// @ts-check + +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { createLintUtils } from '../helpers/lint-utils'; + +describe('playwright config', () => { + const { setup, teardown, loadConfig } = createLintUtils( + 'playwright', + '*.spec.ts', + ); + + beforeAll(setup); + + afterAll(teardown); + + it('should not include playwright rules for non-test file', async () => { + const config = await loadConfig('components/Button.tsx'); + expect(Object.keys(config?.rules ?? {}).join(',')).not.toContain( + 'playwright/', + ); + }); + + it('should include playwright rules for test file', async () => { + const config = await loadConfig('components/login.test.ts'); + expect(Object.keys(config.rules ?? {}).join(',')).toContain('playwright/'); + }); + + it('should include playwright rules for test folder', async () => { + const config = await loadConfig('e2e/dashboard/subscription.ts'); + expect(Object.keys(config.rules ?? {}).join(',')).toContain('playwright/'); + }); + + it('should have rule from extended recommended playwright config', async () => { + const config = await loadConfig(); + expect(config.rules).toHaveProperty('playwright/valid-expect'); + }); + + it('should have explicitly added rule', async () => { + const config = await loadConfig(); + expect(config.rules).toHaveProperty('playwright/prefer-native-locators'); + }); + + it('should have customized severity level for rule from extended config', async () => { + const config = await loadConfig(); + expect(config.rules?.['playwright/no-conditional-expect']).toEqual([1]); + }); +}); From 8d3736b53d122c67210b3248af2f98b9312d3a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Katka=20Pil=C3=A1tov=C3=A1?= Date: Tue, 31 Dec 2024 16:39:52 +0100 Subject: [PATCH 2/2] Update severity and folder pattern, add formatting rules for Playwright --- README.md | 2 +- docs/playwright.md | 14 ++++++++------ package.json | 2 +- src/configs/playwright.js | 14 +++++++++++++- src/lib/patterns.js | 4 ++-- tests/configs/cypress.spec.js | 18 ++++++++++++------ tests/configs/playwright.spec.js | 14 +++++++++++++- 7 files changed, 50 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index e16c034..7968085 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ All peer dependencies used by `@code-pushup/eslint-config` are listed below, alo | ![jest](./docs/icons/material/jest.png) | [eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) | `^28.8.0` | | | ![test](./docs/icons/icons8/test.png) | [eslint-plugin-jest-formatting](https://www.npmjs.com/package/eslint-plugin-jest-formatting) | `^3.0.0` | | | ![nodejs](./docs/icons/material/nodejs.png) | [eslint-plugin-n](https://www.npmjs.com/package/eslint-plugin-n) | `>=17.0.0` | | -| ![playwright](./docs/icons/material/playwright.png) | [eslint-plugin-playwright](https://www.npmjs.com/package/eslint-plugin-playwright) | `>=2.1.0` | | +| ![playwright](./docs/icons/material/playwright.png) | [eslint-plugin-playwright](https://www.npmjs.com/package/eslint-plugin-playwright) | `^2.1.0` | | | ![rxjs](./docs/icons/other/rxjs.png) | [eslint-plugin-rxjs-x](https://www.npmjs.com/package/eslint-plugin-rxjs-x) | `>=0.6.0` | | | ![storybook](./docs/icons/material/storybook.png) | [eslint-plugin-storybook](https://www.npmjs.com/package/eslint-plugin-storybook) | `>=0.10.0` | | | ![vitest](./docs/icons/material/vitest.png) | [eslint-plugin-vitest](https://www.npmjs.com/package/eslint-plugin-vitest) | `>=0.5.0` | | diff --git a/docs/playwright.md b/docs/playwright.md index 8525072..63a1362 100644 --- a/docs/playwright.md +++ b/docs/playwright.md @@ -8,7 +8,7 @@ Config for projects using **Playwright** for testing. 2. Since this plugin requires additional peer dependencies, you have to install them as well: ```sh - npm install -D eslint-plugin-playwright + npm install -D eslint-plugin-{jest-formatting,playwright} ``` 3. Add to your `eslint.config.js` file: @@ -20,18 +20,16 @@ Config for projects using **Playwright** for testing. export default tseslint.config(...playwright); ``` -## 📏 Rules (38) +## 📏 Rules (40) > 🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).
💡 Manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions). -### 🚨 Errors (10) +### 🚨 Errors (8) | Plugin | Rule | Options | Autofix | Overrides | | :-----------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------ | :-----: | :-------: | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [missing-playwright-await](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/missing-playwright-await.md)
Identify false positives when async Playwright APIs are not properly awaited. | | 🔧 | | -| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-focused-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-focused-test.md)
Prevent usage of `.only()` focus test annotation | | 💡 | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-networkidle](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-networkidle.md)
Prevent usage of the networkidle option | | | | -| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-standalone-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-standalone-expect.md)
Disallow using `expect` outside of `test` blocks | | 🔧 | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-unsafe-references](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-unsafe-references.md)
Prevent unsafe variable references in page.evaluate() | | 🔧 | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [prefer-web-first-assertions](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-web-first-assertions.md)
Prefer web first assertions | | 🔧 | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [valid-describe-callback](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-describe-callback.md)
Enforce valid `describe()` callback | | | | @@ -39,10 +37,12 @@ Config for projects using **Playwright** for testing. | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [valid-expect-in-promise](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-expect-in-promise.md)
Require promises that have expectations in their chain to be valid | | | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [valid-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-title.md)
Enforce valid titles | | 🔧 | | -### ⚠️ Warnings (28) +### ⚠️ Warnings (32) | Plugin | Rule | Options | Autofix | Overrides | | :-----------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------ | :-----: | :-------: | +| [![jest-formatting](./icons/icons8/test.png)](https://github.com/dangreenisrael/eslint-plugin-jest-formatting#readme) | padding-around-describe-blocks
| | 🔧 | | +| [![jest-formatting](./icons/icons8/test.png)](https://github.com/dangreenisrael/eslint-plugin-jest-formatting#readme) | padding-around-test-blocks
| | 🔧 | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [expect-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/expect-expect.md)
Enforce assertion to be made in a test body | | | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [max-nested-describe](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/max-nested-describe.md)
Enforces a maximum depth to nested describe calls | | | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-commented-out-tests](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-commented-out-tests.md)
Disallow commented out tests | | | | @@ -51,10 +51,12 @@ Config for projects using **Playwright** for testing. | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-duplicate-hooks](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-duplicate-hooks.md)
Disallow duplicate setup and teardown hooks | | | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-element-handle](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-element-handle.md)
The use of ElementHandle is discouraged, use Locator instead | | 💡 | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-eval](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-eval.md)
The use of `page.$eval` and `page.$$eval` are discouraged, use `locator.evaluate` or `locator.evaluateAll` instead | | | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-focused-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-focused-test.md)
Prevent usage of `.only()` focus test annotation | | 💡 | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-force-option](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-force-option.md)
Prevent usage of `{ force: true }` option. | | | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-nested-step](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-nested-step.md)
Disallow nested `test.step()` methods | | | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-page-pause](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-page-pause.md)
Prevent usage of page.pause() | | | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-skipped-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-skipped-test.md)
Prevent usage of the `.skip()` skip test annotation. | | 💡 | | +| [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-standalone-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-standalone-expect.md)
Disallow using `expect` outside of `test` blocks | | 🔧 | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-useless-await](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-useless-await.md)
Disallow unnecessary awaits for Playwright methods | | 🔧 | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-useless-not](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-useless-not.md)
Disallow usage of 'not' matchers when a more specific matcher exists | | 🔧 | | | [![playwright](./icons/material/playwright.png)](https://github.com/playwright-community/eslint-plugin-playwright#readme) | [no-wait-for-selector](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-selector.md)
Prevent usage of page.waitForSelector() | | 💡 | | diff --git a/package.json b/package.json index e51613d..f642d95 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "eslint-plugin-jest": "^28.8.0", "eslint-plugin-jest-formatting": "^3.0.0", "eslint-plugin-n": ">=17.0.0", - "eslint-plugin-playwright": ">=2.1.0", + "eslint-plugin-playwright": "^2.1.0", "eslint-plugin-promise": ">=6.4.0", "eslint-plugin-rxjs-x": ">=0.6.0", "eslint-plugin-sonarjs": "^1.0.4", diff --git a/src/configs/playwright.js b/src/configs/playwright.js index faba554..d9f6f09 100644 --- a/src/configs/playwright.js +++ b/src/configs/playwright.js @@ -1,5 +1,6 @@ // @ts-check +import * as jestFormatting from 'eslint-plugin-jest-formatting'; import playwright from 'eslint-plugin-playwright'; import tseslint from 'typescript-eslint'; import { PLAYWRIGHT_FILE_PATTERNS } from '../lib/patterns.js'; @@ -8,11 +9,23 @@ export default tseslint.config({ files: PLAYWRIGHT_FILE_PATTERNS, extends: [ playwright.configs['flat/recommended'], + { + name: 'code-pushup/playwright/jest-formatting', + plugins: { + 'jest-formatting': jestFormatting, + }, + rules: { + 'jest-formatting/padding-around-describe-blocks': 'warn', + 'jest-formatting/padding-around-test-blocks': 'warn', + }, + }, { name: 'code-pushup/playwright/customized', rules: { 'playwright/no-conditional-expect': 'warn', 'playwright/no-conditional-in-test': 'warn', + 'playwright/no-focused-test': 'warn', + 'playwright/no-standalone-expect': 'warn', }, }, { @@ -20,7 +33,6 @@ export default tseslint.config({ rules: { 'playwright/no-commented-out-tests': 'warn', 'playwright/no-duplicate-hooks': 'warn', - 'playwright/no-focused-test': 'warn', 'playwright/no-nested-step': 'warn', 'playwright/prefer-comparison-matcher': 'warn', 'playwright/prefer-equality-matcher': 'warn', diff --git a/src/lib/patterns.js b/src/lib/patterns.js index fe9e5f1..9af0a14 100644 --- a/src/lib/patterns.js +++ b/src/lib/patterns.js @@ -69,13 +69,13 @@ export const HTML_FILE_PATTERNS = ['**/*.html']; export const CYPRESS_FILE_PATTERNS = [ '**/*.cy.[jt]s?(x)', - '**/e2e/**/*.[jt]s?(x)', + '**/?(*-)e2e/**/*.[jt]s?(x)', ]; export const PLAYWRIGHT_FILE_PATTERNS = [ '**/*.+(test|spec).[jt]s?(x)', '**/*.po.[jt]s?(x)', - '**/e2e/**/*.[jt]s?(x)', + '**/?(*-)e2e/**/*.[jt]s?(x)', ]; export const NODE_FILE_PATTERNS = ['**/*.ts', '**/*.js']; diff --git a/tests/configs/cypress.spec.js b/tests/configs/cypress.spec.js index 6ab05cd..5c121df 100644 --- a/tests/configs/cypress.spec.js +++ b/tests/configs/cypress.spec.js @@ -1,30 +1,36 @@ // @ts-check -import { afterAll, beforeAll, describe, expect, test } from 'vitest'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { createLintUtils } from '../helpers/lint-utils'; describe('cypress config', () => { const { setup, teardown, loadConfig } = createLintUtils('cypress', '*.cy.js'); beforeAll(setup); + afterAll(teardown); - test('should not include Cypress rules for non-Cypress file', async () => { + it('should not include Cypress rules for non-Cypress file', async () => { const config = await loadConfig('auth.spec.js'); expect(Object.keys(config.rules ?? {}).join(',')).not.toContain('cypress/'); }); - test('should include cypress rules for Cypress file', async () => { - const config = await loadConfig('e2e/login.cy.js'); + it('should include cypress rules for Cypress file', async () => { + const config = await loadConfig('app/submit-form.cy.js'); + expect(Object.keys(config.rules ?? {}).join(',')).toContain('cypress/'); + }); + + it('should include cypress rules for Cypress folder', async () => { + const config = await loadConfig('app-e2e/login.js'); expect(Object.keys(config.rules ?? {}).join(',')).toContain('cypress/'); }); - test('should have rule from extended recommended cypress config', async () => { + it('should have rule from extended recommended cypress config', async () => { const config = await loadConfig(); expect(config.rules).toHaveProperty('cypress/no-unnecessary-waiting'); }); - test('should have explicitly added rule', async () => { + it('should have explicitly added rule', async () => { const config = await loadConfig(); expect(config.rules).toHaveProperty('cypress/no-force'); }); diff --git a/tests/configs/playwright.spec.js b/tests/configs/playwright.spec.js index d2a1114..2bcd233 100644 --- a/tests/configs/playwright.spec.js +++ b/tests/configs/playwright.spec.js @@ -25,8 +25,13 @@ describe('playwright config', () => { expect(Object.keys(config.rules ?? {}).join(',')).toContain('playwright/'); }); + it('should include playwright rules for page object file', async () => { + const config = await loadConfig('components/login.po.ts'); + expect(Object.keys(config.rules ?? {}).join(',')).toContain('playwright/'); + }); + it('should include playwright rules for test folder', async () => { - const config = await loadConfig('e2e/dashboard/subscription.ts'); + const config = await loadConfig('website-e2e/dashboard/subscription.ts'); expect(Object.keys(config.rules ?? {}).join(',')).toContain('playwright/'); }); @@ -40,6 +45,13 @@ describe('playwright config', () => { expect(config.rules).toHaveProperty('playwright/prefer-native-locators'); }); + it('should include jest formatting rule', async () => { + const config = await loadConfig(); + expect(Object.keys(config.rules ?? {}).join(',')).toContain( + 'jest-formatting/', + ); + }); + it('should have customized severity level for rule from extended config', async () => { const config = await loadConfig(); expect(config.rules?.['playwright/no-conditional-expect']).toEqual([1]);