Context
Surfaced during #9491 / PR #12792 whitebox-e2e (operator-greenlit: "if NL does not give you the tools you need, create new tickets"). To verify the cross-toolbar column drag-and-drop re-homes the full column, I needed to simulate a column drag in e2e. I could not: the sanctioned nlApp.simulateEvent path cannot dispatch pointer events, and Neo's drag is pointer-based — so no pointer-drag (SortZone column DnD, draggable dashboards, sort zones) can currently be driven from a whitebox-e2e test.
The Problem
Neo's drag pipeline = real pointer input → Neo.main.DomEvents → synthesized drag:start / drag:move / drag:end custom events → DragDrop addon + the worker SortZone. The whitebox-e2e simulateEvent fixture dispatches through Neo.main.addon.EventSimulator.dispatch(), which constructs events by type but has no PointerEvent branch — pointerdown / pointermove / pointerup fall to the generic Event ctor (no pointer semantics, no clientX). So drag:start never arms.
Empirically (this session): three approaches — Playwright page.mouse (CDP coords) on the unstyled grid, simulateEvent mouse events, and page.mouse on the styled grid — all dispatched but none armed drag:start (mid-drag screenshot showed no drag proxy; column.locked stayed null). The column re-home itself is sound — verified via setProperties({locked}), which exercises the same onColumnLockChange redistribution path onDragEnd triggers. Only the pointer gesture is un-simulatable.
The Architectural Reality
src/main/addon/EventSimulator.mjs — dispatch() switch (type) covers mouse / touch / drag(HTML5 DragEvent) / key / input / wheel; no case 'pointerdown' | 'pointermove' | 'pointerup' | 'pointercancel' → default: ctor = Event.
src/main/addon/DragDrop.mjs:186-188 — document.addEventListener('drag:start' | 'drag:move' | 'drag:end', …, true); these drag:* events are synthesized upstream from real pointer input.
learn/guides/testing/WhiteboxE2E.md (~line 126) explicitly claims simulateEvent is the path for "Grid locked columns" drag — a doc overclaim: it can dispatch a click, not a pointer-drag.
The Fix
Add case 'pointerdown' | 'pointermove' | 'pointerup' | 'pointercancel' to EventSimulator.dispatch() using new PointerEvent(type, options) (options carrying pointerId, clientX, clientY, buttons, bubbles). Verify a dispatched pointer sequence arms Neo's drag:* pipeline end-to-end. Correct the WhiteboxE2E guide to match real capability + add a worked DnD-drag example.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
simulate_event event type (nlApp.simulateEvent → EventSimulator.dispatch) |
src/main/addon/EventSimulator.mjs |
accept pointerdown/pointermove/pointerup/pointercancel → new PointerEvent(type, options) |
today: silently builds a generic Event (no pointer semantics) |
learn/guides/testing/WhiteboxE2E.md |
EventSimulator switch has no pointer case; #9491 drag-sim failures (no drag:start) |
Decision Record impact
none — no ADR governs EventSimulator.
Acceptance Criteria
Out of Scope
- Neo's drag pipeline itself (
DomEvents / DragDrop) — only the simulation tooling.
- The column-DnD feature logic (#9491 / PR #12792).
Related
- #9491 / PR #12792 — the cross-toolbar DnD e2e this unblocks (scaffold
test/playwright/e2e/GridColumnCrossBodyDnD.spec.mjs is ready to consume the fix).
learn/guides/testing/WhiteboxE2E.md — doc to correct.
Origin Session ID: 31325e4f-0fdf-4627-96ed-7983dd1b1002
Retrieval Hint: "EventSimulator PointerEvent whitebox-e2e SortZone drag arming" / src/main/addon/EventSimulator.mjs dispatch() switch.
Authored by Claude Opus 4.8 (Claude Code), @neo-opus-ada.
Context
Surfaced during #9491 / PR #12792 whitebox-e2e (operator-greenlit: "if NL does not give you the tools you need, create new tickets"). To verify the cross-toolbar column drag-and-drop re-homes the full column, I needed to simulate a column drag in e2e. I could not: the sanctioned
nlApp.simulateEventpath cannot dispatch pointer events, and Neo's drag is pointer-based — so no pointer-drag (SortZone column DnD, draggable dashboards, sort zones) can currently be driven from a whitebox-e2e test.The Problem
Neo's drag pipeline = real pointer input →
Neo.main.DomEvents→ synthesizeddrag:start/drag:move/drag:endcustom events →DragDropaddon + the worker SortZone. The whitebox-e2esimulateEventfixture dispatches throughNeo.main.addon.EventSimulator.dispatch(), which constructs events bytypebut has noPointerEventbranch —pointerdown/pointermove/pointerupfall to the genericEventctor (no pointer semantics, noclientX). Sodrag:startnever arms.Empirically (this session): three approaches — Playwright
page.mouse(CDP coords) on the unstyled grid,simulateEventmouse events, andpage.mouseon the styled grid — all dispatched but none armeddrag:start(mid-drag screenshot showed no drag proxy;column.lockedstayednull). The column re-home itself is sound — verified viasetProperties({locked}), which exercises the sameonColumnLockChangeredistribution pathonDragEndtriggers. Only the pointer gesture is un-simulatable.The Architectural Reality
src/main/addon/EventSimulator.mjs—dispatch()switch (type)covers mouse / touch / drag(HTML5DragEvent) / key / input / wheel; nocase 'pointerdown' | 'pointermove' | 'pointerup' | 'pointercancel'→default: ctor = Event.src/main/addon/DragDrop.mjs:186-188—document.addEventListener('drag:start' | 'drag:move' | 'drag:end', …, true); thesedrag:*events are synthesized upstream from real pointer input.learn/guides/testing/WhiteboxE2E.md(~line 126) explicitly claimssimulateEventis the path for "Grid locked columns" drag — a doc overclaim: it can dispatch aclick, not a pointer-drag.The Fix
Add
case 'pointerdown' | 'pointermove' | 'pointerup' | 'pointercancel'toEventSimulator.dispatch()usingnew PointerEvent(type, options)(options carryingpointerId,clientX,clientY,buttons,bubbles). Verify a dispatched pointer sequence arms Neo'sdrag:*pipeline end-to-end. Correct the WhiteboxE2E guide to match real capability + add a worked DnD-drag example.Contract Ledger Matrix
simulate_eventeventtype(nlApp.simulateEvent→EventSimulator.dispatch)src/main/addon/EventSimulator.mjspointerdown/pointermove/pointerup/pointercancel→new PointerEvent(type, options)Event(no pointer semantics)learn/guides/testing/WhiteboxE2E.mddrag:start)Decision Record impact
none— no ADR governs EventSimulator.Acceptance Criteria
EventSimulator.dispatch()supportspointerdown/pointermove/pointerup/pointercancelviaPointerEventwith positional + pointer options.drag:start(drag proxy appears) and drives a SortZone column re-home (column.lockedchanges region). Risk/falsifier: if syntheticPointerEvents still do not armdrag:*, the gap is deeper inDomEvents— this AC surfaces that; escalate with the finding rather than declaring done.test/playwright/e2e/GridColumnCrossBodyDnD.spec.mjscompletes (full column header + cells re-homes across toolbars).learn/guides/testing/WhiteboxE2E.mdupdated: real pointer-drag capability + worked example; the unbacked "Grid locked columns" drag claim removed or backed.Out of Scope
DomEvents/DragDrop) — only the simulation tooling.Related
test/playwright/e2e/GridColumnCrossBodyDnD.spec.mjsis ready to consume the fix).learn/guides/testing/WhiteboxE2E.md— doc to correct.Origin Session ID: 31325e4f-0fdf-4627-96ed-7983dd1b1002
Retrieval Hint: "EventSimulator PointerEvent whitebox-e2e SortZone drag arming" /
src/main/addon/EventSimulator.mjsdispatch()switch.Authored by Claude Opus 4.8 (Claude Code), @neo-opus-ada.