From 4892e0d5372db61eff3e8cb6a71c679d7525514c Mon Sep 17 00:00:00 2001 From: Alec Geatches Date: Wed, 25 Mar 2026 12:45:53 -0600 Subject: [PATCH 1/7] Add failing performance test for huge code editor changes --- ...laboration-code-editor-performance.spec.ts | 130 +++++++++++++++++ .../data/code-editor-perf-step1.md | 132 ++++++++++++++++++ .../data/code-editor-perf-step2.md | 128 +++++++++++++++++ 3 files changed, 390 insertions(+) create mode 100644 test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts create mode 100644 test/e2e/specs/editor/collaboration/data/code-editor-perf-step1.md create mode 100644 test/e2e/specs/editor/collaboration/data/code-editor-perf-step2.md diff --git a/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts new file mode 100644 index 00000000000000..5d7c9152885c51 --- /dev/null +++ b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts @@ -0,0 +1,130 @@ +/** + * External dependencies + */ +import { readFileSync } from 'fs'; +import { join } from 'path'; + +/** + * WordPress dependencies + */ +import { test as base, expect } from '@wordpress/e2e-test-utils-playwright'; + +/** + * Internal dependencies + */ +import { setCollaboration } from './fixtures/collaboration-utils'; + +const STEP1_CONTENT = readFileSync( + join( __dirname, 'data', 'code-editor-perf-step1.md' ), + 'utf-8' +); + +const STEP2_CONTENT = readFileSync( + join( __dirname, 'data', 'code-editor-perf-step2.md' ), + 'utf-8' +); + +// Maximum time (ms) the editor should take to become responsive after +// pasting new content into the code editor. If this threshold is exceeded +// the test fails, indicating a performance regression. +const RESPONSE_TIMEOUT_MS = 5_000; + +type Fixtures = { + collaborationEnabled: boolean; +}; + +// This test only needs collaboration enabled, not a second user. +const test = base.extend< Fixtures >( { + collaborationEnabled: [ + async ( { requestUtils }, use ) => { + await setCollaboration( requestUtils, true ); + await use( true ); + await setCollaboration( requestUtils, false ); + }, + { auto: true }, + ], +} ); + +test.describe( 'Collaboration - Code editor performance', () => { + test( 'should not freeze when replacing content with large HTML in code editor', async ( { + admin, + context, + editor, + page, + pageUtils, + } ) => { + await context.grantPermissions( [ + 'clipboard-read', + 'clipboard-write', + ] ); + await admin.createNewPost(); + + const codeEditor = page.getByRole( 'textbox', { + name: 'Type text or HTML', + } ); + + // Step 1: Open the code editor and paste step 1 content. + await pageUtils.pressKeys( 'secondary+M' ); + await codeEditor.fill( STEP1_CONTENT ); + + // Step 2: Save the draft. + await pageUtils.pressKeys( 'primary+s' ); + await expect( + page + .getByRole( 'button', { name: 'Dismiss this notice' } ) + .filter( { hasText: 'Draft saved' } ) + ).toBeVisible(); + + // Step 3: Replace with step 2 content using clipboard paste, which + // triggers the same input event processing as a real user paste. + // This is the operation that causes the editor to freeze. + await codeEditor.focus(); + + // Write step 2 content to the clipboard. + await page.evaluate( + ( text: string ) => window.navigator.clipboard.writeText( text ), + STEP2_CONTENT + ); + + // Select all existing content and paste over it. Measure wall-clock + // time from the paste through switching to visual mode and blocks + // being fully parsed. This captures any main-thread freeze. + const startTime = performance.now(); + + await page.keyboard.press( 'Meta+a' ); + await page.keyboard.press( 'Meta+v' ); + + // Step 4: Switch to visual mode. The editor must process the + // pasted content before it can handle this shortcut. + await pageUtils.pressKeys( 'secondary+M' ); + + // Wait for blocks to be parsed (generous timeout so the test + // doesn't fail from the Playwright timeout itself). + await expect + .poll( + async () => { + const blocks = await editor.getBlocks(); + return blocks.some( + ( block ) => + block.name === 'core/heading' && + ( block.attributes?.content as string )?.includes( + 'Planned Versions' + ) + ); + }, + { timeout: 60_000 } + ) + .toBe( true ); + + const elapsed = performance.now() - startTime; + + // eslint-disable-next-line no-console + console.log( + `Code editor paste + reparse took ${ elapsed }ms (threshold: ${ RESPONSE_TIMEOUT_MS }ms)` + ); + + // The actual performance assertion: the entire paste-to-blocks + // cycle must complete within the threshold. + expect( elapsed ).toBeLessThan( RESPONSE_TIMEOUT_MS ); + } ); +} ); diff --git a/test/e2e/specs/editor/collaboration/data/code-editor-perf-step1.md b/test/e2e/specs/editor/collaboration/data/code-editor-perf-step1.md new file mode 100644 index 00000000000000..9f7c1e8cbbf43c --- /dev/null +++ b/test/e2e/specs/editor/collaboration/data/code-editor-perf-step1.md @@ -0,0 +1,132 @@ + +

