Trap room

Every control here is broken on purpose. This is the one page in the app where bad markup and hostile behaviour are the point. Nothing on this page should ever be “fixed”.

1. Looks clickable, is not

Approve payout

The first is a bare <div> with no role, no tabindex and no handler. It is styled identically to a button. getByRole('button') will not match it; a CSS class selector will, and the click will silently do nothing.

Nothing approved.

2. Toast that disappears

The 1-second toast is gone before a slow assertion arrives. Auto-retrying expect with a short timeout catches it; textContent() after an await usually does not.

3. Disabled until valid

Must match INV-YYYY-NNNN exactly.
disabled

Validation runs on input, so fill() enables it but setting value through evaluate does not.

4. Responds to tap, not to click

Bound to touchstart only. A mouse click() does nothing. Needs page.tap() with hasTouch: true in the context.

Not confirmed.

5. Covered by an invisible overlay

A transparent pseudo-element sits on top. Playwright's actionability check reports that another element intercepts the pointer event, and the click times out. force: true does not help: it skips the checks but still clicks at those coordinates, where the overlay is still the hit target. Only dispatchEvent('click') reaches the handler. That is the lesson — force is not a universal unblocker.

Not released.

6. pointer-events: none

Enabled, visible, in the accessibility tree, and unclickable. Even force: true will not fire its handler; only dispatchEvent will.

No label printed.

7. Moving target

Runs a 2.6s animation, then settles.

Not accepted.

8. List that re-renders under you

The whole <ul> is replaced every 1200ms, so any handle you hold goes stale. Locators re-resolve and survive; elementHandle does not.

stopped

9. Duplicated test id

Three different controls share data-testid="save". A bare getByTestId('save') resolves to three elements and throws in strict mode.

Nothing saved.

10. Text split across elements

Total due: ₹12,890

The amount is split across four sibling <span> elements, so no leaf element contains ₹12,890. A span-scoped locator such as locator('[data-testid="split-text"] span', { hasText: '₹12,890' }) resolves to nothing. Playwright's getByText walks up and matches the containing paragraph (and its ancestors), so it resolves to more than one element and is not strict-mode safe on its own.

Status: Awaiting pickup

The line above has collapsed whitespace in the DOM but not in the source. Playwright's text matching normalises whitespace; a raw textContent comparison does not.

11. Revealed only on hover

Consignment C-4471

The button is hidden until the container is hovered.

Not cancelled.

12. Input that refuses fill()

Read-only. It only accepts characters typed through real key events, which the page unlocks on focus.

fill() throws on a readonly input. focus() then pressSequentially() works, because focus removes the attribute.

No code entered.

13. Far below the fold

1200px of filler, then the control. Playwright auto-scrolls; a raw document.querySelector().click() in a manual script would not.

Month open.

14. Attaches late

Appears after 3 seconds, above the default 5s action timeout but well past a naive waitForTimeout(500).