Deprecated: Function get_magic_quotes_gpc() is deprecated in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 99

Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 619

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1169

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176
8000 feat(lint): migrate first batch of Playwright rules from eslint-plugin-playwright by voxxit · Pull Request #7775 · biomejs/biome · GitHub
Nothing Special   »   [go: up one dir, main page]

Skip to content

Conversation

voxxit
Copy link
@voxxit voxxit commented Oct 17, 2025

Summary

This PR helps to start bringing the first batch of eslint-plugin-playwright lints into Biome. I chose to write this PR in order to start being able to fully lint my API and E2E tests.

Rules

Rule Description
missingPlaywrightAwait - Rule - Test Identify false positives when async Playwright APIs are not properly awaited.
noPlaywrightElementHandle - Rule - Test The use of ElementHandle is discouraged, use Locator instead.
noPlaywrightEval - Rule - Test The use of page.$eval and page.$$eval are discouraged, use locator.evaluate or locator.evaluateAll instead.
noPlaywrightForceOption - Rule - Test Prevent usage of { force: true } option.
noPlaywrightNetworkidle - Rule - Test Prevent usage of the networkidle option.
noPlaywrightPagePause - Rule - Test Prevent usage of page.pause().
noPlaywrightSkippedTest - Rule - Test Prevent usage of the .skip() skip test annotation.
noPlaywrightUselessAwait - Rule - Test Disallow unnecessary awaits for Playwright methods.
validPlaywrightDescribeCallback - Rule - Test Enforce valid describe() callback.
noPlaywrightWaitForSelector - Rule - Test Prevent usage of page.waitForSelector().
noPlaywrightWaitForTimeout - Rule - Test Prevent usage of page.waitForTimeout().

Test Plan

  • A few valid/invalid test scenarios were written and checked ✅
  • Tested via LSP on a personal project using debug binary ✅

Docs

Examples are part of the code (old, ESLint rules URL is linked - if that needs to be updated to point to biomejs.dev, please let me know how. Thanks!)

Copy link
changeset-bot bot commented Oct 17, 2025

🦋 Changeset detected

Latest commit: 9911849

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor
coderabbitai bot commented Oct 17, 2025

Walkthrough

Adds 13 Playwright lint rules to the nursery (ported from eslint-plugin-playwright), introduces RuleSource::EslintPlaywright and RuleDomain::Playwright in crates/biome_analyze/src/rule.rs, exposes per-rule options in crates/biome_rule_options, and adds tests (valid/invalid) for each rule. Also updates the existing NoFocusedTests rule to include Playwright's test.only() pattern.

Possibly related PRs

Suggested reviewers

  • dyc3
  • ematipico
  • siketyan

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.59% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "feat(lint): migrate first batch of Playwright rules from eslint-plugin-playwright" directly and clearly summarises the main change in the changeset. The raw summary shows the addition of 13 new Playwright lint rules, updates to the rule infrastructure to support a new RuleSource variant and RuleDomain, and comprehensive test specifications — all of which align precisely with the stated objective of migrating the initial batch of Playwright linting rules from eslint-plugin-playwright into Biome. The title is concise, specific, and would allow maintainers scanning the history to immediately understand the primary change.
Description Check ✅ Passed The PR description directly describes the changeset and clearly articulates the intent to migrate a first batch of Playwright lint rules from eslint-plugin-playwright into Biome. The description includes a detailed table of all 11-12 rules being added, explanations of what each rule does, links to the original ESLint implementations, a test plan confirming that tests have been written and are passing, and notes on documentation. The description aligns well with the actual changes, which include the new lint rules, their implementations, test cases, and supporting infrastructure changes to the rule system.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added A-Linter Area: linter L-JavaScript Language: JavaScript and super languages A-Diagnostic Area: diagnostocis labels Oct 17, 2025
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (17)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/wait-for-load-state.js (1)

2-3: Optional: Consider one trailing blank line for consistency.

Most other test files in this batch have a single trailing blank line rather than two.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js (1)

2-3: Optional: Consider one trailing blank line for consistency.

Most other test files in this batch have a single trailing blank line rather than two.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/has-params.js (1)

1-3: Good invalid case for parameterised describe().

This neatly exercises the “no params in describe callback” branch.

If not already covered elsewhere, add a sibling invalid case for an async describe callback to hit that path too.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval.js (1)

1-1: Covers the $eval pitfall; consider broadening.

Solid invalid sample. To harden coverage, also include: page.$$eval, frame.$eval, and frame.$$eval variants, plus a note in docs pointing to locator.evaluate()/evaluateAll().

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js (1)

1-2: Nice invalid fixture; show the preferred pattern too?

This triggers the rule as intended. Consider adding a nearby valid counterpart demonstrating:

  • await expect(page.locator('#dialog')).toBeVisible();
  • await page.locator('#dialog .button').click();
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/valid/other-methods.js (1)

1-3: Good showcase of timeout-free synchronisation.

These are sensible alternatives. Optional: add await expect(page).toHaveURL('/home'); to mirror the URL case with assertions.

crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (3)

41-47: Add the Test domain so this rule self-enables in test contexts

Include the testing domain to pick up globals like test/describe automatically and avoid surprising activation in non-test code.

-    pub NoPlaywrightWaitForNavigation {
+    pub NoPlaywrightWaitForNavigation {
         version: "next",
         name: "noPlaywrightWaitForNavigation",
         language: "js",
         sources: &[RuleSource::EslintPlaywright("no-wait-for-navigation").same()],
+        domains: &[RuleDomain::Test],
         recommended: false,
     }

Also add the import:

-use biome_analyze::{
-    context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource,
-};
+use biome_analyze::{
+    context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, RuleDomain,
+};

96-104: Mark as deprecated and avoid hard-coding “page.” in the message

Tagging the diagnostic as deprecated improves UX in editors. The message should be generic since the receiver could be frame or a variable like myPage.

-                markup! {
-                    "Unexpected use of "<Emphasis>"page.waitForNavigation()"</Emphasis>"."
-                },
+                markup! {
+                    "Unexpected use of "<Emphasis>"waitForNavigation()"</Emphasis>"."
+                },
             )
+            .deprecated()

60-77: Broaden callee detection (optional chaining/computed members are missed)

Currently only JsStaticMemberExpression is handled; calls like page?.waitForNavigation() or page["waitForNavigation"]() won’t be flagged. Not a blocker, but worth a follow-up for parity with ESLint.

Would you like me to open a follow-up to add JsComputedMemberExpression and optional-chaining support?

crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1)

47-53: Add the Test domain to scope this rule correctly

Helps auto-enable where Playwright tests live and keeps noise out of app code.

     pub NoPlaywrightValidDescribeCallback {
         version: "next",
         name: "noPlaywrightValidDescribeCallback",
         language: "js",
         sources: &[RuleSource::EslintPlaywright("valid-describe-callback").same()],
+        domains: &[RuleDomain::Test],
         recommended: false,
     }

And import:

-use biome_analyze::{
-    context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource,
-};
+use biome_analyze::{
+    context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, RuleDomain,
+};
crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (1)

39-45: Add the Test domain to scope activation

Keeps this lint where Playwright lives.

     pub NoPlaywrightNetworkidle {
         version: "next",
         name: "noPlaywrightNetworkidle",
         language: "js",
         sources: &[RuleSource::EslintPlaywright("no-networkidle").same()],
+        domains: &[RuleDomain::Test],
         recommended: false,
     }

And update imports:

-use biome_analyze::{
-    context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource,
-};
+use biome_analyze::{
+    context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, RuleDomain,
+};
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (3)

41-47: Add the Test domain

Same rationale as the other Playwright lints.

     pub NoPlaywrightWaitForTimeout {
         version: "next",
         name: "noPlaywrightWaitForTimeout",
         language: "js",
         sources: &[RuleSource::EslintPlaywright("no-wait-for-timeout").same()],
+        domains: &[RuleDomain::Test],
         recommended: false,
     }

Plus import:

-use biome_analyze::{
-    context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource,
-};
+use biome_analyze::{
+    context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, RuleDomain,
+};

96-104: Make the message receiver‑agnostic

The callee may be frame or myPage; keep the copy generic.

-                markup! {
-                    "Unexpected use of "<Emphasis>"page.waitForTimeout()"</Emphasis>"."
-                },
+                markup! {
+                    "Unexpected use of "<Emphasis>"waitForTimeout()"</Emphasis>"."
+                },

60-77: Optional: support optional chaining/computed members

As with the navigation rule, page?.waitForTimeout() or page["waitForTimeout"]() won’t match. Worth a follow-up if you aim for full parity with ESLint.

crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (1)

55-65: Broaden member handling (optional chaining/computed) later.

Casting only to JsStaticMemberExpression misses page?.$eval and computed forms. Consider using a member‑union (e.g. AnyJsMemberExpression) and normalising the name.

crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (1)

55-109: Reduce false positives by verifying the identifier binding.

This will also flag Jest/Mocha’s test.skip/describe.skip. Prefer a semantic query and confirm test/describe originate from @playwright/test via ctx.model().

Happy to draft a Semantic<JsCallExpression> variant that resolves the import and keeps your recursive chain logic.

crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (1)

459-492: Async-context helper scope

This helper is local-only; if other rules need it, consider pub(crate) and moving it to a shared utils module to avoid drift with similar logic elsewhere.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7e5a14 and 9820f04.

⛔ Files ignored due to path filters (53)
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-async-matcher.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-poll.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/module-level.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/non-async-context.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/promise-all-not-awaited.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/test-step.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/awaited.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/promise-all.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/returned.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval-all.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/valid/locator-evaluate.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-only.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-parallel-only.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/test-only.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/fill.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/valid/click.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/goto.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/wait-for-load-state.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/valid/load.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/frame.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/in-test.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/simple.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/other-methods.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/pause-function.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/describe-skip.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/test-skip.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-sync.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/locator.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/page-frame.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/async-methods.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/async.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/has-params.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/valid/correct.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/with-options.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/valid/alternatives.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/simple.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/valid/locators.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/simple.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/valid/other-methods.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (66)
  • .changeset/common-lizards-sniff.md (1 hunks)
  • crates/biome_analyze/src/rule.rs (5 hunks)
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-async-matcher.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-poll.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/module-level.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/non-async-context.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/promise-all-not-awaited.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/test-step.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/awaited.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/promise-all.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/returned.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval-all.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/valid/locator-evaluate.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-only.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-parallel-only.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/test-only.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/fill.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/valid/click.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/goto.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/wait-for-load-state.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/valid/load.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/frame.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/in-test.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/simple.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/other-methods.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/pause-function.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/describe-skip.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/test-skip.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-sync.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/locator.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/page-frame.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/async-methods.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/async.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/has-params.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/valid/correct.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/with-options.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/valid/alternatives.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/simple.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/valid/locators.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/simple.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/valid/other-methods.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-poll.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/valid/alternatives.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/async.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-parallel-only.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/module-level.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/page-frame.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/wait-for-load-state.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/valid/locators.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/returned.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/goto.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/other-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/in-test.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-async-matcher.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/fill.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/valid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval-all.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-only.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/frame.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/promise-all.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/describe-skip.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/async-methods.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/awaited.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/has-params.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/promise-all-not-awaited.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/valid/locator-evaluate.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/non-async-context.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/valid/other-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/pause-function.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/test-step.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/valid/load.js
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/test-skip.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/valid/correct.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-sync.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/locator.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/test-only.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/with-options.js
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-poll.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/valid/alternatives.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/async.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-parallel-only.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/module-level.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/page-frame.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/wait-for-load-state.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/valid/locators.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/returned.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/goto.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/other-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/in-test.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-async-matcher.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/fill.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/valid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval-all.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-only.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/frame.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/promise-all.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/describe-skip.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/async-methods.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/awaited.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/has-params.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/promise-all-not-awaited.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/valid/locator-evaluate.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/non-async-context.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/valid/other-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/pause-function.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/test-step.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/valid/load.js
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/test-skip.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/valid/correct.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-sync.js
  • crates/biome_analyze/src/rule.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/locator.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/test-only.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/with-options.js
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-poll.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/valid/alternatives.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/async.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-parallel-only.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/module-level.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/page-frame.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/wait-for-load-state.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/valid/locators.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/returned.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/goto.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/other-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/in-test.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-async-matcher.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/fill.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/valid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval-all.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-only.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/frame.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/promise-all.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/describe-skip.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/async-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/awaited.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/has-params.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/promise-all-not-awaited.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/valid/locator-evaluate.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/non-async-context.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/valid/other-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/pause-function.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/test-step.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/valid/load.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/test-skip.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/valid/correct.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-sync.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/locator.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/test-only.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/with-options.js
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Format Rust files before committing (e.g., via just f which formats Rust)
Document rules, assists, and options with inline rustdoc in source

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_analyze/src/rule.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
.changeset/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

