Implant catalog
Browse all implants
Free Playwright practice. Write a catalog test, then run it again.
Ten implant families across five categories. Prices include the surgery window, the pre-op screening, and the implant license. They do not include the clash you will have with your previous installer, which is on you.
What breaks your tests
Every mechanism on this page is active. Click one to learn what it teaches and how to work around it.
These are live on this page. Each one teaches a specific lesson about brittle test patterns. Point Playwright at any of them.
Tier 1hostile
DOM Selector Rotation
Don't hardcode brittle locators; use semantic, resilient patterns.
Breaks when: CSS class selectors like .add-to-cart-btn fail because the class is generated procedurally and rotates every page load.
Try: Use getByRole, getByLabel, or text-based selectors — they survive class rotation.
1-1-dom-selector-rotationTier 1hostile
Data Mutation on Load
Don't assert on dynamic values; verify conditions, not exact states.
Breaks when: Exact-match assertions on stock status or price pass ~70% of the time and fail ~30% — flaky by design.
Try: Accept both valid states (In stock / Out of stock) or check ranges instead of exact values.
1-2-data-mutationTier 2hostile
Decoy / Duplicate Elements
Be specific; verify before interacting; don't trust first match.
Breaks when: click() on nth(0) hits a decoy element that looks identical but is non-functional.
Try: Use filter({ hasText: ... }) or getByRole with accessible name to target the real element.
1-3-decoy-duplicate-elementsTier 2subtle
Structural Drift Between Pages
Don't assume patterns transfer; each page needs fresh recon.
Breaks when: Selectors that work on /catalog fail on /checkout because class names, nesting, or element order changed.
Try: Use semantic locators that don't depend on DOM structure or versioned class names.
1-4-structural-driftTier 3subtle
Email Scenarios
Async delivery and expiry timers break tests that assume instant results.
Breaks when: Emails arrive after a delay, links expire after a few seconds, and some messages route to spam. Tests that check immediately or use stale links fail.
Try: Wait for delivery indicators, poll until expected state appears, and regenerate links before each test pass.
1-5-email-scenariosTier 2hostile
Subscription Trap
A button's label and behavior change after the first click. Naive tests click and check once.
Breaks when: The button transitions through confirm, processing, and done phases. A test that clicks and asserts the original label fails on the second click.
Try: Use a state machine in your test: assert the label changes through each expected phase instead of assuming it stays the same.
1-6-subscription-trapTier 3hostile
File Upload
Upload progress is async and chunked. Tests that expect instant completion miss intermediate states.
Breaks when: The upload emits start, progress (0-100%), processing, and complete events. A test that waits only for 'done' misses timing-sensitive progress assertions.
Try: Use Playwright's waitForEvent or poll for the complete event instead of relying on static timeouts.
1-7-file-uploadTier 3subtle
Webhook Callback
Async callbacks with retry logic break tests that fire-and-forget.
Breaks when: Webhooks transition pending → delivered/failed/retrying. A test that doesn't wait for the final delivery state passes when the webhook is still pending and flaky when it retries.
Try: Poll the delivery log until status reaches delivered or the retry budget is exhausted.
1-8-webhook-callbackTier 2environmental
Rate Limit
Rate-limited endpoints reject rapid requests. Tests that hammer the same route without spacing fail intermittently.
Breaks when: After N requests in a sliding window, subsequent requests return 429. A test that fires concurrent requests without coordination hits the limit.
Try: Stagger requests in your test, or use the reset header to wait for the window to clear before continuing.
1-9-rate-limitTier 2environmental
Timeout / Slow Load
Async content with variable delay breaks tests that assume fixed timings.
Breaks when: Content appears after a configurable delay (or random delay). A test with a hardcoded wait may timeout before content loads, or pass on the first run and fail on CI.
Try: Use Playwright's waitForSelector with a generous timeout instead of fixed sleep(). Enable auto-waiting assertions.
1-10-timeoutTier 3hostile
State Desync
Client state may diverge from server truth. Checking only the rendered DOM misses the real data.
Breaks when: Items show a stale price, wrong stock badge, or outdated count on the client while the server holds correct data. A test that checks only the displayed value catches the desync intermittently.
Try: Cross-reference visible values against hidden data-testid attributes that carry the server ground truth.
1-11-state-desyncTier 2environmental
Permission Error
Role-based access gates break tests that assume all users are admins.
Breaks when: A test using admin-panel actions gets a permission denied response when the current role is viewer or operator.
Try: Set the role explicitly before testing restricted actions, or test across all roles to confirm the access control works.
1-12-permission-errorTier 2hostile
Modal Overlay
Modals that appear after a delay or can't be dismissed break naive interaction tests.
Breaks when: A modal appears after N ms. A test that clicks the trigger and immediately looks for modal content finds nothing because the overlay hasn't rendered yet.
Try: Wait for the modal to be visible (waitForSelector or locator.waitFor) before interacting with its contents.
1-13-modal-overlayTier 2hostile
Focus Trap
Trapped focus loops inside a dialog. Tests that tab-out or expect focus to leave the dialog fail.
Breaks when: Tabbing past the last focusable element inside a dialog wraps back to the first. A test that presses Tab and expects focus on a page-level element never escapes.
Try: Test that focus cycles correctly inside the trap. Use locator.press('Tab') and assert the active element stays within the dialog.
1-14-focus-trapTier 3subtle
Scroll Stealth
Lazy-loaded content below the fold doesn't exist until the user scrolls.
Breaks when: Sections below the viewport are empty divs. A test that scrolls down and immediately checks for content sees nothing because the reveal is tied to scroll position.
Try: Scroll the element into view then wait for its content to render. Use scrollIntoViewIfNeeded or Playwright's locator.scrollIntoViewIfNeeded.
1-15-scroll-stealthTier 2subtle
Tab Visibility
Background tab behavior differs from foreground. Tests that assume the tab stays visible miss hidden-tab branches.
Breaks when: When the tab is hidden, polling pauses or throttles. A test that switches tabs (or the system does) and expects continuous updates sees stale data.
Try: Override document.hidden via page.addInitScript for tests that need to simulate background behavior, or test explicitly in both visibility states.
1-16-tab-visibilityTier 3environmental
Network Jitter
Variable network latency causes test race conditions that are hard to reproduce.
Breaks when: A request-response cycle takes between minDelayMs and maxDelayMs. A test with a fixed timeout succeeds on fast networks and fails on slower CI runners.
Try: Use Playwright's page.route to simulate or record latency, or rely on auto-waiting assertions that give the full timeout budget.
1-17-network-jitterTier 3environmental
Clock Skew
Client and server time differences break tests that compare timestamps locally.
Breaks when: Client time is offset from server time by a configurable amount. A test that compares new Date() against a server-returned timestamp sees a mismatch.
Try: Use the server's reported time as the source of truth. Avoid Date.now() in assertions that cross the client-server boundary.
1-18-clock-skewTier 4subtle
Memory Leak
Accumulated DOM nodes degrade performance and eventually crash the page under test.
Breaks when: A growing list of LeakItem elements is appended every tick. A test that navigates away and back sees accumulated nodes from the previous visit.
Try: Clear or reset the list between test cases. Use page.evaluate to measure DOM node count before and after each scenario.
1-19-memory-leakEvery page load re-rolls bait IDs on each card. Hardcoding an id in your test guarantees a flaky failure. Use semantic locators.
Ripper Gym. Hostile Playwright practice. The shop is Chrome Clinic.