ReviewOS

stacks/very-happy-dom

No second matcher batch: toHaveRole, toHaveAccessibleName, toHaveCSS, toHaveId, toHaveJSProperty

#1618
Closed glennmichael123 opened this 8 days ago ·
8 days ago

Problem

#1603 shipped 17 matchers. Eight more from Playwright's set have no equivalent, and most of them already have their answer sitting on Locator:

Object.keys(matchers)   // no toHaveCSS, toHaveId, toHaveJSProperty, toHaveRole,
                        // toHaveAccessibleName, toHaveAccessibleDescription,
                        // toHaveValues, toBeInViewport
matcherwhat it needsalready present?
toHaveRolethe computed rolelocator.ariaRole()
toHaveAccessibleNamethe accessible namelocator.accessibleName()
toHaveAccessibleDescriptionaria-describedby resolutionpartly, in src/aria/roles.ts
toHaveCSSa resolved propertygetComputedStyle, cascade-aware since #1597
toHaveIdthe id attributetrivial
toHaveJSPropertya property off the elementtrivial
toHaveValuesa multi-select's selected valuesneeds selectOption (#1608) to be worth having
toBeInViewporta bounding box against the viewportnot answerable — no layout

Why it matters

toHaveRole and toHaveAccessibleName are the two that pull their weight. They assert the thing getByRole queries on, which is what makes a failing role query diagnosable: getByRole('button', { name: 'Save' }) finding nothing tells you nothing about why, and the usual next question — "what role does this element actually have, and what name does it compute?" — currently has to be asked through locator.ariaRole() and a plain expect, losing the retry.

toHaveCSS is the one people reach for after #1597 and #1600 made the cascade real. Asserting a colour or a display through getComputedStyle plus a bare expect samples once, so it has the flakiness #1603 exists to remove.

toHaveId and toHaveJSProperty are conveniences over toHaveAttribute and a read; cheap, and their absence is noticed because Playwright examples use them.

Proposed scope

  • toHaveRole(role), toHaveAccessibleName(name, { exact }), toHaveAccessibleDescription(description, { exact }) — over the existing Locator readers, with the same string-or-RegExp comparison the other text matchers use, so "contains" keeps one definition.
  • toHaveCSS(name, value) — resolve through getComputedStyle. Values must be compared as the engine reports them, so a test asserting 'red' against a computed 'rgb(255, 0, 0)' has to fail rather than be normalised into passing; say so in the failure message, since that is the mistake everyone makes first.
  • toHaveId(id), toHaveJSProperty(name, value).
  • toHaveValues(values) — deferred until selectOption (#1608) lands, since there is no way to set a multi-select's state until then.
  • toBeInViewport should not be added. There is no layout, so the box has no position — x and y are always the origin, asserted in test/box-metrics-cascade.test.ts. It could only ever answer "yes", which is the silent-pass failure #1602 was about. Better absent.

Acceptance

  • Each new matcher retries, proven by one test per matcher where the value only becomes correct on a later tick.
  • .not waits for the opposite, as the existing ones do.
  • toHaveCSS('color', 'red') fails against a computed rgb(255, 0, 0), and the message shows both, so the mismatch is obvious rather than mysterious.
  • toHaveRole agrees with getByRole on the same element — asserted together, so a query and an assertion about the same role cannot disagree.
  • toBeInViewport is absent, and a test records why, so it is not added later by someone working from Playwright's list.

Follows #1603.

Sign in to comment on this issue.