.changeset/*.md: In changesets, only use #### or ##### headers; other header levels are not allowed
Changesets should cover user-facing changes only; internal changes do not need changesets
Use past tense for what you did and present tense for current Biome behavior in changesets
When fixing a bug in a changeset, start with an issue link (e.g., “Fixed #1234: …”)
When referencing a rule or assist in a changeset, include a link to its page on the website
Include code blocks in changesets when applicable to illustrate changes
End every sentence in a changeset with a period

Files:

  • .changeset/common-lizards-sniff.md
🧬 Code graph analysis (14)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (2)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js (1)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/frame.js (1)
  • frame (1-1)
crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (2)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (2)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (2)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs (2)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (2)
crates/biome_analyze/src/rule.rs (2)
  • sources (582-585)
  • same (247-252)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (3)
crates/biome_js_analyze/src/ast_utils.rs (1)
  • is_in_async_function (336-350)
crates/biome_js_factory/src/generated/node_factory.rs (1)
  • js_await_expression (207-218)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (2)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (2)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
crates/biome_analyze/src/rule.rs (2)
  • sources (582-585)
  • same (247-252)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (2)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (2)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
🔇 Additional comments (54)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js (1)

1-3: LGTM! Correct valid test case.

All three calls are properly synchronous without unnecessary await keywords. This correctly demonstrates valid usage for the noPlaywrightUselessAwait rule.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/locator.js (1)

1-1: LGTM! Correct invalid test case.

Properly demonstrates the useless await pattern on a synchronous page.locator() call that the rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/valid/alternatives.js (1)

1-3: LGTM! Correct alternatives demonstrated.

Properly shows the recommended alternatives to the deprecated waitForNavigation() method: waitForURL(), waitForLoadState(), and goto().

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/with-options.js (1)

1-2: LGTM! Correct invalid pattern.

Properly demonstrates the deprecated waitForNavigation() usage with options that the rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-parallel-only.js (1)

1-4: LGTM! Correct focused test pattern.

Properly demonstrates the test.describe.parallel.only() focused test pattern that the rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-only.js (1)

1-4: LGTM! Correct focused suite pattern.

Properly demonstrates the test.describe.only() focused suite pattern that the rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/test-only.js (1)

1-1: LGTM! Correct focused test pattern.

Properly demonstrates the test.only() focused test pattern that the rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js (1)

1-1: LGTM! Correct deprecated pattern.

Properly demonstrates the simple deprecated waitForNavigation() usage that the rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/wait-for-load-state.js (1)

1-1: LGTM! Valid test case for networkidle detection.

The test correctly demonstrates the networkidle wait strategy that the lint rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/simple.js (1)

1-1: LGTM! Appropriate test case.

Correctly demonstrates page.pause() usage that should be flagged by the lint rule.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js (1)

1-1: LGTM! Correct demonstration of unnecessary await.

The test properly shows an awaited Locator method (getByRole), which the lint rule should flag as the Locator API is synchronous.

crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/test-step.js (1)

1-5: LGTM! Properly demonstrates missing await on test.step.

The test correctly shows test.step called without await on line 2, which is the pattern the lint rule should catch.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js (1)

1-4: LGTM! Valid test case for hardcoded timeout detection.

Correctly demonstrates waitForTimeout usage that the lint rule should flag as a hardcoded wait anti-pattern.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/test-skip.js (1)

1-1: LGTM! Appropriate test for skipped test detection.

Correctly demonstrates test.skip usage that should be flagged by the lint rule.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/in-test.js (1)

1-4: LGTM! Appropriate test case.

Correctly demonstrates page.pause() usage within a test that the lint rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js (1)

1-1: LGTM! Valid test case for force option detection.

The test correctly demonstrates the force: true option that the lint rule should flag.

crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-async-matcher.js (1)

1-3: Test fixture correctly demonstrates the missing await pattern.

The async matcher toBeVisible() returns a promise that should be awaited, making this a proper invalid case for the lint rule.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js (1)

1-1: Correct invalid pattern for element handle detection.

Properly demonstrates the discouraged page.$$() API that the lint rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js (1)

1-1: Good coverage of element handles on frame objects.

Correctly demonstrates that the rule should catch frame.$() in addition to page.$().

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/describe-skip.js (1)

1-4: Appropriate test case for skipped test detection.

Demonstrates the test.describe.skip() pattern that should be flagged by the lint rule.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js (1)

1-4: Valid test case without debugging utilities.

Correctly demonstrates normal test flow without page.pause(), which the rule should allow.

crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/promise-all.js (1)

1-6: Correct valid pattern for parallel async matchers.

Properly demonstrates awaiting Promise.all() with async matchers inside, which is the recommended approach for parallel assertions.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/valid/locator-evaluate.js (1)

1-2: Demonstrates recommended locator-based evaluation.

Correctly shows the preferred locator.evaluate() and locator.evaluateAll() patterns that the rule should allow.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval-all.js (1)

1-1: Proper invalid case for deprecated eval API.

Correctly demonstrates page.$$eval() usage that should be flagged in favour of the locator-based approach.

crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/non-async-context.js (1)

1-3: LGTM!

This test case correctly demonstrates a synchronous test callback using Playwright's async matchers—exactly the pattern the missingPlaywrightAwait rule should catch.

crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-poll.js (1)

1-3: LGTM!

Proper invalid test case—expect.poll() returns a promise and must be awaited.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/fill.js (1)

1-1: LGTM!

Correctly demonstrates the discouraged force: true option pattern.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-sync.js (1)

1-1: LGTM!

Correct invalid case—toBe() is synchronous, so the await is indeed useless.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/goto.js (1)

1-1: LGTM!

Proper demonstration of the discouraged networkidle option.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/frame.js (1)

1-2: LGTM!

Correctly demonstrates frame.pause() usage that should be flagged as a debugging artefact.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/valid/click.js (1)

1-3: LGTM!

Clean example of sequential Playwright actions without force options—exactly what the rule wants to see.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/valid/locators.js (1)

1-4: LGTM! Proper use of modern Playwright locators.

This correctly demonstrates the recommended pattern using locators instead of waitForSelector.

crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/promise-all-not-awaited.js (1)

1-6: LGTM! Correctly demonstrates missing await on Promise.all.

This accurately captures the anti-pattern where Promise.all is constructed but not awaited, which the lint rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js (1)

1-7: LGTM! Valid locator-based interactions.

This correctly demonstrates the recommended pattern using locators instead of element handles.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js (1)

1-1: LGTM! Correctly demonstrates deprecated element handle pattern.

This accurately captures the use of page.$() that should be flagged by the lint rule.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/simple.js (1)

1-1: LGTM! Correctly demonstrates deprecated waitForSelector pattern.

This accurately captures the use of waitForSelector that the lint rule should flag.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/pause-function.js (1)

1-6: LGTM! Correctly distinguishes user-defined function from page.pause().

This properly tests that a user-defined pause() function is not flagged by the rule.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/other-methods.js (1)

1-3: LGTM! Valid page methods that should not be flagged.

This correctly demonstrates legitimate page interactions that are not pause-related.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/simple.js (1)

1-1: LGTM! Correctly demonstrates hardcoded timeout anti-pattern.

This accurately captures the use of waitForTimeout that should be flagged by the lint rule.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/async-methods.js (1)

1-4: Valid sample looks spot on.

All awaits target async Playwright APIs (click/goto/assert/poll). No useless await lurking here—ship it.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/valid/correct.js (1)

1-9: LGTM!

Valid test specimen demonstrating proper describe callback usage with synchronous callbacks.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/valid/load.js (1)

1-3: LGTM!

Valid specimen showing proper load state usage instead of networkidle.

crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/module-level.js (1)

1-2: LGTM!

Invalid specimen correctly demonstrates missing await on async Playwright matcher at module level.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/page-frame.js (1)

1-3: LGTM!

Invalid specimen correctly demonstrates useless awaits on synchronous Playwright methods.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/async.js (1)

1-3: LGTM!

Invalid specimen correctly demonstrates async describe callback anti-pattern.

crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/returned.js (1)

1-5: LGTM!

Valid specimen demonstrating that returning async Playwright calls is acceptable (the promise is implicitly awaited by the test runner).

crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/awaited.js (1)

1-5: LGTM!

Valid specimen demonstrating proper await usage on async Playwright APIs.

crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (3)

8-46: LGTM!

Rule declaration and documentation are clear and well-structured. The examples effectively demonstrate the anti-pattern.


48-91: Detection logic looks sound.

The implementation correctly identifies page.pause() and frame.pause() patterns, including nested cases like context.page.pause(). The case-sensitive checks for identifiers ending with "Page" or "Frame" (lines 82-86) may miss some edge cases (e.g., PAGE.pause()), but this is acceptable for a nursery rule.


93-110: LGTM!

Diagnostic message is clear, contextual, and actionable. Well done.

crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1)

103-109: Code is correct; no handling needed for single-argument form.

The verification confirms that Playwright's valid-describe-callback rule requires describe("title", () => {}) format—the callback must be the second argument. Single-argument form without a title isn't valid, so the code doesn't need to handle it.

crates/biome_analyze/src/rule.rs (1)

166-168: Docs URL verified—all solid

The canonical docs URL base is correct: https://github.com/playwright-community/eslint-plugin-playwright/blob/main/docs/rules/{rule_name}.md, and the implementation at line 385 matches precisely. Namespacing at 218-219, rule-id format at 344-345, and variant definition at 166-168 are all consistent. No issues found.

crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (1)

8-51: The declare_lint_rule! macro is defined and properly exported.

The macro is defined in crates/biome_analyze/src/rule.rs (line 648) and is widely used across the codebase (JS, JSON, GraphQL, and CSS analyzers all use it). Your code will compile without issues. The version is also correctly set to "next" per the nursery rule guidelines.

crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs (1)

8-48: Solid, concise detection of focused tests

Pattern matching and diagnostics look correct; happy path. No autofix is sensible here.

Also applies to: 56-110, 112-130

@voxxit voxxit changed the title Add first batch of Playwright lint checks from eslint-plugin-playwright feat(lint): Migrate first batch of Playwright lint checks from eslint-plugin-playwright Oct 17, 2025
@dyc3 dyc3 self-requested a review October 17, 2025 10:16
@github-actions github-actions bot added A-CLI Area: CLI A-Project Area: project labels Oct 17, 2025
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (8)
crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (2)

220-251: Bug: expect.poll(...).toBe(...) slips through due to detection order.

Detect expect.poll before gating on async matchers; otherwise sync matchers (e.g. toBe) short-circuit and poll is missed.

 fn get_async_expect_matcher(call_expr: &JsCallExpression) -> Option<MissingAwaitType> {
     let callee = call_expr.callee().ok()?;
-    // Must be a member expression (matcher call)
-    let member_expr = callee.as_js_static_member_expression()?;
-
-    // Get the matcher name
-    let member = member_expr.member().ok()?;
-    let name = member.as_js_name()?;
-    let token = name.value_token().ok()?;
-    let matcher_name = token.text_trimmed().to_string();
-
-    // Check if it's an async Playwright matcher
-    if !ASYNC_PLAYWRIGHT_MATCHERS.contains(&matcher_name.as_str()) {
-        return None;
-    }
-
-    // Walk up the chain to find if this is an expect() call
-    let object = member_expr.object().ok()?;
-
-    // Check for expect.poll
-    if has_poll_in_chain(&object) {
-        return Some(MissingAwaitType::ExpectPoll);
-    }
+    // Must be a member expression (matcher call)
+    let member_expr = callee.as_js_static_member_expression()?;
+
+    // Walk up the chain first and catch `expect.poll(...)`
+    let object = member_expr.object().ok()?;
+    if has_poll_in_chain(&object) {
+        return Some(MissingAwaitType::ExpectPoll);
+    }
+
+    // Get the matcher name
+    let member = member_expr.member().ok()?;
+    let name = member.as_js_name()?;
+    let token = name.value_token().ok()?;
+    let matcher_name = token.text_trimmed().to_string();
+
+    // Check if it's an async Playwright matcher
+    if !ASYNC_PLAYWRIGHT_MATCHERS.contains(&matcher_name.as_str()) {
+        return None;
+    }
 
     // Check if the chain starts with expect
-    if has_expect_in_chain(&object) {
+    if has_expect_in_chain(&object) {
         return Some(MissingAwaitType::ExpectMatcher(matcher_name));
     }

164-189: Auto-fix may serialise Promise.all([...]); prefer awaiting the outer call.

When an offending call is inside a non‑awaited/returned Promise.all, awaiting the inner call changes semantics. Wrap the enclosing Promise.all(...) instead.

 fn action(ctx: &RuleContext<Self>, _: &Self::State) -> Option<JsRuleAction> {
     let call_expr = ctx.query();
@@
     if !is_in_async_context(call_expr.syntax()) {
         return None;
     }
 
-    let mut mutation = ctx.root().begin();
+    // If inside Promise.all([...]) that itself isn’t awaited/returned, fix the outer call.
+    if let Some(promise_all) = find_enclosing_promise_all(call_expr) {
+        if !is_call_awaited_or_returned(&promise_all) {
+            let mut mutation = ctx.root().begin();
+            let await_expr = make::js_await_expression(make::token(T![await]), promise_all.clone().into());
+            mutation.replace_element(promise_all.into_syntax().into(), await_expr.into_syntax().into());
+            return Some(JsRuleAction::new(
+                ctx.metadata().action_category(ctx.category(), ctx.group()),
+                Applicability::MaybeIncorrect,
+                markup! { "Add await to Promise.all" }.to_owned(),
+                mutation,
+            ));
+        }
+    }
+
+    let mut mutation = ctx.root().begin();
@@
     Some(JsRuleAction::new(
         ctx.metadata().action_category(ctx.category(), ctx.group()),
         Applicability::MaybeIncorrect,
         markup! { "Add await" }.to_owned(),
         mutation,
     ))
 }
crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1)

54-57: Report non‑function callbacks; align with ESLint.

Right now non‑function callbacks like describe('x', 42) return None (Line 142), so they slip through. ESLint flags these. Add a NotFunction reason and diagnose it. Also consider flagging return inside the callback for closer parity.

 pub enum InvalidReason {
     Async,
     HasParameters,
+    NotFunction,
 }

@@
         match callback_expr {
             AnyJsExpression::JsArrowFunctionExpression(arrow) => {
@@
             }
             AnyJsExpression::JsFunctionExpression(func) => {
@@
             }
-            _ => return None, // Not a function, but we won't report this
+            _ => return Some(InvalidReason::NotFunction),
         }
@@
     fn diagnostic(ctx: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> {
@@
-        let (message, note) = match state {
+        let (message, note) = match state {
             InvalidReason::Async => (
@@
             ),
             InvalidReason::HasParameters => (
@@
             ),
+            InvalidReason::NotFunction => (
+                markup! { "Second argument to "<Emphasis>"describe()"</Emphasis>" must be a function." },
+                markup! { "Pass a synchronous callback function with no parameters." },
+            ),
         };

Optional parity improvement (brief sketch): after validating arrow/function, scan the body for return statements and report another reason (e.g., ReturnsFromCallback). Happy to wire this if you want it in this batch.

Also applies to: 108-145, 151-160

crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (1)

5-5: Two fixes: (1) scope to Playwright receivers; (2) detect identifier force keys.

  • Without receiver checks, any .click({ force: true }) is flagged (false positives).
  • has_force_true only matches string-literal keys, missing the common { force: true } identifier form.
- use biome_js_syntax::{AnyJsExpression, JsCallExpression, JsObjectExpression};
+ use biome_js_syntax::{AnyJsExpression, AnyJsObjectMemberName, JsCallExpression, JsObjectExpression};
@@
         if !METHODS_WITH_FORCE.contains(&method_name) {
             return None;
         }
 
+        // Narrow to likely Playwright receivers (page/frame/... or locator/getBy* chain)
+        let object = member_expr.object().ok()?;
+        let object_text = match object {
+            AnyJsExpression::JsIdentifierExpression(id) => {
+                id.name().ok()?.value_token().ok()?.text_trimmed().to_string()
+            }
+            AnyJsExpression::JsStaticMemberExpression(member) => {
+                member.member().ok()?.as_js_name()?.value_token().ok()?.text_trimmed().to_string()
+            }
+            AnyJsExpression::JsCallExpression(call) => {
+                // e.g. page.locator(...).click(...)
+                let callee = call.callee().ok()?;
+                if let Some(m) = biome_js_syntax::JsStaticMemberExpression::cast_ref(callee.syntax()) {
+                    let name = m.member().ok()?.as_js_name()?.value_token().ok()?.text_trimmed();
+                    if ["locator","getByRole","getByText","getByLabel","getByTitle","getByAltText","getByPlaceholder","getByTestId"].contains(&name) {
+                        "__pw_locator__".to_string()
+                    } else {
+                        return None;
+                    }
+                } else {
+                    return None;
+                }
+            }
+            _ => return None,
+        };
+        if !(object_text == "page"
+            || object_text == "frame"
+            || object_text.ends_with("Page")
+            || object_text.ends_with("Frame")
+            || object_text == "__pw_locator__")
+        {
+            return None;
+        }
+
@@
 fn has_force_true(obj_expr: &JsObjectExpression) -> bool {
     for member in obj_expr.members().into_iter().flatten() {
         if let Some(prop) = member.as_js_property_object_member() {
-            // Check if property name is 'force'
-            if let Ok(name) = prop.name() {
-                if let Some(name_node) = name.as_js_literal_member_name() {
-                    if let Ok(name_token) = name_node.value() {
-                        if name_token.text_trimmed() == "force" {
-                            // Check if value is true
-                            if let Ok(value) = prop.value() {
-                                if let Some(literal) = value.as_any_js_literal_expression() {
-                                    if let Some(bool_lit) =
-                                        literal.as_js_boolean_literal_expression()
-                                    {
-                                        if let Ok(value_token) = bool_lit.value_token() {
-                                            if value_token.text_trimmed() == "true" {
-                                                return true;
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
+            if let Ok(name) = prop.name() {
+                let is_force_key = match name {
+                    AnyJsObjectMemberName::JsName(n) => {
+                        n.value_token().ok().map_or(false, |t| t.text_trimmed() == "force")
+                    }
+                    AnyJsObjectMemberName::JsLiteralMemberName(lit) => {
+                        lit.value().ok().map_or(false, |t| {
+                            let txt = t.text_trimmed();
+                            txt == "force" || txt.trim_matches(['"', '\'']).eq("force")
+                        })
+                    }
+                };
+                if is_force_key {
+                    if let Ok(value) = prop.value() {
+                        if let Some(literal) = value.as_any_js_literal_expression() {
+                            if let Some(bool_lit) = literal.as_js_boolean_literal_expression() {
+                                if let Ok(value_token) = bool_lit.value_token() {
+                                    if value_token.text_trimmed() == "true" {
+                                        return true;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
         }
     }
 
     false
 }

I can add snapshot tests for identifier keys and non‑PW receivers to keep this tight.

Also applies to: 77-87, 88-102, 124-154

crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (1)

58-68: Add a page/frame receiver guard to avoid false positives.

Right now any waitForLoadState/navigation-like call is flagged regardless of receiver. This will trip on non‑Playwright APIs. Mirror the receiver guard used in sibling rules (page/frame or names ending with Page/Frame) before method checks.

Apply this diff after computing method_name:

         let method_name = member_text.text_trimmed();
 
+        // Ensure the receiver is page/frame-like to avoid false positives
+        let object = member_expr.object().ok()?;
+        let object_text = match object {
+            AnyJsExpression::JsIdentifierExpression(id) => {
+                id.name().ok()?.value_token().ok()?.text_trimmed().to_string()
+            }
+            AnyJsExpression::JsStaticMemberExpression(member) => {
+                member.member().ok()?.as_js_name()?.value_token().ok()?.text_trimmed().to_string()
+            }
+            _ => return None,
+        };
+        let is_page_like = object_text == "page"
+            || object_text == "frame"
+            || object_text.ends_with("Page")
+            || object_text.ends_with("Frame");
+        if !is_page_like {
+            return None;
+        }

Also applies to: 69-87, 89-103

crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (1)

105-107: Make the diagnostic object‑agnostic (don’t hard‑code page.).

This rule also triggers on frames/custom page vars; message should not pin the receiver.

-                    "Unexpected use of "<Emphasis>"page."{{state}}"()"</Emphasis>"."
+                    "Unexpected use of "<Emphasis>"{{state}}()"</Emphasis>"."
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1)

119-127: Note text assumes page.; generalise it.

Apply the earlier suggestion so guidance fits frame/variables too.

-                "Element handles like "<Emphasis>"page."{{state}}"()"</Emphasis>" are discouraged."
+                "Element handles like "<Emphasis>"{{state}}()"</Emphasis>" are discouraged."
-                "Use "<Emphasis>"page.locator()"</Emphasis>" or other locator methods like "<Emphasis>"getByRole()"</Emphasis>" instead."
+                "Use "<Emphasis>"locator()"</Emphasis>" or other locator methods like "<Emphasis>"getByRole()"</Emphasis>" instead."
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1)

108-116: Message assumes page.; make it generic.

Drop the receiver name to avoid misleading users when it’s frame or a variable.

-                    "Unexpected use of "<Emphasis>"page.waitForSelector()"</Emphasis>"."
+                    "Unexpected use of "<Emphasis>"waitForSelector()"</Emphasis>"."
🧹 Nitpick comments (15)
crates/biome_rule_options/src/no_playwright_force_option.rs (1)

1-6: Solid; add a short rustdoc for consistency.

Pattern matches other options. Add a brief comment to aid generated docs and future readers.

+/// Options for the `noPlaywrightForceOption` rule.
+/// Empty placeholder, kept for compatibility with future configuration.
+/// Mirrors `eslint-plugin-playwright/no-force-option`.
 #[derive(Default, Clone, Debug, Deserialize, Deserializable, Eq, PartialEq, Serialize)]

Based on learnings.

crates/biome_rule_options/src/no_playwright_eval.rs (1)

1-6: All good; add rustdoc for discoverability.

Consistent derives/serde. Please add a brief doc comment so it shows up in generated docs.

+/// Options for the `noPlaywrightEval` rule.
+/// Empty; reserved for future flags and schema generation.
+/// Mirrors `eslint-plugin-playwright/no-eval`.
 #[derive(Default, Clone, Debug, Deserialize, Deserializable, Eq, PartialEq, Serialize)]

Based on learnings.

crates/biome_rule_options/src/no_playwright_wait_for_selector.rs (1)

1-6: Nice one; tiny follow-up: add rustdoc.

The struct is correct; add a short doc describing the rule and why the options are empty.

+/// Options for the `noPlaywrightWaitForSelector` rule.
+/// Empty placeholder kept for forwards compatibility and schema generation.
+/// Mirrors `eslint-plugin-playwright/no-wait-for-selector`.
 #[derive(Default, Clone, Debug, Deserialize, Deserializable, Eq, PartialEq, Serialize)]

Based on learnings.

crates/biome_rule_options/src/missing_playwright_await.rs (1)

3-6: Add rustdoc for the options type.

Tiny nit: document the options struct to meet the crate’s “document rules/options” guideline.

 #[derive(Default, Clone, Debug, Deserialize, Deserializable, Eq, PartialEq, Serialize)]
 #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
 #[serde(rename_all = "camelCase", deny_unknown_fields, default)]
-pub struct MissingPlaywrightAwaitOptions {}
+/// Options for the `missingPlaywrightAwait` rule. Currently no configuration.
+pub struct MissingPlaywrightAwaitOptions {}

Based on learnings.

crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (1)

111-120: Message mentions only page.pause() but rule also matches frame.pause().

Widen the phrasing to avoid confusion (or compute the receiver and format dynamically).

-                markup! {
-                    "Unexpected use of "<Emphasis>"page.pause()"</Emphasis>"."
-                },
+                markup! {
+                    "Unexpected use of "<Emphasis>"pause()"</Emphasis>" on Playwright Page/Frame."
+                },
@@
-                <Emphasis>"page.pause()"</Emphasis>" is a debugging utility and should not be committed to version control."
+                <Emphasis>"pause()"</Emphasis>" is a debugging utility and should not be committed to version control."
crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (1)

128-151: Handle quoted property keys for waitUntil.

name_token.text_trimmed() == "waitUntil" misses { 'waitUntil': 'networkidle' }. Trim quotes to support both forms.

-                    if let Ok(name_token) = name_node.value() {
-                        if name_token.text_trimmed() == "waitUntil" {
+                    if let Ok(name_token) = name_node.value() {
+                        let key = name_token.text_trimmed().trim_matches(&['\'', '"'][..]);
+                        if key == "waitUntil" {
crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (1)

66-94: Consider extracting a shared “is page/frame‑like” helper.

This receiver check logic is duplicated across Playwright rules (eval/element-handle/etc.). Extract to a small internal helper to keep behaviour consistent.

crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (1)

73-102: Deduplicate chain-detection with the focused-test rule.

is_test_or_describe_object is effectively the same in focused/skip rules. Extract to a shared helper to avoid divergence.

crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs (1)

74-103: Share the test/describe chain matcher with sibling rules.

Same helper exists in the skipped-test rule; centralise it for consistency and easier maintenance.

crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (2)

107-115: Make the primary message receiver‑agnostic.

Don’t assume the callee is named page; keep it generic.

-                    "Unexpected use of "<Emphasis>"page.waitForTimeout()"</Emphasis>"."
+                    "Unexpected use of "<Emphasis>"waitForTimeout()"</Emphasis>"."

60-88: DRY the page/frame detection and consider optional chaining/computed members.

The object extraction repeats across rules and ignores patterns like page?.waitForTimeout(...) or page['waitForTimeout'](...). Extract a shared helper (e.g., nursery::playwright_utils::is_page_or_frame) and broaden callee handling to cover optional/computed members. Keeps rules consistent and reduces false negatives.

Add tests for:

  • await page?.waitForTimeout(1000)
  • await ctx.page.waitForTimeout(1000)
  • await (page)['waitForTimeout'](1000)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1)

77-96: Consolidate page/frame detection into a shared utility.

Same object‑resolution logic appears in multiple rules; extract once (e.g., playwright_utils::is_page_or_frame) and reuse. Easier to extend (optional chaining, aliases) in one place.

crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1)

70-88: Reuse a shared is_page_or_frame helper and support optional/computed callee shapes.

Mirror the approach across Playwright rules to avoid drift and catch page?.waitForSelector() / page['waitForSelector']().

Please add tests for the optional/computed cases and context.page.waitForSelector(...).

crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (2)

63-79: Include filter in LOCATOR_METHODS.

locator.filter() returns a Locator; awaiting it is equally useless.

 const LOCATOR_METHODS: &[&str] = &[
     "and",
+    "filter",
     "first",
     "getByAltText",
     "getByLabel",

210-240: Promote is_page_or_frame to a shared module and reuse in other rules.

This helper exists here but similar logic is duplicated elsewhere. Extract to a small playwright_utils module in nursery and import from rules to keep behaviour aligned.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9820f04 and 12bf3f1.

⛔ Files ignored due to path filters (5)
  • crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs is excluded by !**/migrate/eslint_any_rule_to_biome.rs and included by **
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (27)
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (1 hunks)
  • crates/biome_rule_options/src/lib.rs (2 hunks)
  • crates/biome_rule_options/src/missing_playwright_await.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_element_handle.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_eval.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_focused_test.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_force_option.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_networkidle.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_page_pause.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_skipped_test.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_useless_await.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_valid_describe_callback.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_wait_for_navigation.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_wait_for_selector.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_wait_for_timeout.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_rule_options/src/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_rule_options/src/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_rule_options/src/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_rule_options/src/no_playwright_element_handle.rs
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/no_playwright_focused_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_timeout.rs
  • crates/biome_rule_options/src/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_rule_options/src/no_playwright_force_option.rs
  • crates/biome_rule_options/src/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_rule_options/src/missing_playwright_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_selector.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_navigation.rs
  • crates/biome_rule_options/src/no_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Format Rust files before committing (e.g., via just f which formats Rust)
