diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-move-block.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-move-block.test.js new file mode 100644 index 00000000000000..a9cb357dc3c465 --- /dev/null +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-move-block.test.js @@ -0,0 +1,158 @@ +/** + * Internal dependencies + */ +import { isAndroid } from './helpers/utils'; +import { blockNames } from './pages/editor-page'; + +describe( 'Gutenberg Editor Block Mover tests', () => { + const cancelButtonName = 'Cancel'; + const buttonIosType = 'Button'; + const moveTopButtonName = 'Move to top'; + const moveBottomButtonName = 'Move to bottom'; + + async function setupBlocks( text = 'p1' ) { + await editorPage.addNewBlock( blockNames.paragraph ); + const paragraphBlock = await editorPage.getBlockAtPosition( + blockNames.paragraph + ); + await editorPage.typeTextToParagraphBlock( paragraphBlock, text ); + + await editorPage.addNewBlock( blockNames.heading ); + const headerBlock = await editorPage.getBlockAtPosition( + blockNames.heading, + 2 + ); + await editorPage.typeTextToParagraphBlock( headerBlock, text ); + } + + async function removeBlocks( blockOrder ) { + for ( let i = blockOrder.length; i > 0; i-- ) { + await editorPage.removeBlockAtPosition( blockOrder[ i - 1 ], i ); + } + } + + async function cancelActionSheet( ePage ) { + if ( isAndroid() ) { + await ePage.tapCoordinates( 100, 100 ); + } else { + await ePage.selectElement( cancelButtonName, buttonIosType ); + } + } + + it( 'should be able to see move block to top when long pressing up and change nothing when pressing cancel', async () => { + await setupBlocks( 'p1-up-cancel' ); + + await editorPage.longPressToolBarButton( + 'Move block up from row 2 to row 1' + ); + const moveUpAction = await editorPage.findElementByXPath( + moveTopButtonName, + buttonIosType + ); + + expect( moveUpAction ).toBeTruthy(); + + await cancelActionSheet( editorPage ); + const paragraphIsFirst = await editorPage.hasBlockAtPosition( + 1, + blockNames.paragraph + ); + + expect( paragraphIsFirst ).toBe( true ); + + await removeBlocks( [ blockNames.paragraph, blockNames.heading ] ); + } ); + + it( 'should be able to move block to first block when pressing move block to top', async () => { + await setupBlocks( 'p1-up' ); + + await editorPage.longPressToolBarButton( + 'Move block up from row 2 to row 1' + ); + await editorPage.selectElement( moveTopButtonName, buttonIosType ); + const headerIsFirst = await editorPage.hasBlockAtPosition( + 1, + blockNames.heading + ); + + expect( headerIsFirst ).toBe( true ); + + await editorPage.selectBlock( blockNames.paragraph, 2 ); + await removeBlocks( [ blockNames.heading, blockNames.paragraph ] ); + } ); + + it( 'should have move up disabled when on top', async () => { + await setupBlocks( 'p1-disabled-top' ); + await editorPage.selectBlock( blockNames.paragraph ); + + await editorPage.longPressToolBarButton( 'Move block up' ); + const moveUpAction = await editorPage.findElementByXPath( + moveTopButtonName, + buttonIosType + ); + + expect( moveUpAction ).toBe( undefined ); + + await editorPage.selectBlock( blockNames.heading, 2 ); + await removeBlocks( [ blockNames.paragraph, blockNames.heading ] ); + } ); + + it( 'should be able to see move block to bottom when long pressing down and change nothing when pressing cancel', async () => { + await setupBlocks( 'p1-down-cancel' ); + await editorPage.selectBlock( blockNames.paragraph ); + + await editorPage.longPressToolBarButton( + 'Move block down from row 1 to row 2' + ); + const moveDownAction = await editorPage.findElementByXPath( + moveBottomButtonName, + buttonIosType + ); + + expect( moveDownAction ).toBeTruthy(); + + await cancelActionSheet( editorPage ); + const paragraphIsFirst = await editorPage.hasBlockAtPosition( + 1, + blockNames.paragraph + ); + + expect( paragraphIsFirst ).toBe( true ); + + await editorPage.selectBlock( blockNames.heading, 2 ); + await removeBlocks( [ blockNames.paragraph, blockNames.heading ] ); + } ); + + it( 'should be able to move block to last block when pressing move block to bottom', async () => { + await setupBlocks( 'p1-down' ); + await editorPage.selectBlock( blockNames.paragraph ); + + await editorPage.longPressToolBarButton( + 'Move block down from row 1 to row 2' + ); + + await editorPage.selectElement( moveBottomButtonName, buttonIosType ); + const headerIsFirst = await editorPage.hasBlockAtPosition( + 1, + blockNames.heading + ); + + expect( headerIsFirst ).toBe( true ); + + await removeBlocks( [ blockNames.heading, blockNames.paragraph ] ); + } ); + + it( 'should have move down disabled when on bottom', async () => { + await setupBlocks( 'p1-disabled-down' ); + + await editorPage.longPressToolBarButton( 'Move block down' ); + const moveDownAction = await editorPage.findElementByXPath( + moveBottomButtonName, + buttonIosType + ); + + expect( moveDownAction ).toBe( undefined ); + + await removeBlocks( [ blockNames.paragraph, blockNames.heading ] ); + } ); +} ); diff --git a/packages/react-native-editor/__device-tests__/pages/editor-page.js b/packages/react-native-editor/__device-tests__/pages/editor-page.js index 18c40226283002..c5fa7cd0a6bc62 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -5,14 +5,20 @@ const { setupDriver, stopDriver, isAndroid, + longPressMiddleOfElement, swipeUp, swipeDown, typeString, toggleHtmlMode, swipeFromTo, - longPressMiddleOfElement, } = require( '../helpers/utils' ); +/** + * External dependencies + */ +// eslint-disable-next-line import/no-extraneous-dependencies +const wd = require( 'wd' ); + const initializeEditorPage = async () => { const driver = await setupDriver(); return new EditorPage( driver ); @@ -288,6 +294,20 @@ class EditorPage { await toolBarButton.click(); } + async longPressToolBarButton( buttonName ) { + let toolBarButton; + if ( isAndroid() ) { + const blockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, "${ buttonName }")]`; + toolBarButton = await this.driver.elementByXPath( blockLocator ); + } else { + toolBarButton = await this.driver.elementByAccessibilityId( + buttonName + ); + } + + await longPressMiddleOfElement( this.driver, toolBarButton ); + } + // ========================= // Inline toolbar functions // ========================= @@ -384,11 +404,15 @@ class EditorPage { } async typeTextToParagraphBlock( block, text, clear ) { - const textViewElement = await this.getTextViewForParagraphBlock( - block - ); - await typeString( this.driver, textViewElement, text, clear ); - await this.driver.sleep( 1000 ); // Give time for the block to rerender (such as for accessibility) + if ( ! isAndroid() ) { + block.type( text ); + } else { + const textViewElement = await this.getTextViewForParagraphBlock( + block + ); + await typeString( this.driver, textViewElement, text, clear ); + await this.driver.sleep( 1000 ); // Give time for the block to rerender (such as for accessibility) + } } async sendTextToParagraphBlock( position, text, clear ) { @@ -572,6 +596,46 @@ class EditorPage { async sauceJobStatus( allPassed ) { await this.driver.sauceJobStatus( allPassed ); } + + // ============================= + // Misc functions + // ============================= + async findElementByXPath( name, iosElementType ) { + const elementName = isAndroid() + ? '//*' + : `//XCUIElementType${ iosElementType }`; + const blockLocator = `${ elementName }[contains(@${ this.accessibilityIdXPathAttrib }, "${ name }")]`; + const elements = await this.driver.elementsByXPath( blockLocator ); + return elements[ 0 ]; + } + + async selectElement( name, iosElementType ) { + const el = await this.findElementByXPath( name, iosElementType ); + el.click(); + } + + async selectBlock( + blockName, + position = 1, + options = { autoscroll: false } + ) { + const block = await this.getBlockAtPosition( + blockName, + position, + options + ); + block.click(); + } + + async tapCoordinates( x, y, longPress = false ) { + const action = new wd.TouchAction( this.driver ); + action.press( { x, y } ); + if ( longPress ) { + action.wait( 1000 ); + } + action.release(); + await action.perform(); + } } const blockNames = {