Add eslint-plugin-playwright enforcement with CI check - #26494
Conversation
Add eslint-plugin-playwright to catch common Playwright anti-patterns automatically. 13 rules configured in two tiers: - Error (blocks CI): no-networkidle, no-page-pause, no-focused-test - Warn (tracks debt): missing-playwright-await, no-wait-for-timeout, no-force-option, no-element-handle, no-eval, no-skipped-test, prefer-web-first-assertions, no-useless-await, no-wait-for-selector, valid-expect Changes: - Install eslint-plugin-playwright, configure rules in eslint.config.mjs - Add yarn lint:playwright script - Repurpose ui-checkstyle.yml workflow to run Playwright lint on PRs - Fix last networkidle usage in ClassificationConditionalRendering - Remove stale eslint-disable comments for undefined rules - Update PLAYWRIGHT_DEVELOPER_HANDBOOK with ESLint Enforcement section - Update playwright, writing-playwright-tests, and playwright-validation skills to reference lint check Current: 0 errors, 1657 warnings (CI passes cleanly) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…quest_target Switch from pull_request_target to pull_request since this workflow only runs yarn lint:playwright — no secrets or write permissions needed. This eliminates the untrusted code execution risk flagged by CodeQL. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🟡 Playwright Results — all passed (14 flaky)✅ 3330 passed · ❌ 0 failed · 🟡 14 flaky · ⏭️ 183 skipped
🟡 14 flaky test(s) (passed on retry)
How to debug locally# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip # view trace |
Resolve every playwright lint rule violation to enforce test quality:
- no-wait-for-selector (1290): replace page.waitForSelector() with locator.waitFor()
- no-wait-for-timeout (130): replace hardcoded waits with event-driven alternatives
- prefer-web-first-assertions (61): use toHaveText/toBeVisible/toHaveValue
- no-force-option (51): remove { force: true } bypassing actionability checks
- missing-playwright-await (40): add await to fire-and-forget assertions
- no-skipped-test (28): acknowledge skipped tests with eslint-disable reasons
- no-eval (11): replace page.$eval with locator APIs
- no-element-handle (10): replace page.$() with page.locator()
- no-useless-await (8): remove await from synchronous operations
- valid-expect (4): add matchers to bare expect() calls
- no-networkidle (2): replace networkidle with domcontentloaded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove Install Antlr4 CLI step from playwright-lint job (not needed for ESLint, saves 10-30s and avoids external network dependency) - Add --frozen-lockfile to yarn install for reproducible CI builds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
yarn install triggers build-check → js-antlr which needs antlr4 CLI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove `(await tableRows.count()) >= 0` which is always true since count() never returns negative. The assertion now properly validates that either the table or empty state is visible after searching. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The no-wait-for-selector conversion (waitForSelector → locator.waitFor)
introduced strict mode violations. Playwright's Locator API throws when
waitFor() matches multiple elements, unlike the old waitForSelector API.
Key patterns fixed:
- getByTestId('loader').waitFor() — multiple loaders on page
- getByTestId('select-owner-tabs').getByTestId('loader') — tab loaders
- locator('.ant-skeleton-active/content').waitFor() — multiple skeletons
- locator('table/thead th').waitFor() — multiple tables
- getByTestId('side-panel-classification') — multiple panels
- locator('.ant-select-dropdown:visible') — multiple dropdowns
- locator('.ant-popover').waitFor() — multiple popovers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@copilot address the security issues in this file .github/workflows/ui-checkstyle.yml |
) * Initial plan * Fix security issues in ui-checkstyle.yml: use pull_request_target with label gating Co-authored-by: karanh37 <33024356+karanh37@users.noreply.github.com> * Fix TOCTOU security and remove redundant conditions in ui-checkstyle.yml Co-authored-by: karanh37 <33024356+karanh37@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: karanh37 <33024356+karanh37@users.noreply.github.com>
CI AnalysisThe previous CI failure was caused by commit This was subsequently fixed in commit Current status: The ANTLR fix is already in place — no further action needed for this failure. |
Code Review
|
| Auto-apply | Compact |
|
|
Was this helpful? React with 👍 / 👎 | Gitar
|



Summary
eslint-plugin-playwrightto automatically catch common Playwright anti-patterns that cause flaky testsui-checkstyle.ymlworkflow to run Playwright lint on PRs touchingplaywright/filesRule Configuration
Blocking (error) — prevents introducing the worst anti-patterns:
no-networkidlewaitForLoadState('networkidle')— unreliable with websockets/pollingno-page-pausepage.pause()— debug statement left in codeno-focused-testtest.only()/describe.only()— accidentally committed focusAspirational (warn) — tracks tech debt to fix over time:
missing-playwright-await,no-wait-for-timeout,no-force-option,no-element-handle,no-eval,no-skipped-test,prefer-web-first-assertions,no-useless-await,no-wait-for-selector,valid-expectCurrent state: 0 errors, 1657 warnings (CI passes cleanly)
Cleanup
networkidleusage inClassificationConditionalRendering.spec.tseslint-disablecomments referencing undefined rules (jest/no-export,react-hooks/rules-of-hooks)Test plan
yarn lint:playwrightexits with 0 errorsyarn lint(forsrc/) is unaffectedplaywright/filesnetworkidle,page.pause(), ortest.only()to any Playwright file fails the check🤖 Generated with Claude Code