Document rules, assists, and options with inline rustdoc in source

Files:

  • crates/biome_rule_options/src/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_rule_options/src/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_rule_options/src/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_rule_options/src/no_playwright_element_handle.rs
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/no_playwright_focused_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_timeout.rs
  • crates/biome_rule_options/src/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_rule_options/src/no_playwright_force_option.rs
  • crates/biome_rule_options/src/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_rule_options/src/missing_playwright_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_selector.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_navigation.rs
  • crates/biome_rule_options/src/no_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
🧠 Learnings (3)
📚 Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : For options types, derive Serialize, Deserialize, Deserializable (and JsonSchema under the schema feature) and use #[serde(rename_all="camelCase", deny_unknown_fields, default)] with skip_serializing_if where appropriate

Applied to files:

  • crates/biome_rule_options/src/no_playwright_skipped_test.rs
  • crates/biome_rule_options/src/no_playwright_eval.rs
  • crates/biome_rule_options/src/no_playwright_useless_await.rs
  • crates/biome_rule_options/src/no_playwright_element_handle.rs
  • crates/biome_rule_options/src/no_playwright_focused_test.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_timeout.rs
  • crates/biome_rule_options/src/no_playwright_page_pause.rs
  • crates/biome_rule_options/src/no_playwright_force_option.rs
  • crates/biome_rule_options/src/no_playwright_networkidle.rs
  • crates/biome_rule_options/src/missing_playwright_await.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_selector.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_navigation.rs
  • crates/biome_rule_options/src/no_playwright_valid_describe_callback.rs
📚 Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/**/lib/src/{lint,assist}/**/*.rs : Use declare_lint_rule! with version set to "next" for every rule definition

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
📚 Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/**/tests/quick_test.rs : Use the quick test at biome_js_analyze/tests/quick_test.rs by un-ignoring and adjusting SOURCE and RuleFilter for ad-hoc checks

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
🧬 Code graph analysis (26)
crates/biome_rule_options/src/no_playwright_skipped_test.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightSkippedTestOptions (8469-8469)
crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (3)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (1)
  • run (73-102)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_rule_options/src/no_playwright_eval.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightEvalOptions (8464-8464)
crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (3)
crates/biome_analyze/src/rule.rs (2)
  • sources (582-585)
  • same (247-252)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1)
  • run (62-107)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_rule_options/src/no_playwright_useless_await.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightUselessAwaitOptions (8470-8470)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (3)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (2)
  • run (57-99)
  • diagnostic (101-118)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_rule_options/src/no_playwright_element_handle.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightElementHandleOptions (8463-8463)
crates/biome_rule_options/src/no_playwright_focused_test.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightFocusedTestOptions (8465-8465)
crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_rule_options/src/no_playwright_wait_for_timeout.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightWaitForTimeoutOptions (8474-8474)
crates/biome_rule_options/src/no_playwright_page_pause.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightPagePauseOptions (8468-8468)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (5)
crates/biome_analyze/src/rule.rs (2)
  • sources (582-585)
  • same (247-252)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (62-107)
  • diagnostic (109-129)
crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (2)
  • run (54-103)
  • diagnostic (105-122)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (2)
  • run (56-98)
  • diagnostic (100-117)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (3)
crates/biome_analyze/src/rule.rs (2)
  • sources (582-585)
  • same (247-252)
crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs (3)
  • run (56-110)
  • is_test_or_describe_object (74-103)
  • diagnostic (112-129)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_rule_options/src/no_playwright_force_option.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightForceOptionOptions (8466-8466)
crates/biome_rule_options/src/no_playwright_networkidle.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightNetworkidleOptions (8467-8467)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (3)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (2)
  • run (51-95)
  • diagnostic (97-116)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_rule_options/src/missing_playwright_await.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • MissingPlaywrightAwaitOptions (8430-8430)
crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (4)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (2)
  • FixKind (8166-8166)
  • Applicability (9963-9963)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (2)
  • run (110-129)
  • action (164-189)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1)
  • run (62-107)
crates/biome_js_analyze/src/lint/nursery/no_playwright_focused_test.rs (3)
crates/biome_analyze/src/rule.rs (1)
  • same (247-252)
crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (3)
  • run (55-109)
  • is_test_or_describe_object (73-102)
  • diagnostic (111-128)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (4)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (62-107)
  • diagnostic (109-129)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (2)
  • run (56-98)
  • diagnostic (100-117)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_rule_options/src/no_playwright_wait_for_selector.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightWaitForSelectorOptions (8473-8473)
crates/biome_rule_options/src/no_playwright_wait_for_navigation.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightWaitForNavigationOptions (8472-8472)
crates/biome_rule_options/src/no_playwright_valid_describe_callback.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightValidDescribeCallbackOptions (8471-8471)
crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (3)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (62-107)
  • diagnostic (109-129)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (4)
crates/biome_analyze/src/rule.rs (3)
  • sources (582-585)
  • same (247-252)
  • recommended (567-570)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1)
  • run (62-107)
crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (1)
  • run (54-106)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (2)
  • FixKind (8166-8166)
  • Applicability (9963-9963)
crates/biome_js_analyze/src/ast_utils.rs (1)
  • is_in_async_function (336-350)
crates/biome_js_factory/src/generated/node_factory.rs (1)
  • js_await_expression (207-218)
🔇 Additional comments (10)
crates/biome_rule_options/src/no_playwright_focused_test.rs (1)

1-6: LGTM!

Correctly follows the options type pattern with appropriate derives and serde attributes.

crates/biome_rule_options/src/no_playwright_useless_await.rs (1)

1-6: LGTM!

Correctly follows the options type pattern with appropriate derives and serde attributes.

crates/biome_rule_options/src/no_playwright_wait_for_timeout.rs (1)

1-6: LGTM!

Correctly follows the options type pattern with appropriate derives and serde attributes.

crates/biome_rule_options/src/no_playwright_page_pause.rs (1)

1-6: LGTM!

Correctly follows the options type pattern with appropriate derives and serde attributes.

crates/biome_rule_options/src/no_playwright_valid_describe_callback.rs (1)

1-6: LGTM!

Correctly follows the options type pattern with appropriate derives and serde attributes.

crates/biome_rule_options/src/no_playwright_skipped_test.rs (1)

1-6: LGTM!

Correctly follows the options type pattern with appropriate derives and serde attributes.

crates/biome_rule_options/src/no_playwright_networkidle.rs (1)

1-6: LGTM!

Correctly follows the options type pattern with appropriate derives and serde attributes.

crates/biome_rule_options/src/no_playwright_element_handle.rs (1)

1-6: LGTM!

Correctly follows the options type pattern with appropriate derives and serde attributes.

crates/biome_rule_options/src/lib.rs (1)

5-5: LGTM: options modules exposed consistently.

Entries are in the expected alphabetical region; no concerns.

Also applies to: 144-155

crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (1)

62-69: Parity confirmed—no changes needed.

ESLint's plugin targets the .skip annotation only, so your implementation is already aligned. .fixme falls outside scope for that rule, so the current check is correct.

Copy link
codspeed-hq bot commented Oct 17, 2025

CodSpeed Performance Report

Merging #7775 will not alter performance

Comparing voxxit:main (9911849) with main (f1e3841)1

Summary

✅ 53 untouched
⏩ 85 skipped2

Footnotes

  1. No successful run was found on main (9047c44) during the generation of this report, so f1e3841 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

  2. 85 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Copy link
Contributor
@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow! Huge! Thanks for the hard work. Great job sticking to the rule pillars for the diagnostics for the most part.

I'd like to encourage you to make smaller PRs in the future though, as github's UX for reviewing huge PRs is terrible. 😅

I think it would make sense to add a playwright domain for all these rules.

@voxxit voxxit changed the title feat(lint): Migrate first batch of Playwright lint checks from eslint-plugin-playwright feat(lint): migrate first batch of Playwright rules from eslint-plugin-playwright Oct 19, 2025
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (9)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (1)

107-108: Use generic "waitForNavigation()" in diagnostic message.

The diagnostic hard-codes "page.waitForNavigation()" but the rule matches frame.waitForNavigation() and other receivers (lines 90-93). The message should use the generic form to avoid misleading output.

crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (1)

55-105: Missing receiver guard causes potential false positives.

The rule matches any call to goto, reload, waitForLoadState, etc., regardless of the receiver. This risks flagging unrelated APIs. Add a page/frame guard (as suggested in past reviews) for both the waitForLoadState branch (lines 71-87) and navigation methods branch (lines 90-102).

crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (2)

74-102: Missing receiver guard risks false positives.

The rule matches any call to click, fill, etc., regardless of receiver. This could flag non-Playwright code. Add a page/frame/locator guard as suggested in past reviews.


124-147: Critical: has_force_true misses identifier keys.

The function only checks as_js_literal_member_name(), so it won't detect the common { force: true } form with an identifier key. This severely limits the rule's effectiveness in real code.

crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (1)

72-91: Prefer TokenText over to_string() to avoid allocations.

The .to_string() calls at lines 78-79 and 88-89 allocate unnecessarily. Use TokenText for comparisons as suggested in past reviews.

crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1)

108-109: Use generic "waitForSelector()" in diagnostic message.

The diagnostic hard-codes "page.waitForSelector()" but the rule matches frame.waitForSelector() and other receivers (lines 91-94). Use the generic form to avoid misleading output.

crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (1)

243-306: False negatives for expect(...).resolves/rejects/poll chains

Walking parent nodes misses async modifiers embedded in the callee/object chain (e.g. expect.soft.resolves.toBe). Walk the callee/object chain instead.

-    // Check if this is an expect().matcher() pattern
-    // The call should be a member expression where the object is expect()
+    // Check if this is an expect().matcher() pattern
     let member_expr = match callee {
         Some(AnyJsExpression::JsStaticMemberExpression(member)) => member,
         _ => return false,
     };
