mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
5.3 KiB
5.3 KiB
High-Quality, Low-Flake Playwright Testing Guide
This guide defines how we write and evolve Playwright tests in bitvid so they stay reliable while increasing coverage.
Goals
- Catch user-visible regressions early.
- Keep CI signal trustworthy (low flake rate).
- Improve runtime coverage with deterministic tests, not brittle ones.
Core Principles
- Test behavior, not implementation details.
- Prefer deterministic setup over live-network dependence.
- Wait for explicit app state, not arbitrary sleep.
- Use resilient selectors and scoped locators.
- Keep assertions strict on outcomes, flexible on timing and non-critical internals.
Determinism First
Use the existing harness and fixture stack:
tests/e2e/helpers/bitvidTestFixture.tswindow.__bitvidTest__APIs injs/testHarness.js
Prefer these patterns:
- Use seeded relay data instead of remote relays.
- Use programmatic login (
loginAs) instead of manual UI auth for most tests. - Use harness APIs to force edge conditions (
setSignerDecryptBehavior, relay overrides). - Open complex modals via deterministic controller/harness methods when available.
Why: nondeterministic setup is the top source of flaky tests.
Selector Strategy
- Prefer
data-testidselectors. - Scope locators to visible/active containers when duplicates can exist.
- Avoid broad selectors like
buttonunless nested under a stable root.
Example:
- Better:
#playerModal #closeModal:visible - Worse:
#closeModal(can resolve hidden duplicate elements)
Waiting Strategy (Do This, Not That)
Do:
- Wait for explicit state changes (
data-open="true", absence of.hidden, expected text/attribute). - Use
waitForFunctiononly with concrete conditions and clear timeouts. - Add fallback action paths for known intermittent UI event misses when justified.
Avoid:
waitForTimeout(...)as primary synchronization.- Assuming a click always opens a modal immediately.
- Chained interactions without state confirmation between steps.
Modal Testing Pattern
Recommended flow:
- Trigger modal open via deterministic helper (harness/controller), fallback to UI click.
- Wait for modal-open condition.
- Assert visible + semantic state (
role,aria-modal,data-open). - Interact with modal.
- Close modal and verify closed state.
If your tests repeatedly fail opening the same modal, add a harness helper for that modal instead of adding sleeps.
Accessibility Test Stability
Accessibility tests are especially prone to timing flake. For keyboard and modal tests:
- Ensure focus target exists and is visible before key presses.
- Prefer element-scoped key actions (
locator.press("Enter")) over global keyboard when possible. - Verify modal opened before asserting internals.
- For Escape-close tests, first confirm modal open state deterministically.
Coverage Without Brittle UI Tests
Not all coverage gains require full UI choreography.
Use runtime module tests in browser context for pure/mostly-pure modules:
page.evaluate(async () => import("/js/module.js"))- Execute exported APIs with deterministic inputs.
- Assert normalized outputs and edge cases.
This is effective for utilities, state transforms, and view-logic modules that are hard to reach through full flows.
Anti-Patterns to Avoid
- Duplicate clicks on close/open controls “just in case.”
- Assertions that only check element attachment but not visible state when visibility matters.
- Overly permissive assertions (
expect(x || true).toBe(true)) unless documented as intentional. - Tests that rely on console noise rather than asserting intended state transitions.
Flake Triage Workflow
When a test flakes:
- Reproduce with repeats:
npx playwright test <spec> --project=e2e --repeat-each=5
- Identify failure type:
- setup nondeterminism
- selector ambiguity
- missing state wait
- race in app init
- Fix in this order:
- add deterministic harness/controller hook
- tighten selectors
- add explicit state waits
- only then consider timeout tuning
- Re-run repeats and then run coverage lane.
Quality Gates for Sustainable Improvement
- Keep
npm run test:e2e:coveragegreen before raising coverage thresholds. - Track coverage trend, but do not trade reliability for raw percentage.
- Ratchet coverage targets gradually (example: 40% -> 45% -> 50%).
- Require flaky-test fixes to include deterministic synchronization changes, not just longer timeouts.
PR Checklist for E2E Changes
- Does this test use deterministic fixture/harness setup?
- Are selectors stable (
data-testidor scoped stable IDs)? - Are waits condition-based (not sleep-based)?
- Does the test fail for real regressions and pass reliably under repeats?
- If adding coverage tests, do they increase useful coverage in high-risk paths?
Useful Commands
- Single spec:
npx playwright test tests/e2e/<spec>.spec.ts --project=e2e
- Repeat to detect flake:
npx playwright test tests/e2e/<spec>.spec.ts --project=e2e --repeat-each=5
- Full deterministic E2E coverage run:
npm run test:e2e:coverage
- Regenerate report from existing raw artifacts:
npm run test:e2e:coverage:report
Related Docs
docs/testing/playwright-function-coverage.mddocs/testing/playwright-integration-recommendations-2026-02-22.mdAGENTS.md(testing infrastructure and harness references)