From 0fd631f10773896f07ad71a86fffb58a808d10c9 Mon Sep 17 00:00:00 2001 From: Mark Tolmacs Date: Fri, 17 Apr 2026 13:27:01 +0000 Subject: [PATCH] fix: Test issue Signed-off-by: Mark Tolmacs --- packages/excalidraw/components/App.tsx | 16 +- .../tests/__snapshots__/history.test.tsx.snap | 491 +----------------- packages/excalidraw/tests/history.test.tsx | 3 + 3 files changed, 21 insertions(+), 489 deletions(-) diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index ca17459074..c77a7ebd0a 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -9475,6 +9475,17 @@ class App extends React.Component { }); } + /** + * Returns the timestamp of a pointer event, in milliseconds. + * Extracted as a method so tests can spy on it and return a fixed value, + * making the One Euro Filter in the freedraw handler deterministic. + */ + protected getPointerEventTimestamp( + ev: Pick, + ): number { + return ev.timeStamp; + } + private onPointerMoveFromPointerDownHandler( pointerDownState: PointerDownState, ) { @@ -10232,8 +10243,9 @@ class App extends React.Component { if (event.pointerType === "mouse") { // One Euro Filter: speed-adaptive low-pass for mouse events. + const evTs = this.getPointerEventTimestamp(ev); const dt = Math.max( - prevTs !== null ? (ev.timeStamp - prevTs) / 1000 : 1 / 60, + prevTs !== null ? (evTs - prevTs) / 1000 : 1 / 60, 0.001, ); // seconds @@ -10261,7 +10273,7 @@ class App extends React.Component { prevRawX = rawDx; prevRawY = rawDy; - prevTs = ev.timeStamp; + prevTs = evTs; dx = emaX; dy = emaY; diff --git a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap index 703608e772..cb5332a5c3 100644 --- a/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap +++ b/packages/excalidraw/tests/__snapshots__/history.test.tsx.snap @@ -9084,8 +9084,8 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte 20, ], [ - "23.68762", - "23.68762", + "21.04874", + "21.04874", ], [ 50, @@ -9186,8 +9186,8 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte 20, ], [ - "23.68762", - "23.68762", + "21.04874", + "21.04874", ], [ 50, @@ -11292,489 +11292,6 @@ exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end o exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end of test] undo stack 1`] = `[]`; -exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] appState 1`] = ` -{ - "activeEmbeddable": null, - "activeLockedId": null, - "activeTool": { - "customType": null, - "fromSelection": false, - "lastActiveTool": null, - "locked": false, - "type": "selection", - }, - "bindMode": "orbit", - "bindingPreference": "enabled", - "collaborators": Map {}, - "contextMenu": null, - "croppingElementId": null, - "currentHoveredFontFamily": null, - "currentItemArrowType": "round", - "currentItemBackgroundColor": "transparent", - "currentItemEndArrowhead": "arrow", - "currentItemFillStyle": "solid", - "currentItemFontFamily": 5, - "currentItemFontSize": 20, - "currentItemOpacity": 100, - "currentItemRoughness": 1, - "currentItemRoundness": "sharp", - "currentItemStartArrowhead": null, - "currentItemStrokeColor": "#1e1e1e", - "currentItemStrokeStyle": "solid", - "currentItemStrokeWidth": 2, - "currentItemTextAlign": "left", - "cursorButton": "up", - "defaultSidebarDockedPreference": false, - "editingFrame": null, - "editingGroupId": null, - "editingTextElement": null, - "elementsToHighlight": null, - "errorMessage": null, - "exportBackground": true, - "exportEmbedScene": false, - "exportScale": 1, - "exportWithDarkMode": false, - "fileHandle": null, - "followedBy": Set {}, - "frameRendering": { - "clip": true, - "enabled": true, - "name": true, - "outline": true, - }, - "frameToHighlight": null, - "gridModeEnabled": false, - "gridSize": 20, - "gridStep": 5, - "height": 0, - "hoveredElementIds": {}, - "isBindingEnabled": true, - "isCropping": false, - "isLoading": false, - "isMidpointSnappingEnabled": true, - "isResizing": false, - "isRotating": false, - "lastPointerDownWith": "mouse", - "lockedMultiSelections": {}, - "multiElement": null, - "newElement": null, - "objectsSnapModeEnabled": false, - "offsetLeft": 0, - "offsetTop": 0, - "openDialog": null, - "openMenu": null, - "openPopup": null, - "openSidebar": null, - "originSnapOffset": null, - "penDetected": false, - "penMode": false, - "preferredSelectionTool": { - "initialized": true, - "type": "selection", - }, - "previousSelectedElementIds": {}, - "resizingElement": null, - "scrollX": 0, - "scrollY": 0, - "searchMatches": null, - "selectedElementIds": {}, - "selectedElementsAreBeingDragged": false, - "selectedGroupIds": {}, - "selectionElement": null, - "shouldCacheIgnoreZoom": false, - "showHyperlinkPopup": false, - "showWelcomeScreen": true, - "snapLines": [], - "startBoundElement": null, - "stats": { - "open": false, - "panels": 3, - }, - "suggestedBinding": null, - "theme": "light", - "toast": null, - "userToFollow": null, - "viewBackgroundColor": "#ffffff", - "viewModeEnabled": false, - "width": 0, - "zenModeEnabled": false, - "zoom": { - "value": 1, - }, -} -`; - -exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] element 0 1`] = ` -{ - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [ - "inner", - "outer", - ], - "height": 100, - "id": "id0", - "index": "a0", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "updated": 1, - "version": 2, - "width": 100, - "x": 0, - "y": 0, -} -`; - -exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] element 1 1`] = ` -{ - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [ - "outer", - ], - "height": 100, - "id": "id1", - "index": "a1", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "updated": 1, - "version": 2, - "width": 100, - "x": 100, - "y": 100, -} -`; - -exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] element 2 1`] = ` -{ - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [ - "inner", - "outer", - ], - "height": 100, - "id": "id2", - "index": "a2", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "updated": 1, - "version": 2, - "width": 100, - "x": 200, - "y": 200, -} -`; - -exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] number of elements 1`] = `3`; - -exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] number of renders 1`] = `16`; - -exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] redo stack 1`] = `[]`; - -exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] undo stack 1`] = ` -[ - { - "appState": AppStateDelta { - "delta": Delta { - "deleted": { - "selectedElementIds": { - "id0": true, - "id1": true, - "id2": true, - }, - "selectedGroupIds": { - "outer": true, - }, - }, - "inserted": { - "selectedElementIds": {}, - "selectedGroupIds": {}, - }, - }, - }, - "elements": { - "added": {}, - "removed": { - "id0": { - "deleted": { - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [ - "inner", - "outer", - ], - "height": 100, - "index": "a0", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "version": 2, - "width": 100, - "x": 0, - "y": 0, - }, - "inserted": { - "isDeleted": true, - "version": 1, - }, - }, - "id1": { - "deleted": { - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [ - "outer", - ], - "height": 100, - "index": "a1", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "version": 2, - "width": 100, - "x": 100, - "y": 100, - }, - "inserted": { - "isDeleted": true, - "version": 1, - }, - }, - "id2": { - "deleted": { - "angle": 0, - "backgroundColor": "transparent", - "boundElements": null, - "customData": undefined, - "fillStyle": "solid", - "frameId": null, - "groupIds": [ - "inner", - "outer", - ], - "height": 100, - "index": "a2", - "isDeleted": false, - "link": null, - "locked": false, - "opacity": 100, - "roughness": 1, - "roundness": null, - "strokeColor": "#1e1e1e", - "strokeStyle": "solid", - "strokeWidth": 2, - "type": "rectangle", - "version": 2, - "width": 100, - "x": 200, - "y": 200, - }, - "inserted": { - "isDeleted": true, - "version": 1, - }, - }, - }, - "updated": {}, - }, - "id": "id5", - }, - { - "appState": AppStateDelta { - "delta": Delta { - "deleted": { - "editingGroupId": "outer", - "selectedElementIds": {}, - "selectedGroupIds": { - "inner": true, - }, - }, - "inserted": { - "editingGroupId": null, - "selectedElementIds": { - "id1": true, - }, - "selectedGroupIds": { - "outer": true, - }, - }, - }, - }, - "elements": { - "added": {}, - "removed": {}, - "updated": {}, - }, - "id": "id7", - }, - { - "appState": AppStateDelta { - "delta": Delta { - "deleted": { - "editingGroupId": "inner", - "selectedElementIds": {}, - "selectedGroupIds": {}, - }, - "inserted": { - "editingGroupId": "outer", - "selectedElementIds": { - "id2": true, - }, - "selectedGroupIds": { - "inner": true, - }, - }, - }, - }, - "elements": { - "added": {}, - "removed": {}, - "updated": {}, - }, - "id": "id9", - }, - { - "appState": AppStateDelta { - "delta": Delta { - "deleted": { - "editingGroupId": "outer", - "selectedElementIds": { - "id2": true, - }, - "selectedGroupIds": { - "inner": true, - }, - }, - "inserted": { - "editingGroupId": "inner", - "selectedElementIds": {}, - "selectedGroupIds": {}, - }, - }, - }, - "elements": { - "added": {}, - "removed": {}, - "updated": {}, - }, - "id": "id19", - }, - { - "appState": AppStateDelta { - "delta": Delta { - "deleted": { - "editingGroupId": null, - "selectedElementIds": { - "id1": true, - }, - "selectedGroupIds": { - "outer": true, - }, - }, - "inserted": { - "editingGroupId": "outer", - "selectedElementIds": {}, - "selectedGroupIds": { - "inner": true, - }, - }, - }, - }, - "elements": { - "added": {}, - "removed": {}, - "updated": {}, - }, - "id": "id20", - }, - { - "appState": AppStateDelta { - "delta": Delta { - "deleted": { - "selectedElementIds": {}, - "selectedGroupIds": {}, - }, - "inserted": { - "selectedElementIds": { - "id0": true, - "id1": true, - "id2": true, - }, - "selectedGroupIds": { - "outer": true, - }, - }, - }, - }, - "elements": { - "added": {}, - "removed": {}, - "updated": {}, - }, - "id": "id21", - }, -] -`; - exports[`history > multiplayer undo/redo > should update history entries after remote changes on the same properties > [end of test] appState 1`] = ` { "activeEmbeddable": null, diff --git a/packages/excalidraw/tests/history.test.tsx b/packages/excalidraw/tests/history.test.tsx index 78b431b773..e990517a98 100644 --- a/packages/excalidraw/tests/history.test.tsx +++ b/packages/excalidraw/tests/history.test.tsx @@ -2120,6 +2120,9 @@ describe("history", () => { await render( , ); + // Pin the One Euro Filter clock to a fixed value so the smoothed + // intermediate freedraw point is deterministic regardless of system load. + vi.spyOn(h.app as any, "getPointerEventTimestamp").mockReturnValue(0); }); it("should not override remote changes on different elements", async () => {