@@
-    // Check if the object is an expect() call
-    let object = member_expr.object().ok();
-    if let Some(AnyJsExpression::JsCallExpression(expect_call)) = object {
-        let expect_callee = expect_call.callee().ok();
-        match expect_callee {
-            Some(AnyJsExpression::JsIdentifierExpression(id)) => {
-                if let Ok(name) = id.name()
-                    && let Ok(token) = name.value_token()
-                    && token.text_trimmed() == "expect"
-                {
-                    // Make sure there's no "poll", "resolves", or "rejects" in the chain
-                    return !has_async_modifier(&expect_call, call_expr);
-                }
-            }
-            Some(AnyJsExpression::JsStaticMemberExpression(expect_member)) => {
-                // Check for expect.soft, but not expect.poll
-                if let Ok(member) = expect_member.member()
-                    && let Some(name) = member.as_js_name()
-                    && let Ok(token) = name.value_token()
-                {
-                    let member_text = token.text_trimmed();
-                    // soft is OK, poll makes it async
-                    if member_text == "soft" {
-                        return !has_async_modifier(&expect_call, call_expr);
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
+    // Ensure base is expect(...) and there is no async modifier in the chain
+    if let Ok(obj) = member_expr.object() {
+        if is_expect_call_chain(&obj) && !has_async_modifier_in_chain(&obj) {
+            return true;
+        }
+    }
@@
-fn has_async_modifier(expect_call: &JsCallExpression, final_call: &JsCallExpression) -> bool {
-    // Walk the chain from expect_call to final_call looking for "poll", "resolves", "rejects"
-    let mut current = final_call.syntax().clone();
-    let expect_syntax = expect_call.syntax();
-
-    while current != *expect_syntax {
-        if let Some(member) = JsStaticMemberExpression::cast_ref(&current) {
-            if let Ok(member_name) = member.member()
-                && let Some(name) = member_name.as_js_name()
-                && let Ok(token) = name.value_token()
-            {
-                let text = token.text_trimmed();
-                if text == "poll" || text == "resolves" || text == "rejects" {
-                    return true;
-                }
-            }
-            if let Some(parent) = member.syntax().parent() {
-                current = parent;
-            } else {
-                break;
-            }
-        } else if let Some(call) = JsCallExpression::cast_ref(&current) {
-            if let Some(parent) = call.syntax().parent() {
-                current = parent;
-            } else {
-                break;
-            }
-        } else {
-            break;
-        }
-    }
-
-    false
-}
+fn has_async_modifier_in_chain(expr: &AnyJsExpression) -> bool {
+    match expr {
+        AnyJsExpression::JsStaticMemberExpression(member) => {
+            if let Ok(m) = member.member()
+                && let Some(n) = m.as_js_name()
+                && let Ok(t) = n.value_token()
+            {
+                let text = t.text_trimmed();
+                if text == "poll" || text == "resolves" || text == "rejects" {
+                    return true;
+                }
+            }
+            if let Ok(obj) = member.object() {
+                return has_async_modifier_in_chain(&obj);
+            }
+            false
+        }
+        AnyJsExpression::JsCallExpression(call) => {
+            if let Ok(callee) = call.callee() {
+                return has_async_modifier_in_chain(&callee);
+            }
+            false
+        }
+        _ => false,
+    }
+}
+
+fn is_expect_call_chain(expr: &AnyJsExpression) -> bool {
+    match expr {
+        AnyJsExpression::JsCallExpression(call) => {
+            if let Ok(callee) = call.callee() {
+                return matches!(callee,
+                    AnyJsExpression::JsIdentifierExpression(id)
+                        if id.name().ok().and_then(|n| n.value_token().ok()).map(|t| t.text_trimmed()=="expect").unwrap_or(false)
+                ) || is_expect_call_chain(&callee);
+            }
+            false
+        }
+        AnyJsExpression::JsStaticMemberExpression(member) => {
+            member.object().ok().map(|o| is_expect_call_chain(&o)).unwrap_or(false)
+        }
+        _ => false,
+    }
+}

Also add valid tests for:

  • await expect(promise).resolves.toBe(1)
  • await expect(promise).rejects.toThrow()
  • await expect(locator).poll().toHaveText('x')

Also applies to: 308-341

crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (2)

243-274: Bug: expect.poll(...).toBe(...) is gated behind async matcher check

You return early when matcher isn’t in ASYNC_PLAYWRIGHT_MATCHERS, so poll with sync matchers is missed. Detect poll first, then gate on matchers.

-    // Get the matcher name
+    // Get the matcher name
     let member = member_expr.member().ok()?;
@@
-    // Check if it's an async Playwright matcher
-    if !ASYNC_PLAYWRIGHT_MATCHERS.contains(&matcher_name.text()) {
-        return None;
-    }
-
-    // Walk up the chain to find if this is an expect() call
-    let object = member_expr.object().ok()?;
-
-    // Check for expect.poll
-    if has_poll_in_chain(&object) {
-        return Some(MissingAwaitType::ExpectPoll);
-    }
+    // Walk up the chain first: `expect.poll(...)` is always async regardless of matcher
+    let object = member_expr.object().ok()?;
+    if has_poll_in_chain(&object) {
+        return Some(MissingAwaitType::ExpectPoll);
+    }
+
+    // Now gate by Playwright async matchers
+    if !ASYNC_PLAYWRIGHT_MATCHERS.contains(&matcher_name.text()) {
+        return None;
+    }

179-205: Auto-fix inside Promise.all currently serialises; prefer awaiting the outer Promise

When the call lives inside Promise.all([...]) that itself isn’t awaited/returned, adding await to each inner call changes semantics. Wrap the enclosing Promise.* instead.

 fn action(ctx: &RuleContext<Self>, _: &Self::State) -> Option<JsRuleAction> {
     let call_expr = ctx.query();
 
     // Check if we're in an async context
     if !is_in_async_context(call_expr.syntax()) {
         return None;
     }
+
+    // If inside Promise.*([...]) that isn’t awaited/returned, fix the outer call.
+    if let Some(promise_call) = find_enclosing_promise_all(call_expr) {
+        if !is_call_awaited_or_returned(&promise_call) {
+            let mut mutation = ctx.root().begin();
+            let await_expr = make::js_await_expression(make::token(T![await]), promise_call.clone().into());
+            mutation.replace_element(promise_call.into_syntax().into(), await_expr.into_syntax().into());
+            return Some(JsRuleAction::new(
+                ctx.metadata().action_category(ctx.category(), ctx.group()),
+                Applicability::MaybeIncorrect,
+                markup! { "Add await to Promise.*" }.to_owned(),
+                mutation,
+            ));
+        }
+    }
 
     let mut mutation = ctx.root().begin();
@@
 }
@@
-fn find_enclosing_promise_all(call_expr: &JsCallExpression) -> Option<JsCallExpression> {
+fn find_enclosing_promise_all(call_expr: &JsCallExpression) -> Option<JsCallExpression> {
@@
-                && is_promise_all(&call)
+                && is_promise_combinator(&call)
@@
 }
 
-fn is_promise_all(call: &JsCallExpression) -> bool {
-    is_member_call_pattern(call, "Promise", "all")
+fn is_promise_combinator(call: &JsCallExpression) -> bool {
+    // Promise.all / allSettled / any / race
+    if let Ok(callee) = call.callee() {
+        if let Some(member) = callee.as_js_static_member_expression() {
+            if let Ok(member_name) = member.member()
+                && let Some(name) = member_name.as_js_name()
+                && let Ok(tok) = name.value_token()
+            {
+                let m = tok.text_trimmed();
+                if (m == "all" || m == "allSettled" || m == "any" || m == "race")
+                    && let Ok(object) = member.object()
+                    && let AnyJsExpression::JsIdentifierExpression(id) = object
+                    && id.name().ok().and_then(|n| n.value_token().ok()).map(|t| t.text_trimmed()=="Promise").unwrap_or(false)
+                {
+                    return true;
+                }
+            }
+        }
+    }
+    false
 }

Please add invalid/valid specs for allSettled/any/race mirroring the existing Promise.all cases. Based on learnings.

Also applies to: 385-401, 403-439

🧹 Nitpick comments (3)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (1)

70-88: Prefer TokenText over to_string() to avoid allocations.

The .to_string() calls at lines 78 and 86 allocate unnecessarily. Since you're only comparing the text (lines 90-94), use TokenText instead.

crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (1)

60-66: Also match computed members (test['skip'], describe['skip'])

The rule only considers JsStaticMemberExpression; computed property access won’t be flagged. Add JsComputedMemberExpression handling for parity with ESLint.

-        let member_expr = JsStaticMemberExpression::cast_ref(callee.syntax())?;
+        let member_expr = JsStaticMemberExpression::cast_ref(callee.syntax());
+        let computed_expr = biome_js_syntax::JsComputedMemberExpression::cast_ref(callee.syntax());
+        let (member_name, object) = if let Some(m) = member_expr {
+            (m.member().ok()?, m.object().ok()?)
+        } else if let Some(m) = computed_expr {
+            let prop = m.member().ok()?;
+            let name = prop.as_any_js_expression()?.as_js_identifier_expression()?
+                .name().ok()?.value_token().ok()?;
+            (biome_js_syntax::AnyJsName::JsName(name.token().clone().into()), m.object().ok()?)
+        } else {
+            return None;
+        };
-        let member_name = member_expr.member().ok()?;
-        let member_text = member_name.as_js_name()?.value_token().ok()?;
+        let member_text = member_name.as_js_name()?.value_token().ok()?;
-        let object = member_expr.object().ok()?;
+        let object = object;

Also applies to: 85-99

crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (1)

441-474: Minor: consider de-duplicating is_in_async_context

You already import ast_utils::is_in_async_function; this helper appears in multiple rules. Factor it into a shared util to keep one source of truth.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12bf3f1 and 0792ce2.

⛔ Files ignored due to path filters (3)
  • crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs is excluded by !**/migrate/eslint_any_rule_to_biome.rs and included by **
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
📒 Files selected for processing (15)
  • crates/biome_analyze/src/rule.rs (9 hunks)
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs (1 hunks)
  • crates/biome_rule_options/src/lib.rs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
🧰 Additional context used
📓 Path-based instructions (3)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_analyze/src/rule.rs
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
  • crates/biome_rule_options/src/lib.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Format Rust files before committing (e.g., via just f which formats Rust)
Document rules, assists, and options with inline rustdoc in source

Files:

  • crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_analyze/src/rule.rs
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
  • crates/biome_rule_options/src/lib.rs
🧠 Learnings (2)
📚 Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/**/lib/src/{lint,assist}/**/*.rs : Use declare_lint_rule! with version set to "next" for every rule definition

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
📚 Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/**/tests/quick_test.rs : Use the quick test at biome_js_analyze/tests/quick_test.rs by un-ignoring and adjusting SOURCE and RuleFilter for ad-hoc checks

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
🧬 Code graph analysis (11)
crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs (1)
crates/biome_analyze/src/rule.rs (1)
  • same (247-252)
crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (5)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-912)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (63-108)
  • diagnostic (110-130)
crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (2)
  • run (74-102)
  • diagnostic (104-121)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (5)
crates/biome_analyze/src/rule.rs (9)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1)
  • run (63-108)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1)
  • run (58-100)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (1)
  • run (57-99)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (2)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (3)
crates/biome_analyze/src/rule.rs (7)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (63-108)
  • diagnostic (110-130)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (2)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (63-108)
  • diagnostic (110-130)
crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (3)
  • FixKind (8166-8166)
  • RuleDomain (904-912)
  • Applicability (9963-9963)
crates/biome_analyze/src/rule.rs (6)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
  • action (1128-1134)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-912)
crates/biome_analyze/src/rule.rs (7)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (2)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (63-108)
  • diagnostic (110-130)
crates/biome_analyze/src/rule.rs (1)
crates/biome_service/src/documentation/mod.rs (2)
  • fmt (125-205)
  • fmt (211-278)
crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (4)
crates/biome_js_analyze/src/ast_utils.rs (1)
  • is_in_async_function (336-350)
crates/biome_analyze/src/rule.rs (3)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_factory/src/generated/node_factory.rs (1)
  • js_await_expression (207-218)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: triage
🔇 Additional comments (10)
crates/biome_rule_options/src/lib.rs (1)

5-5: LGTM! Verify codegen was executed.

The new Playwright rule option modules are correctly placed in alphabetical order and follow the existing naming convention. Since this is a generated file, ensure that the codegen commands (cargo codegen-configuration, cargo codegen-bindings, cargo codegen-schema) have been executed successfully.

Based on coding guidelines.

Also applies to: 144-154

crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs (1)

50-54: LGTM! Playwright source added correctly.

The addition of EslintPlaywright("no-focused-test").same() properly extends the rule to cover Playwright test frameworks. Note that the singular "no-focused-test" naming matches the upstream ESLint plugin.

crates/biome_analyze/src/rule.rs (4)

166-167: LGTM! EslintPlaywright variant added correctly.

The new RuleSource variant follows the established pattern for ESLint plugins.


218-218: LGTM! Display and as_rule_name implementations correct.

Both additions properly handle the new EslintPlaywright variant.

Also applies to: 274-274


344-344: Namespace and URL format look correct.

The playwright/{rule_name} namespace and GitHub docs URL follow the established patterns for ESLint plugins.

Also applies to: 385-385


456-457: LGTM! RuleDomain::Playwright additions are correct.

The new domain variant is properly integrated:

  • Display impl uses lowercase "playwright"
  • Manifest dependencies correctly reference @playwright/test
  • Globals ["test", "expect"] match Playwright's standard globals

Also applies to: 476-476, 513-513, 542-542

crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (1)

8-47: Nice rule meta and docs

Version "next", source mapping to ESLint, examples look good. Ship it.

crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (1)

54-63: Good rule shape and safe fix

Metadata, d FCC0 iagnostics, and “remove await” mutation look sound. Nice.

Also applies to: 173-209

crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (2)

56-64: Rule meta and matcher list look solid

Good use of FixKind::Unsafe and clear diagnostics. Nicely done.

Also applies to: 67-99


100-105: State plumbing and signal filtering read cleanly

Enum variants are precise; run() short-circuits sensibly. 👍

Also applies to: 112-131

voxxit and others added 14 commits October 18, 2025 23:23
Add comprehensive Playwright testing framework support with 13 new lint rules
converted from eslint-plugin-playwright. All rules are placed in the nursery
category with proper namespacing (noPlaywright* prefix).

Rules implemented:
- missingPlaywrightAwait: Enforce async Playwright APIs are awaited/returned
- noPlaywrightUselessAwait: Disallow unnecessary await on sync methods
- noPlaywrightPagePause: Disallow page.pause() debugging calls
- noPlaywrightFocusedTest: Disallow .only test annotations
- noPlaywrightSkippedTest: Disallow .skip test annotations
- noPlaywrightWaitForTimeout: Disallow hardcoded timeouts
- noPlaywrightWaitForNavigation: Disallow deprecated waitForNavigation
- noPlaywrightWaitForSelector: Disallow waitForSelector in favor of locators
- noPlaywrightElementHandle: Disallow element handles (page.$, page.26692)
- noPlaywrightEval: Disallow page.$eval and page.$$eval
- noPlaywrightForceOption: Disallow { force: true } option
- noPlaywrightNetworkidle: Disallow networkidle option
- noPlaywrightValidDescribeCallback: Enforce valid describe callbacks

Infrastructure changes:
- Added EslintPlaywright variant to RuleSource enum
- Registered all rules in nursery lint group
- Added diagnostic categories for all rules
- Comprehensive test coverage with 1813 passing tests

Auto-fixes available for:
- missingPlaywrightAwait (unsafe)
- noPlaywrightUselessAwait (safe)

Source: https://github.com/playwright-community/eslint-plugin-playwright
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…ait.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…dd tests

Refactor the missingPlaywrightAwait lint rule to improve its logic for checking if call expressions are properly awaited or returned. The function is_properly_handled now verifies both direct awaits and Promise.all usage.

Additionally, introduce a new test case to validate the behavior of the rule when encountering Promise.all without awaits, ensuring accurate diagnostics are provided.

New test file:
- promise-all-not-awaited.js
- promise-all-not-awaited.js.snap

This update enhances the linting capabilities for Playwright async APIs.
…for describe calls

Add functionality to the missingPlaywrightAwait lint rule to enforce that "describe" calls are awaited or returned. Introduce a new type for handling "describe" in the MissingAwaitType enum.

Update test cases to reflect changes in the rule, ensuring that both valid and invalid usages of "describe" are properly tested. Adjust existing test cases to use `page.locator('body')` for consistency.

New test cases include:
- invalid/expect-async-matcher.js
- valid/awaited.js
- valid/returned.js

This update improves linting accuracy for Playwright async APIs.
…pdate tests

Enhance the missingPlaywrightAwait lint rule to ensure that arrow function calls are only considered awaited if they match the exact body of the function. This change improves the accuracy of the linting process for Playwright async APIs.

Additionally, update test cases to reflect the new logic, including modifications to the valid alternatives for waitForLoadState in the Playwright navigation tests.

This update aims to provide more precise diagnostics and improve the overall linting experience.
…ssingPlaywrightAwait rule

Eliminate the check for "describe"-like calls in the missingPlaywrightAwait lint rule to streamline its logic. This change focuses the rule on enforcing that Playwright async APIs are awaited or returned without the additional complexity of handling describe calls.

No changes to test cases were necessary as a result of this refactor, maintaining the existing linting functionality for Playwright async APIs.
…gPlaywrightAwait rule

Eliminate the handling of "describe" calls in the missingPlaywrightAwait lint rule to simplify its logic. This change focuses the rule on ensuring that Playwright async APIs are awaited or returned, enhancing clarity and maintainability. No modifications to test cases were required, preserving existing linting functionality.
…ses for missingPlaywrightAwait rule

Delete obsolete test cases related to "describe" calls in the missingPlaywrightAwait rule. This cleanup aligns with recent refactors that focus on ensuring Playwright async APIs are awaited or returned, enhancing the clarity and maintainability of the test suite. No functional changes to the linting logic were made.
…eck async context

Update the missingPlaywrightAwait lint rule to include a check for async contexts, ensuring that Playwright async APIs are only considered awaited when within an async function or module. This change improves the accuracy of the linting process. Additionally, remove obsolete test snapshots related to the noPlaywrightForceOption rule, streamlining the test suite.
…test rule

This commit introduces a new `Playwright` domain to the linting rules, allowing for better categorization of Playwright-specific rules. The `NoPlaywrightFocusedTest` rule has been removed, as its functionality is covered under the `suspicious` rules. Additionally, the Playwright rules have been updated to include the `Playwright` domain. Ran `just l` and fixed any remaining issues.
This commit introduces 13 new lint rules for Playwright, enhancing the linting capabilities for Playwright-specific code. The new rules include checks for awaiting async APIs, usage of element handles, and deprecated methods. Additionally, the existing `noFocusedTests` rule has been updated to detect Playwright's `test.only()` pattern.

The following rules have been added:
- `missingPlaywrightAwait`
- `noPlaywrightElementHandle`
- `noPlaywrightEval`
- `noPlaywrightForceOption`
- `noPlaywrightNetworkidle`
- `noPlaywrightPagePause`
- `noPlaywrightSkippedTest`
- `noPlaywrightUselessAwait`
- `noPlaywrightValidDescribeCallback`
- `noPlaywrightWaitForNavigation`
- `noPlaywrightWaitForSelector`
- `noPlaywrightWaitForTimeout`

These additions aim to improve code quality and adherence to best practices in Playwright usage.
@voxxit
Copy link
Author
voxxit commented Oct 19, 2025

@dyc3 Thanks for all of the thoughtful feedback on my first PR :-) I believe I've addressed it all, but do check again and let me know if anything else needs to be adjusted.

Cheers!

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1)

104-104: Make the diagnostic message generic.

The rule matches frame.waitForSelector() and other receivers (lines 86-89), but the message hard-codes page.. Use the generic form instead.

-                    "Unexpected use of "<Emphasis>"page.waitForSelector()"</Emphasis>"."
+                    "Unexpected use of "<Emphasis>"waitForSelector()"</Emphasis>"."
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1)

119-124: Generalise diagnostics: drop hard‑coded “page.” prefix.

Keep guidance receiver‑agnostic so it’s correct for frame, this.page, etc. Apply:

-            .note(markup! {
-                "Element handles like "<Emphasis>"page."{{state_text}}"()"</Emphasis>" are discouraged."
-            })
+            .note(markup! {
+                "Element handles like "<Emphasis>"{{state_text}}"()"</Emphasis>" are discouraged."
+            })
-            .note(markup! {
-                "Use "<Emphasis>"page.locator()"</Emphasis>" or other locator methods like "<Emphasis>"getByRole()"</Emphasis>" instead."
-            })
+            .note(markup! {
+                "Use "<Emphasis>"locator()"</Emphasis>" or other locator methods like "<Emphasis>"getByRole()"</Emphasis>" instead."
+            })
🧹 Nitpick comments (3)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (3)

