[lexical-playground] Chore: De-flake collab "Undo with collaboration on" e2e test#8637
Merged
Merged
Conversation
…on" e2e test The "Undo with collaboration on" test intermittently failed on webkit (and is racy on all browsers) with a partial undo: e.g. after undoing the "hello world again" burst the middle paragraph still showed "hello world aga", or after undoing the checklist a list item or "a" remained, instead of the expected empty paragraph. Root cause: in collab, @lexical/history is disabled in favor of the Yjs UndoManager, which coalesces consecutive local edits into one undo stack item only while each lands within captureTimeout (500ms) of the previous, measured with Date.now(). Unlike the local-history clock that advanceHistoryClock drives, that timer is not injectable, so under CI load a stall longer than the window silently splits a single logical edit group across multiple stack items. The test issues one undo() per group and asserts the whole group reverts, so a split leaves a partial revert. Fix: add freezeCollabUndoGrouping(), which sets the UndoManager's captureTimeout to Infinity for the edited frame. Grouping is then driven solely by explicit boundaries -- advanceHistoryClock's stopCapturing() and the stopCapturing() the UndoManager runs automatically after every undo/redo (both reset lastChange to 0). Edits in between always coalesce regardless of timing, so a burst reverts as a unit. This does not change any asserted behavior: a run that happened to stay under the 500ms window already grouped the same way. https://claude.ai/code/session_01MznZZ3zPCNqBWLvstr9P9J
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
zurfyx
approved these changes
Jun 4, 2026
potatowagon
reviewed
Jun 14, 2026
potatowagon
left a comment
Contributor
There was a problem hiding this comment.
Reviewed by Navi (Tater Thoughts Bobblehead) on behalf of @potatowagon.
LGTM ✅ — Excellent de-flaking fix for collab undo tests.
What this does: Introduces a freezeCollabUndoGrouping(page) test utility that sets the Yjs UndoManager's captureTimeout to Infinity during test execution. This ensures that undo grouping is purely boundary-driven (explicit stopCapturing() calls) rather than wall-clock-driven (500ms default window), preventing CI stalls from splitting edit groups across multiple undo stack items.
What I checked:
- ✅ Logic correctness: The fix correctly identifies the root cause — Yjs UndoManager coalesces edits only within its
captureTimeoutwindow. Under CI load, a >500ms stall between keystrokes splits what should be a single group. Setting to Infinity removes the time dimension while keeping boundary-driven grouping intact. - ✅ Edge cases: The helper is properly guarded (
if (!IS_COLLAB) return), correctly polls viawaitForFunctionuntil the UndoManager is available (handles async collab connection), accesses it via the well-known symbolSymbol.for('@lexical/yjs/UndoManager'), and is idempotent. - ✅ No behavioral change: The test assertions remain unchanged — a run that naturally stayed under 500ms already grouped the same way. Only flaky intermittent splits are eliminated.
- ✅ Thorough documentation: The JSDoc explains the mechanism, rationale, and interaction with
advanceHistoryClock. - ✅ No www compat concerns — test-only change.
- ✅ CI: All checks green.
Safe to land. This eliminates a well-understood class of CI flakiness without changing any test semantics.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
The "Undo with collaboration on" test intermittently failed on webkit (and is racy on all browsers) with a partial undo: e.g. after undoing the "hello world again" burst the middle paragraph still showed "hello world aga", or after undoing the checklist a list item or "a" remained, instead of the expected empty paragraph.
Root cause: in collab, @lexical/history is disabled in favor of the Yjs UndoManager, which coalesces consecutive local edits into one undo stack item only while each lands within captureTimeout (500ms) of the previous, measured with Date.now(). Unlike the local-history clock that advanceHistoryClock drives, that timer is not injectable, so under CI load a stall longer than the window silently splits a single logical edit group across multiple stack items. The test issues one undo() per group and asserts the whole group reverts, so a split leaves a partial revert.
Fix: add freezeCollabUndoGrouping(), which sets the UndoManager's captureTimeout to Infinity for the edited frame. Grouping is then driven solely by explicit boundaries -- advanceHistoryClock's stopCapturing() and the stopCapturing() the UndoManager runs automatically after every undo/redo (both reset lastChange to 0). Edits in between always coalesce regardless of timing, so a burst reverts as a unit. This does not change any asserted behavior: a run that happened to stay under the 500ms window already grouped the same way.
Test plan
Test changes only