From d7c07538633eeb12f985c31fa664595b87a3692e Mon Sep 17 00:00:00 2001 From: Mark Skelton Date: Sat, 10 Feb 2024 18:23:53 -0600 Subject: [PATCH] feat: Add `no-get-by-title` rule Fixes #196 --- README.md | 9 +++++---- docs/rules/no-get-by-title.md | 20 ++++++++++++++++++++ src/rules/no-get-by-title.ts | 27 +++++++++++++++++++++++++++ test/spec/no-get-by-title.spec.ts | 21 +++++++++++++++++++++ 4 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 docs/rules/no-get-by-title.md create mode 100644 src/rules/no-get-by-title.ts create mode 100644 test/spec/no-get-by-title.spec.ts diff --git a/README.md b/README.md index c68fdfc..bffcb3e 100644 --- a/README.md +++ b/README.md @@ -124,20 +124,21 @@ command line option.\ | ✔ | 🔧 | | [missing-playwright-await](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/missing-playwright-await.md) | Enforce Playwright APIs to be awaited | | ✔ | | | [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 | | ✔ | | 💡 | [no-element-handle](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-element-handle.md) | Disallow usage of element handles | -| ✔ | | | [no-eval](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-eval.md) | Disallow usage of `page.$eval` and `page.$$eval` | +| ✔ | | | [no-eval](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-eval.md) | Disallow usage of `page.$eval()` and `page.$$eval()` | | ✔ | | 💡 | [no-focused-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-focused-test.md) | Disallow usage of `.only` annotation | | ✔ | | | [no-force-option](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-force-option.md) | Disallow usage of the `{ force: true }` option | | ✔ | | | [no-nested-step](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-nested-step.md) | Disallow nested `test.step()` methods | | ✔ | | | [no-networkidle](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-networkidle.md) | Disallow usage of the `networkidle` option | | | | | [no-nth-methods](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-nth-methods.md) | Disallow usage of `first()`, `last()`, and `nth()` methods | -| ✔ | | | [no-page-pause](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-page-pause.md) | Disallow using `page.pause` | +| ✔ | | | [no-page-pause](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-page-pause.md) | Disallow using `page.pause()` | +| | 🔧 | | [no-get-by-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-get-by-title.md) | Disallow using `getByTitle()` | | | | | [no-raw-locators](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-raw-locators.md) | Disallow using raw locators | | ✔ | 🔧 | | [no-useless-await](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-useless-await.md) | Disallow unnecessary `await`s for Playwright methods | | | | | [no-restricted-matchers](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-restricted-matchers.md) | Disallow specific matchers & modifiers | | ✔ | | 💡 | [no-skipped-test](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-skipped-test.md) | Disallow usage of the `.skip` annotation | | ✔ | 🔧 | | [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 specific matcher exists | -| ✔ | | 💡 | [no-wait-for-selector](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-selector.md) | Disallow usage of `page.waitForSelector` | -| ✔ | | 💡 | [no-wait-for-timeout](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-timeout.md) | Disallow usage of `page.waitForTimeout` | +| ✔ | | 💡 | [no-wait-for-selector](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-selector.md) | Disallow usage of `page.waitForSelector()` | +| ✔ | | 💡 | [no-wait-for-timeout](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-wait-for-timeout.md) | Disallow usage of `page.waitForTimeout()` | | | | 💡 | [prefer-strict-equal](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | | 🔧 | | [prefer-lowercase-title](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names | | | 🔧 | | [prefer-to-be](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-be.md) | Suggest using `toBe()` | diff --git a/docs/rules/no-get-by-title.md b/docs/rules/no-get-by-title.md new file mode 100644 index 0000000..fc0a4c7 --- /dev/null +++ b/docs/rules/no-get-by-title.md @@ -0,0 +1,20 @@ +## Disallow using `getByTitle()` (`no-get-by-title`) + +The HTML `title` attribute does not provide a fully accessible tooltip for +elements so relying on it to identify elements can hide accessibility issues in +your code. This rule helps to prevent that by disallowing use of the +`getByTitle` method. + +## Rule Details + +Example of **incorrect** code for this rule: + +```javascript +await page.getByTitle('Delete product').click(); +``` + +Example of **correct** code for this rule: + +```javascript +await page.getByRole('button', { name: 'Delete product' }).click(); +``` diff --git a/src/rules/no-get-by-title.ts b/src/rules/no-get-by-title.ts new file mode 100644 index 0000000..32f9f6f --- /dev/null +++ b/src/rules/no-get-by-title.ts @@ -0,0 +1,27 @@ +import { Rule } from 'eslint'; +import { isPageMethod } from '../utils/ast'; + +export default { + create(context) { + return { + CallExpression(node) { + if (isPageMethod(node, 'getByTitle')) { + context.report({ messageId: 'noGetByTitle', node }); + } + }, + }; + }, + meta: { + docs: { + category: 'Best Practices', + description: 'Disallows the usage of getByTitle()', + recommended: false, + url: 'https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-get-by-title.md', + }, + messages: { + noGetByTitle: + 'The HTML title attribute is not an accessible name. Prefer getByRole() or getByLabelText() instead.', + }, + type: 'suggestion', + }, +} as Rule.RuleModule; diff --git a/test/spec/no-get-by-title.spec.ts b/test/spec/no-get-by-title.spec.ts new file mode 100644 index 0000000..95230ca --- /dev/null +++ b/test/spec/no-get-by-title.spec.ts @@ -0,0 +1,21 @@ +import rule from '../../src/rules/no-get-by-title'; +import { runRuleTester, test } from '../utils/rule-tester'; + +const messageId = 'noGetByTitle'; + +runRuleTester('no-get-by-title', rule, { + invalid: [ + { + code: test('await page.getByTitle("lorem ipsum")'), + errors: [{ column: 34, endColumn: 64, line: 1, messageId }], + }, + { + code: test('await this.page.getByTitle("lorem ipsum")'), + errors: [{ column: 34, endColumn: 69, line: 1, messageId }], + }, + ], + valid: [ + test('await page.locator("[title=lorem ipsum]")'), + test('await page.getByRole("button")'), + ], +});