The following are the various versions of WordPress, listed chronologically from the oldest to newest, along with the Change Log information on new features and improvements in each version.

+ + + +

NOTE: WordPress core developers share a love of jazz music, and since WordPress 1.0, all major releases are named in honor of jazz musicians they admire.

+ + + +

Released Versions

+ + + +

Version 0.7

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
0.70May 27, 2003ChangelogBlog
0.71June 9, 2003ChangelogBlog
0.711June 25, 2003ChangelogBlog
0.72October 11, 2003ChangelogBlog
+ + + +

Version 1.0

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
1.0January 3, 2004Miles DavisChangelogBlog
1.0.1January 25, 2004ChangelogBlog
1.0.2March 11, 2004Art BlakeyChangelogBlog
+ + + +

Version 1.2

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
1.2May 22, 2004Charles MingusChangelogBlog
1.2.1October 6, 2004ChangelogBlog
1.2.2December 15, 2004ChangelogBlog2540
+ + + +

Version 1.5

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
1.5February 17, 2005Billy StrayhornChangelogBlog2541
1.5.1May 9, 2005ChangelogBlog2541
1.5.1.1May 9, 2005Changelog + Blog2541
1.5.1.2May 27, 2005ChangelogBlog2541
1.5.1.3June 29, 2005ChangelogBlog2541
1.5.2August 14, 2005ChangelogBlog2541
+ + + +

Version 2.0

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
2.0December 26, 2005Duke EllingtonChangelogBlog3441
2.0.1January 31, 2006ChangelogBlog3441
2.0.2March 10, 2006ChangelogBlog3441
2.0.3June 1, 2006ChangelogBlog3441
2.0.4July 29, 2006ChangelogBlog3441
2.0.5October 27, 2006Ronan BorenChangelogBlog3441
2.0.6January 5, 2007ChangelogBlog3441
2.0.7January 15, 2007ChangelogBlog3441
2.0.8February 8, 2007ChangelogNo blog3441
2.0.9February 21, 2007ChangelogBlog3441
2.0.10April 3, 2007ChangelogBlog3441
2.0.11August 5, 2007ChangelogBlog3441
+ + + +

Version 2.1

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
2.1January 22, 2007Ella FitzgeraldChangelogBlog4772
2.1.1February 21, 2007ChangelogBlog4773
2.1.2March 2, 2007ChangelogBlog4773
2.1.3April 3, 2007ChangelogBlog4773
+ + + +

Version 2.2

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
2.2May 16, 2007Stan GetzChangelogBlog5183
2.2.1June 21, 2007ChangelogBlog5183
2.2.2August 5, 2007ChangelogBlog5183
2.2.3September 8, 2007ChangelogBlog5183
+ + + +

Version 2.3

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
2.3September 24, 2007Dexter GordonChangelogBlog6124
2.3.1October 26, 2007ChangelogBlog6124
2.3.2December 29, 2007ChangelogBlog6124
2.3.3February 5, 2008ChangelogBlog6124
+ + + +

Version 2.5

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
2.5March 29, 2008Michael BreckerChangelogBlog7558
2.5.1April 25, 2008ChangelogBlog7796
+ + + +

Version 2.6

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
2.6July 15, 2008McCoy TynerChangelogBlog8201
2.6.1August 15, 2008ChangelogBlog8204
2.6.2September 8, 2008ChangelogBlog8204
2.6.3October 23, 2008ChangelogBlog8204
2.6.5November 25, 2008ChangelogBlog8204
+ + + +

Version 2.7

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
2.7December 10, 2008John ColtraneChangelogBlog9872
2.7.1February 10, 2009ChangelogBlog9872
+ + + +

