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 17f58bfec5f71..f806b43d2f8e4 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,9 +51,12 @@ describe( 'Gutenberg Editor tests', () => { blockNames.paragraph, 5 ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( 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-lists-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js index 80939bd9e5801..970fd66967528 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,26 @@ 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.getListBlockAtPosition( 1, { + isEmptyBlock: true, + } ); - // Send the first list item text. - await editorPage.sendTextToListBlock( + await editorPage.typeTextToTextBlock( listBlockElement, - testData.listItem1 + testData.listItem1, + false ); + listBlockElement = await editorPage.getListBlockAtPosition(); + // Send an Enter. - await editorPage.sendTextToListBlock( listBlockElement, '\n' ); + await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); // 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 +37,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.getListBlockAtPosition(); - // Click List block to force EditText focus. - await listBlockElement.click(); + if ( isAndroid() ) { + await 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 d113be5371a38..e242f2f6f99e0 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.getListBlockAtPosition(); - // 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 12537bfcae535..6b2cb1c04509f 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,28 +2,33 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { backspace, isAndroid } 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 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.getListBlockAtPosition(); // 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 ); + + // 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.sendTextToListBlock( listBlockElement, backspace ); + await editorPage.typeTextToTextBlock( + listBlockElement, + backspace, + false + ); + + // 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 + await waitIfAndroid(); // Switch to html and verify html. const html = await editorPage.getHtmlContent(); @@ -35,9 +40,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.getListBlockAtPosition(); await listBlockElement.click(); await editorPage.removeBlockAtPosition( blockNames.list ); } ); 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 0ac9bae0743ef..20d7d690b96bf 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 16c291584b8ed..7404b0969d6ad 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 9056854fa42c7..215b81bf98b4d 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 563445768709a..d16e37bc7090f 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,105 +5,68 @@ 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; - 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(); - } -} - -// 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 ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( true ); await editorPage.removeBlockAtPosition( blockNames.paragraph ); } ); 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 ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( 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 ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( false ); await editorPage.removeBlockAtPosition( blockNames.paragraph ); } ); 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 ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( true ); // Find Image block button. const imageButtonElement = await editorPage.driver.elementByAccessibilityId( @@ -120,30 +83,27 @@ describe.skip( '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 ); } ); - 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__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 03f3002c59b14..1658d553c9c7a 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -464,48 +464,102 @@ 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 {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 + */ +const waitForVisible = async ( + driver, + elementLocator, + maxIteration = 25, + iteration = 0 +) => { const timeout = 1000; if ( iteration >= maxIteration ) { - throw new Error( + // 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 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 + * @return {boolean} - Returns true if element is found, false otherwise + */ +const isElementVisible = async ( + driver, + elementLocator, + maxIteration = 25 +) => { + const element = await waitForVisible( + driver, + elementLocator, + maxIteration + ); + + // if there is no element, return false + if ( ! element ) { + return false; + } + + return true; +}; + +// Only for Android +const waitIfAndroid = async () => { + if ( isAndroid() ) { + await editorPage.driver.sleep( 1000 ); } - return locator[ 0 ]; }; 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, + waitIfAndroid, }; 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 f408d8baf0fc8..440e256566ef9 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,18 @@ * Internal dependencies */ const { + doubleTap, + isAndroid, + isEditorVisible, + isElementVisible, + longPressMiddleOfElement, setupDriver, stopDriver, - isAndroid, - swipeUp, swipeDown, - typeString, - toggleHtmlMode, swipeFromTo, - longPressMiddleOfElement, - doubleTap, - isEditorVisible, + swipeUp, + toggleHtmlMode, + typeString, waitForVisible, } = require( '../helpers/utils' ); @@ -45,11 +46,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 +69,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 +515,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 +527,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,35 +543,68 @@ 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; + } + + async assertSlashInserterPresent() { + const slashInserterLocator = isAndroid() + ? '//android.widget.HorizontalScrollView[@content-desc="Slash inserter results"]/android.view.ViewGroup' + : '(//XCUIElementTypeOther[@name="Slash inserter results"])[1]'; + + return await isElementVisible( this.driver, slashInserterLocator, 5 ); + } + // ========================= // List Block functions // ========================= - async getTextViewForListBlock( block ) { - let textViewElementName = 'XCUIElementTypeTextView'; - if ( isAndroid() ) { - textViewElementName = 'android.widget.EditText'; - } + async getListBlockAtPosition( + position = 1, + options = { isEmptyBlock: false } + ) { + // 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 accessibilityId = await block.getAttribute( - this.accessibilityIdKey - ); - const blockLocator = `//*[@${ - this.accessibilityIdXPathAttrib - }=${ JSON.stringify( accessibilityId ) }]//${ textViewElementName }`; - return await this.driver.elementByXPath( blockLocator ); - } + const listBlockLocator = options.isEmptyBlock + ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` + : `//XCUIElementTypeButton[contains(@name, "List")]`; - async sendTextToListBlock( block, text ) { - const textViewElement = await this.getTextViewForListBlock( block ); + // Wait for and click the list to get the text element + listBlock = await waitForVisible( this.driver, listBlockLocator ); + await listBlock.click(); + } - // Cannot clear list blocks because it messes up the list bullet. - const clear = false; + const listBlockTextLocatorIOS = options.isEmptyBlock + ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` + : `//XCUIElementTypeButton[contains(@name, "List")]//XCUIElementTypeTextView`; - return await typeString( this.driver, textViewElement, text, clear ); + const listBlockTextLocator = isAndroid() + ? `//android.view.ViewGroup[contains(@content-desc, "List Block. Row ${ position }")]//android.widget.EditText` + : listBlockTextLocatorIOS; + + return await waitForVisible( this.driver, listBlockTextLocator ); } 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 ); } @@ -609,34 +643,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 +766,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 };