From 7bf096d1633e008e220ea78b40d689c6df8a95e6 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Wed, 18 May 2022 14:27:28 +0800 Subject: [PATCH 01/14] add waits to all block - 1st try --- .../gutenberg-editor-block-insertion.test.js | 7 - .../gutenberg-editor-columns.test.js | 2 +- .../gutenberg-editor-cover.test.js | 89 +++----- .../gutenberg-editor-image-@canary.test.js | 6 +- .../gutenberg-editor-search.test.js | 4 + .../gutenberg-editor-spacer.test.js | 3 +- ...utenberg-editor-unsupported-blocks.test.js | 7 +- .../__device-tests__/helpers/utils.js | 8 +- .../__device-tests__/pages/editor-page.js | 193 ++++++++++++------ 9 files changed, 167 insertions(+), 152 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js index ead25e7bee9a2..4dfbf56f52a5a 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js @@ -39,9 +39,6 @@ describe( 'Gutenberg Editor tests for Block insertion', () => { testData.blockInsertionHtml.toLowerCase() ); - // wait for the block editor to load and for accessibility ids to update - await editorPage.driver.sleep( 3000 ); - // Workaround for now since deleting the first element causes a crash on CI for Android if ( isAndroid() ) { paragraphBlockElement = await editorPage.getTextBlockAtPosition( @@ -55,8 +52,6 @@ describe( 'Gutenberg Editor tests for Block insertion', () => { await paragraphBlockElement.click(); await editorPage.removeBlockAtPosition( blockNames.paragraph, 3 ); for ( let i = 3; i > 0; i-- ) { - // wait for accessibility ids to update - await editorPage.driver.sleep( 1000 ); paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph, i, @@ -72,8 +67,6 @@ describe( 'Gutenberg Editor tests for Block insertion', () => { } } else { for ( let i = 4; i > 0; i-- ) { - // wait for accessibility ids to update - await editorPage.driver.sleep( 1000 ); paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js index b57ea2588d7ee..fc4dc3a870f84 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js @@ -10,7 +10,7 @@ describe( 'Gutenberg Editor Columns Block test', () => { testData.columnsWithDifferentUnitsHtml ); - const columnsBlock = await editorPage.getFirstBlockVisible(); + const columnsBlock = await editorPage.getColumnsBlockVisible(); await columnsBlock.click(); expect( columnsBlock ).toBeTruthy(); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js index 8d6eaa429a9e8..216d2bf934051 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js @@ -2,95 +2,54 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { isAndroid, waitForVisible } from './helpers/utils'; +import { isAndroid } from './helpers/utils'; import testData from './helpers/test-data'; describe( 'Gutenberg Editor Cover Block test', () => { it( 'should displayed properly and have properly converted height (ios only)', async () => { - await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); - - const coverBlock = await editorPage.getBlockAtPosition( - blockNames.cover, - 1, - { useWaitForVisible: true } - ); - // Temporarily this test is skipped on Android,due to the inconsistency of the results, // which are related to getting values in raw pixels instead of density pixels on Android. /* eslint-disable jest/no-conditional-expect */ if ( ! isAndroid() ) { + await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); + + const coverBlock = await editorPage.getBlockAtPosition( + blockNames.cover + ); + const { height } = await coverBlock.getSize(); // Height is set to 20rem, where 1rem is 16. // There is also block's vertical padding equal 32. // Finally, the total height should be 20 * 16 + 32 = 352. expect( height ).toBe( 352 ); - } - /* eslint-enable jest/no-conditional-expect */ + /* eslint-enable jest/no-conditional-expect */ - await coverBlock.click(); - expect( coverBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( blockNames.cover ); + await coverBlock.click(); + expect( coverBlock ).toBeTruthy(); + await editorPage.removeBlockAtPosition( blockNames.cover ); + } } ); // Testing this for iOS on a device is valuable to ensure that it properly // handles opening multiple modals, as only one can be open at a time. it( 'allows modifying media from within block settings', async () => { - await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); - - const coverBlock = await editorPage.getBlockAtPosition( - blockNames.cover, - 1, - { useWaitForVisible: true } - ); - await coverBlock.click(); - // Can only add image from media library on iOS if ( ! isAndroid() ) { - // Open block settings. - const settingsButton = await editorPage.driver.elementByAccessibilityId( - 'Open Settings' - ); - await settingsButton.click(); + await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); - // Add initial media via button within bottom sheet. - const mediaSection = await editorPage.driver.elementByAccessibilityId( - 'Media Add image or video' + const coverBlock = await editorPage.getBlockAtPosition( + blockNames.cover ); - const addMediaButton = await mediaSection.elementByAccessibilityId( - 'Add image or video' - ); - await addMediaButton.click(); + + await editorPage.openBlockSettings( coverBlock ); + await editorPage.clickAddMediaFromCoverBlock(); await editorPage.chooseMediaLibrary(); - await editorPage.driver.sleep( 2000 ); // Await media load. - // Get Edit image button of block - const editImageButtonLocator = - '//XCUIElementTypeButton[@name="Edit image"][@enabled="true"]'; - const blockEditImageButton = await waitForVisible( - editorPage.driver, - editImageButtonLocator + const settingsButton = await coverBlock.elementByAccessibilityId( + 'Open Settings' ); - - // Edit media within block settings. await settingsButton.click(); - await editorPage.driver.sleep( 2000 ); // Await media load. - - // Get Edit image button of block settings. - // NOTE: Since we have multiple Edit image buttons at this - // point, we have to filter them to obtain the correct one. - const settingsEditImageButtons = await editorPage.driver.elementsByXPath( - editImageButtonLocator - ); - const settingsEditImageButton = settingsEditImageButtons.find( - ( element ) => element.value !== blockEditImageButton.value - ); - await settingsEditImageButton.click(); - - // Replace image. - const replaceButton = await editorPage.driver.elementByAccessibilityId( - 'Replace' - ); - await replaceButton.click(); + await editorPage.replaceMediaImage(); // First modal should no longer be presented. const replaceButtons = await editorPage.driver.elementsByAccessibilityId( @@ -101,9 +60,9 @@ describe( 'Gutenberg Editor Cover Block test', () => { // Select different media. await editorPage.chooseMediaLibrary(); - } - expect( coverBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( blockNames.cover ); + expect( coverBlock ).toBeTruthy(); + await editorPage.removeBlockAtPosition( blockNames.cover ); + } } ); } ); 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 03a929d5c1209..d07e8a3ea2cb2 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 @@ -11,11 +11,7 @@ describe( 'Gutenberg Editor Image Block tests', () => { await editorPage.closePicker(); const imageBlock = await editorPage.getBlockAtPosition( - blockNames.image, - 1, - { - useWaitForVisible: true, - } + blockNames.image ); // Can only add image from media library on iOS diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js index 7387e4b7125bc..fee5e677c099b 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js @@ -124,6 +124,8 @@ describe( 'Gutenberg Editor Search Block tests.', () => { searchBlock, 'Button inside' ); + + await editorPage.isSearchSettingsVisible(); await editorPage.dismissBottomSheet(); // Switch to html and verify. @@ -141,6 +143,8 @@ describe( 'Gutenberg Editor Search Block tests.', () => { searchBlock, 'No button' ); + + await editorPage.isSearchSettingsVisible(); await editorPage.dismissBottomSheet(); // Switch to html and verify. diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js index d234e9ae1dcae..6a9500d2561d7 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js @@ -8,8 +8,7 @@ describe( 'Gutenberg Editor Spacer Block test', () => { await editorPage.addNewBlock( blockNames.spacer ); const spacerBlock = await editorPage.getBlockAtPosition( blockNames.spacer, - 1, - { useWaitForVisible: true } + 1 ); expect( spacerBlock ).toBeTruthy(); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js index 6e1be2a13dddb..29091b389c979 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js @@ -7,7 +7,7 @@ describe( 'Gutenberg Editor Unsupported Block Editor Tests', () => { it( 'should be able to open the unsupported block web view editor', async () => { await editorPage.setHtmlContent( testData.unsupportedBlockHtml ); - const firstVisibleBlock = await editorPage.getFirstBlockVisible(); + const firstVisibleBlock = await editorPage.getUnsupportedBlockVisible(); await firstVisibleBlock.click(); const helpButton = await editorPage.getUnsupportedBlockHelpButton(); @@ -16,8 +16,7 @@ describe( 'Gutenberg Editor Unsupported Block Editor Tests', () => { const editButton = await editorPage.getUnsupportedBlockBottomSheetEditButton(); await editButton.click(); - await expect( - editorPage.getUnsupportedBlockWebView() - ).resolves.toBeTruthy(); + const webView = await editorPage.getUnsupportedBlockWebView(); + await expect( webView ).toBeTruthy(); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 1658d553c9c7a..8e0a863a53cdb 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -467,6 +467,7 @@ const waitForMediaLibrary = async ( driver ) => { /** * @param {string} driver * @param {string} elementLocator + * @param {number} length - The number of elements returned, default value is 1. Uncommon blocks may return more than 1. * @param {number} maxIteration - Default value is 25 * @param {number} iteration - Default value is 0 * @return {string} - Returns the first element found, empty string if not found @@ -474,6 +475,7 @@ const waitForMediaLibrary = async ( driver ) => { const waitForVisible = async ( driver, elementLocator, + length = 1, maxIteration = 25, iteration = 0 ) => { @@ -492,11 +494,12 @@ const waitForVisible = async ( } const element = await driver.elementsByXPath( elementLocator ); - if ( element.length !== 1 ) { + if ( element.length !== length ) { // if locator is not visible, try again return waitForVisible( driver, elementLocator, + length, maxIteration, iteration + 1 ); @@ -508,17 +511,20 @@ const waitForVisible = async ( /** * @param {string} driver * @param {string} elementLocator + * @param {number} length - The number of elements returned, default value is 1. Uncommon blocks may return more than 1. * @param {number} maxIteration - Default value is 25, can be adjusted to be less to wait for element to not be visible * @return {boolean} - Returns true if element is found, false otherwise */ const isElementVisible = async ( driver, elementLocator, + length = 1, maxIteration = 25 ) => { const element = await waitForVisible( driver, elementLocator, + length, maxIteration ); 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 440e256566ef9..91c3113c66b93 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -79,31 +79,24 @@ class EditorPage { async getBlockAtPosition( blockName, position = 1, - options = { autoscroll: false, useWaitForVisible: false } + options = { autoscroll: false } ) { - let blockLocator; - - // Make it optional to use waitForVisible() so we can handle this test by test. - // This condition can be removed once we have gone through all test cases. - if ( options.useWaitForVisible ) { - let elementType; - switch ( blockName ) { - case blockNames.cover: - elementType = 'XCUIElementTypeButton'; - break; - default: - elementType = 'XCUIElementTypeOther'; - break; - } + let elementType; + switch ( blockName ) { + case blockNames.cover: + elementType = 'XCUIElementTypeButton'; + break; + default: + elementType = 'XCUIElementTypeOther'; + break; + } - blockLocator = isAndroid() - ? `//android.view.ViewGroup[contains(@${ this.accessibilityIdXPathAttrib }, "${ blockName } Block. Row ${ position }")]` - : `(//${ elementType }[contains(@${ this.accessibilityIdXPathAttrib }, "${ blockName } Block. Row ${ position }")])[1]`; + const blockLocator = isAndroid() + ? `//android.view.ViewGroup[contains(@${ this.accessibilityIdXPathAttrib }, "${ blockName } Block. Row ${ position }")]` + : `(//${ elementType }[contains(@${ this.accessibilityIdXPathAttrib }, "${ blockName } Block. Row ${ position }")])[1]`; + + await waitForVisible( this.driver, blockLocator ); - await waitForVisible( this.driver, blockLocator ); - } else { - blockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, "${ blockName } Block. Row ${ position }")]`; - } const elements = await this.driver.elementsByXPath( blockLocator ); const lastElementFound = elements[ elements.length - 1 ]; if ( elements.length === 0 && options.autoscroll ) { @@ -164,9 +157,7 @@ class EditorPage { async hasBlockAtPosition( position = 1, blockName = '' ) { return ( undefined !== - ( await this.getBlockAtPosition( blockName, position, { - useWaitForVisible: true, - } ) ) + ( await this.getBlockAtPosition( blockName, position ) ) ); } @@ -257,14 +248,12 @@ class EditorPage { ); await pasteButton.click(); - await this.driver.sleep( 3000 ); // Wait for paste notification to disappear. } await toggleHtmlMode( this.driver, false ); } async dismissKeyboard() { - await this.driver.sleep( 1000 ); // Wait for any keyboard animations. const keyboardShown = await this.driver.isKeyboardShown(); if ( ! keyboardShown ) { return; @@ -486,9 +475,7 @@ class EditorPage { blockActionsMenuButtonLocator ); if ( isAndroid() ) { - const block = await this.getBlockAtPosition( blockName, position, { - useWaitForVisible: true, - } ); + const block = await this.getBlockAtPosition( blockName, position ); let checkList = await this.driver.elementsByXPath( blockActionsMenuButtonLocator ); @@ -608,6 +595,41 @@ class EditorPage { await this.clickToolBarButton( this.orderedListButtonName ); } + // ========================= + // Cover Block functions + // For iOS only + // ========================= + + async clickAddMediaFromCoverBlock() { + const mediaSection = await waitForVisible( + this.driver, + '//XCUIElementTypeOther[@name="Media Add image or video"]' + ); + const addMediaButton = await mediaSection.elementByAccessibilityId( + 'Add image or video' + ); + await addMediaButton.click(); + } + + async replaceMediaImage() { + await waitForVisible( + this.driver, + '(//XCUIElementTypeButton[@name="Edit image"])' + ); + + // There are multiple "Edit image" buttons layered on the screen, this is to get the right button + const editImageButtons = await this.driver.elementsByAccessibilityId( + 'Edit image' + ); + await editImageButtons[ editImageButtons.length - 1 ].click(); + + const replaceButton = await waitForVisible( + this.driver, + '//XCUIElementTypeButton[@name="Replace"]' + ); + await replaceButton.click(); + } + // ========================= // Image Block functions // ========================= @@ -685,78 +707,115 @@ class EditorPage { await this.openBlockSettings( block ); const elementName = isAndroid() ? '//*' : '//XCUIElementTypeOther'; - const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Hide search heading")]`; - return await this.driver - .elementByXPath( locator ) - .click() - .sleep( isAndroid() ? 200 : 0 ); + const hideSearchHeadingToggle = await waitForVisible( + this.driver, + locator + ); + + return await hideSearchHeadingToggle.click(); } async changeSearchButtonPositionSetting( block, buttonPosition ) { await this.openBlockSettings( block ); const elementName = isAndroid() ? '//*' : '//XCUIElementTypeButton'; - const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Button position")]`; - await this.driver.elementByXPath( locator ).click(); + let optionMenuButton = await waitForVisible( this.driver, locator ); + await optionMenuButton.click(); const optionMenuButtonLocator = `${ elementName }[contains(@${ this.accessibilityIdXPathAttrib }, "${ buttonPosition }")]`; - return await this.driver - .elementByXPath( optionMenuButtonLocator ) - .click() - .sleep( isAndroid() ? 600 : 200 ); // sleep a little longer due to multiple menus. + optionMenuButton = await waitForVisible( + this.driver, + optionMenuButtonLocator + ); + + return await optionMenuButton.click(); } async toggleSearchIconOnlySetting( block ) { await this.openBlockSettings( block ); const elementName = isAndroid() ? '//*' : '//XCUIElementTypeOther'; - const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Use icon button")]`; - return await this.driver - .elementByXPath( locator ) - .click() - .sleep( isAndroid() ? 200 : 0 ); + const useIconButton = await waitForVisible( this.driver, locator ); + + return await useIconButton.click(); + } + + async isSearchSettingsVisible() { + const elementName = isAndroid() ? '//*' : '//XCUIElementTypeButton'; + const buttonPositionLocator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Button position")]`; + + return await waitForVisible( this.driver, buttonPositionLocator ); + } + + // ============================= + // Columns Block functions + // ============================= + + async getColumnsBlockVisible() { + const firstBlockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, " Block. Row ")]`; + + const expectedLength = isAndroid() ? 12 : 6; + return await waitForVisible( + this.driver, + firstBlockLocator, + expectedLength + ); } // ============================= // Unsupported Block functions // ============================= + async getUnsupportedBlockVisible() { + const firstBlockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, " Block. Row ")]`; + + // Unsupported blocks does not return length === 1 like other blocks + // Not sure how this is derived but this is the combination that works for unsupported blocks + const expectedLength = isAndroid() ? 2 : 6; + return await waitForVisible( + this.driver, + firstBlockLocator, + expectedLength + ); + } + async getUnsupportedBlockHelpButton() { const accessibilityId = 'Help button'; - let blockLocator = - '//android.widget.Button[@content-desc="Help button, Tap here to show help"]'; + const blockLocator = isAndroid() + ? '//android.widget.Button[@content-desc="Help button, Tap here to show help"]' + : `//XCUIElementTypeButton[@name="${ accessibilityId }"]`; - if ( ! isAndroid() ) { - blockLocator = `//XCUIElementTypeButton[@name="${ accessibilityId }"]`; - } - return await this.driver.elementByXPath( blockLocator ); + const expectedLength = isAndroid() ? 2 : 1; + return await waitForVisible( + this.driver, + blockLocator, + expectedLength + ); } async getUnsupportedBlockBottomSheetEditButton() { const accessibilityId = 'Edit using web editor'; - let blockLocator = - '//android.widget.Button[@content-desc="Edit using web editor"]'; + const blockLocator = isAndroid() + ? '//android.widget.Button[@content-desc="Edit using web editor"]' + : `//XCUIElementTypeButton[@name="${ accessibilityId }"]`; - if ( ! isAndroid() ) { - blockLocator = `//XCUIElementTypeButton[@name="${ accessibilityId }"]`; - } - return await this.driver.elementByXPath( blockLocator ); + return await waitForVisible( this.driver, blockLocator ); } async getUnsupportedBlockWebView() { - let blockLocator = '//android.webkit.WebView'; - - if ( ! isAndroid() ) { - blockLocator = '//XCUIElementTypeWebView'; - } + const blockLocator = isAndroid() + ? '//android.webkit.WebView' + : '//XCUIElementTypeWebView'; - this.driver.setImplicitWaitTimeout( 20000 ); - const element = await this.driver.elementByXPath( blockLocator ); - this.driver.setImplicitWaitTimeout( 5000 ); - return element; + const expectedLength = isAndroid() ? 1 : 3; + return await waitForVisible( + this.driver, + blockLocator, + expectedLength + ); } async stopDriver() { From 85bfebf96f56c69b43e0e48ae36d885b11438c77 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Wed, 18 May 2022 19:25:15 +0800 Subject: [PATCH 02/14] fix failing tests --- .../__device-tests__/helpers/utils.js | 65 +++++++++++++++---- .../__device-tests__/pages/editor-page.js | 14 ++-- 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 8e0a863a53cdb..cc680ca064660 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -419,24 +419,30 @@ const toggleHtmlMode = async ( driver, toggleOn ) => { const showHtmlButtonXpath = '/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.ListView/android.widget.TextView[9]'; - const showHtmlButton = await driver.elementByXPath( - showHtmlButtonXpath - ); - await showHtmlButton.click(); + + await clickIfClickable( driver, showHtmlButtonXpath ); } else { - const menuButton = await driver.elementByAccessibilityId( '...' ); - await menuButton.click(); - let toggleHtmlButton; if ( toggleOn ) { - toggleHtmlButton = await driver.elementByAccessibilityId( - 'Switch to HTML' + await clickIfClickable( + driver, + '//XCUIElementTypeButton[@name="..."]' ); - } else { - toggleHtmlButton = await driver.elementByAccessibilityId( - 'Switch To Visual' + await clickIfClickable( + driver, + '//XCUIElementTypeButton[@name="Switch to HTML"]' ); } - await toggleHtmlButton.click(); + + // This is to wait for the clipboard paste notification to disappear, currently it overlaps with the menu button + await driver.sleep( 3000 ); + await clickIfClickable( + driver, + '//XCUIElementTypeButton[@name="..."]' + ); + await clickIfClickable( + driver, + '//XCUIElementTypeButton[@name="Switch To Visual"]' + ); } }; @@ -536,6 +542,38 @@ const isElementVisible = async ( return true; }; +const clickIfClickable = async ( + driver, + elementLocator, + length = 1, + maxIteration = 10, + iteration = 0 +) => { + const element = await waitForVisible( + driver, + elementLocator, + length, + maxIteration, + iteration + ); + + try { + await element.click(); + } catch ( error ) { + if ( iteration >= maxIteration ) { + // eslint-disable-next-line no-console + console.error( `Not clickable after "${ iteration }" tries` ); + } + return clickIfClickable( + driver, + elementLocator, + length, + maxIteration, + iteration + 1 + ); + } +}; + // Only for Android const waitIfAndroid = async () => { if ( isAndroid() ) { @@ -546,6 +584,7 @@ const waitIfAndroid = async () => { module.exports = { backspace, clickBeginningOfElement, + clickIfClickable, clickMiddleOfElement, doubleTap, isAndroid, 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 91c3113c66b93..d0334ad948dbe 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -225,11 +225,9 @@ class EditorPage { // Set html editor content explicitly. async setHtmlContent( html ) { await toggleHtmlMode( this.driver, true ); - const base64String = Buffer.from( html ).toString( 'base64' ); await this.driver.setClipboard( base64String, 'plaintext' ); - const htmlContentView = await this.getTextViewForHtmlViewContent(); if ( isAndroid() ) { @@ -239,14 +237,15 @@ class EditorPage { await htmlContentView.type( html ); } else { await htmlContentView.click(); + await doubleTap( this.driver, htmlContentView ); // Sometimes double tap is not enough for paste menu to appear, so we also long press. await longPressMiddleOfElement( this.driver, htmlContentView ); - const pasteButton = this.driver.elementByXPath( + const pasteButton = await waitForVisible( + this.driver, '//XCUIElementTypeMenuItem[@name="Paste"]' ); - await pasteButton.click(); } @@ -546,7 +545,12 @@ class EditorPage { ? '//android.widget.HorizontalScrollView[@content-desc="Slash inserter results"]/android.view.ViewGroup' : '(//XCUIElementTypeOther[@name="Slash inserter results"])[1]'; - return await isElementVisible( this.driver, slashInserterLocator, 5 ); + return await isElementVisible( + this.driver, + slashInserterLocator, + 1, + 5 + ); } // ========================= From db54e1856e20a4605a2714e47e9c4e0ba1ee550c Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 19 May 2022 10:59:50 +0800 Subject: [PATCH 03/14] correct if else condition --- .../__device-tests__/helpers/utils.js | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index cc680ca064660..a2bffa3ae02ee 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -421,18 +421,16 @@ const toggleHtmlMode = async ( driver, toggleOn ) => { '/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.ListView/android.widget.TextView[9]'; await clickIfClickable( driver, showHtmlButtonXpath ); + } else if ( ! isAndroid() && toggleOn ) { + await clickIfClickable( + driver, + '//XCUIElementTypeButton[@name="..."]' + ); + await clickIfClickable( + driver, + '//XCUIElementTypeButton[@name="Switch to HTML"]' + ); } else { - if ( toggleOn ) { - await clickIfClickable( - driver, - '//XCUIElementTypeButton[@name="..."]' - ); - await clickIfClickable( - driver, - '//XCUIElementTypeButton[@name="Switch to HTML"]' - ); - } - // This is to wait for the clipboard paste notification to disappear, currently it overlaps with the menu button await driver.sleep( 3000 ); await clickIfClickable( From 44322985f7f05f38025735c9a011314549f2dc72 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 19 May 2022 12:22:34 +0800 Subject: [PATCH 04/14] fix failing cover block test --- .../gutenberg-editor-cover.test.js | 5 ----- .../__device-tests__/pages/editor-page.js | 15 ++++----------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js index 216d2bf934051..4905936f7f8f3 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js @@ -44,11 +44,6 @@ describe( 'Gutenberg Editor Cover Block test', () => { await editorPage.openBlockSettings( coverBlock ); await editorPage.clickAddMediaFromCoverBlock(); await editorPage.chooseMediaLibrary(); - - const settingsButton = await coverBlock.elementByAccessibilityId( - 'Open Settings' - ); - await settingsButton.click(); await editorPage.replaceMediaImage(); // First modal should no longer be presented. 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 d0334ad948dbe..019eab3cde008 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -15,6 +15,7 @@ const { toggleHtmlMode, typeString, waitForVisible, + clickIfClickable, } = require( '../helpers/utils' ); const initializeEditorPage = async () => { @@ -616,22 +617,14 @@ class EditorPage { } async replaceMediaImage() { - await waitForVisible( + await clickIfClickable( this.driver, - '(//XCUIElementTypeButton[@name="Edit image"])' + '(//XCUIElementTypeButton[@name="Edit image"])[1]' ); - - // There are multiple "Edit image" buttons layered on the screen, this is to get the right button - const editImageButtons = await this.driver.elementsByAccessibilityId( - 'Edit image' - ); - await editImageButtons[ editImageButtons.length - 1 ].click(); - - const replaceButton = await waitForVisible( + await clickIfClickable( this.driver, '//XCUIElementTypeButton[@name="Replace"]' ); - await replaceButton.click(); } // ========================= From 08435cb969ec3751759df5c4b9f6ee98b81b5ee2 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 19 May 2022 16:08:48 +0800 Subject: [PATCH 05/14] update spaces --- .../__device-tests__/gutenberg-editor-search.test.js | 2 -- .../react-native-editor/__device-tests__/pages/editor-page.js | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js index fee5e677c099b..2233b56f6c37d 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js @@ -124,7 +124,6 @@ describe( 'Gutenberg Editor Search Block tests.', () => { searchBlock, 'Button inside' ); - await editorPage.isSearchSettingsVisible(); await editorPage.dismissBottomSheet(); @@ -143,7 +142,6 @@ describe( 'Gutenberg Editor Search Block tests.', () => { searchBlock, 'No button' ); - await editorPage.isSearchSettingsVisible(); await editorPage.dismissBottomSheet(); 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 019eab3cde008..43028d9bf151e 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -226,9 +226,11 @@ class EditorPage { // Set html editor content explicitly. async setHtmlContent( html ) { await toggleHtmlMode( this.driver, true ); + const base64String = Buffer.from( html ).toString( 'base64' ); await this.driver.setClipboard( base64String, 'plaintext' ); + const htmlContentView = await this.getTextViewForHtmlViewContent(); if ( isAndroid() ) { @@ -238,7 +240,6 @@ class EditorPage { await htmlContentView.type( html ); } else { await htmlContentView.click(); - await doubleTap( this.driver, htmlContentView ); // Sometimes double tap is not enough for paste menu to appear, so we also long press. await longPressMiddleOfElement( this.driver, htmlContentView ); @@ -247,6 +248,7 @@ class EditorPage { this.driver, '//XCUIElementTypeMenuItem[@name="Paste"]' ); + await pasteButton.click(); } From 65f3c62c7eb9763957c02f1dfa072e0d9d736fb5 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 19 May 2022 19:00:10 +0800 Subject: [PATCH 06/14] update to use new click helper --- .../__device-tests__/pages/editor-page.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 43028d9bf151e..57f2c1678e747 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -566,20 +566,17 @@ class EditorPage { ) { // iOS needs a few extra steps to get the text element if ( ! isAndroid() ) { - // Wait for and click the list in the correct position - let listBlock = await waitForVisible( + // Click the list in the correct position + await clickIfClickable( this.driver, `(//XCUIElementTypeOther[contains(@name, "List Block. Row ${ position }")])[1]` ); - await listBlock.click(); const listBlockLocator = options.isEmptyBlock ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` : `//XCUIElementTypeButton[contains(@name, "List")]`; - // Wait for and click the list to get the text element - listBlock = await waitForVisible( this.driver, listBlockLocator ); - await listBlock.click(); + await clickIfClickable( this.driver, listBlockLocator ); } const listBlockTextLocatorIOS = options.isEmptyBlock From 1a00cd10b7fe4a608fad83dfbe879981dbae5951 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 19 May 2022 20:19:33 +0800 Subject: [PATCH 07/14] wait for blocks to be visible first in getFirstBlock and getLastBlock --- .../__device-tests__/gutenberg-editor-spacer.test.js | 3 +-- .../react-native-editor/__device-tests__/pages/editor-page.js | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js index 6a9500d2561d7..721ae86ea7eee 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js @@ -7,8 +7,7 @@ describe( 'Gutenberg Editor Spacer Block test', () => { it( 'should be able to add a spacer block', async () => { await editorPage.addNewBlock( blockNames.spacer ); const spacerBlock = await editorPage.getBlockAtPosition( - blockNames.spacer, - 1 + blockNames.spacer ); expect( spacerBlock ).toBeTruthy(); 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 57f2c1678e747..741910eccdb8a 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -145,12 +145,12 @@ class EditorPage { async getFirstBlockVisible() { const firstBlockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, " Block. Row ")]`; - const elements = await this.driver.elementsByXPath( firstBlockLocator ); - return elements[ 0 ]; + return await waitForVisible( this.driver, firstBlockLocator ); } 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 ]; } From a5ba8292fda304a38dd34eeef25b8e9b6eaaca91 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 19 May 2022 21:22:40 +0800 Subject: [PATCH 08/14] remove length as a parameter --- .../__device-tests__/helpers/utils.js | 11 +------ .../__device-tests__/pages/editor-page.js | 30 +++---------------- 2 files changed, 5 insertions(+), 36 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index a2bffa3ae02ee..c8d2719e77dac 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -471,7 +471,6 @@ const waitForMediaLibrary = async ( driver ) => { /** * @param {string} driver * @param {string} elementLocator - * @param {number} length - The number of elements returned, default value is 1. Uncommon blocks may return more than 1. * @param {number} maxIteration - Default value is 25 * @param {number} iteration - Default value is 0 * @return {string} - Returns the first element found, empty string if not found @@ -479,7 +478,6 @@ const waitForMediaLibrary = async ( driver ) => { const waitForVisible = async ( driver, elementLocator, - length = 1, maxIteration = 25, iteration = 0 ) => { @@ -498,12 +496,11 @@ const waitForVisible = async ( } const element = await driver.elementsByXPath( elementLocator ); - if ( element.length !== length ) { + if ( element.length === 0 ) { // if locator is not visible, try again return waitForVisible( driver, elementLocator, - length, maxIteration, iteration + 1 ); @@ -515,20 +512,17 @@ const waitForVisible = async ( /** * @param {string} driver * @param {string} elementLocator - * @param {number} length - The number of elements returned, default value is 1. Uncommon blocks may return more than 1. * @param {number} maxIteration - Default value is 25, can be adjusted to be less to wait for element to not be visible * @return {boolean} - Returns true if element is found, false otherwise */ const isElementVisible = async ( driver, elementLocator, - length = 1, maxIteration = 25 ) => { const element = await waitForVisible( driver, elementLocator, - length, maxIteration ); @@ -543,14 +537,12 @@ const isElementVisible = async ( const clickIfClickable = async ( driver, elementLocator, - length = 1, maxIteration = 10, iteration = 0 ) => { const element = await waitForVisible( driver, elementLocator, - length, maxIteration, iteration ); @@ -565,7 +557,6 @@ const clickIfClickable = async ( return clickIfClickable( driver, elementLocator, - length, maxIteration, 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 741910eccdb8a..433f48b5df695 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -753,12 +753,7 @@ class EditorPage { async getColumnsBlockVisible() { const firstBlockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, " Block. Row ")]`; - const expectedLength = isAndroid() ? 12 : 6; - return await waitForVisible( - this.driver, - firstBlockLocator, - expectedLength - ); + return await waitForVisible( this.driver, firstBlockLocator ); } // ============================= @@ -768,14 +763,7 @@ class EditorPage { async getUnsupportedBlockVisible() { const firstBlockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, " Block. Row ")]`; - // Unsupported blocks does not return length === 1 like other blocks - // Not sure how this is derived but this is the combination that works for unsupported blocks - const expectedLength = isAndroid() ? 2 : 6; - return await waitForVisible( - this.driver, - firstBlockLocator, - expectedLength - ); + return await waitForVisible( this.driver, firstBlockLocator ); } async getUnsupportedBlockHelpButton() { @@ -784,12 +772,7 @@ class EditorPage { ? '//android.widget.Button[@content-desc="Help button, Tap here to show help"]' : `//XCUIElementTypeButton[@name="${ accessibilityId }"]`; - const expectedLength = isAndroid() ? 2 : 1; - return await waitForVisible( - this.driver, - blockLocator, - expectedLength - ); + return await waitForVisible( this.driver, blockLocator ); } async getUnsupportedBlockBottomSheetEditButton() { @@ -806,12 +789,7 @@ class EditorPage { ? '//android.webkit.WebView' : '//XCUIElementTypeWebView'; - const expectedLength = isAndroid() ? 1 : 3; - return await waitForVisible( - this.driver, - blockLocator, - expectedLength - ); + return await waitForVisible( this.driver, blockLocator ); } async stopDriver() { From 9dc1b8f382ded235400087efd888f6c1ccd7c014 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Fri, 20 May 2022 10:29:14 +0800 Subject: [PATCH 09/14] update timing for long press since it's failing intermittently in ci --- .../react-native-editor/__device-tests__/helpers/utils.js | 3 ++- .../__device-tests__/pages/editor-page.js | 6 +----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index c8d2719e77dac..d8d0b7f0eb6c3 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -310,7 +310,8 @@ const longPressMiddleOfElement = async ( driver, element ) => { const x = location.x + size.width / 2; const y = location.y + size.height / 2; action.press( { x, y } ); - action.wait( 2000 ); + // Setting to wait a bit longer because this is failing more frequently on the CI + action.wait( 3500 ); action.release(); await action.perform(); }; 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 433f48b5df695..9e87d4716c48c 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -316,11 +316,7 @@ class EditorPage { ? '//android.widget.Button[@content-desc="Add Block Before"]' : '//XCUIElementTypeButton[@name="Add Block Before"]'; - const addBlockBeforeButton = await waitForVisible( - this.driver, - addBlockBeforeButtonLocator - ); - await addBlockBeforeButton.click(); + await clickIfClickable( this.driver, addBlockBeforeButtonLocator ); } else { await addButton.click(); } From dd7aa72437e2ecd51c375da82f291ad3b7702679 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Fri, 20 May 2022 11:13:30 +0800 Subject: [PATCH 10/14] remove deleted param, revert space changes --- .../__device-tests__/helpers/utils.js | 4 +++- .../__device-tests__/pages/editor-page.js | 10 ++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index d8d0b7f0eb6c3..66e1cc935d8f9 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -553,7 +553,9 @@ const clickIfClickable = async ( } catch ( error ) { if ( iteration >= maxIteration ) { // eslint-disable-next-line no-console - console.error( `Not clickable after "${ iteration }" tries` ); + console.error( + `Element still not clickable after "${ iteration }" retries` + ); } return clickIfClickable( driver, 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 9e87d4716c48c..62ca5f45b9d54 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -544,12 +544,7 @@ class EditorPage { ? '//android.widget.HorizontalScrollView[@content-desc="Slash inserter results"]/android.view.ViewGroup' : '(//XCUIElementTypeOther[@name="Slash inserter results"])[1]'; - return await isElementVisible( - this.driver, - slashInserterLocator, - 1, - 5 - ); + return await isElementVisible( this.driver, slashInserterLocator, 5 ); } // ========================= @@ -699,6 +694,7 @@ class EditorPage { await this.openBlockSettings( block ); const elementName = isAndroid() ? '//*' : '//XCUIElementTypeOther'; + const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Hide search heading")]`; const hideSearchHeadingToggle = await waitForVisible( this.driver, @@ -712,6 +708,7 @@ class EditorPage { await this.openBlockSettings( block ); const elementName = isAndroid() ? '//*' : '//XCUIElementTypeButton'; + const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Button position")]`; let optionMenuButton = await waitForVisible( this.driver, locator ); await optionMenuButton.click(); @@ -729,6 +726,7 @@ class EditorPage { await this.openBlockSettings( block ); const elementName = isAndroid() ? '//*' : '//XCUIElementTypeOther'; + const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Use icon button")]`; const useIconButton = await waitForVisible( this.driver, locator ); From 0b2c965aad459bf3232236c94aca5601c550f0f5 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Fri, 20 May 2022 11:56:04 +0800 Subject: [PATCH 11/14] remove redundant code --- .../gutenberg-editor-columns.test.js | 2 +- ...utenberg-editor-unsupported-blocks.test.js | 2 +- .../__device-tests__/pages/editor-page.js | 20 ++----------------- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js index fc4dc3a870f84..b57ea2588d7ee 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js @@ -10,7 +10,7 @@ describe( 'Gutenberg Editor Columns Block test', () => { testData.columnsWithDifferentUnitsHtml ); - const columnsBlock = await editorPage.getColumnsBlockVisible(); + const columnsBlock = await editorPage.getFirstBlockVisible(); await columnsBlock.click(); expect( columnsBlock ).toBeTruthy(); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js index 29091b389c979..e2ae927d4f944 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js @@ -7,7 +7,7 @@ describe( 'Gutenberg Editor Unsupported Block Editor Tests', () => { it( 'should be able to open the unsupported block web view editor', async () => { await editorPage.setHtmlContent( testData.unsupportedBlockHtml ); - const firstVisibleBlock = await editorPage.getUnsupportedBlockVisible(); + const firstVisibleBlock = await editorPage.getFirstBlockVisible(); await firstVisibleBlock.click(); const helpButton = await editorPage.getUnsupportedBlockHelpButton(); 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 62ca5f45b9d54..f7e69057ece2a 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -740,30 +740,14 @@ class EditorPage { return await waitForVisible( this.driver, buttonPositionLocator ); } - // ============================= - // Columns Block functions - // ============================= - - async getColumnsBlockVisible() { - const firstBlockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, " Block. Row ")]`; - - return await waitForVisible( this.driver, firstBlockLocator ); - } - // ============================= // Unsupported Block functions // ============================= - async getUnsupportedBlockVisible() { - const firstBlockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, " Block. Row ")]`; - - return await waitForVisible( this.driver, firstBlockLocator ); - } - async getUnsupportedBlockHelpButton() { const accessibilityId = 'Help button'; const blockLocator = isAndroid() - ? '//android.widget.Button[@content-desc="Help button, Tap here to show help"]' + ? `//android.widget.Button[starts-with(@content-desc, "${ accessibilityId }")]` : `//XCUIElementTypeButton[@name="${ accessibilityId }"]`; return await waitForVisible( this.driver, blockLocator ); @@ -772,7 +756,7 @@ class EditorPage { async getUnsupportedBlockBottomSheetEditButton() { const accessibilityId = 'Edit using web editor'; const blockLocator = isAndroid() - ? '//android.widget.Button[@content-desc="Edit using web editor"]' + ? `//android.widget.Button[@content-desc="${ accessibilityId }"]` : `//XCUIElementTypeButton[@name="${ accessibilityId }"]`; return await waitForVisible( this.driver, blockLocator ); From 46798aff0d1771e2bce6267913b52e6ed11f52b8 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Fri, 20 May 2022 16:12:08 +0800 Subject: [PATCH 12/14] exit function once condition is met --- .../react-native-editor/__device-tests__/helpers/utils.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 66e1cc935d8f9..fc002232c98a8 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -538,7 +538,7 @@ const isElementVisible = async ( const clickIfClickable = async ( driver, elementLocator, - maxIteration = 10, + maxIteration = 25, iteration = 0 ) => { const element = await waitForVisible( @@ -556,7 +556,9 @@ const clickIfClickable = async ( console.error( `Element still not clickable after "${ iteration }" retries` ); + return ''; } + return clickIfClickable( driver, elementLocator, From 36adacdf183ed04c05eed89b28711a7b11655666 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Mon, 23 May 2022 10:07:08 +0800 Subject: [PATCH 13/14] increse wait time for long press --- packages/react-native-editor/__device-tests__/helpers/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index fc002232c98a8..842da5d3d4e1a 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -311,7 +311,7 @@ const longPressMiddleOfElement = async ( driver, element ) => { const y = location.y + size.height / 2; action.press( { x, y } ); // Setting to wait a bit longer because this is failing more frequently on the CI - action.wait( 3500 ); + action.wait( 5000 ); action.release(); await action.perform(); }; From 86a5d30805990eb22387907c2cf352b3dee8ea21 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Wed, 25 May 2022 12:14:25 +0800 Subject: [PATCH 14/14] remove unneccesary condition, update message, return click value --- .../react-native-editor/__device-tests__/helpers/utils.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 842da5d3d4e1a..b3225a03cfbae 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -422,7 +422,7 @@ const toggleHtmlMode = async ( driver, toggleOn ) => { '/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.ListView/android.widget.TextView[9]'; await clickIfClickable( driver, showHtmlButtonXpath ); - } else if ( ! isAndroid() && toggleOn ) { + } else if ( toggleOn ) { await clickIfClickable( driver, '//XCUIElementTypeButton[@name="..."]' @@ -549,12 +549,12 @@ const clickIfClickable = async ( ); try { - await element.click(); + return await element.click(); } catch ( error ) { if ( iteration >= maxIteration ) { // eslint-disable-next-line no-console console.error( - `Element still not clickable after "${ iteration }" retries` + `"${ elementLocator }" still not clickable after "${ iteration }" retries` ); return ''; }