Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require soft assertions #118

Merged
merged 30 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 21 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,24 @@ command line option.\
💡: Some problems reported by this rule are manually fixable by editor
[suggestions](https://eslint.org/docs/latest/developer-guide/working-with-rules#providing-suggestions).

| ✔ | 🔧 | 💡 | Rule | Description |
| :-: | :-: | :-: | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| ✔ | | | [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 |
| ✔ | 🔧 | | [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-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-page-pause](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-page-pause.md) | Disallow using `page.pause` |
| | | | [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-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()` |
| | 🔧 | | [prefer-to-have-length](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` |
| | | | [require-top-level-describe](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/require-top-level-describe.md) | Require test cases and hooks to be inside a `test.describe` block |
| ✔ | | | [valid-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-expect.md) | Enforce valid `expect()` usage |
| ✔ | 🔧 | 💡 | Rule | Description |
| :-: | :-: | :-: | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| ✔ | | | [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 |
| ✔ | 🔧 | | [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-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-page-pause](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/no-page-pause.md) | Disallow using `page.pause` |
| | | | [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-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()` |
| | 🔧 | | [prefer-to-have-length](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` |
| | | | [require-top-level-describe](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/require-top-level-describe.md) | Require test cases and hooks to be inside a `test.describe` block |
| | 🔧 | | [require-require-soft-assertions](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/require-require-soft-assertions.md) | Require assertions to use `expect.soft()` |
| ✔ | | | [valid-expect](https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/valid-expect.md) | Enforce valid `expect()` usage |
26 changes: 26 additions & 0 deletions docs/rules/require-soft-assertions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Require soft assertions (`require-soft-assertions`)

Some find it easier to write longer test that perform more assertions per test.
In cases like these, it can be helpful to require
[soft assertions](https://playwright.dev/docs/test-assertions#soft-assertions)
in your tests.

This rule is not enabled by default and is only intended to be used it if fits
your workflow. If you aren't sure if you should use this rule, you probably
shouldn't 🙂.

## Rule Details

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

```javascript
await expect(page.locator('foo')).toHaveText('bar');
await expect(page).toHaveTitle('baz');
```

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

```javascript
await expect.soft(page.locator('foo')).toHaveText('bar');
await expect.soft(page).toHaveTitle('baz');
```
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import preferLowercaseTitle from './rules/prefer-lowercase-title';
import preferToBe from './rules/prefer-to-be';
import preferToHaveLength from './rules/prefer-to-have-length';
import preferStrictEqual from './rules/prefer-strict-equal';
import requireSoftAssertions from './rules/require-soft-assertions';
import requireTopLevelDescribe from './rules/require-top-level-describe';
import validExpect from './rules/valid-expect';

Expand Down Expand Up @@ -91,6 +92,7 @@ export = {
'prefer-to-be': preferToBe,
'prefer-to-have-length': preferToHaveLength,
'require-top-level-describe': requireTopLevelDescribe,
'require-soft-assertions': requireSoftAssertions,
'valid-expect': validExpect,
},
};
31 changes: 31 additions & 0 deletions src/rules/require-soft-assertions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Rule } from 'eslint';
import { getExpectType } from '../utils/ast';

export default {
create(context) {
return {
CallExpression(node) {
if (getExpectType(node) === 'standalone') {
context.report({
node: node.callee,
messageId: 'requireSoft',
fix: (fixer) => fixer.insertTextAfter(node.callee, '.soft'),
});
}
},
};
},
meta: {
docs: {
description: 'Require all assertions to use `expect.soft`',
recommended: false,
url: 'https://github.com/playwright-community/eslint-plugin-playwright/tree/main/docs/rules/require-soft-assertions.md',
},
messages: {
requireSoft: 'Unexpected non-soft assertion',
},
fixable: 'code',
type: 'suggestion',
schema: [],
},
} as Rule.RuleModule;
33 changes: 33 additions & 0 deletions test/spec/require-soft-assertions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import rule from '../../src/rules/require-soft-assertions';
import { runRuleTester } from '../utils/rule-tester';

const messageId = 'requireSoft';

runRuleTester('require-soft-assertions', rule, {
valid: [
'expect.soft(page).toHaveTitle("baz")',
'expect.soft(page.locator("foo")).toHaveText("bar")',
'expect["soft"](foo).toBe("bar")',
'expect[`soft`](bar).toHaveText("bar")',
'expect.poll(() => foo).toBe("bar")',
'expect["poll"](() => foo).toBe("bar")',
'expect[`poll`](() => foo).toBe("bar")',
],
invalid: [
{
code: 'expect(page).toHaveTitle("baz")',
output: 'expect.soft(page).toHaveTitle("baz")',
errors: [{ messageId, line: 1, column: 1, endColumn: 7 }],
},
{
code: 'expect(page.locator("foo")).toHaveText("bar")',
output: 'expect.soft(page.locator("foo")).toHaveText("bar")',
errors: [{ messageId, line: 1, column: 1, endColumn: 7 }],
},
{
code: 'await expect(page.locator("foo")).toHaveText("bar")',
output: 'await expect.soft(page.locator("foo")).toHaveText("bar")',
errors: [{ messageId, line: 1, column: 7, endColumn: 13 }],
},
],
});
3 changes: 2 additions & 1 deletion test/utils/rule-tester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { RuleTester } from 'eslint';
export function runRuleTester(...args: Parameters<RuleTester['run']>) {
const config = {
parserOptions: {
ecmaVersion: 2018,
ecmaVersion: 2022,
sourceType: 'module',
},
};

Expand Down