63-80: Also handle bracket access and optional chaining.

Calls like page['$']('a') and page?.$('a') won’t be caught. Consider matching JsComputedMemberExpression (string literal "$" or "$$") and optional‑chained members too, with tests.

Add tests for:

  • await page['$']('#id')
  • await page?.$('button')

9-9: Tweak rule summary to be receiver‑agnostic.

Minor copy edit to match the generalised notes:

-    /// Disallow usage of element handles (`page.$()` and `page.$$()`).
+    /// Disallow usage of element handles (e.g. `.$()` and `. $$()`), commonly seen as `page.$()/page.$$()` or `frame.$()`.

81-105: De‑duplicate “page‑like receiver” detection across Playwright rules.

no_playwright_eval implements near‑identical object/member extraction. Extract a tiny helper (e.g. is_page_like(&AnyJsExpression) -> bool) in a shared module and reuse here and there to keep fixes in one place.

Also applies to: 63-75

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0792ce2 and 2f73505.

⛔ Files ignored due to path filters (1)
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
📒 Files selected for processing (7)
  • .changeset/common-lizards-sniff.md (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Format Rust files before committing (e.g., via just f which formats Rust)
Document rules, assists, and options with inline rustdoc in source

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
.changeset/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

.changeset/*.md: In changesets, only use #### or ##### headers; other header levels are not allowed
Changesets should cover user-facing changes only; internal changes do not need changesets
Use past tense for what you did and present tense for current Biome behavior in changesets
When fixing a bug in a changeset, start with an issue link (e.g., “Fixed #1234: …”)
When referencing a rule or assist in a changeset, include a link to its page on the website
Include code blocks in changesets when applicable to illustrate changes
End every sentence in a changeset with a period

Files:

  • .changeset/common-lizards-sniff.md
🧠 Learnings (1)
📚 Learning: 2025-10-15T09:19:01.347Z
Learnt from: CR
PR: biomejs/biome#0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:19:01.347Z
Learning: Applies to .changeset/*.md : End every sentence in a changeset with a period

Applied to files:

  • .changeset/common-lizards-sniff.md
🧬 Code graph analysis (4)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (63-106)
  • diagnostic (108-129)
crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (2)
  • run (55-99)
  • diagnostic (101-118)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (2)
  • run (52-94)
  • diagnostic (96-116)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (4)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (63-106)
  • diagnostic (108-129)
crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (2)
  • run (55-99)
  • diagnostic (101-118)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (4)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (5)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (2)
  • run (58-95)
  • diagnostic (97-114)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
🪛 LanguageTool
.changeset/common-lizards-sniff.md

[uncategorized] ~9-~9: Loose punctuation mark.
Context: .../linter/rules/missing-playwright-await/): Enforce awaiting async Playwright APIs....

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~10-~10: Loose punctuation mark.
Context: ...ter/rules/no-playwright-element-handle/): Prefer locators over element handles (`...

(UNLIKELY_OPENING_PUNCTUATION)


[grammar] ~10-~10: Consider using “to” with “prefer”.
Context: ...r/rules/no-playwright-element-handle/): Prefer locators over element handles (page.$() and page.$$()). -...

(PREFER_OVER_TO)


[uncategorized] ~11-~11: Loose punctuation mark.
Context: ...js.dev/linter/rules/no-playwright-eval/): Disallow page.$eval() and `page.$$eva...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~12-~12: Loose punctuation mark.
Context: ...inter/rules/no-playwright-force-option/): Disallow the force option on user int...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~13-~13: Loose punctuation mark.
Context: ...linter/rules/no-playwright-networkidle/): Disallow deprecated networkidle wait ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~14-~14: Loose punctuation mark.
Context: .../linter/rules/no-playwright-page-pause/): Disallow page.pause() debugging calls...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~15-~15: Loose punctuation mark.
Context: ...inter/rules/no-playwright-skipped-test/): Disallow skipped tests with `test.skip(...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~16-~16: Loose punctuation mark.
Context: ...nter/rules/no-playwright-useless-await/): Disallow unnecessary await on synchro...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~17-~17: Loose punctuation mark.
Context: .../no-playwright-valid-describe-callback/): Validate describe callback signatures a...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~18-~18: Loose punctuation mark.
Context: ...ules/no-playwright-wait-for-navigation/): Prefer modern navigation APIs over depr...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~19-~19: Loose punctuation mark.
Context: .../rules/no-playwright-wait-for-selector/): Prefer locators over deprecated `waitFo...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~20-~20: Loose punctuation mark.
Context: ...r/rules/no-playwright-wait-for-timeout/): Disallow hard-coded timeouts with `wait...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (6)
.changeset/common-lizards-sniff.md (1)

1-22: ✓ Changeset looks excellent.

The previous feedback has been addressed. Header format is correct, all sentences end with periods, rule links and descriptions are included, and the content is clearly user-facing. The tense usage is spot-on (past tense for the action, present tense for what the rules do).

The static analysis flags are false positives—markdown link syntax is expected here.

crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1)

58-95: Implementation follows established pattern.

The logic correctly identifies waitForSelector calls on page, frame, or Page/Frame-suffixed objects. Well done.

crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (2)

57-94: Clean implementation.

The pattern matching correctly handles all targeted receiver types.


103-103: Spot on – generic diagnostic message.

Unlike the other two files, this correctly uses the generic "waitForTimeout()" instead of hard-coding the receiver. Nicely done.

crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (1)

57-94: Solid pattern matching.

The implementation correctly identifies waitForNavigation calls across all targeted receiver types.

crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1)

96-105: Current implementation is already in parity with eslint-plugin-playwright.

The upstream rule flags calls that create ElementHandles (page.$/page.$$ and Frame equivalents), but not calls on existing ElementHandles (elementHandle.$/elementHandle.$$). Your code does exactly that—it checks for page/frame/*Page/*Frame receivers. No broadening needed.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

♻️ Duplicate comments (8)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (1)

101-105: Use receiver‑agnostic primary message.

The rule fires for frame and other receivers; message still hard‑codes page..

-                    "Unexpected use of "<Emphasis>"page.waitForNavigation()"</Emphasis>"."
+                    "Unexpected use of "<Emphasis>"waitForNavigation()"</Emphasis>"."
crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (1)

104-108: Make the message object‑agnostic.

Avoid hard‑coding page.; the rule also targets frames/custom pages.

-                    "Unexpected use of "<Emphasis>"page."{{state_text}}"()"</Emphasis>"."
+                    "Unexpected use of "<Emphasis>"{{state_text}}"()</Emphasis>"."
crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (2)

243-274: expect.poll() with synchronous matchers is still not detected.

The early return at line 256 prevents detection of expect.poll(...).toBe(...) where toBe isn't in ASYNC_PLAYWRIGHT_MATCHERS. Check for poll before validating the matcher.

Apply this diff:

 fn get_async_expect_matcher(call_expr: &JsCallExpression) -> Option<MissingAwaitType> {
     let callee = call_expr.callee().ok()?;
 
     // Must be a member expression (matcher call)
     let member_expr = callee.as_js_static_member_expression()?;
+
+    // Walk up the chain first
+    let object = member_expr.object().ok()?;
+
+    // Check for expect.poll (must come before matcher validation)
+    if has_poll_in_chain(&object) {
+        return Some(MissingAwaitType::ExpectPoll);
+    }
 
     // Get the matcher name
     let member = member_expr.member().ok()?;
     let name = member.as_js_name()?;
     let token = name.value_token().ok()?;
     let matcher_name = token.token_text_trimmed();
 
     // Check if it's an async Playwright matcher
     if !ASYNC_PLAYWRIGHT_MATCHERS.contains(&matcher_name.text()) {
         return None;
     }
 
-    // Walk up the chain to find if this is an expect() call
-    let object = member_expr.object().ok()?;
-
-    // Check for expect.poll
-    if has_poll_in_chain(&object) {
-        return Some(MissingAwaitType::ExpectPoll);
-    }
-
     // Check if the chain starts with expect
     if has_expect_in_chain(&object) {
         return Some(MissingAwaitType::ExpectMatcher(matcher_name));
     }
 
     None
 }

437-439: Expand to cover Promise.allSettled(), Promise.any(), and Promise.race().

Awaited calls inside Promise.allSettled([...]), Promise.any([...]), or Promise.race([...]) currently escape detection.

Apply this diff:

 fn is_promise_all(call: &JsCallExpression) -> bool {
-    is_member_call_pattern(call, "Promise", "all")
+    if let Ok(callee) = call.callee() {
+        if let Some(member) = callee.as_js_static_member_expression() {
+            if let Ok(member_name) = member.member() {
+                if let Some(name) = member_name.as_js_name() {
+                    if let Ok(token) = name.value_token() {
+                        let method = token.text_trimmed();
+                        if matches!(method, "all" | "allSettled" | "any" | "race") {
+                            if let Ok(object) = member.object() {
+                                if let AnyJsExpression::JsIdentifierExpression(id) = object {
+                                    if let Ok(obj_name) = id.name() {
+                                        if let Ok(obj_token) = obj_name.value_token() {
+                                            return obj_token.text_trimmed() == "Promise";
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    false
 }
crates/biome_rule_options/src/no_playwright_wait_for_navigation.rs (1)

1-6: Add rustdoc comment to the struct.

Per coding guidelines, inline rustdoc is required. Document what this options type configures.

+/// Options for the `noPlaywrightWaitForNavigation` rule.
+/// Currently empty; reserved for future configuration.
 #[derive(Default, Clone, Debug, Deserialize, Deserializable, Eq, PartialEq, Serialize)]

As per coding guidelines.

crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (1)

55-87: Add receiver guard to prevent false positives.

The rule matches waitForLoadState("networkidle") on any receiver, not just Playwright page/frame objects. Add the same page/frame guard used in the other Playwright rules.

Apply this diff:

         let member_expr = JsStaticMemberExpression::cast_ref(callee.syntax())?;
         let member_name = member_expr.member().ok()?;
         let member_text = member_name.as_js_name()?.value_token().ok()?;
         let method_name = member_text.text_trimmed();
 
+        // Ensure receiver is page/frame-like to avoid false positives
+        let object = member_expr.object().ok()?;
+        let object_text = match object {
+            AnyJsExpression::JsIdentifierExpression(id) => {
+                id.name().ok()?.value_token().ok()?.token_text_trimmed()
+            }
+            AnyJsExpression::JsStaticMemberExpression(member) => {
+                member.member().ok()?.as_js_name()?.value_token().ok()?.token_text_trimmed()
+            }
+            _ => return None,
+        };
+        let is_page_like = object_text == "page"
+            || object_text == "frame"
+            || object_text.ends_with("Page")
+            || object_text.ends_with("Frame");
+        if !is_page_like {
+            return None;
+        }
+
         // Check if it's one of the navigation methods
         let is_navigation_method = matches!(
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1)

97-114: Generalise diagnostic messages.

The messages hardcode page. which is incorrect for other receivers like frame.waitForSelector(). Remove the receiver prefix.

Apply this diff:

             RuleDiagnostic::new(
                 rule_category!(),
                 node.range(),
                 markup! {
-                    "Unexpected use of "<Emphasis>"page.waitForSelector()"</Emphasis>"."
+                    "Unexpected use of "<Emphasis>"waitForSelector()"</Emphasis>"."
                 },
             )
             .note(markup! {
-                "Use locator-based "<Emphasis>"page.locator()"</Emphasis>" or "<Emphasis>"page.getByRole()"</Emphasis>" APIs instead."
+                "Use locator-based "<Emphasis>"locator()"</Emphasis>" or "<Emphasis>"getByRole()"</Emphasis>" APIs instead."
             })
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1)

108-129: Generalise diagnostic notes.

The notes hardcode page. which is incorrect for other receivers. Remove the receiver prefix to make guidance applicable to any Playwright actor.

Apply this diff:

             .note(markup! {
-                "Element handles like "<Emphasis>"page."{{state_text}}"()"</Emphasis>" are discouraged."
+                "Element handles like "<Emphasis>{{state_text}}"()"</Emphasis>" are discouraged."
             })
             .note(markup! {
-                "Use "<Emphasis>"page.locator()"</Emphasis>" or other locator methods like "<Emphasis>"getByRole()"</Emphasis>" instead."
+                "Use "<Emphasis>"locator()"</Emphasis>" or other locator methods like "<Emphasis>"getByRole()"</Emphasis>" instead."
             })
🧹 Nitpick comments (8)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js (1)

1-4: Good invalid fixture for waitForTimeout.

Covers the canonical misuse. For extra bite, consider a sibling case using aliases or other targets:

  • const p = page; await p.waitForTimeout(1000)
  • await frame.waitForTimeout(1000)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js (1)

1-1: Forceful, in the right way.

Nice invalid for { force: true } on a locator click. If not elsewhere, add twins for:

  • await page.click('button', { force: true })
  • await page.locator('btn').click({ force: !!1 }) // truthy variant
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js (1)

1-1: Crisp “useless await” example.

getByRole returns a Locator; awaiting it should be flagged. Consider adding a guard case elsewhere to ensure no false positives on chains like await page.getByRole('button').click() (await applies to click, not the locator creation).

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js (1)

1-3: Solid valid samples.

These ensure we don’t penalise synchronous locator retrieval. Optional: add an assignment form to widen coverage:

  • const btn = page.getByRole('button');
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js (1)

1-1: Right on target for ElementHandle APIs.

Good catch on $$. To fully mirror eslint-plugin-playwright parity, consider siblings for:

  • const el = await page.$('#id')
  • const kids = await handle.$$('.child')
crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1)

70-96: Broaden detection: returns/generators and test.describe.parallel/serial.

  • Add a check that the callback body contains no return statements.
  • Treat generator functions (function*) as invalid.
  • Recognise test.describe.parallel(...) / test.describe.serial(...) (callee chain where final member isn’t describe but the parent is).

Happy to sketch these out if you want this in the first batch.

Also applies to: 102-128

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js (1)

1-2: Add one more invalid for frame.waitForNavigation().

Helps prove the rule isn’t page‑only (and guards against regressions). Happy to add it if you like.

crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (1)

74-101: Scope to Playwright receivers to avoid false positives.

Limit matches to page/frame/*Page/*Frame or locator/getBy* chains.

@@
-        // Check the arguments for { force: true }
+        // Narrow to Playwright receivers to avoid false positives
+        let object = member_expr.object().ok()?;
+        if !is_playwright_receiver(&object) {
+            return None;
+        }
+
+        // Check the arguments for { force: true }
         let args = call_expr.arguments().ok()?;
@@
         None
     }
@@
+fn is_playwright_receiver(expr: &AnyJsExpression) -> bool {
+    match expr {
+        AnyJsExpression::JsIdentifierExpression(id) => {
+            let t = id.name().ok()?.value_token().ok()?.text_trimmed();
+            t == "page" || t == "frame" || t.ends_with("Page") || t.ends_with("Frame")
+        }
+        AnyJsExpression::JsStaticMemberExpression(member) => {
+            let t = member.member().ok()?.as_js_name()?.value_token().ok()?.text_trimmed();
+            t == "page" || t == "frame" || t.ends_with("Page") || t.ends_with("Frame")
+        }
+        AnyJsExpression::JsCallExpression(call) => {
+            // e.g. page.locator(...).click(...)
+            let callee = call.callee().ok()?;
+            let m = JsStaticMemberExpression::cast_ref(callee.syntax())?;
+            let name = m.member().ok()?.as_js_name()?.value_token().ok()?.text_trimmed();
+            matches!(name.as_str(), "locator" | "getByRole" | "getByText" | "getByLabel" | "getByTitle" | "getByAltText" | "getByPlaceholder" | "getByTestId")
+        }
+        _ => false,
+    }
+}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f73505 and 7194c72.

⛔ Files ignored due to path filters (57)
  • crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs is excluded by !**/migrate/eslint_any_rule_to_biome.rs and included by **
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-async-matcher.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-poll.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/module-level.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/non-async-context.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/promise-all-not-awaited.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/test-step.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/awaited.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/promise-all.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/returned.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval-all.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/valid/locator-evaluate.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-only.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-parallel-only.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/test-only.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/fill.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/valid/click.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/goto.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/wait-for-load-state.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/valid/load.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/frame.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/in-test.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/simple.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/other-methods.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/pause-function.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/describe-skip.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/test-skip.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-sync.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/locator.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/page-frame.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/async-methods.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/async.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/has-params.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/valid/correct.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/with-options.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/valid/alternatives.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/simple.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/valid/locators.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/simple.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/valid/other-methods.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (79)
  • .changeset/common-lizards-sniff.md (1 hunks)
  • crates/biome_analyze/src/rule.rs (9 hunks)
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-async-matcher.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-poll.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/module-level.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/non-async-context.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/promise-all-not-awaited.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/test-step.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/awaited.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/promise-all.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/returned.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval-all.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/valid/locator-evaluate.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-only.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-parallel-only.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/test-only.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/fill.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/valid/click.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/goto.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/wait-for-load-state.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/valid/load.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/frame.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/in-test.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/simple.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/other-methods.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/pause-function.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/describe-skip.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/test-skip.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-sync.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/locator.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/page-frame.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/async-methods.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js (1 hunks)
  • crates/biome_j F00 s_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/async.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/has-params.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/valid/correct.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/with-options.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/valid/alternatives.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/simple.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/valid/locators.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/simple.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/valid/other-methods.js (1 hunks)
  • crates/biome_rule_options/src/lib.rs (2 hunks)
  • crates/biome_rule_options/src/missing_playwright_await.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_element_handle.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_eval.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_force_option.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_networkidle.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_page_pause.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_skipped_test.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_useless_await.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_valid_describe_callback.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_wait_for_navigation.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_wait_for_selector.rs (1 hunks)
  • crates/biome_rule_options/src/no_playwright_wait_for_timeout.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (46)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/module-level.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/wait-for-load-state.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/valid/locators.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/valid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrigh FCC0 tAwait/invalid/non-async-context.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/fill.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/other-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/goto.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/valid/correct.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/promise-all-not-awaited.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-sync.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/valid/alternatives.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/test-skip.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/valid/other-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/awaited.js
  • crates/biome_rule_options/src/no_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/locator.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js
  • crates/biome_rule_options/src/no_playwright_useless_await.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval-all.js
  • crates/biome_rule_options/src/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_skipped_test.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/test-only.js
  • crates/biome_analyze/src/rule.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightSkippedTest/invalid/describe-skip.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-async-matcher.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/page-frame.js
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/has-params.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-parallel-only.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/async.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/in-test.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/with-options.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/valid/locator-evaluate.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightFocusedTest/invalid/describe-only.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/pause-function.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/returned.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/async-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/frame.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
🧰 Additional context used
📓 Path-based instructions (5)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/test-step.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-poll.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/valid/load.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/promise-all.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js
  • crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/test-step.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-poll.js
  • crates/biome_rule_options/src/no_playwright_force_option.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_rule_options/src/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/valid/load.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_rule_options/src/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/promise-all.js
  • crates/biome_rule_options/src/no_playwright_page_pause.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js
  • crates/biome_rule_options/src/no_playwright_skipped_test.rs
  • crates/biome_rule_options/src/no_playwright_element_handle.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js
  • crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js
  • crates/biome_rule_options/src/missing_playwright_await.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_navigation.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/test-step.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/getByRole.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/invalid/expect-poll.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/valid/load.js
  • crates/biome_js_analyze/tests/specs/nursery/missingPlaywrightAwait/valid/promise-all.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Format Rust files before committing (e.g., via just f which formats Rust)
Document rules, assists, and options with inline rustdoc in source

Files:

  • crates/biome_rule_options/src/no_playwright_force_option.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_timeout.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_rule_options/src/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_rule_options/src/no_playwright_networkidle.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
  • crates/biome_rule_options/src/no_playwright_page_pause.rs
  • crates/biome_rule_options/src/no_playwright_skipped_test.rs
  • crates/biome_rule_options/src/no_playwright_element_handle.rs
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
  • crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
  • crates/biome_rule_options/src/missing_playwright_await.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_navigation.rs
.changeset/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

.changeset/*.md: In changesets, only use #### or ##### headers; other header levels are not allowed
Changesets should cover user-facing changes only; internal changes do not need changesets
Use past tense for what you did and present tense for current Biome behavior in changesets
When fixing a bug in a changeset, start with an issue link (e.g., “Fixed #1234: …”)
When referencing a rule or assist in a changeset, include a link to its page on the website
Include code blocks in changesets when applicable to illustrate changes
End every sentence in a changeset with a period

Files:

  • .changeset/common-lizards-sniff.md
🧠 Learnings (5)
📚 Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : For options types, derive Serialize, Deserialize, Deserializable (and JsonSchema under the schema feature) and use #[serde(rename_all="camelCase", deny_unknown_fields, default)] with skip_serializing_if where appropriate

Applied to files:

  • crates/biome_rule_options/src/no_playwright_force_option.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_timeout.rs
  • crates/biome_rule_options/src/no_playwright_eval.rs
  • crates/biome_rule_options/src/no_playwright_networkidle.rs
  • crates/biome_rule_options/src/no_playwright_page_pause.rs
  • crates/biome_rule_options/src/no_playwright_skipped_test.rs
  • crates/biome_rule_options/src/no_playwright_element_handle.rs
  • crates/biome_rule_options/src/missing_playwright_await.rs
  • crates/biome_rule_options/src/no_playwright_wait_for_navigation.rs
📚 Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/**/lib/src/{lint,assist}/**/*.rs : Use declare_lint_rule! with version set to "next" for every rule definition

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
📚 Learning: 2025-10-15T09:21:24.116Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_diagnostics/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:21:24.116Z
Learning: A diagnostic must explain why something went wrong, using log advices and links to documentation when helpful

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
📚 Learning: 2025-10-15T09:19:01.347Z
Learnt from: CR
PR: biomejs/biome#0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:19:01.347Z
Learning: Applies to .changeset/*.md : End every sentence in a changeset with a period

Applied to files:

  • .changeset/common-lizards-sniff.md
📚 Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/**/lib/src/{lint,assist}/**/*.rs : If a lint rule is ported or inspired by another linter, add sources metadata in declare_lint_rule! with RuleSource (use .same() or .inspired())

Applied to files:

  • crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs
🧬 Code graph analysis (19)
crates/biome_rule_options/src/no_playwright_force_option.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightForceOptionOptions (8467-8467)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (63-106)
  • diagnostic (108-129)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_rule_options/src/no_playwright_wait_for_timeout.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightWaitForTimeoutOptions (8475-8475)
crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (4)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (63-106)
  • diagnostic (108-129)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_rule_options/src/no_playwright_eval.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightEvalOptions (8465-8465)
crates/biome_js_analyze/src/lint/nursery/no_playwright_force_option.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (2)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (3)
  • FixKind (8167-8167)
  • RuleDomain (904-913)
  • Applicability (9964-9964)
crates/biome_analyze/src/rule.rs (12)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • fix_kind (582-585)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
  • action (1128-1134)
crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (2)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (11)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • note (1381-1383)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_rule_options/src/no_playwright_networkidle.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightNetworkidleOptions (8468-8468)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (2)
  • run (52-94)
  • diagnostic (96-116)
crates/biome_rule_options/src/no_playwright_page_pause.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightPagePauseOptions (8469-8469)
crates/biome_rule_options/src/no_playwright_skipped_test.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightSkippedTestOptions (8470-8470)
crates/biome_rule_options/src/no_playwright_element_handle.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightElementHandleOptions (8464-8464)
crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (3)
  • FixKind (8167-8167)
  • RuleDomain (904-913)
  • Applicability (9964-9964)
crates/biome_js_analyze/src/ast_utils.rs (1)
  • is_in_async_function (336-350)
crates/biome_js_factory/src/generated/node_factory.rs (1)
  • js_await_expression (207-218)
crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs (1)
crates/biome_analyze/src/rule.rs (1)
  • same (247-252)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (4)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (2)
  • run (63-106)
  • diagnostic (108-129)
crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (2)
  • run (55-99)
  • diagnostic (101-118)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (2)
  • run (58-95)
  • diagnostic (97-114)
crates/biome_rule_options/src/missing_playwright_await.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • MissingPlaywrightAwaitOptions (8431-8431)
crates/biome_rule_options/src/no_playwright_wait_for_navigation.rs (1)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • NoPlaywrightWaitForNavigationOptions (8473-8473)
🪛 LanguageTool
.changeset/common-lizards-sniff.md

[uncategorized] ~9-~9: Loose punctuation mark.
Context: .../linter/rules/missing-playwright-await/): Enforce awaiting async Playwright APIs....

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~10-~10: Loose punctuation mark.
Context: ...ter/rules/no-playwright-element-handle/): Prefer locators over element handles (`...

(UNLIKELY_OPENING_PUNCTUATION)


[grammar] ~10-~10: Consider using “to” with “prefer”.
Context: ...r/rules/no-playwright-element-handle/): Prefer locators over element handles (page.$() and page.$$()). -...

(PREFER_OVER_TO)


[uncategorized] ~11-~11: Loose punctuation mark.
Context: ...js.dev/linter/rules/no-playwright-eval/): Disallow page.$eval() and `page.$$eva...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~12-~12: Loose punctuation mark.
Context: ...inter/rules/no-playwright-force-option/): Disallow the force option on user int...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~13-~13: Loose punctuation mark.
Context: ...linter/rules/no-playwright-networkidle/): Disallow deprecated networkidle wait ...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~14-~14: Loose punctuation mark.
Context: .../linter/rules/no-playwright-page-pause/): Disallow page.pause() debugging calls...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~15-~15: Loose punctuation mark.
Context: ...inter/rules/no-playwright-skipped-test/): Disallow skipped tests with `test.skip(...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~16-~16: Loose punctuation mark.
Context: ...nter/rules/no-playwright-useless-await/): Disallow unnecessary await on synchro...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~17-~17: Loose punctuation mark.
Context: .../no-playwright-valid-describe-callback/): Validate describe callback signatures a...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~18-~18: Loose punctuation mark.
Context: ...ules/no-playwright-wait-for-navigation/): Prefer modern navigation APIs over depr...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~19-~19: Loose punctuation mark.
Context: .../rules/no-playwright-wait-for-selector/): Prefer locators over deprecated `waitFo...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~20-~20: Loose punctuation mark.
Context: ...r/rules/no-playwright-wait-for-timeout/): Disallow hard-coded timeouts with `wait...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (8)
crates/biome_js_analyze/src/lint/suspicious/no_focused_tests.rs (1)

53-53: Rule name is correct.

The eslint-plugin-playwright rule is indeed configured as "playwright/no-focused-test" (singular), so the code change is accurate. The naming difference from the Jest and Vitest sources is intentional.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js (1)

1-5: Valid test case looks good.

This correctly demonstrates Playwright code that should pass the noPlaywrightPagePause rule—no page.pause() call present, standard async/await pattern, proper use of page methods.

Note: While page.waitForSelector() may be flagged by other Playwright rules in this batch, that's intentional for this specific test fixture.

.changeset/common-lizards-sniff.md (1)

1-22: Changeset looks solid.

The documentation is clear, comprehensive, and follows the project's conventions. The LanguageTool hints about punctuation are false positives from the markdown link syntax.

crates/biome_rule_options/src/no_playwright_networkidle.rs (1)

1-6: LGTM!

Options struct follows the coding guidelines precisely: correct derives, serde attributes, and schema feature gating.

crates/biome_js_analyze/src/lint/nursery/no_playwright_networkidle.rs (2)

107-124: LGTM!

Diagnostic messages are clear and avoid hardcoded receiver names, making them applicable to any receiver.


127-150: LGTM!

Helper function correctly identifies waitUntil: "networkidle" in object expressions.

crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1)

58-95: LGTM!

Proper receiver guard in place to avoid false positives on non-Playwright objects.

crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (1)

63-106: LGTM!

Proper receiver validation and correct state capture for the diagnostic.

…pdate tests

This commit improves the `missingPlaywrightAwait` lint rule by adding checks for Promise combinators and ensuring that they are awaited when necessary. The logic now correctly identifies when a Promise combinator is not awaited and suggests adding an `await` to preserve concurrency semantics.

Additionally, the rule's test cases have been updated to reflect these changes, including new diagnostics for invalid usages of `expect.poll` and `Promise.all`. Obsolete test snapshots related to focused tests have also been removed, streamlining the test suite.

These enhancements aim to improve linting accuracy and maintainability for Playwright-specific code.
This commit updates the indices of various lint rules in the `rules.rs` file to ensure proper alignment with the intended rule group structure. The changes involve shifting the indices for multiple rules related to Playwright and Vue, correcting their positions to maintain consistency in rule filtering. This fix aims to enhance the accuracy of the linter's functionality and ensure that all rules are correctly referenced.
…te RuleDomain

This commit removes the `noPlaywrightFocusedTest` rule and its associated configurations from the JSON schema and TypeScript definitions, streamlining the linting rules for Playwright. Additionally, the `RuleDomain` type has been updated to include `playwright`, enhancing the categorization of Playwright-specific rules. These changes aim to improve the clarity and maintainability of the codebase.
…Text for state

This commit modifies several Playwright lint rules to utilize `TokenText` as the state type instead of a unit type. The changes enhance the diagnostic messages by including the specific receiver in the output, improving clarity for users. The affected rules include `noPlaywrightPagePause`, `noPlaywrightWaitForNavigation`, `noPlaywrightWaitForSelector`, and `noPlaywrightWaitForTimeout`. Additionally, the `noPlaywrightValidDescribeCallback` rule has been updated to handle new invalid reasons for missing or non-function callbacks. These improvements aim to provide more informative feedback during linting.
…rule

This commit refactors the `missingPlaywrightAwait` lint rule to streamline the logic for checking if a Promise combinator is awaited. The conditional check has been simplified using a single `if let` statement, enhancing code readability while maintaining the same functionality. This change aims to improve the maintainability of the lint rule.
@voxxit voxxit requested a review from dyc3 October 19, 2025 05:17
Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (1)

270-304: Critical: async modifier detection is broken (see earlier reviews).

The previous reviewers correctly identified that has_async_modifier walks the syntax tree upwards (parent nodes) but async modifiers like .resolves, .rejects, and .poll() live in the object/callee chain going down from the final call. This means patterns like await expect(promise).resolves.toBe(1) will incorrectly trigger the rule.

The suggested fix in earlier reviews—recursively walking the object/callee chain—is architecturally correct. Please apply the refactor as outlined in the previous review comments.

🧹 Nitpick comments (4)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (1)

57-94: Consider extracting shared receiver-checking logic.

The pattern here (lines 57–94) is almost identical to no_playwright_wait_for_selector.rs and no_playwright_page_pause.rs. All three rules extract the same receiver validation (page/frame/ending-with-Page-or-Frame) and method-name check. A helper like is_playwright_page_or_frame_method_call(call_expr, method_name) -> Option<TokenText> would eliminate ~40 duplicated lines across the three rules.

crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1)

126-136: Inconsistent emptiness check.

Line 130 uses p.items().len() > 0 whilst line 146 uses the more idiomatic !params.items().is_empty(). Please standardise on !is_empty().

Apply this diff:

                     let has_params = match params {
                         biome_js_syntax::AnyJsArrowFunctionParameters::AnyJsBinding(_) => true,
                         biome_js_syntax::AnyJsArrowFunctionParameters::JsParameters(p) => {
-                            p.items().len() > 0
+                            !p.items().is_empty()
                         }
                     };
crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (1)

55-99: Logic is sound; TokenText usage follows past feedback.

The implementation correctly detects page.pause(), frame.pause(), and related patterns. Good to see TokenText being used throughout, which avoids unnecessary heap allocations as suggested in the previous review.

However, lines 72-87 (object extraction logic) are duplicated across no_playwright_wait_for_navigation, no_playwright_wait_for_selector, and no_playwright_wait_for_timeout. Consider extracting this into a shared helper function to improve maintainability.

For example, a helper like:

// In a shared module (e.g., utils.rs)
pub(crate) fn extract_playwright_receiver_text(
    member_expr: &JsStaticMemberExpression,
) -> Option<TokenText> {
    let object = member_expr.object().ok()?;
    match object {
        biome_js_syntax::AnyJsExpression::JsIdentifierExpression(id) => {
            Some(id.name().ok()?.value_token().ok()?.token_text_trimmed())
        }
        biome_js_syntax::AnyJsExpression::JsStaticMemberExpression(member) => {
            Some(member.member().ok()?.as_js_name()?.value_token().ok()?.token_text_trimmed())
        }
        _ => None,
    }
}

pub(crate) fn is_playwright_page_or_frame(text: &TokenText) -> bool {
    text == "page" || text == "frame" || text.ends_with("Page") || text.ends_with("Frame")
}

Then simplify the run method to:

let object_text = extract_playwright_receiver_text(&member_expr)?;
if is_playwright_page_or_frame(&object_text) {
    Some(object_text)
} else {
    None
}
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1)

58-95: Consider extracting the common pattern into a helper.

This method's logic is nearly identical to no_playwright_page_pause.rs and no_playwright_wait_for_navigation.rs, differing only in the method name checked. A shared helper accepting the method name as a parameter would reduce duplication.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5cc62d5 and 9911849.

⛔ Files ignored due to path filters (10)
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/frame.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-mixed-chains.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/expect-with-async-modifiers.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/missing-callback.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/not-function.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/simple.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (11)
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-mixed-chains.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/expect-with-async-modifiers.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/missing-callback.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/not-function.js (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/not-function.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/biome_js_analyze/src/lint/nursery/missing_playwright_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/expect-with-async-modifiers.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-mixed-chains.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/missing-callback.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/expect-with-async-modifiers.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-mixed-chains.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/missing-callback.js
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/expect-with-async-modifiers.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-mixed-chains.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/missing-callback.js
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Format Rust files before committing (e.g., via just f which formats Rust)
Document rules, assists, and options with inline rustdoc in source

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs
🧠 Learnings (1)
📚 Learning: 2025-10-15T09:21:24.116Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_diagnostics/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:21:24.116Z
Learning: A diagnostic must explain why something went wrong, using log advices and links to documentation when helpful

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs
🧬 Code graph analysis (5)
crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (2)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • note (1381-1383)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (3)
  • FixKind (8150-8150)
  • RuleDomain (904-913)
  • Applicability (9946-9946)
crates/biome_service/src/workspace.rs (1)
  • markup (1130-1132)
crates/biome_analyze/src/rule.rs (3)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (4)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (2)
  • run (55-99)
  • diagnostic (101-119)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (2)
  • run (57-94)
  • diagnostic (96-114)
crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (6)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (10)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
  • new (552-570)
  • new (1316-1327)
  • new (1441-1453)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (2)
  • run (57-94)
  • diagnostic (96-114)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (2)
  • run (58-95)
  • diagnostic (97-115)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_timeout.rs (2)
  • run (57-94)
  • diagnostic (96-114)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (142-151)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (4)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
  • RuleDomain (904-913)
crates/biome_analyze/src/rule.rs (7)
  • language (592-595)
  • sources (587-590)
  • same (247-252)
  • recommended (572-575)
  • domains (602-605)
  • run (1033-1033)
  • diagnostic (1120-1122)
crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (2)
  • run (55-99)
  • diagnostic (101-119)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (2)
  • run (58-95)
  • diagnostic (97-115)
🔇 Additional comments (15)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_navigation.rs (1)

96-114: Diagnostic messages correctly use dynamic receiver.

Past review concerns about hardcoded "page.waitForNavigation()" have been addressed—lines 104 and 108 now interpolate {receiver}, so frame.waitForNavigation() will produce the correct message.

crates/biome_js_analyze/src/lint/nursery/no_playwright_valid_describe_callback.rs (4)

8-53: Rule declaration looks solid.

Metadata is complete, documentation provides clear examples, and the ESLint source is properly attributed.


55-60: Comprehensive coverage of invalid cases.

The NotFunction variant properly addresses previous feedback about reporting non-function callbacks.


68-102: Describe call identification is thorough.

Correctly handles both describe() and test.describe() patterns.


157-188: Excellent diagnostic messages.

Each case provides clear guidance: what's wrong, why it's wrong, and how to fix it. The "why" explanations address previous feedback effectively.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightValidDescribeCallback/invalid/missing-callback.js (1)

1-4: Appropriate test coverage for missing callbacks.

Tests both describe() and test.describe() forms without callbacks, ensuring the rule catches both patterns.

crates/biome_js_analyze/src/lint/nursery/no_playwright_page_pause.rs (4)

1-7: LGTM! Imports are appropriate.

The imports are well-chosen for this rule implementation, including TokenText which aligns with the previous feedback about avoiding heap allocations.


8-47: LGTM! Rule declaration is well-structured.

The rule metadata and documentation are properly configured. The examples clearly demonstrate the invalid patterns and valid alternatives, which will help users understand why page.pause() should be avoided in production code.


49-54: LGTM! Rule trait setup is correct.

The choice of TokenText as the state type is efficient and follows best practices for avoiding unnecessary heap allocations.


101-119: LGTM! Diagnostic message is clear and helpful.

The diagnostic provides excellent guidance, explaining both why pause() is problematic and what developers should do instead. The use of markup! with <Emphasis> makes the key information stand out appropriately.

crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (2)

1-50: LGTM! Clear documentation and correct metadata.

The rule declaration follows established conventions, and the documentation provides helpful examples contrasting invalid patterns with locator-based alternatives.


97-115: Dynamic receiver correctly addresses past feedback.

The diagnostic uses {receiver} to show the actual object name (e.g., page, testPage), making the message specific and helpful. This addresses the previous concern about hard-coding "page." whilst remaining more informative than a generic message.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/expect-with-async-modifiers.js (1)

1-18: LGTM! Comprehensive coverage of valid async expect patterns.

These test cases correctly cover all the async modifier scenarios (.resolves, .rejects, .poll()) where await is necessary. Well done.

crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (1)

306-365: Async modifier detection appears correct—verify against test cases.

The earlier reviews flagged parent-walking logic, but the current implementation walks the object/callee chain downward (lines 317, 333, 354-355), which is the correct approach for detecting .resolves, .rejects, and .poll() modifiers.

Traced examples:

  • expect(promise).resolves.toBe(1) → correctly identifies "resolves" at line 328
  • expect.soft(promise).resolves.toBe(1) → correctly returns true from async modifier check

The valid test cases in expect-with-async-modifiers.js should confirm this handles all async modifier patterns correctly.

crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-mixed-chains.js (1)

1-9: LGTM! Invalid cases appropriately cover sync expect patterns.

These test cases correctly demonstrate unnecessary await usage with synchronous expect matchers, including the expect.soft variant. Well structured.

@ematipico
Copy link
Member

Hi @voxxit

Thank you for the contribution!

In order to not miss on things, could you please do the following:

  • update the PR description and list the lint rules that were implemented in this PR
  • create a new issue (task template) and track the porting of playwright rules. We should list all the rules to port

@voxxit
Copy link
Author
voxxit commented Oct 20, 2025

@ematipico Done! #7796 was created, and updated this description, too.

Copy link
Contributor
@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rules are looking much more clean now. But it doesn't look like the implementations are flagging all the invalid cases.

///
pub MissingPlaywrightAwait {
version: "next",
name: "missingPlaywrightAwait",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rule name doesn't follow our naming convention. Rules must begin with either use or no.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think "usePlaywrightAwait" sounds weird? Maybe it's just me :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could also be noPlaywrightMissingAwait, i do think having "missing" in there is useful

///
pub NoPlaywrightSkippedTest {
version: "next",
name: "noPlaywrightSkippedTest",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, I must have missed this in my last review. We already have noSkippedTests.

///
pub NoPlaywrightValidDescribeCallback {
version: "next",
name: "noPlaywrightValidDescribeCallback",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name should be either:

  • usePlaywrightValidDescribeCallback
  • noPlaywrightInvalidDescribeCallback

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid test files must always start with the comment:

/* should generate diagnostics */

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similarly, all valid test files should start with:

/* should not generate diagnostics */

Comment on lines +5 to +20
# Input
```js
// Test bracket notation for element handles
const handle1 = await page["$"]("button");

