Skip to content

feat(lint): migrate first batch of Playwright rules from eslint-plugin-playwright#7775

Closed
voxxit wants to merge 25 commits intobiomejs:mainfrom
voxxit:main
Closed

feat(lint): migrate first batch of Playwright rules from eslint-plugin-playwright#7775
voxxit wants to merge 25 commits intobiomejs:mainfrom
voxxit:main

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
noPlaywrightMissingAwait - 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().
noPlaywrightUselessAwait - Rule - Test Disallow unnecessary awaits for Playwright methods.
usePlaywrightValidDescribeCallback - Rule - Test Enforce valid describe() callback.
noPlaywrightWaitForNavigation - Rule - Test Prevent usage of page.waitForNavigation().
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!)

Recent Changes

Dynamic Receiver in Diagnostic Messages

Fixed noPlaywrightEval and noPlaywrightElementHandle to use dynamic receiver names in diagnostic messages instead of hardcoding page.. This ensures accurate diagnostics when the receiver is frame, myPage, myFrame, etc.

Before: "Unexpected use of page.$eval()" (even when called on frame)
After: "Unexpected use of frame.$eval()" (correctly shows actual receiver)

This aligns with the behavior of the other Playwright rules which already use dynamic receivers.

@changeset-bot
Copy link

changeset-bot bot commented Oct 17, 2025

🦋 Changeset detected

Latest commit: ae23713

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 17, 2025

Walkthrough

Adds Playwright support and 11 new Playwright nursery lint rules (sourced from eslint-plugin-playwright). Introduces a new EslintPlaywright RuleSource and a Playwright RuleDomain (with manifest dependency, globals, display/string/url mappings). New nursery rules cover missing await, element handles, $eval/$$eval, force option, networkidle, page.pause, useless await, describe callback validation, deprecated waitForNavigation/waitForSelector, and waitForTimeout. Also adds empty options structs for each rule and a large set of unit test specs; extends focused-test detection to Playwright patterns.

Possibly related PRs

Suggested reviewers

  • dyc3
  • ematipico

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title precisely describes the primary change: migrating Playwright rules from eslint-plugin-playwright into Biome's linter, which is the core focus of this changeset.
Description check ✅ Passed The description is well-structured, providing a comprehensive summary of the 11 implemented rules, test coverage, and a recent fix for dynamic receiver handling in diagnostic messages. It directly relates to the changeset.
✨ 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.

@codspeed-hq
Copy link

codspeed-hq bot commented Oct 17, 2025

CodSpeed Performance Report

Merging #7775 will not alter performance

Comparing voxxit:main (ae23713) with main (8de2774)

Summary

✅ 58 untouched
⏩ 95 skipped1

Footnotes

  1. 95 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, diagnostics, 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.
…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.

- Resolved conflicts in crates/biome_analyze/src/rule.rs by keeping
  both HEAD's EslintPlaywright and upstream's GraphqlSchemaLinter/Stylelint
- Added RuleDomain::Playwright to as_str and from_str implementations
- Renamed missingPlaywrightAwait to noPlaywrightMissingAwait per project conventions
- Regenerated code with just gen-analyzer
- Updated snapshots for renamed rule
@github-actions github-actions bot added the A-Formatter Area: formatter label Nov 30, 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: 0

🧹 Nitpick comments (1)
crates/biome_analyze/src/rule.rs (1)

533-556: Playwright globals are minimal but pragmatic

Exposing just test and expect as globals for the Playwright domain seems like a good balance; it avoids overreach while covering the common API surface. If you later see false positives around fixtures, you can always extend this list.

📜 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 9911849 and d84f9c8.

⛔ Files ignored due to path filters (1)
  • 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 **