Version 2.8

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
2.8June 10, 2009Chet BakerChangelogBlog11548
2.8.1July 9, 2009ChangelogBlog11548
2.8.2July 20, 2009ChangelogBlog11548
2.8.3August 3, 2009ChangelogBlog11548
2.8.4August 12, 2009ChangelogBlog11548
2.8.5October 20, 2009ChangelogBlog11548
2.8.6November 12, 2009ChangelogBlog11548
+ + + +

Version 2.9

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
2.9December 18, 2009Carmen McRaeChangelogBlog12329
2.9.1January 4, 2010ChangelogBlog12329
2.9.2February 15, 2010ChangelogBlog12329
+ + + +

Version 3.0

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
3.0June 17, 2010Thelonious MonkChangelogBlog15260
3.0.1July 29, 2010ChangelogBlog15477
3.0.2November 30, 2010ChangelogBlog15477
3.0.3December 8, 2010ChangelogBlog15477
3.0.4December 29, 2010ChangelogBlog15477
3.0.5February 7, 2011ChangelogBlog15477
3.0.6April 26, 2011ChangelogNo blog15477
+ + + +

Version 3.1

+ + + +
VersionRelease DateMusicianChange LogAnnouncementDB Version
3.1February 23, 2011Django ReinhardtChangelogBlog17056
3.1.1April 5, 2011ChangelogBlog17516
3.1.2April 26, 2011ChangelogBlog17516
3.1.3May 25, 2011ChangelogBlog17516
3.1.4June 29, 2011ChangelogBlog17516
+ diff --git a/test/e2e/specs/editor/collaboration/data/code-editor-perf-step2.md b/test/e2e/specs/editor/collaboration/data/code-editor-perf-step2.md new file mode 100644 index 00000000000000..8e73cdfa68531b --- /dev/null +++ b/test/e2e/specs/editor/collaboration/data/code-editor-perf-step2.md @@ -0,0 +1,128 @@ + +

The following are the various versions of WordPress, listed chronologically from newest to oldest, along with the Change Log information on new features and improvements in each version.

+ + + +

NOTE: WordPress core developers share a love of jazz music, and since WordPress 1.0, all major releases are named in honor of jazz musicians they admire.

+ + + +

See Also

+ + + + + + + +

Planned Versions

+ + + +

The schedule for 2026 will be published as soon as it is available.  Follow changes in progress for the next major version in our bug tracker.

+ + + +
VersionPlanned Release DateRemarks
7.0Tentatively 2026Roadmap
+ + + +

Released Versions

+ + + +

Version 6.9 - Gene Harris

+ + + +
VersionRelease DateChange LogAnnouncementDB Version
6.9.1February 3, 2026ChangelogBlog60717
6.9December 2, 2025ChangelogBlog60717
+ + + +

Version 6.8 - Cecil

+ + + +
VersionRelease DateChange LogAnnouncementDB Version
6.8.3September 30, 2025ChangelogBlog60717
6.8.2July 15, 2025ChangelogBlog60421
6.8.1April 30, 2025ChangelogBlog58975
6.8April 15, 2025ChangelogBlog58975
+ + + +

Version 6.7 - Rollins

+ + + +
VersionRelease DateChange LogAnnouncementDB Version
6.7.4September 30, 2025ChangelogNo blog58975
6.7.3August 5, 2025ChangelogBlog58975
6.7.2February 11, 2025ChangelogBlog58975
6.7.1November 21, 2024ChangelogBlog58975
6.7November 12, 2024ChangelogBlog58975
+ + + +

Version 6.6 - Dorsey

+ + + +
VersionRelease DateChange LogAnnouncementDB Version
6.6.4September 30, 2025ChangelogNo blog57155
6.6.3August 5, 2025ChangelogBlog57155
6.6.2September 10, 2024ChangelogBlog57155
6.6.1July 23, 2024ChangelogBlog57155
6.6July 16, 2024ChangelogBlog57155
+ + + +

Version 6.5 - Regina

+ + + +
VersionRelease DateChange LogAnnouncementDB Version
6.5.7September 30, 2025ChangelogNo blog57155
6.5.6August 5, 2025ChangelogBlog57155
6.5.5June 24, 2024ChangelogBlog57155
6.5.4June 5, 2024ChangelogBlog57155
6.5.3May 7, 2024ChangelogBlog57155
6.5.2April 9, 2024ChangelogBlog57155
6.5.1ChangelogBlog57155
6.5April 2, 2024ChangelogBlog57155
+ + + +

Version 6.4 - Shirley

