LearnNewsExamplesServices
Frontmatter
id18966
titleA runtime cellEditing toggle has no witness for what the user sees
stateClosed
labels
bugaitestinggrid
assigneesneo-opus-ada
createdAtSep 19, 2026, 3:39 PM
updatedAtSep 19, 2026, 5:47 PM
githubUrlhttps://github.com/neomjs/neo/issues/18966
authorneo-opus-vega
commentsCount0
parentIssue18762
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 19, 2026, 5:47 PM

A runtime cellEditing toggle has no witness for what the user sees

neo-opus-vega
neo-opus-vega commented on Sep 19, 2026, 3:39 PM

Context

Leaf of #18762, split out of #18964 by its own author before that PR opened, on the #18863 → #18927 pattern: the ACs a PR cannot honestly tick move to a new leaf rather than being softened to fit it.

#18964 fixes grid.Container#cellEditing so the config governs its plugin in both directions — off disables, on reuses the same instance. Its unit arms pin the plugin lifecycle and the refusal (startEdit returns false while disabled). They cannot pin what a user sees, and that is this ticket.

The Problem

Two of #18964's acceptance criteria describe a live edit session, not a plugin's lifecycle:

  • a double-click after an off→on cycle must open exactly one editor;
  • an edit that is already open when cellEditing goes false must be cancelled, not committed — draft discarded, record unchanged.

Both need an editor that actually mounts. In the single-thread unit harness it cannot: creating a session drives form.field.Text into registerPreventDefaultKeys / unregisterPreventDefaultKeys, which are Neo.main.DomEvents calls with no main thread behind them. Measured — the arm's own assertions passed and teardown then threw TypeError: Cannot read properties of undefined (reading 'registerPreventDefaultKeys') from plugin.destroy.

That is a statement about the tier, not about the product. The behaviour is reachable; only the unit harness cannot reach it.

The Architectural Reality

test/playwright/component/apps/grid-cell-editing is the fixture the epic's other leaves drive — real editors mount there, and #18959/#18960 have just made its editor-drag arm deterministic. src/grid/Container.mjs#afterSetCellEditing (post-#18964) routes cellEditing to plugin.disabled, and grid.plugin.CellEditing#afterSetDisabled (:65-71) cancels an open edit with reason 'disabled'. So the production path for AC-2 below already exists and is unwitnessed.

The fixture's grid is constructed with editing already on, so the arm has to reach cellEditing at runtime — the same surface #18964 fixed.

The Fix

Two arms on the existing grid-cell-editing fixture. No source change is expected; if one turns out to be needed, that is a finding for this ticket rather than a reason to widen #18964.

Acceptance Criteria

  • AC-1 On the fixture, toggling cellEditing off and on again and then double-clicking an editable cell opens exactly one editor — asserted by counting mounted editors in the cell, not by counting plugin instances (#18964's unit arms already own the instance count).
  • AC-2 An edit open when cellEditing goes false is cancelled: the editor leaves, the record keeps its pre-edit value, and a cellEditCancel with reason 'disabled' is observed. A commit would write the draft, so the arm must distinguish the two rather than assert only that the editor is gone.
  • AC-3 Both arms are mutation-proven against #18964's fix: reverting afterSetCellEditing's off branch reddens AC-2; reverting its reuse branch reddens AC-1.

Out of Scope

afterSetCellEditing itself — delivered by #18964. The plugin's disabled semantics, which are existing behaviour this ticket only witnesses. Anything about a first runtime enable, which is @neo-fable's still-open defect-note of 2026-09-19 13:22Z.

Avoided Traps

  • Do not re-assert the plugin instance count here. #18964's unit arms own it; this ticket exists precisely because a count is not what a user feels.
  • Do not assert "no editor" for AC-2 and stop. A commit also removes the editor. The record's value is what separates cancel from commit.
  • Do not move these arms back to the unit tier. The registerPreventDefaultKeys failure above is why they are here; a unit arm would have to stub the main thread and would then be testing the stub.

Related

  • #18964 — the fix these arms witness; this ticket carries the two ACs its PR could not honestly tick.
  • #18762 — parent epic; its "one editor at a time" predicate is what AC-1 protects.
  • #18959 / #18960 — recent determinism work on the same fixture.

Handoff Retrieval Hints

Retrieval Hint: "grid cellEditing runtime toggle fixture arms one editor cancel on disable" · fixture test/playwright/component/apps/grid-cell-editing · source anchors src/grid/Container.mjs#afterSetCellEditing, src/grid/plugin/CellEditing.mjs:65-71.