const handle2 = await page[`$`]("button");

const handles1 = await page["$$"]("a");

const handles2 = await page[`$$`]("a");

await this.page["$"]("input");

await this.page[`$$`]("div");

```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having those comments would have caught this invalid case having no diagnostics.

Comment on lines +172 to +174
.note(markup! {
"Add "<Emphasis>"await"</Emphasis>" before the test.step call or return the promise."
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a rule has an action, we should put the "how to fix it" part in the action's message instead.

1 1 │ test('example', async ({ page }) => {
2 │ - ····expect(page.locator('body')).toBeVisible();
2 │ + ····await
3 │ + ····expect(page.locator('body')).toBeVisible();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we try to have our fixes match what the formatter would output (on a best effort basis, sometimes it's not reasonably possible). The await should be on the same line as the expect.

Comment on lines +115 to +127
markup! {
"Unexpected use of element handles."
},
)
.note(markup! {
"Element handles like "<Emphasis>"page."{{state_text}}"()"</Emphasis>" are discouraged."
})
.note(markup! {
"Use "<Emphasis>"page.locator()"</Emphasis>" or other locator methods like "<Emphasis>"getByRole()"</Emphasis>" instead."
})
.note(markup! {
"Locators auto-wait and are more reliable than element handles."
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
markup! {
"Unexpected use of element handles."
},
)
.note(markup! {
"Element handles like "<Emphasis>"page."{{state_text}}"()"</Emphasis>" are discouraged."
})
.note(markup! {
"Use "<Emphasis>"page.locator()"</Emphasis>" or other locator methods like "<Emphasis>"getByRole()"</Emphasis>" instead."
})
.note(markup! {
"Locators auto-wait and are more reliable than element handles."
}),
markup! {
"Element handles like "<Emphasis>"page."{{state_text}}"()"</Emphasis>" are discouraged."
},
)
.note(markup! {
"Use "<Emphasis>"page.locator()"</Emphasis>" or other locator methods like "<Emphasis>"getByRole()"</Emphasis>" instead."
})
.note(markup! {
"Locators auto-wait and are more reliable than element handles."
}),

sources: &[
RuleSource::EslintJest("no-focused-tests").inspired(),
RuleSource::EslintVitest("no-focused-tests").inspired(),
RuleSource::EslintPlaywright("no-focused-test").same(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen this format used in other tests:

test.describe.only(() => ...)

Does this rule cover that? There should be some test we can point to in order to confirm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-CLI Area: CLI A-Diagnostic Area: diagnostocis A-Linter Area: linter A-Project Area: project L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

0