+ + + +
VersionRelease DateChange LogAnnouncementDB Version
6.4.7September 30, 2025ChangelogNo blog56657
6.4.6August 5, 2025ChangelogBlog56657
6.4.5June 24, 2024ChangelogBlog56657
6.4.4April 9, 2024ChangelogBlog56657
6.4.3January 30, 2024ChangelogBlog56657
6.4.2December 6, 2023ChangelogBlog56657
6.4.1November 9, 2023ChangelogBlog56657
6.4November 7, 2023ChangelogBlog56657
+ + + +

Version 6.3 - Lionel

+ + + +
VersionRelease DateChange LogAnnouncementDB Version
6.3.7September 30, 2025ChangelogNo blog55853
6.3.6August 5, 2025ChangelogBlog55853
6.3.5June 24, 2024ChangelogNo blog55853
6.3.4April 9, 2024ChangelogNo blog55853
6.3.3January 30, 2024ChangelogNo blog55853
6.3.2October 12, 2023ChangelogBlog55853
6.3.1August 29, 2023ChangelogBlog55853
6.3August 8, 2023ChangelogBlog55853
+ + + +

Version 6.2 - Dolphy

+ + + +
VersionRelease DateChange LogAnnouncementDB Version
6.2.8September 30, 2025ChangelogNo blog53496
6.2.7August 5, 2025ChangelogBlog53496
6.2.6June 24, 2024No blog53496
6.2.5April 9, 2024No blog53496
6.2.4January 30, 2024No blog53496
6.2.3October 12, 2023No blog53496
6.2.2May 20, 2023Changelog Blog 53496
6.2.1May 16, 2023Changelog Blog53496
6.2March 29, 2023ChangelogBlog53496
+ + + +

Version 6.1 - Mikhail "Misha" Alperin

+ + + +
VersionRelease DateChange LogAnnouncementDB Version
6.1.9September 30, 2025ChangelogNo blog53496
6.1.8August 5, 2025ChangelogBlog53496
6.1.7June 24, 2024No blog53496
6.1.6April 9, 2024No blog53496
6.1.5January 30, 2024No blog53496
6.1.4October 12, 2023No blog53496
6.1.3May 20, 2023No ChangelogNo Blog53496
6.1.2May 16, 2023ChangelogNo Blog53496
6.1.1November 15, 2022Changelog Blog 53496
6.1November 1, 2022Changelog Blog53496
+ + + +

Version 6.0 - Arturo O’Farrill

