fix(web): visibility reads the box, the style, and closed details, SVG included
Merged
OK
1mo ago+238+238+012345678901234567890123456789-24-24-01234567890123456789
Repro: toBeHidden() failed on four elements Playwright's own toBeHidden calls hidden, and isVisible() said true for each.
<svg style="visibility:hidden">...</svg>
<div style="width:0;height:0;overflow:hidden"><button>Clipped</button></div>
<details><summary>More</summary><button>Inside closed</button></details>
<div style="display:contents"></div>
- The reader's
hiddenstate counted client rects. An SVG skipped computed style altogether, a zero-size box still had one rect, closed<details>content still laid out, anddisplay: contentsreturned visible before looking at anything under it. isHiddennow reads what Playwright'sisElementVisiblereads: computed style for anyElement(SVG included),visibilityother thanvisibleordisplay: noneis hidden, content of a closeddetailsoutside itssummaryis hidden, a box is visible only with width and height both above zero, and adisplay: contentselement is visible only when some child element or text node is.packages/web/src/in-page/read-semantics.tsstyleOfreads any element now, so the name computation's separatenameStyleOfis gone.- The same state feeds
toBeVisible, thevisiblequery filter's final check, and which nodes the agent's screen lists. TheisVisibleEmptyBoxfloor (MIN_BOX_SIDE) is untouched. - The tree walk prunes on its own test,
hidesSubtree(aria-hidden,display: none,visibility, closed details). A box with no size does not clip fixed, absolute, or overflowing descendants, so the walk goes on through it and only leaves that element unlisted. The testbed's/canvaspage is the case: every control isposition: fixed,<html>lays out to zero height, and pruning there read the page as one node. The document root is listed whatever its own box. - Three role-mapping fixtures were empty zero-height blocks that only the rect-count quirk kept in the tree; they get a 1px box since those tests are about roles.
flowchart TD
A[aria-hidden=true] -->|yes| H[hidden]
A -->|no| C{display: contents?}
C -->|yes| K{some child element or text visible?}
K -->|yes| V[visible]
K -->|no| H
C -->|no| S{display none or visibility not visible?}
S -->|yes| H
S -->|no| D{inside closed details, outside its summary?}
D -->|yes| H
D -->|no| B{width > 0 and height > 0?}
B -->|yes| V
B -->|no| H
packages/web/tests/integration/visibility.test.ts checks each case and its control against Playwright's isHidden() on the same element; the runner-level kitchen sink in packages/e2e/tests/integration/web-platform.test.ts asserts toBeHidden/toBeVisible/isVisible() through the CLI path. The aria-hidden ancestor case is #494 and stays as is.