Context
Carved out of #18439 at review. That ticket covers four intermittent reds in the e2e tier across two specs; this is the one that is diagnosed and repairable now. #18439 stays open on grid/ThumbDragPause:49, which is a different spec with a different assertion and no established mechanism — so it cannot be the close target for a PR that does not touch it.
Third time tonight I wrote "the parent stays open" in a body and put Resolves <parent> at its top. @neo-gpt caught it here, as they did on #18422, and @neo-gpt-emmy on #17853.
The Problem
grid/TreeBigData's selection arm is three of #18439's four observed reds, at roughly 20% of hosted attempts. It binds rows by DOM position across a re-projection:
const childRow = firstFolderRow.locator('+ .neo-grid-row');
await childRow.locator('.neo-grid-cell').nth(2).click();
await expect(childRow).toHaveClass(/neo-selected/); The failed attempt's call log caught one element resolving to two records inside that single assertion:
2 × id="neo-grid-body-1__row-1" data-record-id="neo-record-2" level=1
12 × id="neo-grid-body-1__row-1" data-record-id="neo-record-79" level=2
Same element, same data-row-id — the same pooled Row, recycled onto a different record. src/grid/Body.mjs documents the strategy: rows are pooled and "recycled in place", their record and rowIndex updated as the projection moves.
neo-selected is applied to the row rendering the selected record. The locator named a position. The expand re-projects between the click and the assertion, so the arm selects one record and then waits for a class on whatever later occupies that slot.
No timeout can repair this — nothing is late. The class will never appear on the element being watched.
The Architectural Reality
The repository already has the correct idiom, including in this same file:
TreeBigData.spec.mjs:115 — captures data-record-id, rebinds, and asserts on identity
BigDataMultiBodyNL.spec.mjs:41-43 — binds rows by [data-record-id="..."]
GridViewFocus.spec.mjs:87,107 — captures then rebinds
The selection arm is the one that never got it. This is not a new pattern; it is an unevenly applied one.
The Fix
Pin every asserted row by data-record-id before anything else happens. Position still finds the revealed child — "the row below the folder" is a positional fact — and identity carries every step after that.
Acceptance Criteria
Out of Scope
grid/ThumbDragPause:49 — #18439's remaining arm, undiagnosed.
- The positional bindings at
TreeBigData:30/38/45. Those assert a change at a position, which a recycle also produces, so they are less exposed. Deliberately left rather than swept.
#14853 / #14854, reconciled on #18439.
Related
#18439 — the parent, staying open on ThumbDragPause.
#18408 — the tier that surfaced all four.
#18215 — the precedent: a poll that cannot see the state it waits for.
Origin Session ID: 95f9ff6e-1ba5-43b6-860a-208fbf7f4442
Context
Carved out of
#18439at review. That ticket covers four intermittent reds in the e2e tier across two specs; this is the one that is diagnosed and repairable now.#18439stays open ongrid/ThumbDragPause:49, which is a different spec with a different assertion and no established mechanism — so it cannot be the close target for a PR that does not touch it.Third time tonight I wrote "the parent stays open" in a body and put
Resolves <parent>at its top. @neo-gpt caught it here, as they did on#18422, and @neo-gpt-emmy on#17853.The Problem
grid/TreeBigData's selection arm is three of#18439's four observed reds, at roughly 20% of hosted attempts. It binds rows by DOM position across a re-projection:const childRow = firstFolderRow.locator('+ .neo-grid-row'); // a POSITION await childRow.locator('.neo-grid-cell').nth(2).click(); await expect(childRow).toHaveClass(/neo-selected/); // re-resolves that positionThe failed attempt's call log caught one element resolving to two records inside that single assertion:
Same element, same
data-row-id— the same pooled Row, recycled onto a different record.src/grid/Body.mjsdocuments the strategy: rows are pooled and "recycled in place", theirrecordandrowIndexupdated as the projection moves.neo-selectedis applied to the row rendering the selected record. The locator named a position. The expand re-projects between the click and the assertion, so the arm selects one record and then waits for a class on whatever later occupies that slot.No timeout can repair this — nothing is late. The class will never appear on the element being watched.
The Architectural Reality
The repository already has the correct idiom, including in this same file:
TreeBigData.spec.mjs:115— capturesdata-record-id, rebinds, and asserts on identityBigDataMultiBodyNL.spec.mjs:41-43— binds rows by[data-record-id="..."]GridViewFocus.spec.mjs:87,107— captures then rebindsThe selection arm is the one that never got it. This is not a new pattern; it is an unevenly applied one.
The Fix
Pin every asserted row by
data-record-idbefore anything else happens. Position still finds the revealed child — "the row below the folder" is a positional fact — and identity carries every step after that.Acceptance Criteria
Out of Scope
grid/ThumbDragPause:49—#18439's remaining arm, undiagnosed.TreeBigData:30/38/45. Those assert a change at a position, which a recycle also produces, so they are less exposed. Deliberately left rather than swept.#14853/#14854, reconciled on#18439.Related
#18439— the parent, staying open onThumbDragPause.#18408— the tier that surfaced all four.#18215— the precedent: a poll that cannot see the state it waits for.Origin Session ID: 95f9ff6e-1ba5-43b6-860a-208fbf7f4442