+ + + +
VersionRelease DateChange LogAnnouncementDB Version
6.0.11September 30, 2025ChangelogNo blog53496
6.0.10August 5, 2025ChangelogBlog53496
6.0.9June 24, 2024No Blog53496
6.0.8April 10, 2024No Blog53496
6.0.7January 30, 2024No Blog53496
6.0.6October 12, 2023No Blog53496
6.0.5May 20, 2023No Blog53496
6.0.4May 16, 2023No Blog53496
6.0.3 + October 17, 2022Changelog + Blog53496
6.0.2August 30, 2022Changelog + Blog + 53496
6.0.1July 12, 2022Changelog + Blog + 53496
6.0May 24, 2022Changelog + Blog53496
+ From 25b7b19ae79a9b93fa56452489728906ac8a97ee Mon Sep 17 00:00:00 2001 From: Alec Geatches Date: Wed, 25 Mar 2026 13:21:23 -0600 Subject: [PATCH 2/7] Use diffLines() when strings are > 10_000 characters --- packages/sync/src/quill-delta/Delta.ts | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/sync/src/quill-delta/Delta.ts b/packages/sync/src/quill-delta/Delta.ts index 5be4e15c545a30..858ad6b16670b2 100644 --- a/packages/sync/src/quill-delta/Delta.ts +++ b/packages/sync/src/quill-delta/Delta.ts @@ -8,7 +8,7 @@ * External dependencies */ import type { Change } from 'diff'; -import { diffChars } from 'diff'; +import { diffChars, diffLines } from 'diff'; import { default as isEqual } from 'fast-deep-equal/es6'; /** @@ -23,6 +23,7 @@ function cloneDeep< T >( value: T ): T { } const NULL_CHARACTER = String.fromCharCode( 0 ); // Placeholder char for embed in diff() +const STRING_TOO_LARGE_THRESHOLD = 10000; // If either string is larger than this, use a less precise diff algorithm optimized for large strings /** * Normalize diff changes so that `count` reflects UTF-16 code-unit length @@ -414,9 +415,26 @@ class Delta { return new Delta(); } const strings = this.deltasToStrings( other ); - const diffResult = normalizeChangeCounts( - diffChars( strings[ 0 ], strings[ 1 ] ) + const maxStringLength = Math.max( + ...strings.map( ( str ) => str.length ) ); + + let rawDiff; + if ( maxStringLength > STRING_TOO_LARGE_THRESHOLD ) { + // When large changes are pasted into the code editor, this can + // result in very long strings as a result of comparing the + // `content` of the code editor before and after the change. + // In this case, diffChars() can take a very long time to complete, + // which causes the editor to freeze. + // When we detect strings that are too long, use a less-precise diff + // method that is optimized for large inputs, at the cost of less + // accurate diffing. + rawDiff = diffLines( strings[ 0 ], strings[ 1 ] ); + } else { + rawDiff = diffChars( strings[ 0 ], strings[ 1 ] ); + } + + const diffResult = normalizeChangeCounts( rawDiff ); const thisIter = new OpIterator( this.ops ); const otherIter = new OpIterator( other.ops ); const retDelta = this.convertChangesToDelta( From 377b8095196fbccdd53b7be2182134aa15e7e844 Mon Sep 17 00:00:00 2001 From: Alec Geatches Date: Wed, 25 Mar 2026 15:10:54 -0600 Subject: [PATCH 3/7] Handle diffLines in diffWithCursor() when cursor information is sent. We don't care about the cursor for huge performance-sensitive updates --- packages/sync/src/quill-delta/Delta.ts | 52 +++++++++++++++----------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/packages/sync/src/quill-delta/Delta.ts b/packages/sync/src/quill-delta/Delta.ts index 858ad6b16670b2..6e32dca922013f 100644 --- a/packages/sync/src/quill-delta/Delta.ts +++ b/packages/sync/src/quill-delta/Delta.ts @@ -415,26 +415,9 @@ class Delta { return new Delta(); } const strings = this.deltasToStrings( other ); - const maxStringLength = Math.max( - ...strings.map( ( str ) => str.length ) + const diffResult = normalizeChangeCounts( + diffChars( strings[ 0 ], strings[ 1 ] ) ); - - let rawDiff; - if ( maxStringLength > STRING_TOO_LARGE_THRESHOLD ) { - // When large changes are pasted into the code editor, this can - // result in very long strings as a result of comparing the - // `content` of the code editor before and after the change. - // In this case, diffChars() can take a very long time to complete, - // which causes the editor to freeze. - // When we detect strings that are too long, use a less-precise diff - // method that is optimized for large inputs, at the cost of less - // accurate diffing. - rawDiff = diffLines( strings[ 0 ], strings[ 1 ] ); - } else { - rawDiff = diffChars( strings[ 0 ], strings[ 1 ] ); - } - - const diffResult = normalizeChangeCounts( rawDiff ); const thisIter = new OpIterator( this.ops ); const otherIter = new OpIterator( other.ops ); const retDelta = this.convertChangesToDelta( @@ -641,12 +624,39 @@ class Delta { diffWithCursor( other: Delta, cursorAfterChange: number | null ): Delta { if ( this.ops === other.ops ) { return new Delta(); + } + + const strings = this.deltasToStrings( other ); + + // When large changes are pasted into the code editor, this can + // result in very long strings as a result of comparing the + // `content` of the code editor before and after the change. + // In this case, diffChars() can take a very long time to complete, + // which causes the editor to freeze. + // When we detect strings that are too long, use a less-precise diff + // method that is optimized for large inputs, at the cost of less + // accurate diffing and cursor placement. + const maxStringLength = Math.max( + ...strings.map( ( str ) => str.length ) + ); + + if ( maxStringLength > STRING_TOO_LARGE_THRESHOLD ) { + const diffResult = normalizeChangeCounts( + diffLines( strings[ 0 ], strings[ 1 ] ) + ); + const thisIterLarge = new OpIterator( this.ops ); + const otherIterLarge = new OpIterator( other.ops ); + return this.convertChangesToDelta( + diffResult, + thisIterLarge, + otherIterLarge + ).chop(); } else if ( cursorAfterChange === null ) { - // If no cursor position is provided, do a regular diff. + // If no cursor position is provided and the string + // is a reasonable length, do a regular diff. return this.diff( other ); } - const strings = this.deltasToStrings( other ); let diffs = normalizeChangeCounts( diffChars( strings[ 0 ], strings[ 1 ] ) ); From 33e7aba3486809d10a39f57d66b21e53b1961bd8 Mon Sep 17 00:00:00 2001 From: Alec Geatches Date: Wed, 25 Mar 2026 16:00:17 -0600 Subject: [PATCH 4/7] Explicitly use "primary" instead of "meta" key --- .../collaboration-code-editor-performance.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts index 5d7c9152885c51..cd1499b299a3ff 100644 --- a/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts +++ b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts @@ -91,8 +91,8 @@ test.describe( 'Collaboration - Code editor performance', () => { // being fully parsed. This captures any main-thread freeze. const startTime = performance.now(); - await page.keyboard.press( 'Meta+a' ); - await page.keyboard.press( 'Meta+v' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+v' ); // Step 4: Switch to visual mode. The editor must process the // pasted content before it can handle this shortcut. From 57c9a793092262a87ca14072b1530fece1b22f74 Mon Sep 17 00:00:00 2001 From: Alec Geatches Date: Wed, 25 Mar 2026 16:07:48 -0600 Subject: [PATCH 5/7] Revert "Explicitly use "primary" instead of "meta" key" This reverts commit 33e7aba3486809d10a39f57d66b21e53b1961bd8. --- .../collaboration-code-editor-performance.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts index cd1499b299a3ff..5d7c9152885c51 100644 --- a/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts +++ b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts @@ -91,8 +91,8 @@ test.describe( 'Collaboration - Code editor performance', () => { // being fully parsed. This captures any main-thread freeze. const startTime = performance.now(); - await pageUtils.pressKeys( 'primary+a' ); - await pageUtils.pressKeys( 'primary+v' ); + await page.keyboard.press( 'Meta+a' ); + await page.keyboard.press( 'Meta+v' ); // Step 4: Switch to visual mode. The editor must process the // pasted content before it can handle this shortcut. From bd1725abd9d5ac73c04587f2eda8427614276baf Mon Sep 17 00:00:00 2001 From: Alec Geatches Date: Wed, 25 Mar 2026 16:12:39 -0600 Subject: [PATCH 6/7] Fix paste keys for CI, lower timeouts --- .../collaboration-code-editor-performance.spec.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts index 5d7c9152885c51..5a416fc946b114 100644 --- a/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts +++ b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts @@ -27,7 +27,7 @@ const STEP2_CONTENT = readFileSync( // Maximum time (ms) the editor should take to become responsive after // pasting new content into the code editor. If this threshold is exceeded // the test fails, indicating a performance regression. -const RESPONSE_TIMEOUT_MS = 5_000; +const RESPONSE_TIMEOUT_MS = 3_000; type Fixtures = { collaborationEnabled: boolean; @@ -91,8 +91,14 @@ test.describe( 'Collaboration - Code editor performance', () => { // being fully parsed. This captures any main-thread freeze. const startTime = performance.now(); - await page.keyboard.press( 'Meta+a' ); - await page.keyboard.press( 'Meta+v' ); + // Use pressKeys for select-all (maps to correct modifier per + // platform). We can't use pressKeys for paste because it emulates + // the ClipboardEvent instead of doing a real keyboard paste, so we + // use page.keyboard.press directly with the platform modifier. + await pageUtils.pressKeys( 'primary+a' ); + const pasteModifier = + process.platform === 'darwin' ? 'Meta' : 'Control'; + await page.keyboard.press( `${ pasteModifier }+v` ); // Step 4: Switch to visual mode. The editor must process the // pasted content before it can handle this shortcut. @@ -112,7 +118,7 @@ test.describe( 'Collaboration - Code editor performance', () => { ) ); }, - { timeout: 60_000 } + { timeout: 20_000 } ) .toBe( true ); From a46655df716002718d340f548942ececfae4a5e0 Mon Sep 17 00:00:00 2001 From: Alec Geatches Date: Wed, 25 Mar 2026 16:32:22 -0600 Subject: [PATCH 7/7] Remove console.log --- .../collaboration-code-editor-performance.spec.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts index 5a416fc946b114..0f48429f552168 100644 --- a/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts +++ b/test/e2e/specs/editor/collaboration/collaboration-code-editor-performance.spec.ts @@ -124,11 +124,6 @@ test.describe( 'Collaboration - Code editor performance', () => { const elapsed = performance.now() - startTime; - // eslint-disable-next-line no-console - console.log( - `Code editor paste + reparse took ${ elapsed }ms (threshold: ${ RESPONSE_TIMEOUT_MS }ms)` - ); - // The actual performance assertion: the entire paste-to-blocks // cycle must complete within the threshold. expect( elapsed ).toBeLessThan( RESPONSE_TIMEOUT_MS );