diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion-2.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion-2.test.js index 2c6c9bf348118..2ab5bdda7f6ca 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion-2.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion-2.test.js @@ -24,11 +24,9 @@ describe( 'Gutenberg Editor tests for Block insertion 2', () => { } ); it( 'inserts between 2 existing blocks', async () => { - const headingBlockElement = await editorPage.getBlockAtPosition( - blockNames.heading - ); + const firstBlock = await editorPage.getFirstBlockVisible(); + await firstBlock.click(); - await headingBlockElement.click(); await editorPage.addNewBlock( blockNames.separator ); const expectedHtml = [ diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js index d07e8a3ea2cb2..1d8d6fe3c9ea2 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js @@ -7,15 +7,15 @@ import testData from './helpers/test-data'; describe( 'Gutenberg Editor Image Block tests', () => { it( 'should be able to add an image block', async () => { - await editorPage.addNewBlock( blockNames.image ); - await editorPage.closePicker(); + // iOS only test - Can only add image from media library on iOS + if ( ! isAndroid() ) { + await editorPage.addNewBlock( blockNames.image ); + await editorPage.closePicker(); - const imageBlock = await editorPage.getBlockAtPosition( - blockNames.image - ); + const imageBlock = await editorPage.getBlockAtPosition( + blockNames.image + ); - // Can only add image from media library on iOS - if ( ! isAndroid() ) { await editorPage.selectEmptyImageBlock( imageBlock ); await editorPage.chooseMediaLibrary(); @@ -31,27 +31,14 @@ describe( 'Gutenberg Editor Image Block tests', () => { true ); await editorPage.dismissKeyboard(); - } - await editorPage.addNewBlock( blockNames.paragraph ); - const paragraphBlockElement = await editorPage.getTextBlockAtPosition( - blockNames.paragraph, - 2 - ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - - await editorPage.sendTextToParagraphBlock( 2, testData.shortText ); - // skip HTML check for Android since we couldn't add image from media library - /* eslint-disable jest/no-conditional-expect */ - if ( ! isAndroid() ) { + await editorPage.addNewBlock( blockNames.paragraph ); + await editorPage.sendTextToParagraphBlock( 2, testData.shortText ); const html = await editorPage.getHtmlContent(); expect( html.toLowerCase() ).toBe( testData.imageShorteHtml.toLowerCase() ); } - /* eslint-enable jest/no-conditional-expect */ } ); } ); diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index cc1585892dfb9..538df4e57172c 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -516,6 +516,7 @@ const waitForMediaLibrary = async ( driver ) => { * @param {string} driver * @param {string} elementLocator * @param {number} maxIteration - Default value is 25 + * @param {string} elementToReturn - Options are allElements, lastElement, firstElement. Defaults to "firstElement" * @param {number} iteration - Default value is 0 * @return {string} - Returns the first element found, empty string if not found */ @@ -523,6 +524,7 @@ const waitForVisible = async ( driver, elementLocator, maxIteration = 25, + elementToReturn = 'firstElement', iteration = 0 ) => { const timeout = 1000; @@ -539,35 +541,47 @@ const waitForVisible = async ( await driver.sleep( timeout ); } - const element = await driver.elementsByXPath( elementLocator ); - if ( element.length === 0 ) { + const elements = await driver.elementsByXPath( elementLocator ); + if ( elements.length === 0 ) { // if locator is not visible, try again return waitForVisible( driver, elementLocator, maxIteration, + elementToReturn, iteration + 1 ); } - return element[ 0 ]; + switch ( elementToReturn ) { + case 'allElements': + return elements; + case 'lastElement': + return elements[ elements.length - 1 ]; + default: + // Default is to return first element + return elements[ 0 ]; + } }; /** * @param {string} driver * @param {string} elementLocator * @param {number} maxIteration - Default value is 25, can be adjusted to be less to wait for element to not be visible + * @param {string} elementToReturn - Options are allElements, lastElement, firstElement. Defaults to "firstElement" * @return {boolean} - Returns true if element is found, false otherwise */ const isElementVisible = async ( driver, elementLocator, - maxIteration = 25 + maxIteration = 25, + elementToReturn = 'firstElement' ) => { const element = await waitForVisible( driver, elementLocator, - maxIteration + maxIteration, + elementToReturn ); // if there is no element, return false @@ -582,12 +596,14 @@ const clickIfClickable = async ( driver, elementLocator, maxIteration = 25, + elementToReturn = 'firstElement', iteration = 0 ) => { const element = await waitForVisible( driver, elementLocator, maxIteration, + elementToReturn, iteration ); @@ -606,6 +622,7 @@ const clickIfClickable = async ( driver, elementLocator, maxIteration, + elementToReturn, iteration + 1 ); } 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 92829551843ec..5f3130e499211 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -57,11 +57,7 @@ class EditorPage { if ( ! isAndroid() ) { const textBlockLocator = `(//XCUIElementTypeButton[contains(@name, "${ blockName } Block. Row ${ position }")])`; - const textBlock = await waitForVisible( - this.driver, - textBlockLocator - ); - await textBlock.click(); + await clickIfClickable( this.driver, textBlockLocator ); } const blockLocator = isAndroid() @@ -151,9 +147,12 @@ class EditorPage { async getLastBlockVisible() { const firstBlockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, " Block. Row ")]`; - await waitForVisible( this.driver, firstBlockLocator ); - const elements = await this.driver.elementsByXPath( firstBlockLocator ); - return elements[ elements.length - 1 ]; + return await waitForVisible( + this.driver, + firstBlockLocator, + 25, + 'lastElement' + ); } async hasBlockAtPosition( position = 1, blockName = '' ) { @@ -243,12 +242,10 @@ class EditorPage { // Sometimes double tap is not enough for paste menu to appear, so we also long press. await longPressMiddleOfElement( this.driver, htmlContentView ); - const pasteButton = await waitForVisible( + await clickIfClickable( this.driver, '//XCUIElementTypeMenuItem[@name="Paste"]' ); - - await pasteButton.click(); } await toggleHtmlMode( this.driver, false ); @@ -262,10 +259,11 @@ class EditorPage { if ( isAndroid() ) { return await this.driver.hideDeviceKeyboard(); } - const hideKeyboardToolbarButton = await this.driver.elementByXPath( + + await clickIfClickable( + this.driver, '//XCUIElementTypeButton[@name="Hide keyboard"]' ); - await hideKeyboardToolbarButton.click(); } async dismissAndroidClipboardSmartSuggestion() { @@ -645,11 +643,7 @@ class EditorPage { ? `//android.widget.Button[@content-desc="WordPress Media Library"]` : `//XCUIElementTypeButton[@name="WordPress Media Library"]`; - const mediaLibraryButton = await waitForVisible( - this.driver, - mediaLibraryLocator - ); - await mediaLibraryButton.click(); + await clickIfClickable( this.driver, mediaLibraryLocator ); } async enterCaptionToSelectedImageBlock( caption, clear = true ) { @@ -669,11 +663,10 @@ class EditorPage { await swipeDown( this.driver ); } else { - const cancelButton = await waitForVisible( + await clickIfClickable( this.driver, '//XCUIElementTypeButton[@name="Cancel"]' ); - await cancelButton.click(); } } @@ -703,13 +696,11 @@ class EditorPage { const elementName = isAndroid() ? '//*' : '//XCUIElementTypeOther'; - const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Hide search heading")]`; - const hideSearchHeadingToggle = await waitForVisible( + const hideSearchHeadingToggleLocator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Hide search heading")]`; + return await clickIfClickable( this.driver, - locator + hideSearchHeadingToggleLocator ); - - return await hideSearchHeadingToggle.click(); } async changeSearchButtonPositionSetting( block, buttonPosition ) { @@ -717,17 +708,11 @@ class EditorPage { const elementName = isAndroid() ? '//*' : '//XCUIElementTypeButton'; - const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Button position")]`; - let optionMenuButton = await waitForVisible( this.driver, locator ); - await optionMenuButton.click(); + const optionMenuLocator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Button position")]`; + await clickIfClickable( this.driver, optionMenuLocator ); const optionMenuButtonLocator = `${ elementName }[contains(@${ this.accessibilityIdXPathAttrib }, "${ buttonPosition }")]`; - optionMenuButton = await waitForVisible( - this.driver, - optionMenuButtonLocator - ); - - return await optionMenuButton.click(); + return await clickIfClickable( this.driver, optionMenuButtonLocator ); } async toggleSearchIconOnlySetting( block ) { @@ -735,10 +720,8 @@ class EditorPage { const elementName = isAndroid() ? '//*' : '//XCUIElementTypeOther'; - const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Use icon button")]`; - const useIconButton = await waitForVisible( this.driver, locator ); - - return await useIconButton.click(); + const useIconButtonLocator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Use icon button")]`; + return await clickIfClickable( this.driver, useIconButtonLocator ); } async isSearchSettingsVisible() {