From 698572c7e00c69261605a26c4dca71d214e918ee Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Fri, 3 Mar 2023 03:06:37 +0900 Subject: [PATCH 01/15] add test `should undo typing after a pause` --- test/e2e/specs/editor/various/undo.spec.js | 141 +++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 test/e2e/specs/editor/various/undo.spec.js diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js new file mode 100644 index 00000000000000..1b9e4c376cc2aa --- /dev/null +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -0,0 +1,141 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.use( { + undoUtils: async ( { page }, use ) => { + await use( new UndoUtils( { page } ) ); + }, +} ); + +class UndoUtils { + constructor( { page } ) { + this.page = page; + + this.getSelection = this.getSelection.bind( this ); + } + + async getSelection() { + return await this.page.evaluate( () => { + const selectedBlock = document.activeElement.closest( '.wp-block' ); + const blocks = Array.from( + document.querySelectorAll( '.wp-block' ) + ); + const blockIndex = blocks.indexOf( selectedBlock ); + + if ( blockIndex === -1 ) { + return {}; + } + + let editables; + + if ( selectedBlock.getAttribute( 'contenteditable' ) ) { + editables = [ selectedBlock ]; + } else { + editables = Array.from( + selectedBlock.querySelectorAll( '[contenteditable]' ) + ); + } + + const editableIndex = editables.indexOf( document.activeElement ); + const selection = window.getSelection(); + + if ( editableIndex === -1 || ! selection.rangeCount ) { + return { blockIndex }; + } + + const range = selection.getRangeAt( 0 ); + const cloneStart = range.cloneRange(); + const cloneEnd = range.cloneRange(); + + cloneStart.setStart( document.activeElement, 0 ); + cloneEnd.setStart( document.activeElement, 0 ); + + /** + * Zero width non-breaking space, used as padding in the editable DOM + * tree when it is empty otherwise. + */ + const ZWNBSP = '\ufeff'; + + return { + blockIndex, + editableIndex, + startOffset: cloneStart.toString().replace( ZWNBSP, '' ).length, + endOffset: cloneEnd.toString().replace( ZWNBSP, '' ).length, + }; + } ); + } +} + +test.describe( 'undo', () => { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'should undo typing after a pause', async ( { + editor, + page, + pageUtils, + undoUtils, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'before pause' ); + await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) ); + await page.keyboard.type( ' after pause' ); + + const after = await editor.getEditedPostContent(); + + expect( after ).toBe( + ` +

before pause after pause

+` + ); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + + const before = await editor.getEditedPostContent(); + expect( before ).toBe( + ` +

before pause

+` + ); + + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 'before pause'.length, + endOffset: 'before pause'.length, + } ); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + + await expect.poll( editor.getEditedPostContent ).toBe( '' ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 0, + endOffset: 0, + } ); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); + + await expect.poll( editor.getEditedPostContent ).toBe( before ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 'before pause'.length, + endOffset: 'before pause'.length, + } ); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); + + await expect.poll( editor.getEditedPostContent ).toBe( after ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 'before pause after pause'.length, + endOffset: 'before pause after pause'.length, + } ); + } ); +} ); From 6f05720fd08afc7ead29ff76174148274a3bc087 Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Fri, 3 Mar 2023 15:24:48 +0900 Subject: [PATCH 02/15] convert all tests --- test/e2e/specs/editor/various/undo.spec.js | 380 +++++++++++++++++++++ 1 file changed, 380 insertions(+) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index 1b9e4c376cc2aa..cb7da3902f0d2c 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -138,4 +138,384 @@ test.describe( 'undo', () => { endOffset: 'before pause after pause'.length, } ); } ); + + test( 'should undo typing after non input change', async ( { + editor, + page, + pageUtils, + undoUtils, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + + await page.keyboard.type( 'before keyboard ' ); + await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await page.keyboard.type( 'after keyboard' ); + + const after = await editor.getEditedPostContent(); + + expect( after ).toBe( + ` +

before keyboard after keyboard

+` + ); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + + const before = await editor.getEditedPostContent(); + + expect( before ).toBe( + ` +

before keyboard

+` + ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 'before keyboard '.length, + endOffset: 'before keyboard '.length, + } ); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + + await expect.poll( editor.getEditedPostContent ).toBe( '' ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 0, + endOffset: 0, + } ); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); + + await expect.poll( editor.getEditedPostContent ).toBe( before ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 'before keyboard '.length, + endOffset: 'before keyboard '.length, + } ); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); + + await expect.poll( editor.getEditedPostContent ).toBe( after ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 'before keyboard after keyboard'.length, + endOffset: 'before keyboard after keyboard'.length, + } ); + } ); + + test( 'should undo bold', async ( { page, pageUtils } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'test' ); + await page.click( 'role=button[name="Save draft"i]' ); + await expect( + page.locator( + 'role=button[name="Dismiss this notice"i] >> text=Draft saved' + ) + ).toBeVisible(); + await page.reload(); + await page.click( '[data-type="core/paragraph"]' ); + await pageUtils.pressKeyWithModifier( 'primary', 'a' ); + await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + const visibleResult = await page.evaluate( + () => document.activeElement.innerHTML + ); + expect( visibleResult ).toBe( 'test' ); + } ); + + test( 'Should undo/redo to expected level intervals', async ( { + editor, + page, + pageUtils, + undoUtils, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + + const firstBlock = await editor.getEditedPostContent(); + + await page.keyboard.type( 'This' ); + + const firstText = await editor.getEditedPostContent(); + + await page.keyboard.press( 'Enter' ); + + const secondBlock = await editor.getEditedPostContent(); + + await page.keyboard.type( 'is' ); + + const secondText = await editor.getEditedPostContent(); + + await page.keyboard.press( 'Enter' ); + + const thirdBlock = await editor.getEditedPostContent(); + + await page.keyboard.type( 'test' ); + + const thirdText = await editor.getEditedPostContent(); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 3rd paragraph text. + + await expect.poll( editor.getEditedPostContent ).toBe( thirdBlock ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 3, + editableIndex: 0, + startOffset: 0, + endOffset: 0, + } ); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 3rd block. + + await expect.poll( editor.getEditedPostContent ).toBe( secondText ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 2, + editableIndex: 0, + startOffset: 'is'.length, + endOffset: 'is'.length, + } ); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 2nd paragraph text. + + await expect.poll( editor.getEditedPostContent ).toBe( secondBlock ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 2, + editableIndex: 0, + startOffset: 0, + endOffset: 0, + } ); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 2nd block. + + await expect.poll( editor.getEditedPostContent ).toBe( firstText ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 'This'.length, + endOffset: 'This'.length, + } ); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 1st paragraph text. + + await expect.poll( editor.getEditedPostContent ).toBe( firstBlock ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 0, + endOffset: 0, + } ); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 1st block. + + await expect.poll( editor.getEditedPostContent ).toBe( '' ); + await expect.poll( undoUtils.getSelection ).toEqual( {} ); + // After undoing every action, there should be no more undo history. + await expect( + page.locator( 'role=button[name="Undo"]' ) + ).toBeDisabled(); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 1st block. + + await expect.poll( editor.getEditedPostContent ).toBe( firstBlock ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 0, + endOffset: 0, + } ); + // After redoing one change, the undo button should be enabled again. + await expect( + page.locator( 'role=button[name="Undo"]' ) + ).toBeEnabled(); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 1st paragraph text. + + await expect.poll( editor.getEditedPostContent ).toBe( firstText ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 1, + editableIndex: 0, + startOffset: 'This'.length, + endOffset: 'This'.length, + } ); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 2nd block. + + await expect.poll( editor.getEditedPostContent ).toBe( secondBlock ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 2, + editableIndex: 0, + startOffset: 0, + endOffset: 0, + } ); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 2nd paragraph text. + + await expect.poll( editor.getEditedPostContent ).toBe( secondText ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 2, + editableIndex: 0, + startOffset: 'is'.length, + endOffset: 'is'.length, + } ); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 3rd block. + + await expect.poll( editor.getEditedPostContent ).toBe( thirdBlock ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 3, + editableIndex: 0, + startOffset: 0, + endOffset: 0, + } ); + + await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 3rd paragraph text. + + await expect.poll( editor.getEditedPostContent ).toBe( thirdText ); + await expect.poll( undoUtils.getSelection ).toEqual( { + blockIndex: 3, + editableIndex: 0, + startOffset: 'test'.length, + endOffset: 'test'.length, + } ); + } ); + + test( 'should undo for explicit persistence editing post', async ( { + page, + pageUtils, + } ) => { + // Regression test: An issue had occurred where the creation of an + // explicit undo level would interfere with blocks values being synced + // correctly to the block editor. + // + // See: https://github.com/WordPress/gutenberg/issues/14950 + + // Issue is demonstrated from an edited post: create, save, and reload. + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'original' ); + await page.click( 'role=button[name="Save draft"i]' ); + await expect( + page.locator( + 'role=button[name="Dismiss this notice"i] >> text=Draft saved' + ) + ).toBeVisible(); + await page.reload(); + await page.waitForSelector( '.edit-post-layout' ); + + // Issue is demonstrated by forcing state merges (multiple inputs) on + // an existing text after a fresh reload. + await page.click( '[data-type="core/paragraph"] >> nth=0' ); + await page.keyboard.type( 'modified' ); + + // The issue is demonstrated after the one second delay to trigger the + // creation of an explicit undo persistence level. + await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) ); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + + // Assert against the _visible_ content. Since editor state with the + // regression present was accurate, it would produce the correct + // content. The issue had manifested in the form of what was shown to + // the user since the blocks state failed to sync to block editor. + const visibleContent = await page.evaluate( + () => document.activeElement.textContent + ); + expect( visibleContent ).toBe( 'original' ); + } ); + + test( 'should not create undo levels when saving', async ( { + editor, + page, + pageUtils, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '1' ); + await page.click( 'role=button[name="Save draft"i]' ); + await expect( + page.locator( + 'role=button[name="Dismiss this notice"i] >> text=Draft saved' + ) + ).toBeVisible(); + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + + await expect.poll( editor.getEditedPostContent ).toBe( '' ); + } ); + + test( 'should not create undo levels when publishing', async ( { + editor, + page, + pageUtils, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '1' ); + await editor.publishPost(); + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + + await expect.poll( editor.getEditedPostContent ).toBe( '' ); + } ); + + test( 'should immediately create an undo level on typing', async ( { + editor, + page, + pageUtils, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + + await page.keyboard.type( '1' ); + await page.click( 'role=button[name="Save draft"i]' ); + await expect( + page.locator( + 'role=button[name="Dismiss this notice"i] >> text=Draft saved' + ) + ).toBeVisible(); + await page.reload(); + await page.waitForSelector( '.edit-post-layout' ); + + // Expect undo button to be disabled. + await expect( + page.locator( 'role=button[name="Undo"]' ) + ).toBeDisabled(); + await page.click( '[data-type="core/paragraph"]' ); + + await page.keyboard.type( '2' ); + + // Expect undo button to be enabled. + await expect( + page.locator( 'role=button[name="Undo"]' ) + ).toBeEnabled(); + + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + + // Expect "1". + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

