Skip to content

Commit

Permalink
feat: Add no-nth-methods
Browse files Browse the repository at this point in the history
  • Loading branch information
mskelton committed Jun 19, 2023
1 parent 56eb171 commit 0579822
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ command line option.\
|| | | [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-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 |
Expand Down
14 changes: 14 additions & 0 deletions docs/rules/no-nth-methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Disallow usage of `nth` methods (`no-nth-methods`)

This rule prevents the usage of `nth` methods (`first()`, `last()`, and
`nth()`). These methods can be prone to flakiness if the DOM structure changes.

## Rule Details

Examples of **incorrect** code for this rule:

```javascript
page.locator('button').first();
page.locator('button').last();
page.locator('button').nth(3);
```
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import noFocusedTest from './rules/no-focused-test';
import noForceOption from './rules/no-force-option';
import noNestedStep from './rules/no-nested-step';
import noNetworkidle from './rules/no-networkidle';
import noNthMethods from './rules/no-nth-methods';
import noPagePause from './rules/no-page-pause';
import noRestrictedMatchers from './rules/no-restricted-matchers';
import noSkippedTest from './rules/no-skipped-test';
Expand Down Expand Up @@ -95,6 +96,7 @@ export = {
'no-force-option': noForceOption,
'no-nested-step': noNestedStep,
'no-networkidle': noNetworkidle,
'no-nth-methods': noNthMethods,
'no-page-pause': noPagePause,
'no-restricted-matchers': noRestrictedMatchers,
'no-skipped-test': noSkippedTest,
Expand Down
38 changes: 38 additions & 0 deletions src/rules/no-nth-methods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Rule } from 'eslint';
import { getStringValue } from '../utils/ast';

const methods = new Set(['first', 'last', 'nth']);

export default {
create(context) {
return {
CallExpression(node) {
if (node.callee.type !== 'MemberExpression') return;

const method = getStringValue(node.callee.property);
if (!methods.has(method)) return;

context.report({
data: { method },
loc: {
end: node.loc!.end,
start: node.callee.property.loc!.start,
},
messageId: 'noNthMethod',
});
},
};
},
meta: {
docs: {
category: 'Best Practices',
description: 'Disallow usage of nth methods',
recommended: true,
url: 'https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-nth-methods.md',
},
messages: {
noNthMethod: 'Unexpected use of {{method}}()',
},
type: 'problem',
},
} as Rule.RuleModule;
73 changes: 73 additions & 0 deletions test/spec/no-nth-methods.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import rule from '../../src/rules/no-nth-methods';
import { runRuleTester } from '../utils/rule-tester';

const messageId = 'noNthMethod';

runRuleTester('no-nth-methods', rule, {
invalid: [
// First
{
code: 'page.locator("button").first()',
errors: [{ column: 24, endColumn: 31, line: 1, messageId }],
},
{
code: 'frame.locator("button").first()',
errors: [{ column: 25, endColumn: 32, line: 1, messageId }],
},
{
code: 'foo.locator("button").first()',
errors: [{ column: 23, endColumn: 30, line: 1, messageId }],
},
{
code: 'foo.first()',
errors: [{ column: 5, endColumn: 12, line: 1, messageId }],
},

// Last
{
code: 'page.locator("button").last()',
errors: [{ column: 24, endColumn: 30, line: 1, messageId }],
},
{
code: 'frame.locator("button").last()',
errors: [{ column: 25, endColumn: 31, line: 1, messageId }],
},
{
code: 'foo.locator("button").last()',
errors: [{ column: 23, endColumn: 29, line: 1, messageId }],
},
{
code: 'foo.last()',
errors: [{ column: 5, endColumn: 11, line: 1, messageId }],
},

// nth
{
code: 'page.locator("button").nth(3)',
errors: [{ column: 24, endColumn: 30, line: 1, messageId }],
},
{
code: 'frame.locator("button").nth(3)',
errors: [{ column: 25, endColumn: 31, line: 1, messageId }],
},
{
code: 'foo.locator("button").nth(3)',
errors: [{ column: 23, endColumn: 29, line: 1, messageId }],
},
{
code: 'foo.nth(32)',
errors: [{ column: 5, endColumn: 12, line: 1, messageId }],
},
],
valid: [
'page',
'page.locator("button")',
'frame.locator("button")',
'foo.locator("button")',

'page.locator("button").click()',
'frame.locator("button").click()',
'foo.locator("button").click()',
'foo.click()',
],
});

0 comments on commit 0579822

Please sign in to comment.