diff --git a/docs/rules/prefer-user-facing-locators.md b/docs/rules/prefer-user-facing-locators.md new file mode 100644 index 0000000..849ab8c --- /dev/null +++ b/docs/rules/prefer-user-facing-locators.md @@ -0,0 +1,26 @@ +## Disallow using `page.locator` (`prefer-user-facing-locators`) + +Prefer using user-facing locators over `page.locator` to make tests more robust. + +Check out the [Playwright documentation](https://playwright.dev/docs/locators) +for more information. + +## Rule Details + +Example of **incorrect** code for this rule: + +```javascript +await page.locator('button').click(); +``` + +Example of **correct** code for this rule: + +```javascript +await page.getByRole('button').click(); +``` + +```javascript +await page.getByRole('button', { + name: 'Submit', +}); +``` diff --git a/src/rules/prefer-user-facing-locators.ts b/src/rules/prefer-user-facing-locators.ts new file mode 100644 index 0000000..253d626 --- /dev/null +++ b/src/rules/prefer-user-facing-locators.ts @@ -0,0 +1,28 @@ +import { Rule } from 'eslint'; +import { isPageMethod } from '../utils/ast'; + +export default { + create(context) { + return { + CallExpression(node) { + if (isPageMethod(node, 'locator')) { + context.report({ messageId: 'preferUserFacingLocators', node }); + } + }, + }; + }, + meta: { + docs: { + category: 'Best Practices', + description: + 'Enforces the usage of user facing locators over page.locator()', + recommended: true, + url: 'https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-user-facing-locators.md', + }, + messages: { + preferUserFacingLocators: + 'Usage of page.locator() detected. Prefer user facing locators over page.locator()', + }, + type: 'problem', + }, +} as Rule.RuleModule; diff --git a/test/spec/prefer-user-facing-locators.spec.ts b/test/spec/prefer-user-facing-locators.spec.ts new file mode 100644 index 0000000..02ade80 --- /dev/null +++ b/test/spec/prefer-user-facing-locators.spec.ts @@ -0,0 +1,44 @@ +import rule from '../../src/rules/prefer-user-facing-locators'; +import { runRuleTester, test } from '../utils/rule-tester'; + +const messageId = 'preferUserFacingLocators'; + +runRuleTester('prefer-user-facing-locators', rule, { + invalid: [ + { + code: test('await page.locator()'), + errors: [{ column: 34, endColumn: 48, line: 1, messageId }], + }, + { + code: test('await this.page.locator()'), + errors: [{ column: 34, endColumn: 53, line: 1, messageId }], + }, + { + code: test("await page.locator('.btn')"), + errors: [{ column: 34, endColumn: 54, line: 1, messageId }], + }, + { + code: test('await page["locator"](".btn")'), + errors: [{ column: 34, endColumn: 57, line: 1, messageId }], + }, + { + code: test('await page[`locator`](".btn")'), + errors: [{ column: 34, endColumn: 57, line: 1, messageId }], + }, + ], + valid: [ + test('await page.click()'), + test('await this.page.click()'), + test('await page["hover"]()'), + test('await page[`check`]()'), + + // Preferred user facing locators + test('await page.getByText("lorem ipsum")'), + test('await page.getByLabel(/Email/)'), + test('await page.getByRole("button", { name: /submit/i })'), + test('await page.getByTestId("my-test-button").click()'), + test( + 'await page.getByRole("button").filter({ hasText: "Add to cart" }).click()' + ), + ], +});