1

+` + ); + } ); + + test( 'should be able to undo and redo when transient changes have been made and we update/publish', async ( { + editor, + page, + pageUtils, + } ) => { + // Typing consecutive characters in a `Paragraph` block updates the same + // block attribute as in the previous action and results in transient edits + // and skipping `undo` history steps. + const text = 'tonis'; + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( text ); + await editor.publishPost(); + await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await expect.poll( editor.getEditedPostContent ).toBe( '' ); + await page.waitForSelector( + '.editor-history__redo[aria-disabled="false"]' + ); + await page.click( '.editor-history__redo[aria-disabled="false"]' ); + await expect.poll( editor.getEditedPostContent ).toBe( + ` +

tonis

+` + ); + } ); } ); From b77145671c3099a65977e964feba7cc5b1fa20f6 Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Fri, 3 Mar 2023 18:04:20 +0900 Subject: [PATCH 03/15] remove waitForSelector --- test/e2e/specs/editor/various/undo.spec.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index cb7da3902f0d2c..251a6972677099 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -400,7 +400,6 @@ test.describe( 'undo', () => { ) ).toBeVisible(); await page.reload(); - await page.waitForSelector( '.edit-post-layout' ); // Issue is demonstrated by forcing state merges (multiple inputs) on // an existing text after a fresh reload. @@ -469,7 +468,6 @@ test.describe( 'undo', () => { ) ).toBeVisible(); await page.reload(); - await page.waitForSelector( '.edit-post-layout' ); // Expect undo button to be disabled. await expect( @@ -508,9 +506,6 @@ test.describe( 'undo', () => { await editor.publishPost(); await pageUtils.pressKeyWithModifier( 'primary', 'z' ); await expect.poll( editor.getEditedPostContent ).toBe( '' ); - await page.waitForSelector( - '.editor-history__redo[aria-disabled="false"]' - ); await page.click( '.editor-history__redo[aria-disabled="false"]' ); await expect.poll( editor.getEditedPostContent ).toBe( ` From c5bea0bf04dad3bf7610b65e0b0984572190dc6f Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Mon, 20 Mar 2023 14:59:56 +0900 Subject: [PATCH 04/15] remove old test --- .../specs/editor/various/undo.test.js | 444 ------------------ 1 file changed, 444 deletions(-) delete mode 100644 packages/e2e-tests/specs/editor/various/undo.test.js diff --git a/packages/e2e-tests/specs/editor/various/undo.test.js b/packages/e2e-tests/specs/editor/various/undo.test.js deleted file mode 100644 index 9402ce8624c1a1..00000000000000 --- a/packages/e2e-tests/specs/editor/various/undo.test.js +++ /dev/null @@ -1,444 +0,0 @@ -/** - * WordPress dependencies - */ -import { - clickBlockAppender, - getEditedPostContent, - createNewPost, - pressKeyWithModifier, - selectBlockByClientId, - getAllBlocks, - saveDraft, - publishPost, -} from '@wordpress/e2e-test-utils'; - -const getSelection = async () => { - return await page.evaluate( () => { - const selectedBlock = document.activeElement.closest( '.wp-block' ); - const blocks = Array.from( document.querySelectorAll( '.wp-block' ) ); - const blockIndex = blocks.indexOf( selectedBlock ); - - if ( blockIndex === -1 ) { - return {}; - } - - let editables; - - if ( selectedBlock.getAttribute( 'contenteditable' ) ) { - editables = [ selectedBlock ]; - } else { - editables = Array.from( - selectedBlock.querySelectorAll( '[contenteditable]' ) - ); - } - - const editableIndex = editables.indexOf( document.activeElement ); - const selection = window.getSelection(); - - if ( editableIndex === -1 || ! selection.rangeCount ) { - return { blockIndex }; - } - - const range = selection.getRangeAt( 0 ); - const cloneStart = range.cloneRange(); - const cloneEnd = range.cloneRange(); - - cloneStart.setStart( document.activeElement, 0 ); - cloneEnd.setStart( document.activeElement, 0 ); - - /** - * Zero width non-breaking space, used as padding in the editable DOM - * tree when it is empty otherwise. - */ - const ZWNBSP = '\ufeff'; - - return { - blockIndex, - editableIndex, - startOffset: cloneStart.toString().replace( ZWNBSP, '' ).length, - endOffset: cloneEnd.toString().replace( ZWNBSP, '' ).length, - }; - } ); -}; - -describe( 'undo', () => { - beforeEach( async () => { - await createNewPost(); - } ); - - it( 'should undo typing after a pause', async () => { - await clickBlockAppender(); - - await page.keyboard.type( 'before pause' ); - await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) ); - await page.keyboard.type( ' after pause' ); - - const after = await getEditedPostContent(); - - expect( after ).toMatchSnapshot(); - - await pressKeyWithModifier( 'primary', 'z' ); - - const before = await getEditedPostContent(); - - expect( before ).toMatchSnapshot(); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 'before pause'.length, - endOffset: 'before pause'.length, - } ); - - await pressKeyWithModifier( 'primary', 'z' ); - - expect( await getEditedPostContent() ).toBe( '' ); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 0, - endOffset: 0, - } ); - - await pressKeyWithModifier( 'primaryShift', 'z' ); - - expect( await getEditedPostContent() ).toBe( before ); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 'before pause'.length, - endOffset: 'before pause'.length, - } ); - - await pressKeyWithModifier( 'primaryShift', 'z' ); - - expect( await getEditedPostContent() ).toBe( after ); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 'before pause after pause'.length, - endOffset: 'before pause after pause'.length, - } ); - } ); - - it( 'should undo typing after non input change', async () => { - await clickBlockAppender(); - - await page.keyboard.type( 'before keyboard ' ); - await pressKeyWithModifier( 'primary', 'b' ); - await page.keyboard.type( 'after keyboard' ); - - const after = await getEditedPostContent(); - - expect( after ).toMatchSnapshot(); - - await pressKeyWithModifier( 'primary', 'z' ); - - const before = await getEditedPostContent(); - - expect( before ).toMatchSnapshot(); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 'before keyboard '.length, - endOffset: 'before keyboard '.length, - } ); - - await pressKeyWithModifier( 'primary', 'z' ); - - expect( await getEditedPostContent() ).toBe( '' ); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 0, - endOffset: 0, - } ); - - await pressKeyWithModifier( 'primaryShift', 'z' ); - - expect( await getEditedPostContent() ).toBe( before ); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 'before keyboard '.length, - endOffset: 'before keyboard '.length, - } ); - - await pressKeyWithModifier( 'primaryShift', 'z' ); - - expect( await getEditedPostContent() ).toBe( after ); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 'before keyboard after keyboard'.length, - endOffset: 'before keyboard after keyboard'.length, - } ); - } ); - - it( 'should undo bold', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'test' ); - await saveDraft(); - await page.reload(); - await page.waitForSelector( '.edit-post-layout' ); - await page.click( '[data-type="core/paragraph"]' ); - await pressKeyWithModifier( 'primary', 'a' ); - await pressKeyWithModifier( 'primary', 'b' ); - await pressKeyWithModifier( 'primary', 'z' ); - - const visibleResult = await page.evaluate( - () => document.activeElement.innerHTML - ); - expect( visibleResult ).toBe( 'test' ); - } ); - - it( 'Should undo/redo to expected level intervals', async () => { - await clickBlockAppender(); - - const firstBlock = await getEditedPostContent(); - - await page.keyboard.type( 'This' ); - - const firstText = await getEditedPostContent(); - - await page.keyboard.press( 'Enter' ); - - const secondBlock = await getEditedPostContent(); - - await page.keyboard.type( 'is' ); - - const secondText = await getEditedPostContent(); - - await page.keyboard.press( 'Enter' ); - - const thirdBlock = await getEditedPostContent(); - - await page.keyboard.type( 'test' ); - - const thirdText = await getEditedPostContent(); - - await pressKeyWithModifier( 'primary', 'z' ); // Undo 3rd paragraph text. - - expect( await getEditedPostContent() ).toBe( thirdBlock ); - expect( await getSelection() ).toEqual( { - blockIndex: 3, - editableIndex: 0, - startOffset: 0, - endOffset: 0, - } ); - - await pressKeyWithModifier( 'primary', 'z' ); // Undo 3rd block. - - expect( await getEditedPostContent() ).toBe( secondText ); - expect( await getSelection() ).toEqual( { - blockIndex: 2, - editableIndex: 0, - startOffset: 'is'.length, - endOffset: 'is'.length, - } ); - - await pressKeyWithModifier( 'primary', 'z' ); // Undo 2nd paragraph text. - - expect( await getEditedPostContent() ).toBe( secondBlock ); - expect( await getSelection() ).toEqual( { - blockIndex: 2, - editableIndex: 0, - startOffset: 0, - endOffset: 0, - } ); - - await pressKeyWithModifier( 'primary', 'z' ); // Undo 2nd block. - - expect( await getEditedPostContent() ).toBe( firstText ); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 'This'.length, - endOffset: 'This'.length, - } ); - - await pressKeyWithModifier( 'primary', 'z' ); // Undo 1st paragraph text. - - expect( await getEditedPostContent() ).toBe( firstBlock ); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 0, - endOffset: 0, - } ); - - await pressKeyWithModifier( 'primary', 'z' ); // Undo 1st block. - - expect( await getEditedPostContent() ).toBe( '' ); - expect( await getSelection() ).toEqual( {} ); - // After undoing every action, there should be no more undo history. - expect( - await page.$( '.editor-history__undo[aria-disabled="true"]' ) - ).not.toBeNull(); - - await pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 1st block. - - expect( await getEditedPostContent() ).toBe( firstBlock ); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 0, - endOffset: 0, - } ); - // After redoing one change, the undo button should be enabled again. - expect( - await page.$( '.editor-history__undo[aria-disabled="true"]' ) - ).toBeNull(); - - await pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 1st paragraph text. - - expect( await getEditedPostContent() ).toBe( firstText ); - expect( await getSelection() ).toEqual( { - blockIndex: 1, - editableIndex: 0, - startOffset: 'This'.length, - endOffset: 'This'.length, - } ); - - await pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 2nd block. - - expect( await getEditedPostContent() ).toBe( secondBlock ); - expect( await getSelection() ).toEqual( { - blockIndex: 2, - editableIndex: 0, - startOffset: 0, - endOffset: 0, - } ); - - await pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 2nd paragraph text. - - expect( await getEditedPostContent() ).toBe( secondText ); - expect( await getSelection() ).toEqual( { - blockIndex: 2, - editableIndex: 0, - startOffset: 'is'.length, - endOffset: 'is'.length, - } ); - - await pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 3rd block. - - expect( await getEditedPostContent() ).toBe( thirdBlock ); - expect( await getSelection() ).toEqual( { - blockIndex: 3, - editableIndex: 0, - startOffset: 0, - endOffset: 0, - } ); - - await pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 3rd paragraph text. - - expect( await getEditedPostContent() ).toBe( thirdText ); - expect( await getSelection() ).toEqual( { - blockIndex: 3, - editableIndex: 0, - startOffset: 'test'.length, - endOffset: 'test'.length, - } ); - } ); - - it( 'should undo for explicit persistence editing post', async () => { - // Regression test: An issue had occurred where the creation of an - // explicit undo level would interfere with blocks values being synced - // correctly to the block editor. - // - // See: https://github.com/WordPress/gutenberg/issues/14950 - - // Issue is demonstrated from an edited post: create, save, and reload. - await clickBlockAppender(); - await page.keyboard.type( 'original' ); - await saveDraft(); - await page.reload(); - await page.waitForSelector( '.edit-post-layout' ); - - // Issue is demonstrated by forcing state merges (multiple inputs) on - // an existing text after a fresh reload. - await selectBlockByClientId( ( await getAllBlocks() )[ 0 ].clientId ); - await page.keyboard.type( 'modified' ); - - // The issue is demonstrated after the one second delay to trigger the - // creation of an explicit undo persistence level. - await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) ); - - await pressKeyWithModifier( 'primary', 'z' ); - - // Assert against the _visible_ content. Since editor state with the - // regression present was accurate, it would produce the correct - // content. The issue had manifested in the form of what was shown to - // the user since the blocks state failed to sync to block editor. - const visibleContent = await page.evaluate( - () => document.activeElement.textContent - ); - expect( visibleContent ).toBe( 'original' ); - } ); - - it( 'should not create undo levels when saving', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await saveDraft(); - await pressKeyWithModifier( 'primary', 'z' ); - - expect( await getEditedPostContent() ).toBe( '' ); - } ); - - it( 'should not create undo levels when publishing', async () => { - await clickBlockAppender(); - await page.keyboard.type( '1' ); - await publishPost(); - await pressKeyWithModifier( 'primary', 'z' ); - - expect( await getEditedPostContent() ).toBe( '' ); - } ); - - it( 'should immediately create an undo level on typing', async () => { - await clickBlockAppender(); - - await page.keyboard.type( '1' ); - await saveDraft(); - await page.reload(); - await page.waitForSelector( '.edit-post-layout' ); - - // Expect undo button to be disabled. - expect( - await page.$( '.editor-history__undo[aria-disabled="true"]' ) - ).not.toBeNull(); - - await page.click( '[data-type="core/paragraph"]' ); - - await page.keyboard.type( '2' ); - - // Expect undo button to be enabled. - expect( - await page.$( '.editor-history__undo[aria-disabled="true"]' ) - ).toBeNull(); - - await pressKeyWithModifier( 'primary', 'z' ); - - // Expect "1". - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'should be able to undo and redo when transient changes have been made and we update/publish', async () => { - // Typing consecutive characters in a `Paragraph` block updates the same - // block attribute as in the previous action and results in transient edits - // and skipping `undo` history steps. - const text = 'tonis'; - await clickBlockAppender(); - await page.keyboard.type( text ); - await publishPost(); - await pressKeyWithModifier( 'primary', 'z' ); - expect( await getEditedPostContent() ).toBe( '' ); - await page.waitForSelector( - '.editor-history__redo[aria-disabled="false"]' - ); - await page.click( '.editor-history__redo[aria-disabled="false"]' ); - expect( await getEditedPostContent() ).toMatchInlineSnapshot( ` - " -

tonis

- " - ` ); - } ); -} ); From 4a48a9826000f06bf6f2c0eb08322bba6de188c4 Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Mon, 20 Mar 2023 15:18:44 +0900 Subject: [PATCH 05/15] remove snapshot for old test --- .../various/__snapshots__/undo.test.js.snap | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 packages/e2e-tests/specs/editor/various/__snapshots__/undo.test.js.snap diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/undo.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/undo.test.js.snap deleted file mode 100644 index 5d1601b9f0d9a0..00000000000000 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/undo.test.js.snap +++ /dev/null @@ -1,31 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`undo should immediately create an undo level on typing 1`] = ` -" -

1

-" -`; - -exports[`undo should undo typing after a pause 1`] = ` -" -

before pause after pause

-" -`; - -exports[`undo should undo typing after a pause 2`] = ` -" -

before pause

-" -`; - -exports[`undo should undo typing after non input change 1`] = ` -" -

before keyboard after keyboard

-" -`; - -exports[`undo should undo typing after non input change 2`] = ` -" -

before keyboard

-" -`; From 526e3286ee702ada6502c7a593f68c4b9288a7ca Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Wed, 5 Apr 2023 22:25:04 +0900 Subject: [PATCH 06/15] replace pressKeyWithModifier to pressKeys --- test/e2e/specs/editor/various/undo.spec.js | 58 +++++++++++----------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index 251a6972677099..9016d710a09fe2 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -92,7 +92,7 @@ test.describe( 'undo', () => { ` ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); const before = await editor.getEditedPostContent(); expect( before ).toBe( @@ -108,7 +108,7 @@ test.describe( 'undo', () => { endOffset: 'before pause'.length, } ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); await expect.poll( editor.getEditedPostContent ).toBe( '' ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -118,7 +118,7 @@ test.describe( 'undo', () => { endOffset: 0, } ); - await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); + await pageUtils.pressKeys( 'primaryShift+z' ); await expect.poll( editor.getEditedPostContent ).toBe( before ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -128,7 +128,7 @@ test.describe( 'undo', () => { endOffset: 'before pause'.length, } ); - await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); + await pageUtils.pressKeys( 'primaryShift+z' ); await expect.poll( editor.getEditedPostContent ).toBe( after ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -148,7 +148,7 @@ test.describe( 'undo', () => { await page.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'before keyboard ' ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); + await pageUtils.pressKeys( 'primary+b' ); await page.keyboard.type( 'after keyboard' ); const after = await editor.getEditedPostContent(); @@ -159,7 +159,7 @@ test.describe( 'undo', () => { ` ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); const before = await editor.getEditedPostContent(); @@ -175,7 +175,7 @@ test.describe( 'undo', () => { endOffset: 'before keyboard '.length, } ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); await expect.poll( editor.getEditedPostContent ).toBe( '' ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -185,7 +185,7 @@ test.describe( 'undo', () => { endOffset: 0, } ); - await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); + await pageUtils.pressKeys( 'primaryShift+z' ); await expect.poll( editor.getEditedPostContent ).toBe( before ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -195,7 +195,7 @@ test.describe( 'undo', () => { endOffset: 'before keyboard '.length, } ); - await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); + await pageUtils.pressKeys( 'primaryShift+z' ); await expect.poll( editor.getEditedPostContent ).toBe( after ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -217,9 +217,9 @@ test.describe( 'undo', () => { ).toBeVisible(); await page.reload(); await page.click( '[data-type="core/paragraph"]' ); - await pageUtils.pressKeyWithModifier( 'primary', 'a' ); - await pageUtils.pressKeyWithModifier( 'primary', 'b' ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+a' ); + await pageUtils.pressKeys( 'primary+b' ); + await pageUtils.pressKeys( 'primary+z' ); const visibleResult = await page.evaluate( () => document.activeElement.innerHTML ); @@ -256,7 +256,7 @@ test.describe( 'undo', () => { const thirdText = await editor.getEditedPostContent(); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 3rd paragraph text. + await pageUtils.pressKeys( 'primary+z' ); // Undo 3rd paragraph text. await expect.poll( editor.getEditedPostContent ).toBe( thirdBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -266,7 +266,7 @@ test.describe( 'undo', () => { endOffset: 0, } ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 3rd block. + await pageUtils.pressKeys( 'primary+z' ); // Undo 3rd block. await expect.poll( editor.getEditedPostContent ).toBe( secondText ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -276,7 +276,7 @@ test.describe( 'undo', () => { endOffset: 'is'.length, } ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 2nd paragraph text. + await pageUtils.pressKeys( 'primary+z' ); // Undo 2nd paragraph text. await expect.poll( editor.getEditedPostContent ).toBe( secondBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -286,7 +286,7 @@ test.describe( 'undo', () => { endOffset: 0, } ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 2nd block. + await pageUtils.pressKeys( 'primary+z' ); // Undo 2nd block. await expect.poll( editor.getEditedPostContent ).toBe( firstText ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -296,7 +296,7 @@ test.describe( 'undo', () => { endOffset: 'This'.length, } ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 1st paragraph text. + await pageUtils.pressKeys( 'primary+z' ); // Undo 1st paragraph text. await expect.poll( editor.getEditedPostContent ).toBe( firstBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -306,7 +306,7 @@ test.describe( 'undo', () => { endOffset: 0, } ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); // Undo 1st block. + await pageUtils.pressKeys( 'primary+z' ); // Undo 1st block. await expect.poll( editor.getEditedPostContent ).toBe( '' ); await expect.poll( undoUtils.getSelection ).toEqual( {} ); @@ -315,7 +315,7 @@ test.describe( 'undo', () => { page.locator( 'role=button[name="Undo"]' ) ).toBeDisabled(); - await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 1st block. + await pageUtils.pressKeys( 'primaryShift+z' ); // Redo 1st block. await expect.poll( editor.getEditedPostContent ).toBe( firstBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -329,7 +329,7 @@ test.describe( 'undo', () => { page.locator( 'role=button[name="Undo"]' ) ).toBeEnabled(); - await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 1st paragraph text. + await pageUtils.pressKeys( 'primaryShift+z' ); // Redo 1st paragraph text. await expect.poll( editor.getEditedPostContent ).toBe( firstText ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -339,7 +339,7 @@ test.describe( 'undo', () => { endOffset: 'This'.length, } ); - await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 2nd block. + await pageUtils.pressKeys( 'primaryShift+z' ); // Redo 2nd block. await expect.poll( editor.getEditedPostContent ).toBe( secondBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -349,7 +349,7 @@ test.describe( 'undo', () => { endOffset: 0, } ); - await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 2nd paragraph text. + await pageUtils.pressKeys( 'primaryShift+z' ); // Redo 2nd paragraph text. await expect.poll( editor.getEditedPostContent ).toBe( secondText ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -359,7 +359,7 @@ test.describe( 'undo', () => { endOffset: 'is'.length, } ); - await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 3rd block. + await pageUtils.pressKeys( 'primaryShift+z' ); // Redo 3rd block. await expect.poll( editor.getEditedPostContent ).toBe( thirdBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -369,7 +369,7 @@ test.describe( 'undo', () => { endOffset: 0, } ); - await pageUtils.pressKeyWithModifier( 'primaryShift', 'z' ); // Redo 3rd paragraph text. + await pageUtils.pressKeys( 'primaryShift+z' ); // Redo 3rd paragraph text. await expect.poll( editor.getEditedPostContent ).toBe( thirdText ); await expect.poll( undoUtils.getSelection ).toEqual( { @@ -410,7 +410,7 @@ test.describe( 'undo', () => { // creation of an explicit undo persistence level. await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) ); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); // Assert against the _visible_ content. Since editor state with the // regression present was accurate, it would produce the correct @@ -435,7 +435,7 @@ test.describe( 'undo', () => { 'role=button[name="Dismiss this notice"i] >> text=Draft saved' ) ).toBeVisible(); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); await expect.poll( editor.getEditedPostContent ).toBe( '' ); } ); @@ -448,7 +448,7 @@ test.describe( 'undo', () => { await page.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( '1' ); await editor.publishPost(); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); await expect.poll( editor.getEditedPostContent ).toBe( '' ); } ); @@ -482,7 +482,7 @@ test.describe( 'undo', () => { page.locator( 'role=button[name="Undo"]' ) ).toBeEnabled(); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); // Expect "1". await expect.poll( editor.getEditedPostContent ).toBe( @@ -504,7 +504,7 @@ test.describe( 'undo', () => { await page.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( text ); await editor.publishPost(); - await pageUtils.pressKeyWithModifier( 'primary', 'z' ); + await pageUtils.pressKeys( 'primary+z' ); await expect.poll( editor.getEditedPostContent ).toBe( '' ); await page.click( '.editor-history__redo[aria-disabled="false"]' ); await expect.poll( editor.getEditedPostContent ).toBe( From d278172eb56de0fca005c73e8028de7e30f02aad Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Sun, 23 Apr 2023 15:45:50 +0900 Subject: [PATCH 07/15] use waitForTimeout --- test/e2e/specs/editor/various/undo.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index 9016d710a09fe2..43fc9d029f6f79 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -383,6 +383,7 @@ test.describe( 'undo', () => { test( 'should undo for explicit persistence editing post', async ( { page, pageUtils, + editor, } ) => { // Regression test: An issue had occurred where the creation of an // explicit undo level would interfere with blocks values being synced @@ -408,7 +409,7 @@ test.describe( 'undo', () => { // The issue is demonstrated after the one second delay to trigger the // creation of an explicit undo persistence level. - await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) ); + await editor.page.waitForTimeout( 1000 ); await pageUtils.pressKeys( 'primary+z' ); From 1f3f5d5877cfb0c4e9d314538da42660aa5510b0 Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Sun, 23 Apr 2023 16:02:37 +0900 Subject: [PATCH 08/15] use role selector --- test/e2e/specs/editor/various/undo.spec.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index 43fc9d029f6f79..84a745dfa5e18a 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -507,7 +507,10 @@ test.describe( 'undo', () => { await editor.publishPost(); await pageUtils.pressKeys( 'primary+z' ); await expect.poll( editor.getEditedPostContent ).toBe( '' ); - await page.click( '.editor-history__redo[aria-disabled="false"]' ); + await expect( + page.locator( 'role=button[name="Redo"]' ) + ).not.toBeDisabled(); + await page.click( 'role=button[name="Redo"]' ); await expect.poll( editor.getEditedPostContent ).toBe( `

tonis

From 3a80a50162f460da7015169ff89a176eabc3c675 Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Sun, 23 Apr 2023 16:22:45 +0900 Subject: [PATCH 09/15] replace to toHaveText --- test/e2e/specs/editor/various/undo.spec.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index 84a745dfa5e18a..fbe945dce145fe 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -220,10 +220,8 @@ test.describe( 'undo', () => { await pageUtils.pressKeys( 'primary+a' ); await pageUtils.pressKeys( 'primary+b' ); await pageUtils.pressKeys( 'primary+z' ); - const visibleResult = await page.evaluate( - () => document.activeElement.innerHTML - ); - expect( visibleResult ).toBe( 'test' ); + const activeElementLocator = page.locator( ':focus' ); + await expect( activeElementLocator ).toHaveText( 'test' ); } ); test( 'Should undo/redo to expected level intervals', async ( { @@ -417,10 +415,8 @@ test.describe( 'undo', () => { // regression present was accurate, it would produce the correct // content. The issue had manifested in the form of what was shown to // the user since the blocks state failed to sync to block editor. - const visibleContent = await page.evaluate( - () => document.activeElement.textContent - ); - expect( visibleContent ).toBe( 'original' ); + const activeElementLocator = page.locator( ':focus' ); + await expect( activeElementLocator ).toHaveText( 'original' ); } ); test( 'should not create undo levels when saving', async ( { From 98f043adf66af8284345d24651587ee3f6a50458 Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Sun, 23 Apr 2023 16:28:38 +0900 Subject: [PATCH 10/15] use waitForTimeout --- test/e2e/specs/editor/various/undo.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index fbe945dce145fe..ee964a2cf8cc91 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -81,7 +81,7 @@ test.describe( 'undo', () => { } ) => { await page.click( 'role=button[name="Add default block"i]' ); await page.keyboard.type( 'before pause' ); - await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) ); + await editor.page.waitForTimeout( 1000 ); await page.keyboard.type( ' after pause' ); const after = await editor.getEditedPostContent(); From 15985ecb5a646da8b37b591886e346bf10c442a3 Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Mon, 24 Apr 2023 16:54:15 +0900 Subject: [PATCH 11/15] replace to editor.getBlocks --- test/e2e/specs/editor/various/undo.spec.js | 116 +++++++++++++-------- 1 file changed, 73 insertions(+), 43 deletions(-) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index ee964a2cf8cc91..88e3fbac60428c 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -84,23 +84,21 @@ test.describe( 'undo', () => { await editor.page.waitForTimeout( 1000 ); await page.keyboard.type( ' after pause' ); - const after = await editor.getEditedPostContent(); - - expect( after ).toBe( - ` -

before pause after pause

-` - ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: 'before pause after pause' }, + }, + ] ); await pageUtils.pressKeys( 'primary+z' ); - const before = await editor.getEditedPostContent(); - expect( before ).toBe( - ` -

before pause

-` - ); - + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: 'before pause' }, + }, + ] ); await expect.poll( undoUtils.getSelection ).toEqual( { blockIndex: 1, editableIndex: 0, @@ -120,7 +118,12 @@ test.describe( 'undo', () => { await pageUtils.pressKeys( 'primaryShift+z' ); - await expect.poll( editor.getEditedPostContent ).toBe( before ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: 'before pause' }, + }, + ] ); await expect.poll( undoUtils.getSelection ).toEqual( { blockIndex: 1, editableIndex: 0, @@ -130,7 +133,12 @@ test.describe( 'undo', () => { await pageUtils.pressKeys( 'primaryShift+z' ); - await expect.poll( editor.getEditedPostContent ).toBe( after ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: 'before pause after pause' }, + }, + ] ); await expect.poll( undoUtils.getSelection ).toEqual( { blockIndex: 1, editableIndex: 0, @@ -151,23 +159,25 @@ test.describe( 'undo', () => { await pageUtils.pressKeys( 'primary+b' ); await page.keyboard.type( 'after keyboard' ); - const after = await editor.getEditedPostContent(); - - expect( after ).toBe( - ` -

before keyboard after keyboard

-` - ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { + content: 'before keyboard after keyboard', + }, + }, + ] ); await pageUtils.pressKeys( 'primary+z' ); - const before = await editor.getEditedPostContent(); - - expect( before ).toBe( - ` -

before keyboard

-` - ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { + content: 'before keyboard ', + }, + }, + ] ); await expect.poll( undoUtils.getSelection ).toEqual( { blockIndex: 1, editableIndex: 0, @@ -187,7 +197,14 @@ test.describe( 'undo', () => { await pageUtils.pressKeys( 'primaryShift+z' ); - await expect.poll( editor.getEditedPostContent ).toBe( before ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { + content: 'before keyboard ', + }, + }, + ] ); await expect.poll( undoUtils.getSelection ).toEqual( { blockIndex: 1, editableIndex: 0, @@ -197,7 +214,14 @@ test.describe( 'undo', () => { await pageUtils.pressKeys( 'primaryShift+z' ); - await expect.poll( editor.getEditedPostContent ).toBe( after ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { + content: 'before keyboard after keyboard', + }, + }, + ] ); await expect.poll( undoUtils.getSelection ).toEqual( { blockIndex: 1, editableIndex: 0, @@ -481,12 +505,14 @@ test.describe( 'undo', () => { await pageUtils.pressKeys( 'primary+z' ); - // Expect "1". - await expect.poll( editor.getEditedPostContent ).toBe( - ` -

1

-` - ); + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { + content: '1', + }, + }, + ] ); } ); test( 'should be able to undo and redo when transient changes have been made and we update/publish', async ( { @@ -507,10 +533,14 @@ test.describe( 'undo', () => { page.locator( 'role=button[name="Redo"]' ) ).not.toBeDisabled(); await page.click( 'role=button[name="Redo"]' ); - await expect.poll( editor.getEditedPostContent ).toBe( - ` -

tonis

-` - ); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { + content: 'tonis', + }, + }, + ] ); } ); } ); From d1219a6a4a6839be6c2bfaf02c288859926bc586 Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Wed, 26 Apr 2023 14:42:09 +0900 Subject: [PATCH 12/15] use getSelectionStart / getSelectionEnd --- test/e2e/specs/editor/various/undo.spec.js | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index 88e3fbac60428c..98f62220013751 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -45,24 +45,15 @@ class UndoUtils { return { blockIndex }; } - const range = selection.getRangeAt( 0 ); - const cloneStart = range.cloneRange(); - const cloneEnd = range.cloneRange(); - - cloneStart.setStart( document.activeElement, 0 ); - cloneEnd.setStart( document.activeElement, 0 ); - - /** - * Zero width non-breaking space, used as padding in the editable DOM - * tree when it is empty otherwise. - */ - const ZWNBSP = '\ufeff'; - return { blockIndex, editableIndex, - startOffset: cloneStart.toString().replace( ZWNBSP, '' ).length, - endOffset: cloneEnd.toString().replace( ZWNBSP, '' ).length, + startOffset: window.wp.data + .select( 'core/block-editor' ) + .getSelectionStart().offset, + endOffset: window.wp.data + .select( 'core/block-editor' ) + .getSelectionEnd().offset, }; } ); } From 248e5722b1fb811a9be0230396b2248f4f609060 Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Wed, 26 Apr 2023 15:30:25 +0900 Subject: [PATCH 13/15] use getSelectionStart / getSelectionEnd --- test/e2e/specs/editor/various/undo.spec.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index 98f62220013751..e9faacbd68fea8 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -103,8 +103,6 @@ test.describe( 'undo', () => { await expect.poll( undoUtils.getSelection ).toEqual( { blockIndex: 1, editableIndex: 0, - startOffset: 0, - endOffset: 0, } ); await pageUtils.pressKeys( 'primaryShift+z' ); @@ -182,8 +180,6 @@ test.describe( 'undo', () => { await expect.poll( undoUtils.getSelection ).toEqual( { blockIndex: 1, editableIndex: 0, - startOffset: 0, - endOffset: 0, } ); await pageUtils.pressKeys( 'primaryShift+z' ); @@ -275,8 +271,6 @@ test.describe( 'undo', () => { await expect.poll( undoUtils.getSelection ).toEqual( { blockIndex: 3, editableIndex: 0, - startOffset: 0, - endOffset: 0, } ); await pageUtils.pressKeys( 'primary+z' ); // Undo 3rd block. @@ -295,8 +289,6 @@ test.describe( 'undo', () => { await expect.poll( undoUtils.getSelection ).toEqual( { blockIndex: 2, editableIndex: 0, - startOffset: 0, - endOffset: 0, } ); await pageUtils.pressKeys( 'primary+z' ); // Undo 2nd block. @@ -315,8 +307,6 @@ test.describe( 'undo', () => { await expect.poll( undoUtils.getSelection ).toEqual( { blockIndex: 1, editableIndex: 0, - startOffset: 0, - endOffset: 0, } ); await pageUtils.pressKeys( 'primary+z' ); // Undo 1st block. From 1539749aaad3bb798fd640663cb994db4640eff1 Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Wed, 26 Apr 2023 15:34:35 +0900 Subject: [PATCH 14/15] use select api --- test/e2e/specs/editor/various/undo.spec.js | 86 +++++++--------------- 1 file changed, 25 insertions(+), 61 deletions(-) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index e9faacbd68fea8..7ed54085812289 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -18,36 +18,19 @@ class UndoUtils { async getSelection() { return await this.page.evaluate( () => { - const selectedBlock = document.activeElement.closest( '.wp-block' ); - const blocks = Array.from( - document.querySelectorAll( '.wp-block' ) - ); - const blockIndex = blocks.indexOf( selectedBlock ); + const selectedBlockId = window.wp.data + .select( 'core/block-editor' ) + .getSelectedBlockClientId(); + const blockIndex = window.wp.data + .select( 'core/block-editor' ) + .getBlockIndex( selectedBlockId ); if ( blockIndex === -1 ) { return {}; } - let editables; - - if ( selectedBlock.getAttribute( 'contenteditable' ) ) { - editables = [ selectedBlock ]; - } else { - editables = Array.from( - selectedBlock.querySelectorAll( '[contenteditable]' ) - ); - } - - const editableIndex = editables.indexOf( document.activeElement ); - const selection = window.getSelection(); - - if ( editableIndex === -1 || ! selection.rangeCount ) { - return { blockIndex }; - } - return { blockIndex, - editableIndex, startOffset: window.wp.data .select( 'core/block-editor' ) .getSelectionStart().offset, @@ -91,8 +74,7 @@ test.describe( 'undo', () => { }, ] ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, startOffset: 'before pause'.length, endOffset: 'before pause'.length, } ); @@ -101,8 +83,7 @@ test.describe( 'undo', () => { await expect.poll( editor.getEditedPostContent ).toBe( '' ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, } ); await pageUtils.pressKeys( 'primaryShift+z' ); @@ -114,8 +95,7 @@ test.describe( 'undo', () => { }, ] ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, startOffset: 'before pause'.length, endOffset: 'before pause'.length, } ); @@ -129,8 +109,7 @@ test.describe( 'undo', () => { }, ] ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, startOffset: 'before pause after pause'.length, endOffset: 'before pause after pause'.length, } ); @@ -168,8 +147,7 @@ test.describe( 'undo', () => { }, ] ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, startOffset: 'before keyboard '.length, endOffset: 'before keyboard '.length, } ); @@ -178,8 +156,7 @@ test.describe( 'undo', () => { await expect.poll( editor.getEditedPostContent ).toBe( '' ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, } ); await pageUtils.pressKeys( 'primaryShift+z' ); @@ -193,8 +170,7 @@ test.describe( 'undo', () => { }, ] ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, startOffset: 'before keyboard '.length, endOffset: 'before keyboard '.length, } ); @@ -210,8 +186,7 @@ test.describe( 'undo', () => { }, ] ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, startOffset: 'before keyboard after keyboard'.length, endOffset: 'before keyboard after keyboard'.length, } ); @@ -269,16 +244,14 @@ test.describe( 'undo', () => { await expect.poll( editor.getEditedPostContent ).toBe( thirdBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 3, - editableIndex: 0, + blockIndex: 2, } ); await pageUtils.pressKeys( 'primary+z' ); // Undo 3rd block. await expect.poll( editor.getEditedPostContent ).toBe( secondText ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 2, - editableIndex: 0, + blockIndex: 1, startOffset: 'is'.length, endOffset: 'is'.length, } ); @@ -287,16 +260,14 @@ test.describe( 'undo', () => { await expect.poll( editor.getEditedPostContent ).toBe( secondBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 2, - editableIndex: 0, + blockIndex: 1, } ); await pageUtils.pressKeys( 'primary+z' ); // Undo 2nd block. await expect.poll( editor.getEditedPostContent ).toBe( firstText ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, startOffset: 'This'.length, endOffset: 'This'.length, } ); @@ -305,8 +276,7 @@ test.describe( 'undo', () => { await expect.poll( editor.getEditedPostContent ).toBe( firstBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, } ); await pageUtils.pressKeys( 'primary+z' ); // Undo 1st block. @@ -322,8 +292,7 @@ test.describe( 'undo', () => { await expect.poll( editor.getEditedPostContent ).toBe( firstBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, startOffset: 0, endOffset: 0, } ); @@ -336,8 +305,7 @@ test.describe( 'undo', () => { await expect.poll( editor.getEditedPostContent ).toBe( firstText ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 1, - editableIndex: 0, + blockIndex: 0, startOffset: 'This'.length, endOffset: 'This'.length, } ); @@ -346,8 +314,7 @@ test.describe( 'undo', () => { await expect.poll( editor.getEditedPostContent ).toBe( secondBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 2, - editableIndex: 0, + blockIndex: 1, startOffset: 0, endOffset: 0, } ); @@ -356,8 +323,7 @@ test.describe( 'undo', () => { await expect.poll( editor.getEditedPostContent ).toBe( secondText ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 2, - editableIndex: 0, + blockIndex: 1, startOffset: 'is'.length, endOffset: 'is'.length, } ); @@ -366,8 +332,7 @@ test.describe( 'undo', () => { await expect.poll( editor.getEditedPostContent ).toBe( thirdBlock ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 3, - editableIndex: 0, + blockIndex: 2, startOffset: 0, endOffset: 0, } ); @@ -376,8 +341,7 @@ test.describe( 'undo', () => { await expect.poll( editor.getEditedPostContent ).toBe( thirdText ); await expect.poll( undoUtils.getSelection ).toEqual( { - blockIndex: 3, - editableIndex: 0, + blockIndex: 2, startOffset: 'test'.length, endOffset: 'test'.length, } ); From 2063755aab65f14641397e392da3652b46be7876 Mon Sep 17 00:00:00 2001 From: "Hiroshi Urabe (torounit)" Date: Wed, 26 Apr 2023 19:42:48 +0900 Subject: [PATCH 15/15] move the UndoUtils to bottom like other tests. --- test/e2e/specs/editor/various/undo.spec.js | 66 +++++++++++----------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/test/e2e/specs/editor/various/undo.spec.js b/test/e2e/specs/editor/various/undo.spec.js index 7ed54085812289..29b34ea416ff29 100644 --- a/test/e2e/specs/editor/various/undo.spec.js +++ b/test/e2e/specs/editor/various/undo.spec.js @@ -9,39 +9,6 @@ test.use( { }, } ); -class UndoUtils { - constructor( { page } ) { - this.page = page; - - this.getSelection = this.getSelection.bind( this ); - } - - async getSelection() { - return await this.page.evaluate( () => { - const selectedBlockId = window.wp.data - .select( 'core/block-editor' ) - .getSelectedBlockClientId(); - const blockIndex = window.wp.data - .select( 'core/block-editor' ) - .getBlockIndex( selectedBlockId ); - - if ( blockIndex === -1 ) { - return {}; - } - - return { - blockIndex, - startOffset: window.wp.data - .select( 'core/block-editor' ) - .getSelectionStart().offset, - endOffset: window.wp.data - .select( 'core/block-editor' ) - .getSelectionEnd().offset, - }; - } ); - } -} - test.describe( 'undo', () => { test.beforeEach( async ( { admin } ) => { await admin.createNewPost(); @@ -489,3 +456,36 @@ test.describe( 'undo', () => { ] ); } ); } ); + +class UndoUtils { + constructor( { page } ) { + this.page = page; + + this.getSelection = this.getSelection.bind( this ); + } + + async getSelection() { + return await this.page.evaluate( () => { + const selectedBlockId = window.wp.data + .select( 'core/block-editor' ) + .getSelectedBlockClientId(); + const blockIndex = window.wp.data + .select( 'core/block-editor' ) + .getBlockIndex( selectedBlockId ); + + if ( blockIndex === -1 ) { + return {}; + } + + return { + blockIndex, + startOffset: window.wp.data + .select( 'core/block-editor' ) + .getSelectionStart().offset, + endOffset: window.wp.data + .select( 'core/block-editor' ) + .getSelectionEnd().offset, + }; + } ); + } +}