From 8b70472d143cf6d890c22e67063c23f4192eef46 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 28 Apr 2022 12:56:42 +0800 Subject: [PATCH 01/20] update tests using paragraph, heading and list blocks --- .../gutenberg-editor-heading-@canary.test.js | 26 ++-- .../gutenberg-editor-lists-@canary.test.js | 36 ++--- .../gutenberg-editor-lists-end.test.js | 21 +-- .../gutenberg-editor-lists.test.js | 22 +-- .../gutenberg-editor-paragraph.test.js | 20 +-- .../gutenberg-editor-paste.test.js | 14 +- .../gutenberg-editor-rotation.test.js | 19 +-- ...berg-editor-slash-inserter-@canary.test.js | 49 ++----- .../__device-tests__/pages/editor-page.js | 135 +++++++----------- 9 files changed, 126 insertions(+), 216 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js index 17f58bfec5f71e..bdcfbd94f79e92 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js @@ -2,26 +2,18 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { isAndroid } from './helpers/utils'; import testData from './helpers/test-data'; describe( 'Gutenberg Editor tests', () => { it( 'should be able to create a post with heading and paragraph blocks', async () => { await editorPage.addNewBlock( blockNames.heading ); - let headingBlockElement = await editorPage.getBlockAtPosition( - blockNames.heading, - 1, - { - useWaitForVisible: true, - } + let headingBlockElement = await editorPage.getTextBlockAtPosition( + blockNames.heading ); - if ( isAndroid() ) { - await headingBlockElement.click(); - } - await editorPage.sendTextToHeadingBlock( + + await editorPage.typeTextToTextBlock( headingBlockElement, - testData.heading, - false + testData.heading ); await editorPage.addNewBlock( blockNames.paragraph ); @@ -29,7 +21,7 @@ describe( 'Gutenberg Editor tests', () => { blockNames.paragraph, 2 ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.mediumText ); @@ -39,7 +31,7 @@ describe( 'Gutenberg Editor tests', () => { blockNames.paragraph, 3 ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.mediumText ); @@ -49,7 +41,7 @@ describe( 'Gutenberg Editor tests', () => { blockNames.heading, 4 ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( headingBlockElement, testData.heading ); @@ -59,7 +51,7 @@ describe( 'Gutenberg Editor tests', () => { blockNames.paragraph, 5 ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.mediumText ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js index 80939bd9e58018..875c876680e868 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js @@ -8,27 +8,28 @@ import testData from './helpers/test-data'; describe( 'Gutenberg Editor tests for List block', () => { it( 'should be able to add a new List block', async () => { await editorPage.addNewBlock( blockNames.list ); - const listBlockElement = await editorPage.getBlockAtPosition( - blockNames.list - ); - // Click List block on Android to force EditText focus - if ( isAndroid() ) { - await listBlockElement.click(); - } + let listBlockElement = await editorPage.getListBlock( { + isEmptyBlock: true, + } ); - // Send the first list item text. - await editorPage.sendTextToListBlock( + await editorPage.typeTextToTextBlock( listBlockElement, - testData.listItem1 + testData.listItem1, + false ); + listBlockElement = await editorPage.getListBlock(); + // Send an Enter. - await editorPage.sendTextToListBlock( listBlockElement, '\n' ); + await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); + + listBlockElement = await editorPage.getListBlock(); // Send the second list item text. - await editorPage.sendTextToListBlock( + await editorPage.typeTextToTextBlock( listBlockElement, - testData.listItem2 + testData.listItem2, + false ); // Switch to html and verify html. @@ -38,12 +39,11 @@ describe( 'Gutenberg Editor tests for List block', () => { // This test depends on being run immediately after 'should be able to add a new List block' it( 'should update format to ordered list, using toolbar button', async () => { - let listBlockElement = await editorPage.getBlockAtPosition( - blockNames.list - ); + let listBlockElement = await editorPage.getListBlock(); - // Click List block to force EditText focus. - await listBlockElement.click(); + if ( isAndroid() ) { + listBlockElement.click(); + } // Send a click on the order list format button. await editorPage.clickOrderedListToolBarButton(); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js index d113be5371a387..a81fcb8b9a344a 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js @@ -2,32 +2,21 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { isAndroid } from './helpers/utils'; import testData from './helpers/test-data'; describe( 'Gutenberg Editor tests for List block (end)', () => { it( 'should be able to end a List block', async () => { await editorPage.addNewBlock( blockNames.list ); - const listBlockElement = await editorPage.getBlockAtPosition( - blockNames.list - ); - - // Click List block on Android to force EditText focus - if ( isAndroid() ) { - await listBlockElement.click(); - } + const listBlockElement = await editorPage.getListBlock(); - // Send the first list item text. - await editorPage.sendTextToListBlock( + await editorPage.typeTextToTextBlock( listBlockElement, - testData.listItem1 + testData.listItem1, + false ); // Send an Enter. - await editorPage.sendTextToListBlock( listBlockElement, '\n' ); - - // Send an Enter. - await editorPage.sendTextToListBlock( listBlockElement, '\n' ); + await editorPage.typeTextToTextBlock( listBlockElement, '\n\n', false ); const html = await editorPage.getHtmlContent(); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index 12537bfcae5350..e0a8d9e4f8a5de 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -8,22 +8,24 @@ describe( 'Gutenberg Editor tests for List block', () => { // Prevent regression of https://github.com/wordpress-mobile/gutenberg-mobile/issues/871 it( 'should handle spaces in a list', async () => { await editorPage.addNewBlock( blockNames.list ); - let listBlockElement = await editorPage.getBlockAtPosition( - blockNames.list - ); - // Click List block on Android to force EditText focus - if ( isAndroid() ) { - await listBlockElement.click(); - } + let listBlockElement = await editorPage.getListBlock(); // Send the list item text. - await editorPage.sendTextToListBlock( listBlockElement, ' a' ); + await editorPage.typeTextToTextBlock( listBlockElement, ' a', false ); // Send an Enter. - await editorPage.sendTextToListBlock( listBlockElement, '\n' ); + await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); + // Click List block on Android to force EditText focus + if ( isAndroid() ) { + await listBlockElement.click(); + } // Send a backspace. - await editorPage.sendTextToListBlock( listBlockElement, backspace ); + await editorPage.typeTextToTextBlock( + listBlockElement, + backspace, + false + ); // Switch to html and verify html. const html = await editorPage.getHtmlContent(); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js index 0ac9bae0743ef3..20d7d690b96bfc 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js @@ -16,12 +16,12 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.shortText ); await clickMiddleOfElement( editorPage.driver, paragraphBlockElement ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, '\n', false @@ -44,19 +44,13 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { let paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.shortText ); await clickMiddleOfElement( editorPage.driver, paragraphBlockElement ); - await editorPage.typeTextToParagraphBlock( - paragraphBlockElement, - '\n' - ); + await editorPage.typeTextToTextBlock( paragraphBlockElement, '\n' ); const text0 = await editorPage.getTextForParagraphBlockAtPosition( 1 ); const text1 = await editorPage.getTextForParagraphBlockAtPosition( 2 ); @@ -71,7 +65,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { paragraphBlockElement ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, backspace ); @@ -112,7 +106,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { editorPage.driver, paragraphBlockElement ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, backspace ); @@ -140,7 +134,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { blockNames.paragraph, 2 ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, backspace ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js index 16c291584b8ed7..7404b0969d6adb 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js @@ -29,11 +29,8 @@ describe( 'Gutenberg Editor paste tests', () => { const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.pastePlainText ); @@ -59,9 +56,6 @@ describe( 'Gutenberg Editor paste tests', () => { blockNames.paragraph, 2 ); - if ( isAndroid() ) { - await paragraphBlockElement2.click(); - } // Paste into second paragraph block. await longPressMiddleOfElement( @@ -83,9 +77,6 @@ describe( 'Gutenberg Editor paste tests', () => { const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } // Copy content to clipboard. await longPressMiddleOfElement( @@ -108,9 +99,6 @@ describe( 'Gutenberg Editor paste tests', () => { blockNames.paragraph, 2 ); - if ( isAndroid() ) { - await paragraphBlockElement2.click(); - } // Paste into second paragraph block. await longPressMiddleOfElement( diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js index 9056854fa42c7f..215b81bf98b4d4 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js @@ -11,11 +11,8 @@ describe( 'Gutenberg Editor tests', () => { let paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.mediumText ); @@ -23,27 +20,21 @@ describe( 'Gutenberg Editor tests', () => { await toggleOrientation( editorPage.driver ); // On Android the keyboard hides the add block button, let's hide it after rotation if ( isAndroid() ) { - await editorPage.driver.hideDeviceKeyboard(); + await editorPage.dismissKeyboard(); } await editorPage.addNewBlock( blockNames.paragraph ); if ( isAndroid() ) { - await editorPage.driver.hideDeviceKeyboard(); + await editorPage.dismissKeyboard(); } paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph, 2 ); - while ( ! paragraphBlockElement ) { - await editorPage.driver.hideDeviceKeyboard(); - paragraphBlockElement = await editorPage.getBlockAtPosition( - blockNames.paragraph, - 2 - ); - } - await editorPage.typeTextToParagraphBlock( + + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.mediumText ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js index 563445768709a7..8f55b81f26cb27 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js @@ -5,8 +5,6 @@ import { blockNames } from './pages/editor-page'; import { isAndroid } from './helpers/utils'; import { slashInserter, shortText } from './helpers/test-data'; -const ANIMATION_TIME = 200; - // Helper function for asserting slash inserter presence. async function assertSlashInserterPresent( checkIsVisible ) { let areResultsDisplayed; @@ -25,24 +23,17 @@ async function assertSlashInserterPresent( checkIsVisible ) { } } -// Due to flakiness, disabling until its more stable -// https://github.com/wordpress-mobile/gutenberg-mobile/issues/3699 -// eslint-disable-next-line jest/no-disabled-tests -describe.skip( 'Gutenberg Editor Slash Inserter tests', () => { +describe( 'Gutenberg Editor Slash Inserter tests', () => { it( 'should show the menu after typing /', async () => { await editorPage.addNewBlock( blockNames.paragraph ); - const paragraphBlockElement = await editorPage.getBlockAtPosition( + const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, slashInserter ); - await editorPage.driver.sleep( ANIMATION_TIME ); assertSlashInserterPresent( true ); @@ -51,36 +42,31 @@ describe.skip( 'Gutenberg Editor Slash Inserter tests', () => { it( 'should hide the menu after deleting the / character', async () => { await editorPage.addNewBlock( blockNames.paragraph ); - const paragraphBlockElement = await editorPage.getBlockAtPosition( + const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, slashInserter ); - await editorPage.driver.sleep( ANIMATION_TIME ); assertSlashInserterPresent( true ); // Remove / character. if ( isAndroid() ) { - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, `${ shortText }`, true ); } else { - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, `\b ${ shortText }`, false ); } - await editorPage.driver.sleep( ANIMATION_TIME ); // Check if the slash inserter UI no longer exists. assertSlashInserterPresent( false ); @@ -90,18 +76,14 @@ describe.skip( 'Gutenberg Editor Slash Inserter tests', () => { it( 'should add an Image block after tying /image and tapping on the Image block button', async () => { await editorPage.addNewBlock( blockNames.paragraph ); - const paragraphBlockElement = await editorPage.getBlockAtPosition( + const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, `${ slashInserter }image` ); - await editorPage.driver.sleep( ANIMATION_TIME ); assertSlashInserterPresent( true ); @@ -126,24 +108,21 @@ describe.skip( 'Gutenberg Editor Slash Inserter tests', () => { await editorPage.removeBlockAtPosition( blockNames.image ); } ); - it( 'should insert an image block with "/img" + enter', async () => { + it( 'should insert an embed image block with "/img" + enter', async () => { await editorPage.addNewBlock( blockNames.paragraph ); - const paragraphBlockElement = await editorPage.getBlockAtPosition( + const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, '/img\n', false ); expect( - await editorPage.hasBlockAtPosition( 1, blockNames.image ) + await editorPage.hasBlockAtPosition( 1, blockNames.embed ) ).toBe( true ); - await editorPage.removeBlockAtPosition( blockNames.image ); + await editorPage.removeBlockAtPosition( blockNames.embed ); } ); } ); 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 34cd18f05243bb..8688c2a86897d5 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -2,17 +2,17 @@ * Internal dependencies */ const { + doubleTap, + isAndroid, + isEditorVisible, + longPressMiddleOfElement, setupDriver, stopDriver, - isAndroid, - swipeUp, swipeDown, - typeString, - toggleHtmlMode, swipeFromTo, - longPressMiddleOfElement, - doubleTap, - isEditorVisible, + swipeUp, + toggleHtmlMode, + typeString, waitForVisible, } = require( '../helpers/utils' ); @@ -45,11 +45,15 @@ class EditorPage { return await this.driver.hasElementByAccessibilityId( 'block-list' ); } - // For text blocks, e.g. Paragraph, Heading + // =============================== + // Text blocks functions + // E.g. Paragraph, Heading blocks + // =============================== async getTextBlockAtPosition( blockName, position = 1 ) { - // iOS needs a click before + // iOS needs a click to get the text element if ( ! isAndroid() ) { const textBlockLocator = `(//XCUIElementTypeButton[contains(@name, "${ blockName } Block. Row ${ position }")])`; + const textBlock = await waitForVisible( this.driver, textBlockLocator @@ -64,6 +68,10 @@ class EditorPage { return await waitForVisible( this.driver, blockLocator ); } + async typeTextToTextBlock( block, text, clear ) { + await typeString( this.driver, block, text, clear ); + } + // Finds the wd element for new block that was added and sets the element attribute // and accessibilityId attributes on this object and selects the block // position uses one based numbering. @@ -506,10 +514,6 @@ class EditorPage { // Paragraph Block functions // ========================= - async typeTextToParagraphBlock( block, text, clear ) { - await typeString( this.driver, block, text, clear ); - } - async sendTextToParagraphBlock( position, text, clear ) { const paragraphs = text.split( '\n' ); for ( let i = 0; i < paragraphs.length; i++ ) { @@ -522,13 +526,9 @@ class EditorPage { await block.click(); } - await this.typeTextToParagraphBlock( - block, - paragraphs[ i ], - clear - ); + await this.typeTextToTextBlock( block, paragraphs[ i ], clear ); if ( i !== paragraphs.length - 1 ) { - await this.typeTextToParagraphBlock( block, '\n', false ); + await this.typeTextToTextBlock( block, '\n', false ); } } } @@ -542,32 +542,44 @@ class EditorPage { return await blockLocator.text(); } + async getNumberOfParagraphBlocks() { + const paragraphBlockLocator = isAndroid() + ? `//android.view.ViewGroup[contains(@content-desc, "Paragraph Block. Row")]/android.widget.EditText` + : `(//XCUIElementTypeButton[contains(@name, "Paragraph Block. Row")])`; + const locator = await this.driver.elementsByXPath( + paragraphBlockLocator + ); + return locator.length; + } + // ========================= // List Block functions // ========================= - async getTextViewForListBlock( block ) { - let textViewElementName = 'XCUIElementTypeTextView'; - if ( isAndroid() ) { - textViewElementName = 'android.widget.EditText'; - } + async getListBlock( options = { isEmptyBlock: false } ) { + // iOS needs a click to get the text element + if ( ! isAndroid() ) { + const listBlockLocator = options.isEmptyBlock + ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` + : `//XCUIElementTypeButton[contains(@name, "List")]`; - const accessibilityId = await block.getAttribute( - this.accessibilityIdKey - ); - const blockLocator = `//*[@${ - this.accessibilityIdXPathAttrib - }=${ JSON.stringify( accessibilityId ) }]//${ textViewElementName }`; - return await this.driver.elementByXPath( blockLocator ); - } + const textBlock = await waitForVisible( + this.driver, + listBlockLocator + ); + await textBlock.click(); + } - async sendTextToListBlock( block, text ) { - const textViewElement = await this.getTextViewForListBlock( block ); + let listBlockTextLocator = + ! isAndroid() && options.isEmptyBlock + ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` + : `//XCUIElementTypeButton[contains(@name, "List")]//XCUIElementTypeTextView`; - // Cannot clear list blocks because it messes up the list bullet. - const clear = false; + if ( isAndroid() ) { + listBlockTextLocator = `//android.view.ViewGroup[contains(@content-desc, "List Block.")]/android.widget.EditText`; + } - return await typeString( this.driver, textViewElement, text, clear ); + return await waitForVisible( this.driver, listBlockTextLocator ); } async clickOrderedListToolBarButton() { @@ -609,34 +621,6 @@ class EditorPage { await typeString( this.driver, imageBlockCaptionField, caption, clear ); } - // ========================= - // Heading Block functions - // ========================= - - // Inner element changes on iOS if Heading Block is empty - async getTextViewForHeadingBlock( block, empty ) { - let textViewElementName = empty - ? 'XCUIElementTypeStaticText' - : 'XCUIElementTypeTextView'; - if ( isAndroid() ) { - textViewElementName = 'android.widget.EditText'; - } - - const accessibilityId = await block.getAttribute( - this.accessibilityIdKey - ); - const blockLocator = `//*[@${ this.accessibilityIdXPathAttrib }="${ accessibilityId }"]//${ textViewElementName }`; - return await this.driver.elementByXPath( blockLocator ); - } - - async sendTextToHeadingBlock( block, text, clear = true ) { - const textViewElement = await this.getTextViewForHeadingBlock( - block, - true - ); - return await typeString( this.driver, textViewElement, text, clear ); - } - async closePicker() { if ( isAndroid() ) { // Wait for media block picker to load before closing @@ -760,34 +744,25 @@ class EditorPage { async sauceJobStatus( allPassed ) { await this.driver.sauceJobStatus( allPassed ); } - - async getNumberOfParagraphBlocks() { - const paragraphBlockLocator = isAndroid() - ? `//android.view.ViewGroup[contains(@content-desc, "Paragraph Block. Row")]/android.widget.EditText` - : `(//XCUIElementTypeButton[contains(@name, "Paragraph Block. Row")])`; - const locator = await this.driver.elementsByXPath( - paragraphBlockLocator - ); - return locator.length; - } } const blockNames = { - paragraph: 'Paragraph', - gallery: 'Gallery', + audio: 'Audio', columns: 'Columns', cover: 'Cover', + embed: 'Embed', + file: 'File', + gallery: 'Gallery', heading: 'Heading', image: 'Image', latestPosts: 'Latest Posts', list: 'List', more: 'More', + paragraph: 'Paragraph', + search: 'Search', separator: 'Separator', spacer: 'Spacer', verse: 'Verse', - file: 'File', - audio: 'Audio', - search: 'Search', }; module.exports = { initializeEditorPage, blockNames }; From c5bf57b0740c04b724284d552cd6d9c4d64d6a11 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 28 Apr 2022 14:46:15 +0800 Subject: [PATCH 02/20] fix slash inserter tests to work in ci --- .../gutenberg-editor-heading-@canary.test.js | 3 ++ ...berg-editor-slash-inserter-@canary.test.js | 29 ++++--------------- .../__device-tests__/helpers/utils.js | 17 +++++++++-- .../__device-tests__/pages/editor-page.js | 8 +++++ 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js index bdcfbd94f79e92..f806b43d2f8e42 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js @@ -55,5 +55,8 @@ describe( 'Gutenberg Editor tests', () => { paragraphBlockElement, testData.mediumText ); + + // Assert that even though there are 5 blocks, there should only be 3 paragraph blocks + expect( await editorPage.getNumberOfParagraphBlocks() ).toEqual( 3 ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js index 8f55b81f26cb27..d16e37bc7090f5 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js @@ -5,24 +5,6 @@ import { blockNames } from './pages/editor-page'; import { isAndroid } from './helpers/utils'; import { slashInserter, shortText } from './helpers/test-data'; -// Helper function for asserting slash inserter presence. -async function assertSlashInserterPresent( checkIsVisible ) { - let areResultsDisplayed; - try { - const foundElements = await editorPage.driver.elementsByAccessibilityId( - 'Slash inserter results' - ); - areResultsDisplayed = !! foundElements.length; - } catch ( e ) { - areResultsDisplayed = false; - } - if ( checkIsVisible ) { - expect( areResultsDisplayed ).toBeTruthy(); - } else { - expect( areResultsDisplayed ).toBeFalsy(); - } -} - describe( 'Gutenberg Editor Slash Inserter tests', () => { it( 'should show the menu after typing /', async () => { await editorPage.addNewBlock( blockNames.paragraph ); @@ -35,8 +17,7 @@ describe( 'Gutenberg Editor Slash Inserter tests', () => { slashInserter ); - assertSlashInserterPresent( true ); - + expect( await editorPage.assertSlashInserterPresent() ).toBe( true ); await editorPage.removeBlockAtPosition( blockNames.paragraph ); } ); @@ -51,7 +32,7 @@ describe( 'Gutenberg Editor Slash Inserter tests', () => { slashInserter ); - assertSlashInserterPresent( true ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( true ); // Remove / character. if ( isAndroid() ) { @@ -69,7 +50,7 @@ describe( 'Gutenberg Editor Slash Inserter tests', () => { } // Check if the slash inserter UI no longer exists. - assertSlashInserterPresent( false ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( false ); await editorPage.removeBlockAtPosition( blockNames.paragraph ); } ); @@ -85,7 +66,7 @@ describe( 'Gutenberg Editor Slash Inserter tests', () => { `${ slashInserter }image` ); - assertSlashInserterPresent( true ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( true ); // Find Image block button. const imageButtonElement = await editorPage.driver.elementByAccessibilityId( @@ -102,7 +83,7 @@ describe( 'Gutenberg Editor Slash Inserter tests', () => { ).toBe( true ); // Slash inserter UI should not be present after adding a block. - assertSlashInserterPresent( false ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( false ); // Remove image block. await editorPage.removeBlockAtPosition( blockNames.image ); diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 03f3002c59b142..9450e3dde8e997 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -464,11 +464,20 @@ const waitForMediaLibrary = async ( driver ) => { await waitForVisible( driver, locator ); }; -const waitForVisible = async ( driver, elementLocator, iteration = 0 ) => { - const maxIteration = 25; +/** + * + * @param {string} driver + * @param {string} elementLocator + * @param {boolean} returnBool - To return boolean instead of the first element/throw error. Useful for asserts. + * @param {number} iteration - Default value is 0 + */ +const waitForVisible = async ( driver, elementLocator, returnBool, iteration = 0 ) => { + const maxIteration = returnBool ? 5 : 25; const timeout = 1000; if ( iteration >= maxIteration ) { + if ( returnBool ) { return false } + throw new Error( `"${ elementLocator }" is still not visible after ${ iteration } retries!` ); @@ -480,8 +489,10 @@ const waitForVisible = async ( driver, elementLocator, iteration = 0 ) => { const locator = await driver.elementsByXPath( elementLocator ); if ( locator.length !== 1 ) { // if locator is not visible, try again - return waitForVisible( driver, elementLocator, iteration + 1 ); + return waitForVisible( driver, elementLocator, returnBool, iteration + 1 ); } + + if ( returnBool ) { return true } return locator[ 0 ]; }; 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 8688c2a86897d5..24213c8f65b0b5 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -552,6 +552,14 @@ class EditorPage { return locator.length; } + async assertSlashInserterPresent() { + const slashInserterLocator = isAndroid() + ? '//android.widget.HorizontalScrollView[@content-desc="Slash inserter results"]/android.view.ViewGroup' + : '(//XCUIElementTypeOther[@name="Slash inserter results"])[1]' + + return await waitForVisible( this.driver, slashInserterLocator, true ) + } + // ========================= // List Block functions // ========================= From 43d780baefd9505bd189d689f7cc29b3fef37e9a Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 28 Apr 2022 14:47:11 +0800 Subject: [PATCH 03/20] lint fixes --- .../__device-tests__/helpers/utils.js | 28 ++++++++++++++----- .../__device-tests__/pages/editor-page.js | 4 +-- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 9450e3dde8e997..ca9abacc3a6ea2 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -465,18 +465,25 @@ const waitForMediaLibrary = async ( driver ) => { }; /** - * - * @param {string} driver - * @param {string} elementLocator + * + * @param {string} driver + * @param {string} elementLocator * @param {boolean} returnBool - To return boolean instead of the first element/throw error. Useful for asserts. * @param {number} iteration - Default value is 0 */ -const waitForVisible = async ( driver, elementLocator, returnBool, iteration = 0 ) => { +const waitForVisible = async ( + driver, + elementLocator, + returnBool, + iteration = 0 +) => { const maxIteration = returnBool ? 5 : 25; const timeout = 1000; if ( iteration >= maxIteration ) { - if ( returnBool ) { return false } + if ( returnBool ) { + return false; + } throw new Error( `"${ elementLocator }" is still not visible after ${ iteration } retries!` @@ -489,10 +496,17 @@ const waitForVisible = async ( driver, elementLocator, returnBool, iteration = 0 const locator = await driver.elementsByXPath( elementLocator ); if ( locator.length !== 1 ) { // if locator is not visible, try again - return waitForVisible( driver, elementLocator, returnBool, iteration + 1 ); + return waitForVisible( + driver, + elementLocator, + returnBool, + iteration + 1 + ); } - if ( returnBool ) { return true } + if ( returnBool ) { + return true; + } return locator[ 0 ]; }; 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 24213c8f65b0b5..b248437367d9e2 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -555,9 +555,9 @@ class EditorPage { async assertSlashInserterPresent() { const slashInserterLocator = isAndroid() ? '//android.widget.HorizontalScrollView[@content-desc="Slash inserter results"]/android.view.ViewGroup' - : '(//XCUIElementTypeOther[@name="Slash inserter results"])[1]' + : '(//XCUIElementTypeOther[@name="Slash inserter results"])[1]'; - return await waitForVisible( this.driver, slashInserterLocator, true ) + return await waitForVisible( this.driver, slashInserterLocator, true ); } // ========================= From 6d0a31917c3cf0c9611a03486419a9405f14903a Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 28 Apr 2022 15:19:11 +0800 Subject: [PATCH 04/20] wait for ordered list to appear --- .../__device-tests__/gutenberg-editor-lists.test.js | 5 ++--- .../__device-tests__/pages/editor-page.js | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index e0a8d9e4f8a5de..5c65f91fd86ecc 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -20,6 +20,7 @@ describe( 'Gutenberg Editor tests for List block', () => { if ( isAndroid() ) { await listBlockElement.click(); } + // Send a backspace. await editorPage.typeTextToTextBlock( listBlockElement, @@ -37,9 +38,7 @@ describe( 'Gutenberg Editor tests for List block', () => { ); // Remove list block to reset editor to clean state. - listBlockElement = await editorPage.getBlockAtPosition( - blockNames.list - ); + listBlockElement = await editorPage.getListBlock(); await listBlockElement.click(); await editorPage.removeBlockAtPosition( blockNames.list ); } ); 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 b248437367d9e2..eed1e67860337a 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -591,6 +591,11 @@ class EditorPage { } async clickOrderedListToolBarButton() { + const toolBarLocator = isAndroid() + ? `//android.widget.Button[@content-desc="${ this.orderedListButtonName }"]` + : `//XCUIElementTypeButton[@name="${ this.orderedListButtonName }"]`; + + await waitForVisible( this.driver, toolBarLocator); await this.clickToolBarButton( this.orderedListButtonName ); } From 39c887049b1b93c19194611ee210addc5075bbe1 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 28 Apr 2022 15:19:44 +0800 Subject: [PATCH 05/20] lint fixes --- .../react-native-editor/__device-tests__/pages/editor-page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 eed1e67860337a..fac333b36f3e91 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -595,7 +595,7 @@ class EditorPage { ? `//android.widget.Button[@content-desc="${ this.orderedListButtonName }"]` : `//XCUIElementTypeButton[@name="${ this.orderedListButtonName }"]`; - await waitForVisible( this.driver, toolBarLocator); + await waitForVisible( this.driver, toolBarLocator ); await this.clickToolBarButton( this.orderedListButtonName ); } From f38d259f2b8aca91ebeb94b2f217b0e56636f248 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 28 Apr 2022 17:18:55 +0800 Subject: [PATCH 06/20] extra click only on local env --- .../__device-tests__/gutenberg-editor-lists.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index 5c65f91fd86ecc..07194ba6bc35ae 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { backspace, isAndroid } from './helpers/utils'; +import { backspace, isAndroid, isLocalEnvironment } from './helpers/utils'; describe( 'Gutenberg Editor tests for List block', () => { // Prevent regression of https://github.com/wordpress-mobile/gutenberg-mobile/issues/871 @@ -16,8 +16,8 @@ describe( 'Gutenberg Editor tests for List block', () => { // Send an Enter. await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); - // Click List block on Android to force EditText focus - if ( isAndroid() ) { + // Click List block on Android. + if ( isAndroid() && isLocalEnvironment() ) { await listBlockElement.click(); } From bd2eea43ea1396db0e06942f64786f578dcc35d8 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Fri, 29 Apr 2022 10:20:57 +0800 Subject: [PATCH 07/20] wait to get backspace click reflected --- .../__device-tests__/gutenberg-editor-lists.test.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index 07194ba6bc35ae..ecd8c1869ecf0e 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { backspace, isAndroid, isLocalEnvironment } from './helpers/utils'; +import { backspace, isAndroid } from './helpers/utils'; describe( 'Gutenberg Editor tests for List block', () => { // Prevent regression of https://github.com/wordpress-mobile/gutenberg-mobile/issues/871 @@ -17,7 +17,7 @@ describe( 'Gutenberg Editor tests for List block', () => { await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); // Click List block on Android. - if ( isAndroid() && isLocalEnvironment() ) { + if ( isAndroid() ) { await listBlockElement.click(); } @@ -28,6 +28,9 @@ describe( 'Gutenberg Editor tests for List block', () => { false ); + // Wait for backspace click to be reflected before checking HTML + listBlockElement = await editorPage.getListBlock(); + // Switch to html and verify html. const html = await editorPage.getHtmlContent(); From 4cf9f9eccc65d3e811c3dec9b6b259747c673370 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Fri, 29 Apr 2022 11:16:33 +0800 Subject: [PATCH 08/20] re-add extra click only for local env --- .../__device-tests__/gutenberg-editor-lists.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index ecd8c1869ecf0e..75868c0ec39be1 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { backspace, isAndroid } from './helpers/utils'; +import { backspace, isAndroid, isLocalEnvironment } from './helpers/utils'; describe( 'Gutenberg Editor tests for List block', () => { // Prevent regression of https://github.com/wordpress-mobile/gutenberg-mobile/issues/871 @@ -17,7 +17,7 @@ describe( 'Gutenberg Editor tests for List block', () => { await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); // Click List block on Android. - if ( isAndroid() ) { + if ( isAndroid() && isLocalEnvironment() ) { await listBlockElement.click(); } From 077a69cad6f8d8a19c7bb3208793c437762c1920 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Fri, 29 Apr 2022 14:31:01 +0800 Subject: [PATCH 09/20] add wait to wait for backspace key to be reflected --- .../__device-tests__/gutenberg-editor-lists.test.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index 75868c0ec39be1..8c5f197ab67b1f 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -17,6 +17,7 @@ describe( 'Gutenberg Editor tests for List block', () => { await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); // Click List block on Android. + // Only needed when testing in local environment if ( isAndroid() && isLocalEnvironment() ) { await listBlockElement.click(); } @@ -28,8 +29,11 @@ describe( 'Gutenberg Editor tests for List block', () => { false ); - // Wait for backspace click to be reflected before checking HTML - listBlockElement = await editorPage.getListBlock(); + // There is a delay in Sauce Labs when a key is sent + // There isn't an element to check as it's being typed into an element that already exists, workaround is to add this wait until there's a better solution + if ( isAndroid() && !isLocalEnvironment() ) { + await editorPage.driver.sleep( 1500 ); + } // Switch to html and verify html. const html = await editorPage.getHtmlContent(); From a6bfbf71653dceb5e289f22d1958910c951eadbd Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Fri, 29 Apr 2022 14:31:45 +0800 Subject: [PATCH 10/20] lint fixes --- .../__device-tests__/gutenberg-editor-lists.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index 8c5f197ab67b1f..421df59d1ac6f7 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -31,7 +31,7 @@ describe( 'Gutenberg Editor tests for List block', () => { // There is a delay in Sauce Labs when a key is sent // There isn't an element to check as it's being typed into an element that already exists, workaround is to add this wait until there's a better solution - if ( isAndroid() && !isLocalEnvironment() ) { + if ( isAndroid() && ! isLocalEnvironment() ) { await editorPage.driver.sleep( 1500 ); } From 46273a83845071272a8950c58365a30608581a1e Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Tue, 10 May 2022 11:36:08 +0800 Subject: [PATCH 11/20] break function, set position to get list block --- .../gutenberg-editor-lists-@canary.test.js | 8 +-- .../gutenberg-editor-lists-end.test.js | 2 +- .../gutenberg-editor-lists.test.js | 11 ++- .../__device-tests__/helpers/utils.js | 68 ++++++++++++------- .../__device-tests__/pages/editor-page.js | 33 +++++---- 5 files changed, 74 insertions(+), 48 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js index 875c876680e868..4c531890d6c1d2 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js @@ -8,7 +8,7 @@ import testData from './helpers/test-data'; describe( 'Gutenberg Editor tests for List block', () => { it( 'should be able to add a new List block', async () => { await editorPage.addNewBlock( blockNames.list ); - let listBlockElement = await editorPage.getListBlock( { + let listBlockElement = await editorPage.getListBlockAtPosition( { isEmptyBlock: true, } ); @@ -18,13 +18,11 @@ describe( 'Gutenberg Editor tests for List block', () => { false ); - listBlockElement = await editorPage.getListBlock(); + listBlockElement = await editorPage.getListBlockAtPosition(); // Send an Enter. await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); - listBlockElement = await editorPage.getListBlock(); - // Send the second list item text. await editorPage.typeTextToTextBlock( listBlockElement, @@ -39,7 +37,7 @@ describe( 'Gutenberg Editor tests for List block', () => { // This test depends on being run immediately after 'should be able to add a new List block' it( 'should update format to ordered list, using toolbar button', async () => { - let listBlockElement = await editorPage.getListBlock(); + let listBlockElement = await editorPage.getListBlockAtPosition(); if ( isAndroid() ) { listBlockElement.click(); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js index a81fcb8b9a344a..e242f2f6f99e08 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js @@ -7,7 +7,7 @@ import testData from './helpers/test-data'; describe( 'Gutenberg Editor tests for List block (end)', () => { it( 'should be able to end a List block', async () => { await editorPage.addNewBlock( blockNames.list ); - const listBlockElement = await editorPage.getListBlock(); + const listBlockElement = await editorPage.getListBlockAtPosition(); await editorPage.typeTextToTextBlock( listBlockElement, diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index 421df59d1ac6f7..28c35f46545c70 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -2,13 +2,13 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { backspace, isAndroid, isLocalEnvironment } from './helpers/utils'; +import { backspace, isAndroid } from './helpers/utils'; describe( 'Gutenberg Editor tests for List block', () => { // Prevent regression of https://github.com/wordpress-mobile/gutenberg-mobile/issues/871 it( 'should handle spaces in a list', async () => { await editorPage.addNewBlock( blockNames.list ); - let listBlockElement = await editorPage.getListBlock(); + let listBlockElement = await editorPage.getListBlockAtPosition(); // Send the list item text. await editorPage.typeTextToTextBlock( listBlockElement, ' a', false ); @@ -17,8 +17,7 @@ describe( 'Gutenberg Editor tests for List block', () => { await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); // Click List block on Android. - // Only needed when testing in local environment - if ( isAndroid() && isLocalEnvironment() ) { + if ( isAndroid() ) { await listBlockElement.click(); } @@ -31,7 +30,7 @@ describe( 'Gutenberg Editor tests for List block', () => { // There is a delay in Sauce Labs when a key is sent // There isn't an element to check as it's being typed into an element that already exists, workaround is to add this wait until there's a better solution - if ( isAndroid() && ! isLocalEnvironment() ) { + if ( isAndroid() ) { await editorPage.driver.sleep( 1500 ); } @@ -45,7 +44,7 @@ describe( 'Gutenberg Editor tests for List block', () => { ); // Remove list block to reset editor to clean state. - listBlockElement = await editorPage.getListBlock(); + listBlockElement = await editorPage.getListBlockAtPosition(); await listBlockElement.click(); await editorPage.removeBlockAtPosition( blockNames.list ); } ); diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index ca9abacc3a6ea2..41b31b809d688f 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -468,23 +468,17 @@ const waitForMediaLibrary = async ( driver ) => { * * @param {string} driver * @param {string} elementLocator - * @param {boolean} returnBool - To return boolean instead of the first element/throw error. Useful for asserts. * @param {number} iteration - Default value is 0 */ const waitForVisible = async ( driver, elementLocator, - returnBool, iteration = 0 ) => { - const maxIteration = returnBool ? 5 : 25; + const maxIteration = 25; const timeout = 1000; if ( iteration >= maxIteration ) { - if ( returnBool ) { - return false; - } - throw new Error( `"${ elementLocator }" is still not visible after ${ iteration } retries!` ); @@ -499,38 +493,64 @@ const waitForVisible = async ( return waitForVisible( driver, elementLocator, - returnBool, iteration + 1 ); } - if ( returnBool ) { - return true; - } return locator[ 0 ]; }; +const isElementVisible = async ( + driver, + elementLocator, + iteration = 0 +) => { + const maxIteration = 5; + const timeout = 1000; + + if ( iteration >= maxIteration ) { + // Element is still not visible after waiting, return false + return false; + } else if ( iteration !== 0 ) { + // wait before trying to locate element again + await driver.sleep( timeout ); + } + + const locator = await driver.elementsByXPath( elementLocator ); + if ( locator.length !== 1 ) { + // if locator is not visible, try again + return waitForVisible( + driver, + elementLocator, + iteration + 1 + ); + } + + return true; +} + module.exports = { backspace, - timer, - setupDriver, - isLocalEnvironment, - isAndroid, - typeString, - clickMiddleOfElement, clickBeginningOfElement, + clickMiddleOfElement, + doubleTap, + isAndroid, + isEditorVisible, + isElementVisible, + isLocalEnvironment, longPressMiddleOfElement, - tapSelectAllAboveElement, - tapCopyAboveElement, - tapPasteAboveElement, + setupDriver, + stopDriver, swipeDown, - swipeUp, swipeFromTo, - stopDriver, + swipeUp, + tapCopyAboveElement, + tapPasteAboveElement, + tapSelectAllAboveElement, + timer, toggleHtmlMode, toggleOrientation, - doubleTap, - isEditorVisible, + typeString, waitForMediaLibrary, waitForVisible, }; 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 fac333b36f3e91..c9a11a46e04b3b 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -5,6 +5,7 @@ const { doubleTap, isAndroid, isEditorVisible, + isElementVisible, longPressMiddleOfElement, setupDriver, stopDriver, @@ -557,35 +558,43 @@ class EditorPage { ? '//android.widget.HorizontalScrollView[@content-desc="Slash inserter results"]/android.view.ViewGroup' : '(//XCUIElementTypeOther[@name="Slash inserter results"])[1]'; - return await waitForVisible( this.driver, slashInserterLocator, true ); + return await isElementVisible( this.driver, slashInserterLocator ); } // ========================= // List Block functions // ========================= - async getListBlock( options = { isEmptyBlock: false } ) { + async getListBlockAtPosition( position = 1, options = { isEmptyBlock: false } ) { + + // Go to the correct list at position + const listBlockPositionLocator = isAndroid() + ? `//android.view.ViewGroup[@content-desc="List Block. Row ${ position }"]` + : `(//XCUIElementTypeOther[contains(@name, "List Block. Row ${ position }")])[5]` + + let listBlock = await waitForVisible( this.driver, listBlockPositionLocator ); + await listBlock.click(); + // iOS needs a click to get the text element if ( ! isAndroid() ) { const listBlockLocator = options.isEmptyBlock ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` : `//XCUIElementTypeButton[contains(@name, "List")]`; - const textBlock = await waitForVisible( + listBlock = await waitForVisible( this.driver, listBlockLocator ); - await textBlock.click(); + await listBlock.click(); } - let listBlockTextLocator = - ! isAndroid() && options.isEmptyBlock - ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` - : `//XCUIElementTypeButton[contains(@name, "List")]//XCUIElementTypeTextView`; - - if ( isAndroid() ) { - listBlockTextLocator = `//android.view.ViewGroup[contains(@content-desc, "List Block.")]/android.widget.EditText`; - } + const listBlockTextLocatorIOS = options.isEmptyBlock + ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` + : `//XCUIElementTypeButton[contains(@name, "List")]//XCUIElementTypeTextView`; + + const listBlockTextLocator = isAndroid() + ? `//android.view.ViewGroup[contains(@content-desc, "List Block.")]/android.widget.EditText` + : listBlockTextLocatorIOS; return await waitForVisible( this.driver, listBlockTextLocator ); } From 6a9dae269c924409694c01400f825c1cadb1771c Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Tue, 10 May 2022 11:37:11 +0800 Subject: [PATCH 12/20] lint fixes --- .../__device-tests__/helpers/utils.js | 26 +++------------ .../__device-tests__/pages/editor-page.js | 32 ++++++++++--------- 2 files changed, 22 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 41b31b809d688f..48e2c3911fe24f 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -470,11 +470,7 @@ const waitForMediaLibrary = async ( driver ) => { * @param {string} elementLocator * @param {number} iteration - Default value is 0 */ -const waitForVisible = async ( - driver, - elementLocator, - iteration = 0 -) => { +const waitForVisible = async ( driver, elementLocator, iteration = 0 ) => { const maxIteration = 25; const timeout = 1000; @@ -490,21 +486,13 @@ const waitForVisible = async ( const locator = await driver.elementsByXPath( elementLocator ); if ( locator.length !== 1 ) { // if locator is not visible, try again - return waitForVisible( - driver, - elementLocator, - iteration + 1 - ); + return waitForVisible( driver, elementLocator, iteration + 1 ); } return locator[ 0 ]; }; -const isElementVisible = async ( - driver, - elementLocator, - iteration = 0 -) => { +const isElementVisible = async ( driver, elementLocator, iteration = 0 ) => { const maxIteration = 5; const timeout = 1000; @@ -519,15 +507,11 @@ const isElementVisible = async ( const locator = await driver.elementsByXPath( elementLocator ); if ( locator.length !== 1 ) { // if locator is not visible, try again - return waitForVisible( - driver, - elementLocator, - iteration + 1 - ); + return waitForVisible( driver, elementLocator, iteration + 1 ); } return true; -} +}; module.exports = { backspace, 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 c9a11a46e04b3b..4b5ee5a46b35bc 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -565,14 +565,19 @@ class EditorPage { // List Block functions // ========================= - async getListBlockAtPosition( position = 1, options = { isEmptyBlock: false } ) { - + async getListBlockAtPosition( + position = 1, + options = { isEmptyBlock: false } + ) { // Go to the correct list at position - const listBlockPositionLocator = isAndroid() + const listBlockPositionLocator = isAndroid() ? `//android.view.ViewGroup[@content-desc="List Block. Row ${ position }"]` - : `(//XCUIElementTypeOther[contains(@name, "List Block. Row ${ position }")])[5]` + : `(//XCUIElementTypeOther[contains(@name, "List Block. Row ${ position }")])[5]`; - let listBlock = await waitForVisible( this.driver, listBlockPositionLocator ); + let listBlock = await waitForVisible( + this.driver, + listBlockPositionLocator + ); await listBlock.click(); // iOS needs a click to get the text element @@ -581,19 +586,16 @@ class EditorPage { ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` : `//XCUIElementTypeButton[contains(@name, "List")]`; - listBlock = await waitForVisible( - this.driver, - listBlockLocator - ); + listBlock = await waitForVisible( this.driver, listBlockLocator ); await listBlock.click(); } - const listBlockTextLocatorIOS = options.isEmptyBlock - ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` - : `//XCUIElementTypeButton[contains(@name, "List")]//XCUIElementTypeTextView`; - - const listBlockTextLocator = isAndroid() - ? `//android.view.ViewGroup[contains(@content-desc, "List Block.")]/android.widget.EditText` + const listBlockTextLocatorIOS = options.isEmptyBlock + ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` + : `//XCUIElementTypeButton[contains(@name, "List")]//XCUIElementTypeTextView`; + + const listBlockTextLocator = isAndroid() + ? `//android.view.ViewGroup[contains(@content-desc, "List Block.")]/android.widget.EditText` : listBlockTextLocatorIOS; return await waitForVisible( this.driver, listBlockTextLocator ); From e3bc3935a6f404a47bf5cf3c773bac172d46c501 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Tue, 10 May 2022 14:58:42 +0800 Subject: [PATCH 13/20] use correct params, update function name --- .../__device-tests__/gutenberg-editor-lists-@canary.test.js | 2 +- .../__device-tests__/gutenberg-editor-lists.test.js | 5 +++-- .../react-native-editor/__device-tests__/helpers/utils.js | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js index 4c531890d6c1d2..9e9051d05193a7 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js @@ -8,7 +8,7 @@ import testData from './helpers/test-data'; describe( 'Gutenberg Editor tests for List block', () => { it( 'should be able to add a new List block', async () => { await editorPage.addNewBlock( blockNames.list ); - let listBlockElement = await editorPage.getListBlockAtPosition( { + let listBlockElement = await editorPage.getListBlockAtPosition( 1, { isEmptyBlock: true, } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index 28c35f46545c70..ecfbf064fd0ff7 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { backspace, isAndroid } from './helpers/utils'; +import { backspace, isAndroid, isLocalEnvironment } from './helpers/utils'; describe( 'Gutenberg Editor tests for List block', () => { // Prevent regression of https://github.com/wordpress-mobile/gutenberg-mobile/issues/871 @@ -17,7 +17,8 @@ describe( 'Gutenberg Editor tests for List block', () => { await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); // Click List block on Android. - if ( isAndroid() ) { + // Only needed when testing in local environment + if ( isAndroid() && isLocalEnvironment() ) { await listBlockElement.click(); } diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 48e2c3911fe24f..c1805259c8b315 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -507,7 +507,7 @@ const isElementVisible = async ( driver, elementLocator, iteration = 0 ) => { const locator = await driver.elementsByXPath( elementLocator ); if ( locator.length !== 1 ) { // if locator is not visible, try again - return waitForVisible( driver, elementLocator, iteration + 1 ); + return isElementVisible( driver, elementLocator, iteration + 1 ); } return true; From 4f6917ea29be33315f89b96ea811278408390207 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Tue, 10 May 2022 16:13:04 +0800 Subject: [PATCH 14/20] lint fixes --- .../gutenberg-editor-lists.test.js | 13 ++++--------- .../__device-tests__/helpers/utils.js | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index ecfbf064fd0ff7..6b2cb1c04509ff 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -2,7 +2,7 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { backspace, isAndroid, isLocalEnvironment } from './helpers/utils'; +import { waitIfAndroid, backspace } from './helpers/utils'; describe( 'Gutenberg Editor tests for List block', () => { // Prevent regression of https://github.com/wordpress-mobile/gutenberg-mobile/issues/871 @@ -16,11 +16,8 @@ describe( 'Gutenberg Editor tests for List block', () => { // Send an Enter. await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); - // Click List block on Android. - // Only needed when testing in local environment - if ( isAndroid() && isLocalEnvironment() ) { - await listBlockElement.click(); - } + // Instead of introducing separate conditions for local and CI environment, add this wait for Android to accomodate both environments + await waitIfAndroid(); // Send a backspace. await editorPage.typeTextToTextBlock( @@ -31,9 +28,7 @@ describe( 'Gutenberg Editor tests for List block', () => { // There is a delay in Sauce Labs when a key is sent // There isn't an element to check as it's being typed into an element that already exists, workaround is to add this wait until there's a better solution - if ( isAndroid() ) { - await editorPage.driver.sleep( 1500 ); - } + await waitIfAndroid(); // Switch to html and verify html. const html = await editorPage.getHtmlContent(); diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index c1805259c8b315..3b877fb4ff5c36 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -465,10 +465,10 @@ const waitForMediaLibrary = async ( driver ) => { }; /** - * * @param {string} driver * @param {string} elementLocator * @param {number} iteration - Default value is 0 + * @return {string} - Returns the first element found, throws error if no element found */ const waitForVisible = async ( driver, elementLocator, iteration = 0 ) => { const maxIteration = 25; @@ -492,6 +492,12 @@ const waitForVisible = async ( driver, elementLocator, iteration = 0 ) => { return locator[ 0 ]; }; +/** + * @param {string} driver + * @param {string} elementLocator + * @param {number} iteration - Default value is 0 + * @return {boolean} - Returns true if element is found, false otherwise + */ const isElementVisible = async ( driver, elementLocator, iteration = 0 ) => { const maxIteration = 5; const timeout = 1000; @@ -513,6 +519,13 @@ const isElementVisible = async ( driver, elementLocator, iteration = 0 ) => { return true; }; +// Only for Android +const waitIfAndroid = async () => { + if ( isAndroid() ) { + await editorPage.driver.sleep( 1000 ); + } +}; + module.exports = { backspace, clickBeginningOfElement, @@ -537,4 +550,5 @@ module.exports = { typeString, waitForMediaLibrary, waitForVisible, + waitIfAndroid, }; From 85c407c286fc18e226f98fbcfa440237f995d093 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Tue, 10 May 2022 17:25:47 +0800 Subject: [PATCH 15/20] make maxIteration a parameter for isElementVisible --- .../__device-tests__/helpers/utils.js | 16 +++++++++++++--- .../__device-tests__/pages/editor-page.js | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 3b877fb4ff5c36..10d4a1b2f1e92e 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -495,11 +495,16 @@ const waitForVisible = async ( driver, elementLocator, iteration = 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 {number} iteration - Default value is 0 * @return {boolean} - Returns true if element is found, false otherwise */ -const isElementVisible = async ( driver, elementLocator, iteration = 0 ) => { - const maxIteration = 5; +const isElementVisible = async ( + driver, + elementLocator, + maxIteration = 25, + iteration = 0 +) => { const timeout = 1000; if ( iteration >= maxIteration ) { @@ -513,7 +518,12 @@ const isElementVisible = async ( driver, elementLocator, iteration = 0 ) => { const locator = await driver.elementsByXPath( elementLocator ); if ( locator.length !== 1 ) { // if locator is not visible, try again - return isElementVisible( driver, elementLocator, iteration + 1 ); + return isElementVisible( + driver, + elementLocator, + maxIteration, + iteration + 1 + ); } return true; 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 4b5ee5a46b35bc..a8314f14a37e44 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -558,7 +558,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 ); + return await isElementVisible( this.driver, slashInserterLocator, 5 ); } // ========================= From d1af9076afc1a54787e003dd167aa1d17e0e1e21 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Tue, 10 May 2022 17:33:29 +0800 Subject: [PATCH 16/20] update xpath for list block --- .../react-native-editor/__device-tests__/pages/editor-page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a8314f14a37e44..516dfafda003b2 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -572,7 +572,7 @@ class EditorPage { // Go to the correct list at position const listBlockPositionLocator = isAndroid() ? `//android.view.ViewGroup[@content-desc="List Block. Row ${ position }"]` - : `(//XCUIElementTypeOther[contains(@name, "List Block. Row ${ position }")])[5]`; + : `(//XCUIElementTypeOther[contains(@name, "List Block. Row ${ position }")])[1]`; let listBlock = await waitForVisible( this.driver, From 0077156bde9c865ea3b79d9bc398e13833531451 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Wed, 11 May 2022 09:34:11 +0800 Subject: [PATCH 17/20] utilize waitForVisible for isElementVisible --- .../__device-tests__/helpers/utils.js | 56 ++++++++----------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 10d4a1b2f1e92e..c2dfc394f0e121 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -467,63 +467,53 @@ const waitForMediaLibrary = async ( driver ) => { /** * @param {string} driver * @param {string} elementLocator + * @param {number} maxIteration - Default value is 25 * @param {number} iteration - Default value is 0 - * @return {string} - Returns the first element found, throws error if no element found + * @return {string} - Returns the first element found, empty string if not found */ -const waitForVisible = async ( driver, elementLocator, iteration = 0 ) => { - const maxIteration = 25; +const waitForVisible = async ( + driver, + elementLocator, + maxIteration = 25, + iteration = 0 +) => { const timeout = 1000; if ( iteration >= maxIteration ) { - throw new Error( - `"${ elementLocator }" is still not visible after ${ iteration } retries!` - ); + // if element not found, print error and return empty string + // eslint-disable-next-line no-console + console.error(`"${ elementLocator }" is still not visible after ${ iteration } retries!`) + return '' } else if ( iteration !== 0 ) { // wait before trying to locate element again await driver.sleep( timeout ); } - const locator = await driver.elementsByXPath( elementLocator ); - if ( locator.length !== 1 ) { + const element = await driver.elementsByXPath( elementLocator ); + if ( element.length !== 1 ) { // if locator is not visible, try again - return waitForVisible( driver, elementLocator, iteration + 1 ); + return waitForVisible( driver, elementLocator, maxIteration, iteration + 1 ); } - return locator[ 0 ]; + return element[ 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 {number} iteration - Default value is 0 * @return {boolean} - Returns true if element is found, false otherwise */ -const isElementVisible = async ( - driver, - elementLocator, - maxIteration = 25, - iteration = 0 +const isElementVisible = async ( + driver, + elementLocator, + maxIteration = 25 ) => { - const timeout = 1000; + const element = await waitForVisible(driver, elementLocator, maxIteration) - if ( iteration >= maxIteration ) { - // Element is still not visible after waiting, return false + // if there is no element, return false + if ( ! element ) { return false; - } else if ( iteration !== 0 ) { - // wait before trying to locate element again - await driver.sleep( timeout ); - } - - const locator = await driver.elementsByXPath( elementLocator ); - if ( locator.length !== 1 ) { - // if locator is not visible, try again - return isElementVisible( - driver, - elementLocator, - maxIteration, - iteration + 1 - ); } return true; From 4c7f918067fae2ce113d893ff77589875321fdf5 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Wed, 11 May 2022 09:35:01 +0800 Subject: [PATCH 18/20] lint fixes --- .../__device-tests__/helpers/utils.js | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index c2dfc394f0e121..1658d553c9c7a2 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -471,19 +471,21 @@ const waitForMediaLibrary = async ( driver ) => { * @param {number} iteration - Default value is 0 * @return {string} - Returns the first element found, empty string if not found */ -const waitForVisible = async ( - driver, - elementLocator, - maxIteration = 25, - iteration = 0 +const waitForVisible = async ( + driver, + elementLocator, + maxIteration = 25, + iteration = 0 ) => { const timeout = 1000; if ( iteration >= maxIteration ) { // if element not found, print error and return empty string // eslint-disable-next-line no-console - console.error(`"${ elementLocator }" is still not visible after ${ iteration } retries!`) - return '' + console.error( + `"${ elementLocator }" is still not visible after ${ iteration } retries!` + ); + return ''; } else if ( iteration !== 0 ) { // wait before trying to locate element again await driver.sleep( timeout ); @@ -492,7 +494,12 @@ const waitForVisible = async ( const element = await driver.elementsByXPath( elementLocator ); if ( element.length !== 1 ) { // if locator is not visible, try again - return waitForVisible( driver, elementLocator, maxIteration, iteration + 1 ); + return waitForVisible( + driver, + elementLocator, + maxIteration, + iteration + 1 + ); } return element[ 0 ]; @@ -504,15 +511,19 @@ const waitForVisible = async ( * @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, - maxIteration = 25 +const isElementVisible = async ( + driver, + elementLocator, + maxIteration = 25 ) => { - const element = await waitForVisible(driver, elementLocator, maxIteration) + const element = await waitForVisible( + driver, + elementLocator, + maxIteration + ); // if there is no element, return false - if ( ! element ) { + if ( ! element ) { return false; } From 58d9e906c23fdbc26197a0ccaa9f3aad5a6b4147 Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 12 May 2022 11:25:20 +0800 Subject: [PATCH 19/20] add wait to getNumberOfParagraphBlocks and update xpath for android list block --- .../gutenberg-editor-lists-@canary.test.js | 2 +- .../__device-tests__/pages/editor-page.js | 25 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js index 9e9051d05193a7..970fd669675283 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js @@ -40,7 +40,7 @@ describe( 'Gutenberg Editor tests for List block', () => { let listBlockElement = await editorPage.getListBlockAtPosition(); if ( isAndroid() ) { - listBlockElement.click(); + await listBlockElement.click(); } // Send a click on the order list format button. 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 89293bc6e47f87..a193a526e7872d 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -547,6 +547,8 @@ class EditorPage { const paragraphBlockLocator = isAndroid() ? `//android.view.ViewGroup[contains(@content-desc, "Paragraph Block. Row")]/android.widget.EditText` : `(//XCUIElementTypeButton[contains(@name, "Paragraph Block. Row")])`; + + await waitForVisible( this.driver, paragraphBlockLocator ); const locator = await this.driver.elementsByXPath( paragraphBlockLocator ); @@ -569,23 +571,20 @@ class EditorPage { position = 1, options = { isEmptyBlock: false } ) { - // Go to the correct list at position - const listBlockPositionLocator = isAndroid() - ? `//android.view.ViewGroup[@content-desc="List Block. Row ${ position }"]` - : `(//XCUIElementTypeOther[contains(@name, "List Block. Row ${ position }")])[1]`; - - let listBlock = await waitForVisible( - this.driver, - listBlockPositionLocator - ); - await listBlock.click(); - - // iOS needs a click to get the text element + // 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( + 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(); } @@ -595,7 +594,7 @@ class EditorPage { : `//XCUIElementTypeButton[contains(@name, "List")]//XCUIElementTypeTextView`; const listBlockTextLocator = isAndroid() - ? `//android.view.ViewGroup[contains(@content-desc, "List Block.")]/android.widget.EditText` + ? `//android.view.ViewGroup[contains(@content-desc, "List Block. Row ${ position }")]/android.widget.EditText` : listBlockTextLocatorIOS; return await waitForVisible( this.driver, listBlockTextLocator ); From 92ffc5eeff491355eb8f8677b8e6d3de3853c1ab Mon Sep 17 00:00:00 2001 From: jos <17252150+jostnes@users.noreply.github.com> Date: Thu, 12 May 2022 16:45:14 +0800 Subject: [PATCH 20/20] update edit text xpath to be read from any level --- .../__device-tests__/pages/editor-page.js | 5 ++--- 1 file changed, 2 insertions(+), 3 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 a193a526e7872d..440e256566ef91 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -545,10 +545,9 @@ class EditorPage { async getNumberOfParagraphBlocks() { const paragraphBlockLocator = isAndroid() - ? `//android.view.ViewGroup[contains(@content-desc, "Paragraph Block. Row")]/android.widget.EditText` + ? `//android.view.ViewGroup[contains(@content-desc, "Paragraph Block. Row")]//android.widget.EditText` : `(//XCUIElementTypeButton[contains(@name, "Paragraph Block. Row")])`; - await waitForVisible( this.driver, paragraphBlockLocator ); const locator = await this.driver.elementsByXPath( paragraphBlockLocator ); @@ -594,7 +593,7 @@ class EditorPage { : `//XCUIElementTypeButton[contains(@name, "List")]//XCUIElementTypeTextView`; const listBlockTextLocator = isAndroid() - ? `//android.view.ViewGroup[contains(@content-desc, "List Block. Row ${ position }")]/android.widget.EditText` + ? `//android.view.ViewGroup[contains(@content-desc, "List Block. Row ${ position }")]//android.widget.EditText` : listBlockTextLocatorIOS; return await waitForVisible( this.driver, listBlockTextLocator );