📒 Files selected for processing (1)
  • crates/biome_analyze/src/rule.rs (11 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use the dbg!() macro for debugging output during testing, and pass the --show-output flag to cargo to view debug output
Use cargo t or cargo test to run tests; for a single test, pass the test name after the test command
Use snapshot testing with the insta crate; run cargo insta accept, cargo insta reject, or cargo insta review to manage snapshot changes
Write doctests as doc comments with code blocks; the code inside code blocks will be run during the testing phase
Use just f (alias for just format) to format Rust and TOML files before committing

Files:

  • crates/biome_analyze/src/rule.rs
🧠 Learnings (15)
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : For rules ported from other ecosystems like ESLint or Clippy, add a `sources` field with `RuleSource` metadata using `.same()` for identical behavior or `.inspired()` for different behavior

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Deprecate rules by adding a `deprecated` field to the `declare_lint_rule!` macro with a message explaining the reason for deprecation (e.g., 'Use the rule noAnotherVar')

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `declare_lint_rule!` macro must include metadata fields: `version` (set to 'next'), `name` (rule identifier), `language` (applicable language), `recommended` (boolean), and optional fields like `severity`, `fix_kind`, `sources`, `domains`, and `deprecated`

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnknown` prefix for rules that report mistyped entities in CSS (e.g., `noUnknownUnit`)

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Set the `language` field in `declare_lint_rule!` to the primary language (js for JavaScript, jsx for JSX, ts for TypeScript, tsx for TSX) that the rule applies to

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Document rules with a one-line brief description in the first paragraph of the doc comment, followed by detailed paragraphs, `## Examples` section with `### Invalid` and `### Valid` subsections, and optional `## Options` section

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Set rule severity to `error` for correctness/security/a11y rules, `warn` for suspicious/performance rules, `info` for style/complexity rules, and `info` for actions

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Implement custom `Queryable` types and `Visitor` traits for rules requiring deep AST inspection to avoid redundant traversal passes

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/src/**/*.rs : Rule options must be defined in the `biome_rule_options` crate and implement traits: `Deserializable`, `Merge`, `Serialize`, `Deserialize`, and `JsonSchema`

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use `rule_category!()` macro instead of dynamic string parsing to refer to rule diagnostic categories for compile-time validation

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Framework-specific rules should be named using the `use` or `no` prefix followed by the framework name (e.g., `noVueReservedProps`)

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `action` function must return a `JsRuleAction` (or equivalent language-specific action type) with category `ctx.action_category(ctx.category(), ctx.group())` and applicability from `ctx.metadata().applicability()`

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rule functions must be prefixed with `parse_` and use the name defined in the grammar file, e.g., `parse_for_statement` or `parse_expression`

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/Cargo.toml : Include development dependencies in `Cargo.toml` for formatter tests: `biome_formatter_test`, `biome_<language>_factory`, `biome_<language>_parser`, `biome_parser`, `biome_service`, `countme`, `iai`, `quickcheck`, `quickcheck_macros`, and `tests_macros`

Applied to files:

  • crates/biome_analyze/src/rule.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Use quick testing via `tests/quick_test.rs` by removing the `#[ignore]` macro and modifying the `SOURCE` variable to rapidly validate rule behavior during development

Applied to files:

  • crates/biome_analyze/src/rule.rs
🧬 Code graph analysis (1)
crates/biome_analyze/src/rule.rs (1)
crates/biome_configuration/src/analyzer/mod.rs (6)
  • fmt (596-598)
  • fmt (602-607)
  • fmt (666-668)
  • fmt (672-677)
  • fmt (878-880)
  • fmt (884-886)
🔇 Additional comments (10)
crates/biome_analyze/src/rule.rs (10)

164-170: Playwright rule source variant looks consistent

The new EslintPlaywright(&'static str) variant and its doc comment align with the existing ESLint plugin variants and naming scheme. No issues spotted here.


178-221: Display mapping for Playwright source is correct

Rendering "eslint-plugin-playwright" matches the plugin’s package name and is consistent with other ESLint plugin labels in this match.


264-302: Inclusion in as_rule_name is correct

Adding Self::EslintPlaywright(rule_name) into the shared arm keeps as_rule_name behaviour uniform for all ESLint-derived sources.


305-347: Namespaced Playwright rule name is spot on

"playwright/{rule_name}" matches the upstream rule namespace convention and fits the rest of to_namespaced_rule_name.


350-388: Playwright rule URL matches the plugin docs layout

The GitHub URL format for Playwright rules mirrors how other plugin docs are wired here and matches the plugin’s documented path structure.


456-475: New RuleDomain::Playwright is well-integrated

The Playwright domain variant and its doc comment follow the existing pattern for framework/test domains; placement and naming look good.


477-491: Display for Playwright domain aligns with configuration naming

Returning "playwright" here keeps it consistent with how domains are referenced in configuration and other modules.


507-531: Auto-enabling Playwright domain via manifest dependency is sensible

Hooking Playwright to @playwright/test >= 1.0.0 mirrors how other domains are activated off key dependencies and should make DX nice and predictable.


559-571: as_str for Playwright keeps the domain API coherent

Returning "playwright" here is consistent with Display and other domains; nothing to tweak.


577-590: FromStr support for Playwright completes the round-trip

Parsing "playwright" into RuleDomain::Playwright keeps serialisation/deserialisation symmetric with other domains. Nicely wired.

…edback)

- Updated `noFocusedTests` rule to detect Playwright's `test.only()` pattern.
- Enhanced `noSkippedTests` to handle Playwright's `test.skip()` pattern.
- Introduced new tests for various Playwright linting scenarios, including valid and invalid cases for `usePlaywrightValidDescribeCallback`.
- Removed outdated tests related to `noPlaywrightSkippedTest` and `noPlaywrightMissingAwait`.
- Added new test cases for `noPlaywrightWaitForTimeout` and other related rules.
- Regenerated snapshots for updated tests and rules.
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

🧹 Nitpick comments (4)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-combinators.js (1)

2-21: Use realistic Playwright assertions for more representative test cases.

The test cases use expect(page).toBeVisible() and expect(page).toHaveText('foo'), but these aren't valid Playwright API calls. Assertions like toBeVisible() and toHaveText() are called on locators, not page objects.

Consider using more realistic patterns:

-test('Promise.allSettled', async ({ page }) => {
-    Promise.allSettled([
-        expect(page).toBeVisible(),
-        expect(page).toHaveText('foo')
-    ]);
-});
+test('Promise.allSettled', async ({ page }) => {
+    Promise.allSettled([
+        expect(page.locator('.foo')).toBeVisible(),
+        expect(page.locator('.bar')).toHaveText('foo')
+    ]);
+});

-test('Promise.race', async ({ page }) => {
-    Promise.race([
-        expect(page).toBeVisible(),
-        expect(page).toHaveText('foo')
-    ]);
-});
+test('Promise.race', async ({ page }) => {
+    Promise.race([
+        expect(page.locator('.foo')).toBeVisible(),
+        expect(page.locator('.bar')).toHaveText('foo')
+    ]);
+});

-test('Promise.any', async ({ page }) => {
-    Promise.any([
-        expect(page).toBeVisible(),
-        expect(page).toHaveText('foo')
-    ]);
-});
+test('Promise.any', async ({ page }) => {
+    Promise.any([
+        expect(page.locator('.foo')).toBeVisible(),
+        expect(page.locator('.bar')).toHaveText('foo')
+    ]);
+});
crates/biome_js_analyze/tests/specs/suspicious/noFocusedTests/invalid.js (1)

31-34: Good coverage of Playwright-focused describe patterns

Nice addition of both simple and .each test.describe.only cases; this lines up well with the existing Jest/Vitest patterns and should keep the rule behaviour consistent across test frameworks. If the implementation is also meant to catch bracket notation for Playwright (like test.describe["only"]), you might consider mirroring the describe["only"] examples here—but that’s strictly optional.

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

1-10: File name and scenarios don’t quite match

The cases here are all single sync matchers (plus expect.soft), so the “mixed-chains” name feels a bit broader than what’s actually covered. If you meant to exercise chains mixing async and sync matchers, consider adding one such example; otherwise, renaming the file to something like expect-sync-matchers.js might be clearer. Tests themselves look solid either way, so this is just a naming nit.

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

127-132: Minor style inconsistency.

Line 130 uses len() > 0 whilst line 146 uses !is_empty() for the same purpose. Consider using !is_empty() consistently.

Apply this diff:

                     biome_js_syntax::AnyJsArrowFunctionParameters::AnyJsBinding(_) => true,
                     biome_js_syntax::AnyJsArrowFunctionParameters::JsParameters(p) => {
-                        p.items().len() > 0
+                        !p.items().is_empty()
                     }
📜 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 d84f9c8 and 95487c4.

⛔ Files ignored due to path filters (70)
  • 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_configuration/src/generated/domain_selector.rs is excluded by !**/generated/**, !**/generated/** 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/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/invalid/nested-calls.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/invalid/multiple-arguments.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/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/invalid/force-string-literal.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/select-option-force.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/noPlaywrightMissingAwait/invalid/expect-async-matcher.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-poll-sync-matchers.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-poll.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/module-level.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/nested-expects.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/non-async-context.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-all-not-awaited.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-combinators.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/test-step.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/awaited.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/bracket-notation-valid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/promise-all.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/promise-combinators-valid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/returned.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/all-methods.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/noPlaywrightNetworkidle/valid/other-wait-options.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/noPlaywrightUselessAwait/invalid/expect-mixed-chains.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/expect-with-async-modifiers.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/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 **
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/async.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/has-params.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/missing-callback.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/not-function.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/valid/correct.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/suspicious/noFocusedTests/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/suspicious/noFocusedTests/valid.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 (68)
  • .changeset/common-lizards-sniff.md (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs (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/invalid/nested-calls.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/invalid/multiple-arguments.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/valid/locator-evaluate.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/invalid/force-string-literal.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/select-option-force.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/valid/click.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-async-matcher.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-poll-sync-matchers.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-poll.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/module-level.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/nested-expects.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/non-async-context.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-all-not-awaited.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-combinators.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/test-step.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/awaited.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/bracket-notation-valid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/promise-all.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/promise-combinators-valid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/returned.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/all-methods.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/noPlaywrightNetworkidle/valid/other-wait-options.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/noPlaywrightUselessAwait/invalid/expect-mixed-chains.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/expect-with-async-modifiers.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/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_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/async.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/has-params.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/missing-callback.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/not-function.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/valid/correct.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/suspicious/noFocusedTests/invalid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/suspicious/noFocusedTests/valid.js (1 hunks)
  • crates/biome_rule_options/src/lib.rs (2 hunks)
  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/simple.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/returned.js
🚧 Files skipped from review as they are similar to previous changes (23)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/sync-no-await.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/valid/locator-evaluate.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval-all.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/force-string-literal.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForNavigation/invalid/with-options.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/page-frame.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/pause-function.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/async-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-sync.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/all-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/nested-calls.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/locator.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/valid/other-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/wait-for-load-state.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/invalid/with-state.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/valid/other-methods.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/multiple-arguments.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightNetworkidle/invalid/goto.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/click.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/valid/expect-with-async-modifiers.js
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use the dbg!() macro for debugging output during testing, and pass the --show-output flag to cargo to view debug output
Use cargo t or cargo test to run tests; for a single test, pass the test name after the test command
Use snapshot testing with the insta crate; run cargo insta accept, cargo insta reject, or cargo insta review to manage snapshot changes
Write doctests as doc comments with code blocks; the code inside code blocks will be run during the testing phase
Use just f (alias for just format) to format Rust and TOML files before committing

Files:

  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_rule_options/src/lib.rs
.changeset/**/*.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

.changeset/**/*.md: Create changesets for user-facing changes using just new-changeset; use headers with #### or ##### only; keep descriptions concise (1-3 sentences) and focus on user-facing changes
Use past tense when describing what was done ('Added new feature'), present tense when describing Biome behavior ('Biome now supports'); end sentences with a full stop
For new lint rules, show an example of an invalid case in an inline code snippet or code block; for rule changes, demonstrate what is now invalid or valid; for formatter changes, use a diff code block

Files:

  • .changeset/common-lizards-sniff.md
🧠 Learnings (40)
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/tests/specs/**/*.{js,ts,tsx,jsx,json,css} : Test rules using snapshot tests via the `insta` library with test cases in `tests/specs/<group>/<rule_name>/` directories prefixed by `invalid` or `valid`

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/promise-all.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/bracket-notation-valid.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/non-async-context.js
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/valid/correct.js
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/has-params.js
  • crates/biome_js_analyze/tests/specs/suspicious/noFocusedTests/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-combinators.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightForceOption/invalid/select-option-force.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/module-level.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/test-step.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/promise-combinators-valid.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-poll-sync-matchers.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-poll.js
  • crates/biome_js_analyze/tests/specs/suspicious/noFocusedTests/invalid.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/nested-expects.js
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/async.js
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/not-function.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/awaited.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightPagePause/invalid/in-test.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-all-not-awaited.js
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/missing-callback.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-async-matcher.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-mixed-chains.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForTimeout/invalid/in-test.js
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/tests/specs/**/*.jsonc : Use `.jsonc` files in test specs with code snippets as array of strings to test rules in script environment (no import/export syntax)

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/promise-all.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/bracket-notation-valid.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/non-async-context.js
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/valid/correct.js
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/has-params.js
  • crates/biome_js_analyze/tests/specs/suspicious/noFocusedTests/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-combinators.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/module-level.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-poll-sync-matchers.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-poll.js
  • crates/biome_js_analyze/tests/specs/suspicious/noFocusedTests/invalid.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/nested-expects.js
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/async.js
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/not-function.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-all-not-awaited.js
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/missing-callback.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-async-matcher.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-mixed-chains.js
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : In rule documentation code blocks, mark invalid examples with the `expect_diagnostic` property and valid examples without it; each invalid example must emit exactly one diagnostic

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/non-async-context.js
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/module-level.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightEval/invalid/eval.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-all-not-awaited.js
  • crates/biome_js_analyze/tests/specs/nursery/usePlaywrightValidDescribeCallback/invalid/missing-callback.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-async-matcher.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightUselessAwait/invalid/expect-mixed-chains.js
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use language tags in documentation code blocks (js, ts, tsx, json, css) and order properties consistently as: language, then `expect_diagnostic`, then options modifiers, then `ignore`, then `file=path`

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js
📚 Learning: 2025-11-28T09:08:10.077Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-28T09:08:10.077Z
Learning: Applies to **/*.tsx : For Node.js package development, build WebAssembly bindings and JSON-RPC bindings; run tests against compiled files after implementing features or bug fixes

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js
📚 Learning: 2025-11-28T09:08:10.077Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-28T09:08:10.077Z
Learning: Applies to **/*.ts : For Node.js package development, build WebAssembly bindings and JSON-RPC bindings; run tests against compiled files after implementing features or bug fixes

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/src/**/*.rs : Rule options struct fields should use `#[serde(rename_all = "camelCase")]`, `#[serde(deny_unknown_fields)]`, and `#[serde(default)]` attributes for proper JSON serialization

Applied to files:

  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/src/**/*.rs : Rule options must be defined in the `biome_rule_options` crate and implement traits: `Deserializable`, `Merge`, `Serialize`, `Deserialize`, and `JsonSchema`

Applied to files:

  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/src/**/*.rs : Wrap optional rule option fields in `Option<_>` to properly track set vs unset options during configuration merging

Applied to files:

  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/src/**/*.rs : Implement the `Merge` trait for rule options to define how options from extended configuration merge with user configuration (usually reset instead of extend)

Applied to files:

  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `useValid` prefix for rules that report code that always evaluates to a constant (e.g., `useValidTypeof`)

Applied to files:

  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-21T01:10:53.059Z
Learnt from: dyc3
Repo: biomejs/biome PR: 8171
File: crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs:125-137
Timestamp: 2025-11-21T01:10:53.059Z
Learning: In the Biome codebase, each lint rule has its own options type declaration (e.g., `type Options = RuleNameOptions`) as part of the codegen process, even if the options struct is empty or unused. This is standard practice and should not be flagged as an issue.

Applied to files:

  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/biome_rule_options/src/**/*.rs : Use `Box<[T]>` instead of `Vec<T>` for rule options array fields to save memory (boxed slices and boxed str use 2 words instead of three words)

Applied to files:

  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Document rules with a one-line brief description in the first paragraph of the doc comment, followed by detailed paragraphs, `## Examples` section with `### Invalid` and `### Valid` subsections, and optional `## Options` section

Applied to files:

  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUseless` prefix for rules that report unnecessary code that could be removed or simplified (e.g., `noUselessConstructor`)

Applied to files:

  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Rules should use the `use` prefix naming convention when the sole intention is to mandate a single concept (e.g., `useValidLang` to enforce valid HTML lang attribute values)

Applied to files:

  • crates/biome_rule_options/src/use_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Rule diagnostics must explain to the user what the error is, why it is triggered, and what should be done to fix it following the three pillars: (1) what the error is, (2) why the error is triggered, (3) what the user should do

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `diagnostic` function must return a `RuleDiagnostic` that defines the message reported to the user using the `markup!` macro

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `declare_lint_rule!` macro must include metadata fields: `version` (set to 'next'), `name` (rule identifier), `language` (applicable language), `recommended` (boolean), and optional fields like `severity`, `fix_kind`, `sources`, `domains`, and `deprecated`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Deprecate rules by adding a `deprecated` field to the `declare_lint_rule!` macro with a message explaining the reason for deprecation (e.g., 'Use the rule noAnotherVar')

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noInvalid` prefix for rules that report runtime errors from mistyping (e.g., `noInvalidConstructorSuper`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `action` function must return a `JsRuleAction` (or equivalent language-specific action type) with category `ctx.action_category(ctx.category(), ctx.group())` and applicability from `ctx.metadata().applicability()`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use `rule_category!()` macro instead of dynamic string parsing to refer to rule diagnostic categories for compile-time validation

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : For rules ported from other ecosystems like ESLint or Clippy, add a `sources` field with `RuleSource` metadata using `.same()` for identical behavior or `.inspired()` for different behavior

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `run` function of a lint rule should return `Option<Self::State>` or an iterable like `Vec<Self::State>` or `Box<[Self::State]>` to signal zero or more diagnostics

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Lint rules must be implemented using the `Rule` trait with type parameters: `Query` (node type to analyze), `State` (information for signals), `Signals` (return type from run function), and `Options` (rule configuration)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Framework-specific rules should be named using the `use` or `no` prefix followed by the framework name (e.g., `noVueReservedProps`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUndeclared` prefix for rules that report undefined entities (e.g., `noUndeclaredVariables`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnsafe` prefix for rules that report code leading to runtime failures (e.g., `noUnsafeOptionalChaining`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noEmpty` prefix for rules that report empty code (e.g., `noEmptyBlockStatements`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnused` prefix for rules that report unused entities (e.g., `noUnusedVariables`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noRestricted` prefix for rules that report user-banned entities (e.g., `noRestrictedGlobals`)

Applied to files:

  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnknown` prefix for rules that report mistyped entities in CSS (e.g., `noUnknownUnit`)

Applied to files:

  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Rules should use the `no` prefix naming convention when the sole intention is to forbid a single concept (e.g., `noDebugger` to disallow debugger statements)

Applied to files:

  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noRedundant` prefix for rules that report redundant code (e.g., `noRedundantUseStrict`)

Applied to files:

  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-11-28T09:08:10.077Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-28T09:08:10.077Z
Learning: Applies to .changeset/**/*.md : For new lint rules, show an example of an invalid case in an inline code snippet or code block; for rule changes, demonstrate what is now invalid or valid; for formatter changes, use a `diff` code block

Applied to files:

  • .changeset/common-lizards-sniff.md
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/nursery/**/*.rs : New rules must be placed inside the `nursery` group as an incubation space exempt from semantic versioning, with promotion to appropriate groups done during minor/major releases

Applied to files:

  • .changeset/common-lizards-sniff.md
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Commit rule changes with message format: `feat(biome_<crate>): <ruleName>` to follow Biome's conventional commit style

Applied to files:

  • .changeset/common-lizards-sniff.md
📚 Learning: 2025-11-28T09:08:10.077Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-28T09:08:10.077Z
Learning: Applies to .changeset/**/*.md : Create changesets for user-facing changes using `just new-changeset`; use headers with `####` or `#####` only; keep descriptions concise (1-3 sentences) and focus on user-facing changes

Applied to files:

  • .changeset/common-lizards-sniff.md
📚 Learning: 2025-11-28T09:08:10.077Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-28T09:08:10.077Z
Learning: Applies to .changeset/**/*.md : Use past tense when describing what was done ('Added new feature'), present tense when describing Biome behavior ('Biome now supports'); end sentences with a full stop

Applied to files:

  • .changeset/common-lizards-sniff.md
🧬 Code graph analysis (4)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js (3)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js (1)
  • button (2-2)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/valid/locators.js (1)
  • button (4-4)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar-dollar.js (1)
  • buttons (2-2)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightWaitForSelector/valid/locators.js (2)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/dollar.js (1)
  • button (2-2)
crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/valid/locator.js (1)
  • button (2-2)
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 (2-2)
crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs (3)
crates/biome_service/src/workspace.rs (1)
  • markup (1176-1178)
crates/biome_js_syntax/src/expr_ext.rs (1)
  • callee (33-38)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (143-152)
🪛 LanguageTool
.changeset/common-lizards-sniff.md

[uncategorized] ~9-~9: Loose punctuation mark.
Context: ...nter/rules/no-playwright-missing-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: ...nter/rules/no-playwright-useless-await/): Disallow unnecessary await on synchro...

(UNLIKELY_OPENING_PUNCTUATION)


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

(UNLIKELY_OPENING_PUNCTUATION)


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

(UNLIKELY_OPENING_PUNCTUATION)


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

(UNLIKELY_OPENING_PUNCTUATION)


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

(UNLIKELY_OPENING_PUNCTUATION)

…seless awaits

- Removed redundant notes suggesting to add "await" or return promises in diagnostic messages for `noPlaywrightMissingAwait` and `noPlaywrightUselessAwait` rules.
- Updated snapshots to reflect changes in diagnostic output for various test cases.
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 (1)
crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (1)

238-362: Async‑modifier handling for expect chains looks correct; please ensure tests cover .resolves/.rejects/.poll

The is_sync_expect_call + has_async_modifier combo now walks the callee/object chain rather than climbing parents, which should avoid the earlier false positives on patterns like:

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

and only flag await when:

  • the final matcher is one of the synchronous matchers, and
  • there’s no poll/resolves/rejects segment between the base expect/expect.soft call and that matcher.

From a quick mental AST walk, this looks correct, but it’s a subtle area. I’d strongly recommend making sure your specs include the above three patterns as valid (no diagnostics) alongside the basic await expect(1).toBe(1) invalid case, to lock this behaviour in.

If you’d like, I can suggest concrete test cases mirroring the upstream eslint-plugin-playwright rule docs to double‑check parity.

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

427-466: Minor naming nit: find_enclosing_promise_all now covers all combinators

The helper correctly detects Promise.all, allSettled, race, and any, so the name find_enclosing_promise_all is a tiny bit misleading. If you touch this code again, consider renaming to something like find_enclosing_promise_combinator for clarity; the implementation itself looks good.

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

63-126: Locator/page/expect matcher lists look reasonable; consider future de‑duplication

The LOCATOR_METHODS, SYNC_PAGE_METHODS, and SYNC_EXPECT_MATCHERS sets match what I’d expect from the upstream rule and keep lookups O(1) via slice contains. If more Playwright rules start needing the same method lists, it might be worth centralising them in a shared Playwright helper module to avoid drift between rules, but that can be a follow‑up.


207-236: is_page_or_frame mirrors other Playwright rules; potential shared helper

is_page_or_frame matches the same “page/frame or *Page/*Frame suffix” heuristic used in the other Playwright rules (e.g. wait‑for‑selector/navigation), and correctly uses text_trimmed()/TokenText instead of allocating Strings. If you find yourself copying this into more rules, a small shared helper in a Playwright utils module would keep everything in sync, but it’s fine as‑is for this PR.

📜 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 95487c4 and 533ed8d.

⛔ Files ignored due to path filters (14)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-async-matcher.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-poll-sync-matchers.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/expect-poll.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/module-level.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/nested-expects.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/non-async-context.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-all-not-awaited.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-combinators.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/test-step.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/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 **
📒 Files selected for processing (2)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use the dbg!() macro for debugging output during testing, and pass the --show-output flag to cargo to view debug output
Use cargo t or cargo test to run tests; for a single test, pass the test name after the test command
Use snapshot testing with the insta crate; run cargo insta accept, cargo insta reject, or cargo insta review to manage snapshot changes
Write doctests as doc comments with code blocks; the code inside code blocks will be run during the testing phase
Use just f (alias for just format) to format Rust and TOML files before committing

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
🧠 Learnings (19)
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `action` function must return a `JsRuleAction` (or equivalent language-specific action type) with category `ctx.action_category(ctx.category(), ctx.group())` and applicability from `ctx.metadata().applicability()`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Framework-specific rules should be named using the `use` or `no` prefix followed by the framework name (e.g., `noVueReservedProps`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : For rules ported from other ecosystems like ESLint or Clippy, add a `sources` field with `RuleSource` metadata using `.same()` for identical behavior or `.inspired()` for different behavior

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Lint rules must be implemented using the `Rule` trait with type parameters: `Query` (node type to analyze), `State` (information for signals), `Signals` (return type from run function), and `Options` (rule configuration)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnsafe` prefix for rules that report code leading to runtime failures (e.g., `noUnsafeOptionalChaining`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noEmpty` prefix for rules that report empty code (e.g., `noEmptyBlockStatements`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Code actions must specify a `fix_kind` field in the `declare_lint_rule!` macro as either `FixKind::Safe` or `FixKind::Unsafe` to indicate whether fixes always preserve program behavior

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Set the `language` field in `declare_lint_rule!` to the primary language (js for JavaScript, jsx for JSX, ts for TypeScript, tsx for TSX) that the rule applies to

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUndeclared` prefix for rules that report undefined entities (e.g., `noUndeclaredVariables`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Implement custom `Queryable` types and `Visitor` traits for rules requiring deep AST inspection to avoid redundant traversal passes

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUseless` prefix for rules that report unnecessary code that could be removed or simplified (e.g., `noUselessConstructor`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnused` prefix for rules that report unused entities (e.g., `noUnusedVariables`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noRedundant` prefix for rules that report redundant code (e.g., `noRedundantUseStrict`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Deprecate rules by adding a `deprecated` field to the `declare_lint_rule!` macro with a message explaining the reason for deprecation (e.g., 'Use the rule noAnotherVar')

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noInvalid` prefix for rules that report runtime errors from mistyping (e.g., `noInvalidConstructorSuper`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Avoid unnecessary string allocations by comparing against `&str` or `TokenText` instead of calling `to_string()` which allocates heap memory

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : In rule documentation code blocks, mark invalid examples with the `expect_diagnostic` property and valid examples without it; each invalid example must emit exactly one diagnostic

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Rules should use the `no` prefix naming convention when the sole intention is to forbid a single concept (e.g., `noDebugger` to disallow debugger statements)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noMisleading` prefix for rules that report valid but potentially misleading code (e.g., `noMisleadingCharacterClass`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs (4)
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_networkidle.rs (2)
  • run (55-105)
  • diagnostic (107-124)
crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (2)
  • run (52-94)
  • diagnostic (96-116)
🔇 Additional comments (6)
crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs (4)

15-66: Rule declaration & metadata look spot on

Docs, examples, rule name, language: "js", sources pointing at missing-playwright-await, fix_kind: FixKind::Unsafe, and domains: &[RuleDomain::Playwright] all line up nicely with the intended behaviour and the CONTRIBUTING guidance for ported framework rules.


69-107: Async matcher detection is thorough and mirrors Playwright semantics

The ASYNC_PLAYWRIGHT_MATCHERS list plus get_async_expect_matcher/has_poll_in_chain/has_expect_in_chain logic gives good coverage of expect(...) chains, including expect.poll and expect.soft/.not style chains. The control flow is defensive (short‑circuiting on poll first, then matcher membership, then expect at the root), which should keep false positives low.

Also applies to: 266-298


468-501: Async‑context guard for fixes is a nice safety net

is_in_async_context combining is_in_async_function with module‑level TLA detection is a good guardrail so the fixer only offers await where it’s syntactically valid, and falls back to diagnostics‑only elsewhere. Nicely balances helpfulness with not rewriting users into a syntax error.


176-227: Align JsRuleAction construction with rule metadata helpers, but fix the method call

Both branches of action hard‑code Applicability::MaybeIncorrect and use ctx.metadata().action_category(...). The suggestion to use ctx.metadata().applicability() instead of hard-coded applicability is correct and matches patterns in other rules, but the proposed change to the category call is incorrect.

The correct pattern used across the codebase is:

  • Use ctx.metadata().action_category(ctx.category(), ctx.group()) for the category (not ctx.action_category()).
  • Use ctx.metadata().applicability() for applicability.
  • Drop the now‑unused Applicability import.

Apply this corrected pattern:

-            return Some(JsRuleAction::new(
-                ctx.metadata().action_category(ctx.category(), ctx.group()),
-                Applicability::MaybeIncorrect,
+            return Some(JsRuleAction::new(
+                ctx.metadata().action_category(ctx.category(), ctx.group()),
+                ctx.metadata().applicability(),
                 markup! { "Add "<Emphasis>"await"</Emphasis>" before the Promise combinator." }
                     .to_owned(),
                 mutation,
             ));
@@
-        Some(JsRuleAction::new(
-            ctx.metadata().action_category(ctx.category(), ctx.group()),
-            Applicability::MaybeIncorrect,
+        Some(JsRuleAction::new(
+            ctx.metadata().action_category(ctx.category(), ctx.group()),
+            ctx.metadata().applicability(),
             markup! { "Add "<Emphasis>"await"</Emphasis>" before the call." }.to_owned(),
             mutation,
         ))

and at the top:

-use biome_diagnostics::Applicability;

This centralizes rule metadata as the single source of truth.

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

1-11: Imports and wiring look consistent with the rest of the lint infra

The imports are tight, used, and match the standard pattern for new JS rules in this crate. Nothing to change here.


12-61: Rule metadata & docs align with Biome and upstream naming

Nice job on the metadata: NoPlaywrightUselessAwait follows the no + framework naming guidance, uses RuleSource::EslintPlaywright("no-useless-await"), and scopes to RuleDomain::Playwright. The doc examples correctly mark invalid snippets with expect_diagnostic and valid ones without.

Comment on lines +128 to +205
impl Rule for NoPlaywrightUselessAwait {
type Query = Ast<JsAwaitExpression>;
type State = ();
type Signals = Option<Self::State>;
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let await_expr = ctx.query();
let argument = await_expr.argument().ok()?;

// Check if the awaited expression is a call expression
let call_expr = argument.as_js_call_expression()?;
let callee = call_expr.callee().ok()?;

// Check for member expressions (method calls)
if let Some(member_expr) = callee.as_js_static_member_expression() {
let member_name = member_expr.member().ok()?;
let member_token = member_name.as_js_name()?.value_token().ok()?;
let method_name = member_token.text_trimmed();

// Check if it's a locator method
if LOCATOR_METHODS.contains(&method_name) {
return Some(());
}

// Check if it's a sync page method
if SYNC_PAGE_METHODS.contains(&method_name) {
// Verify it's called on page/frame
let object = member_expr.object().ok()?;
if is_page_or_frame(&object) {
return Some(());
}
}
}

// Check for expect calls with sync matchers
if is_sync_expect_call(call_expr) {
return Some(());
}

None
}

fn diagnostic(ctx: &RuleContext<Self>, _: &Self::State) -> Option<RuleDiagnostic> {
let node = ctx.query();
Some(
RuleDiagnostic::new(
rule_category!(),
node.range(),
markup! {
"Unnecessary "<Emphasis>"await"</Emphasis>" expression."
},
)
.note(markup! {
"This method does not return a Promise."
}),
)
}

fn action(ctx: &RuleContext<Self>, _: &Self::State) -> Option<JsRuleAction> {
let await_expr = ctx.query();
let argument = await_expr.argument().ok()?;

let mut mutation = ctx.root().begin();
// Replace the entire await expression with just its argument
mutation.replace_element(
await_expr.clone().into_syntax().into(),
argument.into_syntax().into(),
);

Some(JsRuleAction::new(
ctx.metadata().action_category(ctx.category(), ctx.group()),
Applicability::Always,
markup! { "Remove unnecessary await" }.to_owned(),
mutation,
))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use ctx.metadata().applicability() instead of hard‑coding Applicability::Always

The main rule logic in run and the diagnostic text look solid, and the fix (replace the whole await expression with its argument) is indeed safe for the methods you’re matching.

One small but important consistency point: the action currently hard‑codes Applicability::Always, instead of deferring to the rule metadata:

Some(JsRuleAction::new(
    ctx.metadata().action_category(ctx.category(), ctx.group()),
-   Applicability::Always,
+   ctx.metadata().applicability(),
    markup! { "Remove unnecessary await" }.to_owned(),
    mutation,
))

Using ctx.metadata().applicability() keeps this rule aligned with how applicability is configured centrally for lint actions.

🤖 Prompt for AI Agents
In crates/biome_js_analyze/src/lint/nursery/no_playwright_useless_await.rs
around lines 128 to 205, the JsRuleAction is using a hard-coded
Applicability::Always; change it to use ctx.metadata().applicability() instead:
update the JsRuleAction::new call to pass ctx.metadata().applicability() as the
applicability argument so the rule respects centrally configured applicability.

Copy link
Member

Choose a reason for hiding this comment

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

@voxxit please address this comment

- Modified examples in `no_playwright_missing_await` to use `page.getByRole('button')` instead of `page` directly for better clarity.
- Adjusted the `use_playwright_valid_describe_callback` rule to check for non-empty parameter lists more clearly.
- Updated test cases and snapshots to reflect changes in the usage of locators for better accuracy in diagnostics.
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

🧹 Nitpick comments (4)
crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs (3)

62-67: Consider wiring through the dedicated options type instead of ()

AI context mentions a UsePlaywrightValidDescribeCallbackOptions in biome_rule_options; if that exists, it would be more consistent with other rules to import and use it as type Options = UsePlaywrightValidDescribeCallbackOptions, or else drop the unused options struct. That keeps configuration, docs, and type-level wiring in sync for future tweaks.


72-102: Broaden describe detection to match more Playwright call shapes

is_describe currently recognises only bare describe(...) and test.describe(...), so patterns like test.describe.only(...) / test.describe.skip(...) / test.describe.parallel(...) will not be linted, whereas the upstream ESLint rule typically handles the “describe-ish” variants too. If you’re aiming for behavioural parity with eslint-plugin-playwright, consider peeling one level of JsStaticMemberExpression (or reusing a shared helper) so calls where the inner member is describe on test are also treated as describes; otherwise it might be more accurate to mark the rule source as .inspired() rather than .same().


157-188: Diagnostics read well and follow the three‑pillars guidance

Each InvalidReason maps to a clear primary message plus two notes that cover why it’s a problem and how to fix it, and you’re using markup! with rule_category!() as expected; this is exactly the style the lint docs call for. If you ever fancy micro-polish, you could target the callback expression’s range instead of the whole call, but it’s definitely shippable as is. Based on learnings, this ticks the diagnostic requirements.

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

102-106: Consider adding derive macros for better ergonomics.

Adding #[derive(Debug)] would be helpful for debugging during development. Not strictly required since the type is only passed by reference, but a nice-to-have.

+#[derive(Debug)]
 pub enum MissingAwaitType {
     ExpectMatcher(TokenText),
     ExpectPoll,
     TestStep,
 }
📜 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 533ed8d and b6cdc5d.

⛔ Files ignored due to path filters (2)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-combinators.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/promise-combinators-valid.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (4)
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-combinators.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/promise-combinators-valid.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/invalid/promise-combinators.js
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightMissingAwait/valid/promise-combinators-valid.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use the dbg!() macro for debugging output during testing, and pass the --show-output flag to cargo to view debug output
Use cargo t or cargo test to run tests; for a single test, pass the test name after the test command
Use snapshot testing with the insta crate; run cargo insta accept, cargo insta reject, or cargo insta review to manage snapshot changes
Write doctests as doc comments with code blocks; the code inside code blocks will be run during the testing phase
Use just f (alias for just format) to format Rust and TOML files before committing

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
🧠 Learnings (19)
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `declare_lint_rule!` macro must include metadata fields: `version` (set to 'next'), `name` (rule identifier), `language` (applicable language), `recommended` (boolean), and optional fields like `severity`, `fix_kind`, `sources`, `domains`, and `deprecated`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUndeclared` prefix for rules that report undefined entities (e.g., `noUndeclaredVariables`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `action` function must return a `JsRuleAction` (or equivalent language-specific action type) with category `ctx.action_category(ctx.category(), ctx.group())` and applicability from `ctx.metadata().applicability()`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : For rules ported from other ecosystems like ESLint or Clippy, add a `sources` field with `RuleSource` metadata using `.same()` for identical behavior or `.inspired()` for different behavior

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnused` prefix for rules that report unused entities (e.g., `noUnusedVariables`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/nursery/**/*.rs : New rules must be placed inside the `nursery` group as an incubation space exempt from semantic versioning, with promotion to appropriate groups done during minor/major releases

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Framework-specific rules should be named using the `use` or `no` prefix followed by the framework name (e.g., `noVueReservedProps`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnknown` prefix for rules that report mistyped entities in CSS (e.g., `noUnknownUnit`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Lint rules must be implemented using the `Rule` trait with type parameters: `Query` (node type to analyze), `State` (information for signals), `Signals` (return type from run function), and `Options` (rule configuration)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noEmpty` prefix for rules that report empty code (e.g., `noEmptyBlockStatements`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : In rule documentation code blocks, mark invalid examples with the `expect_diagnostic` property and valid examples without it; each invalid example must emit exactly one diagnostic

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Rule diagnostics must explain to the user what the error is, why it is triggered, and what should be done to fix it following the three pillars: (1) what the error is, (2) why the error is triggered, (3) what the user should do

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `diagnostic` function must return a `RuleDiagnostic` that defines the message reported to the user using the `markup!` macro

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Deprecate rules by adding a `deprecated` field to the `declare_lint_rule!` macro with a message explaining the reason for deprecation (e.g., 'Use the rule noAnotherVar')

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noInvalid` prefix for rules that report runtime errors from mistyping (e.g., `noInvalidConstructorSuper`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use `rule_category!()` macro instead of dynamic string parsing to refer to rule diagnostic categories for compile-time validation

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `useValid` prefix for rules that report code that always evaluates to a constant (e.g., `useValidTypeof`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `run` function of a lint rule should return `Option<Self::State>` or an iterable like `Vec<Self::State>` or `Box<[Self::State]>` to signal zero or more diagnostics

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Rules should use the `use` prefix naming convention when the sole intention is to mandate a single concept (e.g., `useValidLang` to enforce valid HTML lang attribute values)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs
🔇 Additional comments (9)
crates/biome_js_analyze/src/lint/nursery/use_playwright_valid_describe_callback.rs (1)

8-52: Rule declaration and docs look solid

Docs, examples (with expect_diagnostic on invalid cases), and metadata (version, language, sources, domains, recommended) all line up with the Playwright rule you’re porting; nothing blocking here. Based on learnings, this matches the expected lint-rule boilerplate.

crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs (8)

15-67: Well-structured rule declaration with clear documentation.

The examples nicely illustrate both the problematic pattern and the corrected version. Good inclusion of the return case as a valid alternative to await.


114-133: Clean early-return pattern in the run function.

The separation of test.step() detection from expect matcher detection is well-organised. The is_properly_handled abstraction keeps this function readable.


135-174: Informative diagnostics with appropriate context.

The notes for ExpectPoll and TestStep variants add useful context. Including the specific matcher name in the ExpectMatcher diagnostic is a nice touch for clarity.


176-227: Thoughtful handling of Promise combinators.

The logic to fix the enclosing Promise.all/Promise.race etc. rather than individual array elements preserves concurrency semantics — a detail that's easy to overlook. Well done.


230-260: Neat helper with clean control flow.

The inner closure pattern with ? operator keeps the code readable whilst handling all the Result/Option unwrapping gracefully.


300-365: Solid chain traversal logic.

The recursive approach handles arbitrarily deep chains like expect.poll(...).not.toBeVisible() and expect.soft(...) variants correctly.


385-399: Pragmatic arrow function body detection.

Using text_trimmed_range() comparison is a reasonable approach to check if the call expression is exactly the arrow body (implicit return). The immediate break correctly prevents matching calls nested deeper inside the arrow body.


468-501: Correct handling of top-level await context.

The function properly distinguishes between ES modules (where TLA is valid) and code nested inside synchronous functions (where being in a module doesn't permit await). The boundary check at lines 487-497 ensures we don't incorrectly assume async context.

Comment on lines +69 to +100
// Playwright async matchers (web-first assertions)
const ASYNC_PLAYWRIGHT_MATCHERS: &[&str] = &[
"toBeAttached",
"toBeChecked",
"toBeDisabled",
"toBeEditable",
"toBeEmpty",
"toBeEnabled",
"toBeFocused",
"toBeHidden",
"toBeInViewport",
"toBeOK",
"toBeVisible",
"toContainText",
"toHaveAccessibleDescription",
"toHaveAccessibleErrorMessage",
"toHaveAccessibleName",
"toHaveAttribute",
"toHaveClass",
"toHaveCount",
"toHaveCSS",
"toHaveId",
"toHaveJSProperty",
"toHaveScreenshot",
"toHaveText",
"toHaveTitle",
"toHaveURL",
"toHaveValue",
"toHaveValues",
"toContainClass",
"toPass",
];
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Playwright async expect matchers complete list 2025 documentation

💡 Result:

Here’s the up‑to‑date (2025) list of Playwright’s async (auto‑retrying) expect matchers, per the Playwright docs:

Element/Locator matchers

  • toBeAttached
  • toBeChecked
  • toBeDisabled
  • toBeEditable
  • toBeEmpty
  • toBeEnabled
  • toBeFocused
  • toBeHidden
  • toBeInViewport
  • toBeVisible
  • toContainText
  • toHaveAccessibleDescription
  • toHaveAccessibleName
  • toHaveAttribute
  • toHaveClass / toContainClass
  • toHaveCount
  • toHaveCSS
  • toHaveId
  • toHaveJSProperty
  • toHaveRole
  • toHaveScreenshot
  • toHaveText
  • toHaveValue
  • toHaveValues

Page matchers

  • toHaveScreenshot (page)
  • toHaveTitle
  • toHaveURL

Network/response matcher

  • toBeOK (response)

(Playwright also provides many synchronous generic Jest‑style matchers — toEqual, toContain, toBeTruthy, etc. — which are not auto‑retrying.)

Sources: Playwright assertions and locator/assertions API docs. [1][2]

References
[1] Playwright — Assertions / Test assertions (Playwright docs).
[2] Playwright — LocatorAssertions / API (Playwright docs).


🌐 Web query:

Playwright toHaveAccessibleErrorMessage async matcher

💡 Result:

Short answer: Playwright provides an async locator matcher expect(locator).toHaveAccessibleErrorMessage(...) (added in v1.50) that waits/retries until the element's aria-errormessage text matches the expected string/RegExp. Use it with await and you can pass options { ignoreCase, timeout }.

Example:
const input = page.getByTestId('username-input');
// waits up to expect timeout (or override with options.timeout)
await expect(input).toHaveAccessibleErrorMessage('Username is required.');
// case-insensitive / regex / timeout
await expect(input).toHaveAccessibleErrorMessage(/required/i, { timeout: 3000 });

References:
[1] Playwright LocatorAssertions (toHaveAccessibleErrorMessage).
[2] Playwright test assertions — async matchers wait/retry.
[3] Playwright v1.50 release notes (added toHaveAccessibleErrorMessage).


🌐 Web query:

Playwright toPass async matcher

💡 Result:

Short answer

  • expect(...).toPass() retries an async block until all assertions inside it pass (or until timeout). Use it to wrap flaky async checks (network, UI, emails). [1][2]

Example

  • Basic:
    await expect(async () => {
    const r = await page.request.get('https://api.example.com');
    expect(r.status()).toBe(200);
    }).toPass();
  • With options:
    await expect(async () => { /* ... */ }).toPass({ intervals: [1000,2000,10000], timeout: 60000 });

Key notes

  • toPass retries the entire block if any assertion fails (unlike expect.poll which polls a value). [2]
  • Default toPass timeout is 0 (no retries) unless you pass options or configure it in config (e.g., expect: { toPass: { timeout: 60000 } }). [1][2]

Sources

  • Playwright docs — Assertions (expect.toPass). [1]
  • Comparison / examples (community notes on toPass vs poll). [2]

Add the missing toHaveRole async matcher to the list.

The constant is missing toHaveRole, which is a documented Playwright async matcher for locators. The list should include it alongside the other element matchers to ensure complete coverage of auto-retrying assertions.

🤖 Prompt for AI Agents
In crates/biome_js_analyze/src/lint/nursery/no_playwright_missing_await.rs
around lines 69 to 100, the ASYNC_PLAYWRIGHT_MATCHERS array is missing the
Playwright async matcher "toHaveRole"; add "toHaveRole" to the list alongside
the other element matchers so it is treated as an async auto-retrying assertion.

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

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

46-49: EvalMethodCall can be private to this module.

The struct doesn’t appear to be used outside this file, so you can drop the pub on the struct and its fields to keep the surface area minimal.

📜 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 b6cdc5d and ae23713.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/nursery/noPlaywrightElementHandle/invalid/frame.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (2)
  • 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)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use the dbg!() macro for debugging output during testing, and pass the --show-output flag to cargo to view debug output
Use cargo t or cargo test to run tests; for a single test, pass the test name after the test command
Use snapshot testing with the insta crate; run cargo insta accept, cargo insta reject, or cargo insta review to manage snapshot changes
Write doctests as doc comments with code blocks; the code inside code blocks will be run during the testing phase
Use just f (alias for just format) to format Rust and TOML files before committing

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
🧠 Learnings (19)
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnsafe` prefix for rules that report code leading to runtime failures (e.g., `noUnsafeOptionalChaining`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Rules should use the `no` prefix naming convention when the sole intention is to forbid a single concept (e.g., `noDebugger` to disallow debugger statements)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Lint rules must be implemented using the `Rule` trait with type parameters: `Query` (node type to analyze), `State` (information for signals), `Signals` (return type from run function), and `Options` (rule configuration)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Framework-specific rules should be named using the `use` or `no` prefix followed by the framework name (e.g., `noVueReservedProps`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noRestricted` prefix for rules that report user-banned entities (e.g., `noRestrictedGlobals`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Deprecate rules by adding a `deprecated` field to the `declare_lint_rule!` macro with a message explaining the reason for deprecation (e.g., 'Use the rule noAnotherVar')

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noConstant` prefix for rules that report computations always evaluated to the same value (e.g., `noConstantMathMinMaxClamp`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUndeclared` prefix for rules that report undefined entities (e.g., `noUndeclaredVariables`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/nursery/**/*.rs : New rules must be placed inside the `nursery` group as an incubation space exempt from semantic versioning, with promotion to appropriate groups done during minor/major releases

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use `rule_category!()` macro instead of dynamic string parsing to refer to rule diagnostic categories for compile-time validation

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `diagnostic` function must return a `RuleDiagnostic` that defines the message reported to the user using the `markup!` macro

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noInvalid` prefix for rules that report runtime errors from mistyping (e.g., `noInvalidConstructorSuper`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : In rule documentation code blocks, mark invalid examples with the `expect_diagnostic` property and valid examples without it; each invalid example must emit exactly one diagnostic

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `useValid` prefix for rules that report code that always evaluates to a constant (e.g., `useValidTypeof`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `action` function must return a `JsRuleAction` (or equivalent language-specific action type) with category `ctx.action_category(ctx.category(), ctx.group())` and applicability from `ctx.metadata().applicability()`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs
  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `declare_lint_rule!` macro must include metadata fields: `version` (set to 'next'), `name` (rule identifier), `language` (applicable language), `recommended` (boolean), and optional fields like `severity`, `fix_kind`, `sources`, `domains`, and `deprecated`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Use the `noUnknown` prefix for rules that report mistyped entities in CSS (e.g., `noUnknownUnit`)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : The `run` function of a lint rule should return `Option<Self::State>` or an iterable like `Vec<Self::State>` or `Box<[Self::State]>` to signal zero or more diagnostics

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
📚 Learning: 2025-11-27T23:04:02.022Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-11-27T23:04:02.022Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/lint/**/*.rs : Rules should use the `use` prefix naming convention when the sole intention is to mandate a single concept (e.g., `useValidLang` to enforce valid HTML lang attribute values)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/no_playwright_element_handle.rs (6)
crates/biome_service/src/workspace.rs (1)
  • markup (1176-1178)
crates/biome_analyze/src/rule.rs (10)
  • language (634-637)
  • sources (629-632)
  • same (249-254)
  • recommended (614-617)
  • domains (644-647)
  • run (1075-1075)
  • diagnostic (1162-1164)
  • new (594-612)
  • new (1396-1408)
  • new (1518-1530)
crates/biome_service/src/workspace_types.rs (1)
  • Self (748-748)
crates/biome_js_analyze/src/lint/nursery/no_playwright_wait_for_selector.rs (2)
  • run (58-95)
  • diagnostic (97-115)
crates/biome_js_syntax/src/expr_ext.rs (1)
  • callee (33-38)
crates/biome_rowan/src/ast/mod.rs (1)
  • cast_ref (143-152)
🔇 Additional comments (5)
crates/biome_js_analyze/src/lint/nursery/no_playwright_eval.rs (1)

8-43: Rule logic and diagnostics look solid and match the stated intent.

Detection is nicely constrained to $eval/$$eval on page/frame‑like receivers, and the diagnostic (including the updated receiver/method wording) steers users cleanly towards locator APIs. Docs, naming (noPlaywright…), nursery placement, and use of rule_category! / markup! are all in line with the CONTRIBUTING guidance.

Based on learnings, this follows the rule‑authoring conventions for nursery rules.

Also applies to: 51-125

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

1-6: LGTM!

Imports are correct and complete for this rule implementation.


8-55: LGTM!

Rule declaration follows Biome conventions correctly. The documentation is clear and includes helpful examples that cover the main use cases.


57-60: LGTM!

State struct is clean and captures the necessary information for the diagnostic.


68-114: LGTM!

The detection logic is solid and consistent with other Playwright rules in the codebase. The pattern matching correctly handles both direct identifiers and member expressions for the receiver.

@voxxit voxxit requested a review from dyc3 November 30, 2025 22:17
Copy link
Member

Choose a reason for hiding this comment

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

A few changes are needed:

  • create a changeset for each rule
  • for each rule, please provide a small summary (seems like you already do so) and optionally a small snippet of invalid code
  • Avoid mentioning the batch thing, it's not needed to the user

Comment on lines +63 to +126
// Locator methods that return Locator (synchronous)
const LOCATOR_METHODS: &[&str] = &[
"and",
"first",
"getByAltText",
"getByLabel",
"getByPlaceholder",
"getByRole",
"getByTestId",
"getByText",
"getByTitle",
"last",
"locator",
"nth",
"or",
];

// Page/Frame methods that are synchronous
const SYNC_PAGE_METHODS: &[&str] = &[
"childFrames",
"frame",
"frameLocator",
"frames",
"isClosed",
"isDetached",
"mainFrame",
"name",
"on",
"page",
"parentFrame",
"setDefaultNavigationTimeout",
"setDefaultTimeout",
"url",
"video",
"viewportSize",
"workers",
];

// Synchronous expect matchers (not Playwright-specific web-first assertions)
const SYNC_EXPECT_MATCHERS: &[&str] = &[
"toBe",
"toBeCloseTo",
"toBeDefined",
"toBeFalsy",
"toBeGreaterThan",
"toBeGreaterThanOrEqual",
"toBeInstanceOf",
"toBeLessThan",
"toBeLessThanOrEqual",
"toBeNaN",
"toBeNull",
"toBeTruthy",
"toBeUndefined",
"toContain",
"toContainEqual",
"toEqual",
"toHaveLength",
"toHaveProperty",
"toMatch",
"toMatchObject",
"toStrictEqual",
"toThrow",
"toThrowError",
];
Copy link
Member

Choose a reason for hiding this comment

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

Please make these lists ordered, and use binary_seach instead of contains. The former is faster than the latter.

To avoid regressions, we have tests to keep them ordered. We have similar tests throughout the linter code base

type Query = Ast<JsAwaitExpression>;
type State = ();
type Signals = Option<Self::State>;
type Options = ();
Copy link
Member

Choose a reason for hiding this comment

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

It seems the PR got old. You should add a file named like the file rule inside the crate biome_rules_options, and use that struct here. Refer to the existing code. It must be applied to all rules

Comment on lines +128 to +205
impl Rule for NoPlaywrightUselessAwait {
type Query = Ast<JsAwaitExpression>;
type State = ();
type Signals = Option<Self::State>;
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let await_expr = ctx.query();
let argument = await_expr.argument().ok()?;

// Check if the awaited expression is a call expression
let call_expr = argument.as_js_call_expression()?;
let callee = call_expr.callee().ok()?;

// Check for member expressions (method calls)
if let Some(member_expr) = callee.as_js_static_member_expression() {
let member_name = member_expr.member().ok()?;
let member_token = member_name.as_js_name()?.value_token().ok()?;
let method_name = member_token.text_trimmed();

// Check if it's a locator method
if LOCATOR_METHODS.contains(&method_name) {
return Some(());
}

// Check if it's a sync page method
if SYNC_PAGE_METHODS.contains(&method_name) {
// Verify it's called on page/frame
let object = member_expr.object().ok()?;
if is_page_or_frame(&object) {
return Some(());
}
}
}

// Check for expect calls with sync matchers
if is_sync_expect_call(call_expr) {
return Some(());
}

None
}

fn diagnostic(ctx: &RuleContext<Self>, _: &Self::State) -> Option<RuleDiagnostic> {
let node = ctx.query();
Some(
RuleDiagnostic::new(
rule_category!(),
node.range(),
markup! {
"Unnecessary "<Emphasis>"await"</Emphasis>" expression."
},
)
.note(markup! {
"This method does not return a Promise."
}),
)
}

fn action(ctx: &RuleContext<Self>, _: &Self::State) -> Option<JsRuleAction> {
let await_expr = ctx.query();
let argument = await_expr.argument().ok()?;

let mut mutation = ctx.root().begin();
// Replace the entire await expression with just its argument
mutation.replace_element(
await_expr.clone().into_syntax().into(),
argument.into_syntax().into(),
);

Some(JsRuleAction::new(
ctx.metadata().action_category(ctx.category(), ctx.group()),
Applicability::Always,
markup! { "Remove unnecessary await" }.to_owned(),
mutation,
))
}
}
Copy link
Member

Choose a reason for hiding this comment

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

@voxxit please address this comment

}
}

fn is_page_or_frame(expr: &AnyJsExpression) -> bool {
Copy link
Member

Choose a reason for hiding this comment

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

Return a SyntaxResult<bool> so you can simplify a lot of code

}
}

fn is_sync_expect_call(call_expr: &JsCallExpression) -> bool {
Copy link
Member

Choose a reason for hiding this comment

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

Same here

false
}

fn has_async_modifier(expect_call: &JsCallExpression, final_call: &JsCallExpression) -> bool {
Copy link
Member

Choose a reason for hiding this comment

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

Ditto

Comment on lines +85 to +93
if object_text == "page"
|| object_text == "frame"
|| object_text.ends_with("Page")
|| object_text.ends_with("Frame")
{
Some(object_text)
} else {
None
}
Copy link
Member

Choose a reason for hiding this comment

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

Many rules have this code. Please consider a share function

@dyc3
Copy link
Contributor

dyc3 commented Dec 16, 2025

Hey @voxxit, still interested in this PR?

@voxxit
Copy link
Author

voxxit commented Dec 16, 2025

Hey @voxxit, still interested in this PR?

Yep! But earliest I can get back to it is this Friday PM. Apologies - it's a busy week.

@heyrmi
Copy link

heyrmi commented Jan 29, 2026

@voxxit if you are occupied with something can I contribute here?

@voxxit
Copy link
Author

voxxit commented Jan 29, 2026

@voxxit if you are occupied with something can I contribute here?

Yeah go for it. I have some other priorities that I'm working on right now that are taking up almost all of my time so I won't have much time for a couple of weeks or even a month or so.

@abossenbroek
Copy link
Contributor

hi @heyrmi and @voxxit do you need any help to finish this work? Is it OK on my side to use LLM assisted coding if supervised by me?

@abossenbroek
Copy link
Contributor

Hi @voxxit! 👋

Thank you for your foundational work on this PR - it's a great start to bringing Playwright linting to Biome!

I noticed this PR had been open for a while without recent activity, and since I need these Playwright rules for my own projects, I decided to pick it up and move it forward. I hope that's okay - I completely understand if you've been busy with other priorities!

I've taken your implementation and created a new PR (#8960) that:

  1. Rebased on latest main - Resolved merge conflicts and updated to current Biome conventions
  2. Addressed all CodeRabbit review comments - Fixed critical bugs, added missing methods, and resolved doc conflicts
  3. Added clippy fix - Resolved large_stack_arrays warning in generated test code

Key fixes made:

  • 🔴 Fixed {{method}} interpolation bug in noPlaywrightElementHandle and noPlaywrightEval (was outputting literal {method})
  • 🟠 Added missing clear method to noPlaywrightForceOption
  • 🟡 Fixed parenthesized await detection in noPlaywrightMissingAwait
  • 🟡 Updated doc example in noPlaywrightPagePause to avoid conflict with noPlaywrightWaitForSelector

I've also created issue #8961 to track the overall eslint-plugin-playwright migration progress (12/57 rules now implemented).

All tests pass and lint checks are green. Once #8960 is merged, this PR can be closed. Thanks again for getting this started! 🙏

cc @ematipico @dyc3

@ematipico
Copy link
Member

I believe we can close this. Thank you @voxxit for starting this, and thank you @abossenbroek for bringing this to the finishing line

@ematipico ematipico closed this Feb 18, 2026
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-Formatter Area: formatter 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.

6 participants