Escape sometimes does not close a cell editor, in a full browser run #161
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Postulo/postulo#161
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
tests/e2e/test_editable_cells.py::test_escape_abandons_and_keeps_the_old_valuefailed oncein a full browser run and passed on the next full run and in isolation.
Escape did not close the editor within Playwright's retry window. One failure in three full
runs; nothing reproducible yet.
Why it is worth an issue rather than a shrug
A test that fails one run in three is worse than a test that fails every time. It trains
whoever sees it to re-run rather than to look, and the next genuine failure of this file
gets the same treatment. This project already learnt that lesson twice: the sign-in limiter
handing a
429to whichever test happened to run past the allowance, and the column-widthsaves tearing the live server down mid-request. Both had the same shape — passes alone,
fails in company — and both turned out to be real.
Worth being careful about
The helper does not wait for the editor to settle.
open_the_editorclicks the cell andreturns; the
fill()that follows auto-waits for the input, so the field exists, but nothingwaits for
app.jsto have finished with it. If Escape can arrive before the handler islistening, the keypress goes nowhere and the editor stays — which is exactly the observed
failure.
That is a hypothesis, not a diagnosis. Adding a wait would probably make the symptom go
away without anyone knowing whether the bug was in the test or in the page, and that
matters here: if a real person can press Escape a beat too early and have it ignored, the
fix belongs in
app.js, not in the test.So: reproduce first. Run the file in a loop, or the full browser suite repeatedly, and find
out whether Escape is being missed or the editor is being reopened. Only then decide which
side the fix belongs on.
Classification
Bug, 0.4.0. Not a regression from any one change — the cell editor arrived in #135 and this
was seen while working on #56.
Not reproduced in fifteen runs, and the stated hypothesis is wrong
Tried, because this issue asks for that before anything is changed:
tests/e2e/test_editable_cells.pyon its own, twelve times — twelve passes.So nothing is being fixed here. What the attempt did settle is what it is not.
The hypothesis in this issue cannot be the cause
The handler is not attached to the editor. It is a single
document-levelkeydownlistener, registered once when
app.jsis evaluated:It is therefore listening long before any cell can be opened, and there is no window in
which an editor exists but its Escape handler does not. The reason this issue gave for
wanting a wait in the test does not hold, which matters because that wait was the obvious
thing to reach for.
Two more things ruled out
The key reaches the right place. The test uses
field.press("Escape"), and Playwrightfocuses the element before pressing, so
event.targetis the input inside the editor andclosest("[data-cell-editor]")matches. It is not a focus race.The right element is clicked.
editor.querySelector("[hx-get]")could in principle findthe wrong control, but
partials/table/cell.htmlputs exactly onehx-getinside the form— the Cancel button. The Save button is a plain submit and the
hx-postis on the form.What is left, and why it points the other way
What remains between the keypress and the assertion is the htmx
GETthat Cancel fires.If the live server is slow — which is what a full run has that an isolated one does not —
the editor is still in the page when Playwright's retry window expires, and the failure
reads exactly as reported: "unexpected value 1".
If that is it, the fix is on the test side after all, but not the fix this issue
anticipated. Lengthening a timeout would hide a slow response; asserting on the outcome
that actually matters — the cell showing its old value again — waits for the same round trip
while saying what it is waiting for. Worth noting the two assertions after the failing one
already do exactly that.
Suggested next step
Fifteen clean runs is not proof of anything for a fault seen once in three, so this is not
evidence it has gone. Either:
diagnosis is now three steps further on than it was, which is most of the value; or
so the next failure says whether the request was slow or never went.
Re-running more is the option with the worst ratio of time to information.
One thing that changed under this
app.jsgained twodragenterlisteners in #174, in the board and widget drag blocks. Theyshare the file and nothing else with this — no
keydown, no cell editor — but the runs abovewere made after that landed, so they describe the current file rather than the one this issue
was opened against.
Fixed in
5e778aef6-- and the comment above was wrongThe earlier comment ruled out this issue's hypothesis, and the hypothesis was right in
substance. It was wrong only in which handler:
True, and beside the point. What that listener does is click the editor's Cancel
button, and that button's
hx-getis wired by htmx only when the swap that brought theeditor settles --
defaultSettleDelay, 20 ms after the swap, in the vendored htmx2.0.10.
htmx:afterSwapfires at once, andapp.jsputs the caret in the input right then.So for 20 ms the editor is visible, focused, and deaf to Escape: the keypress is heard, the
click is made, and nothing is listening to it. A hand is never that quick. A browser test
is, one run in three.
The fix
The
keydownhandler has htmx process the editor before clicking Cancel. Processing isidempotent -- htmx skips an element it has already initialised -- so this wires the button
when the settle has not yet, and does nothing when it has.
Reproduced, deterministically, before fixing
Fifteen clean re-runs settled nothing, as the comment above said. What did: a test that
widens the settle window to two seconds (
htmx.config.defaultSettleDelaythroughadd_init_script) and presses Escape inside it. On the old code it failed every time,at exactly the assertion this issue reports; on the fix it passes. It stays in the suite as
the regression test. The original fast test is untouched -- a fast client is what found this.
Fifteen runs of the file and the full browser suite since, all green.