diff --git a/packages/block-editor/src/components/block-types-list/index.native.js b/packages/block-editor/src/components/block-types-list/index.native.js index d62987d408aea..450e45220436e 100644 --- a/packages/block-editor/src/components/block-types-list/index.native.js +++ b/packages/block-editor/src/components/block-types-list/index.native.js @@ -32,6 +32,7 @@ export default function BlockTypesList( { name, sections, onSelect, + label, listProps, initialNumToRender = 3, } ) { @@ -154,6 +155,7 @@ export default function BlockTypesList( { ); } diff --git a/packages/block-editor/src/components/inserter/reusable-blocks-tab.native.js b/packages/block-editor/src/components/inserter/reusable-blocks-tab.native.js index 825169e17315f..d50a5a99f2728 100644 --- a/packages/block-editor/src/components/inserter/reusable-blocks-tab.native.js +++ b/packages/block-editor/src/components/inserter/reusable-blocks-tab.native.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -31,6 +32,7 @@ function ReusableBlocksTab( { onSelect, rootClientId, listProps } ) { sections={ sections } onSelect={ onSelect } listProps={ listProps } + label={ __( 'Reusable blocks' ) } /> ); } diff --git a/packages/block-editor/src/components/inserter/search-results.native.js b/packages/block-editor/src/components/inserter/search-results.native.js index a32c6ecf1a1c3..bc951dac1de02 100644 --- a/packages/block-editor/src/components/inserter/search-results.native.js +++ b/packages/block-editor/src/components/inserter/search-results.native.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -54,6 +55,7 @@ function InserterSearchResults( { sections={ [ createInserterSection( { key: 'search', items } ) ] } onSelect={ handleSelect } listProps={ listProps } + label={ __( 'Blocks' ) } /> ); } diff --git a/packages/components/src/autocomplete/autocompleter-ui.native.js b/packages/components/src/autocomplete/autocompleter-ui.native.js index a8f0efa447c4f..1785d742d1ee4 100644 --- a/packages/components/src/autocomplete/autocompleter-ui.native.js +++ b/packages/components/src/autocomplete/autocompleter-ui.native.js @@ -144,6 +144,7 @@ export function getAutoCompleterUI( autocompleter ) { { - it( 'should be able to add an audio block and a file to it', async () => { - // add an audio block - await editorPage.addNewBlock( blockNames.audio ); - - // dismiss the media picker automatically opened when adding an audio block - await waitForMediaLibrary( editorPage.driver ); - await editorPage.closePicker(); - - // verify there's an audio block - const block = await editorPage.getFirstBlockVisible(); - await expect( block ).toBeTruthy(); - - // tap on the audio block - block.click(); - - // wait for the media picker's Media Library option to come up - await waitForMediaLibrary( editorPage.driver ); - - // tap on Media Library option - await editorPage.chooseMediaLibrary(); - - // get the html version of the content - const html = await editorPage.getHtmlContent(); - - // verify the html matches the expected content - expect( html.toLowerCase() ).toBe( - testData.audioBlockPlaceholder.toLowerCase() - ); - } ); -} ); 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-block-insertion-@canary.test.js similarity index 51% rename from packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js rename to packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion-@canary.test.js index af2e64d8aa947..e8f31dcef1216 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion-@canary.test.js @@ -3,7 +3,97 @@ */ import { blockNames } from './pages/editor-page'; import { isAndroid } from './helpers/utils'; -import { slashInserter, shortText } from './helpers/test-data'; +import testData, { slashInserter, shortText } from './helpers/test-data'; + +describe( 'Gutenberg Editor tests for Block insertion', () => { + it( 'should be able to insert multi-paragraph text, and text to another paragraph block in between', async () => { + await editorPage.addNewBlock( blockNames.paragraph ); + let paragraphBlockElement = await editorPage.getTextBlockAtPosition( + blockNames.paragraph + ); + if ( isAndroid() ) { + await paragraphBlockElement.click(); + } + + await editorPage.sendTextToParagraphBlock( 1, testData.longText ); + // Should have 3 paragraph blocks at this point. + + paragraphBlockElement = await editorPage.getTextBlockAtPosition( + blockNames.paragraph, + 2 + ); + await paragraphBlockElement.click(); + + await editorPage.addNewBlock( blockNames.paragraph ); + + paragraphBlockElement = await editorPage.getTextBlockAtPosition( + blockNames.paragraph, + 3 + ); + await paragraphBlockElement.click(); + await editorPage.sendTextToParagraphBlock( 3, testData.mediumText ); + + const html = await editorPage.getHtmlContent(); + + expect( html.toLowerCase() ).toBe( + testData.blockInsertionHtml.toLowerCase() + ); + + for ( let i = 4; i > 0; i-- ) { + paragraphBlockElement = await editorPage.getTextBlockAtPosition( + blockNames.paragraph + ); + await paragraphBlockElement.click(); + await editorPage.removeBlock(); + } + } ); + + it( 'should be able to insert block at the beginning of post from the title', async () => { + await editorPage.addNewBlock( blockNames.paragraph ); + let paragraphBlockElement = await editorPage.getTextBlockAtPosition( + blockNames.paragraph + ); + if ( isAndroid() ) { + await paragraphBlockElement.click(); + } + + await editorPage.sendTextToParagraphBlock( 1, testData.longText ); + // Should have 3 paragraph blocks at this point. + + if ( isAndroid() ) { + await editorPage.dismissKeyboard(); + } + + const titleElement = await editorPage.getTitleElement( { + autoscroll: true, + } ); + await titleElement.click(); + + await editorPage.addNewBlock( blockNames.paragraph ); + const emptyParagraphBlock = await editorPage.getBlockAtPosition( + blockNames.paragraph + ); + expect( emptyParagraphBlock ).toBeTruthy(); + const emptyParagraphBlockElement = + await editorPage.getTextBlockAtPosition( blockNames.paragraph ); + expect( emptyParagraphBlockElement ).toBeTruthy(); + + await editorPage.sendTextToParagraphBlock( 1, testData.mediumText ); + const html = await editorPage.getHtmlContent(); + expect( html.toLowerCase() ).toBe( + testData.blockInsertionHtmlFromTitle.toLowerCase() + ); + + // Remove blocks + for ( let i = 4; i > 0; i-- ) { + paragraphBlockElement = await editorPage.getTextBlockAtPosition( + blockNames.paragraph + ); + await paragraphBlockElement.click(); + await editorPage.removeBlock(); + } + } ); +} ); describe( 'Gutenberg Editor Slash Inserter tests', () => { it( 'should show the menu after typing /', async () => { @@ -42,9 +132,10 @@ describe( 'Gutenberg Editor Slash Inserter tests', () => { true ); } else { + await paragraphBlockElement.type( '\b' ); await editorPage.typeTextToTextBlock( paragraphBlockElement, - `\b ${ shortText }`, + `${ shortText }`, false ); } diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js deleted file mode 100644 index 447e1a997ecc8..0000000000000 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Internal dependencies - */ -import { blockNames } from './pages/editor-page'; -import { isAndroid, clickMiddleOfElement } from './helpers/utils'; -import testData from './helpers/test-data'; - -describe( 'Gutenberg Editor tests for Block insertion', () => { - it( 'should be able to insert multi-paragraph text, and text to another paragraph block in between', async () => { - await editorPage.addNewBlock( blockNames.paragraph ); - let paragraphBlockElement = await editorPage.getTextBlockAtPosition( - blockNames.paragraph - ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - - await editorPage.sendTextToParagraphBlock( 1, testData.longText ); - // Should have 3 paragraph blocks at this point. - - paragraphBlockElement = await editorPage.getTextBlockAtPosition( - blockNames.paragraph, - 2 - ); - await paragraphBlockElement.click(); - - await editorPage.addNewBlock( blockNames.paragraph ); - - paragraphBlockElement = await editorPage.getTextBlockAtPosition( - blockNames.paragraph, - 3 - ); - await paragraphBlockElement.click(); - await editorPage.sendTextToParagraphBlock( 3, testData.mediumText ); - - const html = await editorPage.getHtmlContent(); - - expect( html.toLowerCase() ).toBe( - testData.blockInsertionHtml.toLowerCase() - ); - - // Workaround for now since deleting the first element causes a crash on CI for Android - if ( isAndroid() ) { - paragraphBlockElement = await editorPage.getTextBlockAtPosition( - blockNames.paragraph, - 3, - { - autoscroll: true, - } - ); - - await paragraphBlockElement.click(); - await editorPage.removeBlockAtPosition( blockNames.paragraph, 3 ); - for ( let i = 3; i > 0; i-- ) { - paragraphBlockElement = await editorPage.getTextBlockAtPosition( - blockNames.paragraph, - i, - { - autoscroll: true, - } - ); - await paragraphBlockElement.click(); - await editorPage.removeBlockAtPosition( - blockNames.paragraph, - i - ); - } - } else { - for ( let i = 4; i > 0; i-- ) { - paragraphBlockElement = await editorPage.getTextBlockAtPosition( - blockNames.paragraph - ); - await clickMiddleOfElement( - editorPage.driver, - paragraphBlockElement - ); - await editorPage.removeBlockAtPosition( blockNames.paragraph ); - } - } - } ); - - it( 'should be able to insert block at the beginning of post from the title', async () => { - await editorPage.addNewBlock( blockNames.paragraph ); - const paragraphBlockElement = await editorPage.getTextBlockAtPosition( - blockNames.paragraph - ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - - await editorPage.sendTextToParagraphBlock( 1, testData.longText ); - // Should have 3 paragraph blocks at this point. - - if ( isAndroid() ) { - await editorPage.dismissKeyboard(); - } - - const titleElement = await editorPage.getTitleElement( { - autoscroll: true, - } ); - expect( titleElement ).toBeTruthy(); - await titleElement.click(); - - await editorPage.addNewBlock( blockNames.paragraph ); - const emptyParagraphBlock = await editorPage.getBlockAtPosition( - blockNames.paragraph - ); - expect( emptyParagraphBlock ).toBeTruthy(); - const emptyParagraphBlockElement = - await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - expect( emptyParagraphBlockElement ).toBeTruthy(); - - await editorPage.sendTextToParagraphBlock( 1, testData.mediumText ); - const html = await editorPage.getHtmlContent(); - expect( html.toLowerCase() ).toBe( - testData.blockInsertionHtmlFromTitle.toLowerCase() - ); - } ); -} ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js deleted file mode 100644 index e57dfa68debe6..0000000000000 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Internal dependencies - */ -import { blockNames } from './pages/editor-page'; -import { isAndroid } from './helpers/utils'; -import testData from './helpers/test-data'; - -describe( 'Gutenberg Editor Cover Block test', () => { - it( 'should displayed properly and have properly converted height (ios only)', async () => { - // Temporarily this test is skipped on Android,due to the inconsistency of the results, - // which are related to getting values in raw pixels instead of density pixels on Android. - /* eslint-disable jest/no-conditional-expect */ - if ( ! isAndroid() ) { - await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); - - const coverBlock = await editorPage.getBlockAtPosition( - blockNames.cover - ); - - const { height } = await coverBlock.getSize(); - // Height is set to 20rem, where 1rem is 16. - // There is also block's vertical padding equal 32. - // Finally, the total height should be 20 * 16 + 32 = 352. - expect( height ).toBe( 352 ); - /* eslint-enable jest/no-conditional-expect */ - - await coverBlock.click(); - expect( coverBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( blockNames.cover ); - } - } ); - - // Testing this for iOS on a device is valuable to ensure that it properly - // handles opening multiple modals, as only one can be open at a time. - it( 'allows modifying media from within block settings', async () => { - // Can only add image from media library on iOS - if ( ! isAndroid() ) { - await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); - - const coverBlock = await editorPage.getBlockAtPosition( - blockNames.cover - ); - - await editorPage.openBlockSettings( coverBlock ); - await editorPage.clickAddMediaFromCoverBlock(); - await editorPage.chooseMediaLibrary(); - await editorPage.replaceMediaImage(); - - // First modal should no longer be presented. - const replaceButtons = - await editorPage.driver.elementsByAccessibilityId( 'Replace' ); - // eslint-disable-next-line jest/no-conditional-expect - expect( replaceButtons.length ).toBe( 0 ); - - // Select different media. - await editorPage.chooseMediaLibrary(); - - expect( coverBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( blockNames.cover ); - } - } ); -} ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-device-actions.test.js similarity index 73% rename from packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js rename to packages/react-native-editor/__device-tests__/gutenberg-editor-device-actions.test.js index 615c9f26fbe6b..3a64ca3750898 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-device-actions.test.js @@ -8,11 +8,55 @@ import { tapSelectAllAboveElement, tapCopyAboveElement, tapPasteAboveElement, + toggleOrientation, isAndroid, } from './helpers/utils'; import testData from './helpers/test-data'; -describe( 'Gutenberg Editor paste tests', () => { +describe( 'Gutenberg Editor Rotation tests', () => { + it( 'should be able to add blocks , rotate device and continue adding blocks', async () => { + await editorPage.addNewBlock( blockNames.paragraph ); + let paragraphBlockElement = await editorPage.getTextBlockAtPosition( + blockNames.paragraph + ); + + await editorPage.typeTextToTextBlock( + paragraphBlockElement, + testData.mediumText + ); + + await toggleOrientation( editorPage.driver ); + // On Android the keyboard hides the add block button, let's hide it after rotation + if ( isAndroid() ) { + await editorPage.dismissKeyboard(); + } + + await editorPage.addNewBlock( blockNames.paragraph ); + + if ( isAndroid() ) { + await editorPage.dismissKeyboard(); + } + + paragraphBlockElement = await editorPage.getTextBlockAtPosition( + blockNames.paragraph, + 2 + ); + + await editorPage.typeTextToTextBlock( + paragraphBlockElement, + testData.mediumText + ); + await toggleOrientation( editorPage.driver ); + + const html = await editorPage.getHtmlContent(); + + expect( html.toLowerCase() ).toBe( + testData.deviceRotationHtml.toLowerCase() + ); + } ); +} ); + +describe( 'Gutenberg Editor Paste tests', () => { // skip iOS for now if ( ! isAndroid() ) { it( 'skips the tests on any platform other than Android', async () => { diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-drag-and-drop.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-drag-and-drop.test.js index 104c1ca4d4cbf..5d7cce975e373 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-drag-and-drop.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-drag-and-drop.test.js @@ -51,8 +51,8 @@ describe( 'Gutenberg Editor Drag & Drop blocks tests', () => { // Remove the blocks await spacerBlock.click(); - await editorPage.removeBlockAtPosition( blockNames.spacer, 2 ); - await editorPage.removeBlockAtPosition( blockNames.paragraph, 1 ); + await editorPage.removeBlock(); + await editorPage.removeBlock(); } ); onlyOnAndroid( @@ -84,7 +84,7 @@ describe( 'Gutenberg Editor Drag & Drop blocks tests', () => { expect( paragraphText ).toMatch( testData.shortText ); // Remove the block - await editorPage.removeBlockAtPosition( blockNames.paragraph ); + await editorPage.removeBlock(); } ); @@ -118,7 +118,7 @@ describe( 'Gutenberg Editor Drag & Drop blocks tests', () => { expect( shortcodeText ).toMatch( testData.shortText ); // Remove the block - await editorPage.removeBlockAtPosition( blockNames.shortcode ); + await editorPage.removeBlock(); } ); @@ -158,6 +158,6 @@ describe( 'Gutenberg Editor Drag & Drop blocks tests', () => { expect( secondBlockText ).toMatch( testData.shortText ); // Remove the block - await editorPage.removeBlockAtPosition( blockNames.paragraph ); + await editorPage.removeBlock(); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-file-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-file-@canary.test.js deleted file mode 100644 index 09858bda2ede7..0000000000000 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-file-@canary.test.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Internal dependencies - */ -import { blockNames } from './pages/editor-page'; -import { waitForMediaLibrary } from './helpers/utils'; -import testData from './helpers/test-data'; - -describe( 'Gutenberg Editor File Block tests', () => { - it( 'should be able to add a file block and a file to it', async () => { - // add a file block - await editorPage.addNewBlock( blockNames.file ); - - // dismiss the media picker automatically opened when adding a file block - await waitForMediaLibrary( editorPage.driver ); - await editorPage.closePicker(); - - // verify there's a file block - const block = await editorPage.getFirstBlockVisible(); - await expect( block ).toBeTruthy(); - - // tap on the file block - block.click(); - - // wait for the media picker's Media Library option to come up - await waitForMediaLibrary( editorPage.driver ); - - // tap on Media Library option - await editorPage.chooseMediaLibrary(); - - // get the html version of the content - const html = await editorPage.getHtmlContent(); - - // verify the html matches the expected content - expect( html.toLowerCase() ).toBe( - testData.fileBlockPlaceholder.toLowerCase() - ); - } ); -} ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js deleted file mode 100644 index 1d8d6fe3c9ea2..0000000000000 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Internal dependencies - */ -import { blockNames } from './pages/editor-page'; -import { isAndroid, clickMiddleOfElement, swipeUp } from './helpers/utils'; -import testData from './helpers/test-data'; - -describe( 'Gutenberg Editor Image Block tests', () => { - it( 'should be able to add an image block', async () => { - // iOS only test - Can only add image from media library on iOS - if ( ! isAndroid() ) { - await editorPage.addNewBlock( blockNames.image ); - await editorPage.closePicker(); - - const imageBlock = await editorPage.getBlockAtPosition( - blockNames.image - ); - - await editorPage.selectEmptyImageBlock( imageBlock ); - await editorPage.chooseMediaLibrary(); - - // Workaround because of #952. - const titleElement = await editorPage.getTitleElement(); - await clickMiddleOfElement( editorPage.driver, titleElement ); - await editorPage.dismissKeyboard(); - // End workaround. - - await swipeUp( editorPage.driver, imageBlock ); - await editorPage.enterCaptionToSelectedImageBlock( - testData.imageCaption, - true - ); - await editorPage.dismissKeyboard(); - - await editorPage.addNewBlock( blockNames.paragraph ); - await editorPage.sendTextToParagraphBlock( 2, testData.shortText ); - const html = await editorPage.getHtmlContent(); - - expect( html.toLowerCase() ).toBe( - testData.imageShorteHtml.toLowerCase() - ); - } - } ); -} ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-initial-html.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-initial-html-@canary.test.js similarity index 100% rename from packages/react-native-editor/__device-tests__/gutenberg-editor-initial-html.test.js rename to packages/react-native-editor/__device-tests__/gutenberg-editor-initial-html-@canary.test.js diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-media-blocks-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-media-blocks-@canary.test.js new file mode 100644 index 0000000000000..1a12e32b99e90 --- /dev/null +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-media-blocks-@canary.test.js @@ -0,0 +1,165 @@ +/** + * Internal dependencies + */ +import { blockNames } from './pages/editor-page'; +import { waitForMediaLibrary, isAndroid } from './helpers/utils'; +import testData from './helpers/test-data'; + +const onlyOniOS = ! isAndroid() ? describe : describe.skip; + +describe( 'Gutenberg Editor Audio Block tests', () => { + it( 'should be able to add an audio block and a file to it', async () => { + // add an audio block + await editorPage.addNewBlock( blockNames.audio ); + + // dismiss the media picker automatically opened when adding an audio block + await waitForMediaLibrary( editorPage.driver ); + await editorPage.closePicker(); + + // verify there's an audio block + let block = await editorPage.getFirstBlockVisible(); + await expect( block ).toBeTruthy(); + + // tap on the audio block + block.click(); + + // wait for the media picker's Media Library option to come up + await waitForMediaLibrary( editorPage.driver ); + + // tap on Media Library option + await editorPage.chooseMediaLibrary(); + + // get the html version of the content + const html = await editorPage.getHtmlContent(); + + // verify the html matches the expected content + expect( html.toLowerCase() ).toBe( + testData.audioBlockPlaceholder.toLowerCase() + ); + + block = await editorPage.getBlockAtPosition( blockNames.audio ); + await block.click(); + await editorPage.removeBlock(); + } ); +} ); + +describe( 'Gutenberg Editor File Block tests', () => { + it( 'should be able to add a file block and a file to it', async () => { + // add a file block + await editorPage.addNewBlock( blockNames.file ); + + // dismiss the media picker automatically opened when adding a file block + await waitForMediaLibrary( editorPage.driver ); + await editorPage.closePicker(); + + // verify there's a file block + let block = await editorPage.getFirstBlockVisible(); + await expect( block ).toBeTruthy(); + + // tap on the file block + block.click(); + + // wait for the media picker's Media Library option to come up + await waitForMediaLibrary( editorPage.driver ); + + // tap on Media Library option + await editorPage.chooseMediaLibrary(); + + // get the html version of the content + const html = await editorPage.getHtmlContent(); + + // verify the html matches the expected content + expect( html.toLowerCase() ).toBe( + testData.fileBlockPlaceholder.toLowerCase() + ); + + block = await editorPage.getBlockAtPosition( blockNames.file ); + await block.click(); + await editorPage.removeBlock(); + } ); +} ); + +// iOS only test - It can only add images from the media library on iOS. +onlyOniOS( 'Gutenberg Editor Image Block tests', () => { + it( 'should be able to add an image block', async () => { + await editorPage.addNewBlock( blockNames.image ); + await editorPage.closePicker(); + + let imageBlock = await editorPage.getBlockAtPosition( + blockNames.image + ); + + await editorPage.selectEmptyImageBlock( imageBlock ); + await editorPage.chooseMediaLibrary(); + + await editorPage.waitForElementToBeDisplayedById( + 'A snow-capped mountain top in a cloudy sky with red-leafed trees in the foreground', + 2000 + ); + await editorPage.enterCaptionToSelectedImageBlock( + testData.imageCaption, + true + ); + + const html = await editorPage.getHtmlContent(); + + expect( html.toLowerCase() ).toBe( + testData.imageShortHtml.toLowerCase() + ); + + imageBlock = await editorPage.getBlockAtPosition( blockNames.image ); + await imageBlock.click(); + await editorPage.removeBlock(); + } ); +} ); + +onlyOniOS( 'Gutenberg Editor Cover Block test', () => { + it( 'should displayed properly and have properly converted height (ios only)', async () => { + // Temporarily this test is skipped on Android, due to the inconsistency of the results, + // which are related to getting values in raw pixels instead of density pixels on Android. + await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); + + const coverBlock = await editorPage.getBlockAtPosition( + blockNames.cover + ); + + const { height } = await coverBlock.getSize(); + // Height is set to 20rem, where 1rem is 16. + // There is also block's vertical padding equal 32. + // Finally, the total height should be 20 * 16 + 32 = 352. + expect( height ).toBe( 352 ); + + await coverBlock.click(); + expect( coverBlock ).toBeTruthy(); + await editorPage.removeBlockAtPosition( blockNames.cover ); + } ); + + // Testing this for iOS on a device is valuable to ensure that it properly + // handles opening multiple modals, as only one can be open at a time. + // NOTE: It can only add images from the media library on iOS. + it( 'allows modifying media from within block settings', async () => { + await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); + + const coverBlock = await editorPage.getBlockAtPosition( + blockNames.cover + ); + await coverBlock.click(); + + await editorPage.openBlockSettings(); + await editorPage.clickAddMediaFromCoverBlock(); + await editorPage.chooseMediaLibrary(); + await editorPage.replaceMediaImage(); + + // First modal should no longer be presented. + const replaceButtons = + await editorPage.driver.elementsByAccessibilityId( 'Replace' ); + // eslint-disable-next-line jest/no-conditional-expect + expect( replaceButtons.length ).toBe( 0 ); + + // Select different media. + await editorPage.chooseMediaLibrary(); + + expect( coverBlock ).toBeTruthy(); + await editorPage.removeBlockAtPosition( blockNames.cover ); + } ); +} ); 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 e67b7fd6e797b..6604c9ba6f32b 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 @@ -35,8 +35,8 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { new RegExp( `${ text0 + text1 }|${ text0 } ${ text1 }` ) ); - await editorPage.removeBlockAtPosition( blockNames.paragraph, 2 ); - await editorPage.removeBlockAtPosition( blockNames.paragraph ); + await editorPage.removeBlock(); + await editorPage.removeBlock(); } ); it( 'should be able to merge 2 paragraph blocks into 1', async () => { @@ -72,8 +72,13 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { const text = await editorPage.getTextForParagraphBlockAtPosition( 1 ); expect( text0 + text1 ).toMatch( text ); + paragraphBlockElement = await editorPage.getTextBlockAtPosition( + blockNames.paragraph, + 1 + ); + await paragraphBlockElement.click(); expect( await editorPage.getNumberOfParagraphBlocks() ).toEqual( 1 ); - await editorPage.removeBlockAtPosition( blockNames.paragraph ); + await editorPage.removeBlock(); } ); it( 'should be able to create a post with multiple paragraph blocks', async () => { @@ -81,7 +86,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { await editorPage.sendTextToParagraphBlock( 1, testData.longText ); for ( let i = 3; i > 0; i-- ) { - await editorPage.removeBlockAtPosition( blockNames.paragraph, i ); + await editorPage.removeBlock(); } } ); @@ -116,7 +121,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { await editorPage.getTextForParagraphBlockAtPosition( 1 ); expect( text0 + text1 ).toMatch( mergedBlockText ); - await editorPage.removeBlockAtPosition( blockNames.paragraph ); + await editorPage.removeBlock(); } ); // Based on https://github.com/wordpress-mobile/gutenberg-mobile/pull/1507 @@ -145,6 +150,6 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { if ( isAndroid() ) { await paragraphBlockElement.click(); } - await editorPage.removeBlockAtPosition( blockNames.paragraph ); + await editorPage.removeBlock(); } ); } ); 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 deleted file mode 100644 index 215b81bf98b4d..0000000000000 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Internal dependencies - */ -import { blockNames } from './pages/editor-page'; -import { isAndroid, toggleOrientation } from './helpers/utils'; -import testData from './helpers/test-data'; - -describe( 'Gutenberg Editor tests', () => { - it( 'should be able to add blocks , rotate device and continue adding blocks', async () => { - await editorPage.addNewBlock( blockNames.paragraph ); - let paragraphBlockElement = await editorPage.getTextBlockAtPosition( - blockNames.paragraph - ); - - await editorPage.typeTextToTextBlock( - paragraphBlockElement, - testData.mediumText - ); - - await toggleOrientation( editorPage.driver ); - // On Android the keyboard hides the add block button, let's hide it after rotation - if ( isAndroid() ) { - await editorPage.dismissKeyboard(); - } - - await editorPage.addNewBlock( blockNames.paragraph ); - - if ( isAndroid() ) { - await editorPage.dismissKeyboard(); - } - - paragraphBlockElement = await editorPage.getTextBlockAtPosition( - blockNames.paragraph, - 2 - ); - - await editorPage.typeTextToTextBlock( - paragraphBlockElement, - testData.mediumText - ); - await toggleOrientation( editorPage.driver ); - - const html = await editorPage.getHtmlContent(); - - expect( html.toLowerCase() ).toBe( - testData.deviceRotationHtml.toLowerCase() - ); - } ); -} ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js index 2233b56f6c37d..4b425df306c47 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js @@ -92,7 +92,7 @@ describe( 'Gutenberg Editor Search Block tests.', () => { ); await searchBlock.click(); - await editorPage.toggleHideSearchLabelSetting( searchBlock ); + await editorPage.toggleHideSearchLabelSetting(); await editorPage.dismissBottomSheet(); // Switch to html and verify. @@ -106,7 +106,7 @@ describe( 'Gutenberg Editor Search Block tests.', () => { ); await searchBlock.click(); - await editorPage.toggleSearchIconOnlySetting( searchBlock ); + await editorPage.toggleSearchIconOnlySetting(); await editorPage.dismissBottomSheet(); // Switch to html and verify. @@ -121,7 +121,6 @@ describe( 'Gutenberg Editor Search Block tests.', () => { await searchBlock.click(); await editorPage.changeSearchButtonPositionSetting( - searchBlock, 'Button inside' ); await editorPage.isSearchSettingsVisible(); @@ -138,10 +137,7 @@ describe( 'Gutenberg Editor Search Block tests.', () => { ); await searchBlock.click(); - await editorPage.changeSearchButtonPositionSetting( - searchBlock, - 'No button' - ); + await editorPage.changeSearchButtonPositionSetting( 'No button' ); await editorPage.isSearchSettingsVisible(); await editorPage.dismissBottomSheet(); @@ -159,7 +155,7 @@ const removeSearchBlock = async () => { await searchBlock.click(); // Remove search block. - await editorPage.removeBlockAtPosition( blockNames.search ); + await editorPage.removeBlock(); }; const verifySearchElementText = async ( testId, expected ) => { diff --git a/packages/react-native-editor/__device-tests__/helpers/test-data.js b/packages/react-native-editor/__device-tests__/helpers/test-data.js index db84a7ad02926..a4fca9f89ab59 100644 --- a/packages/react-native-editor/__device-tests__/helpers/test-data.js +++ b/packages/react-native-editor/__device-tests__/helpers/test-data.js @@ -95,13 +95,9 @@ exports.imageCompletehtml = `

The finer continuum interprets the polynomial rabbit. When can the geology runs? An astronomer runs. Should a communist consent?

`; -exports.imageShorteHtml = ` +exports.imageShortHtml = `
A snow-capped mountain top in a cloudy sky with red-leafed trees in the foreground
C'est la vie my friends
- - - -

rock music approaches at high velocity.

-`; +`; exports.unsupportedBlockHtml = ``; 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 c78cc135c06ba..57490584cb3b9 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -1,3 +1,9 @@ +/** + * External dependencies + */ +// eslint-disable-next-line import/no-extraneous-dependencies +const wd = require( 'wd' ); + /** * Internal dependencies */ @@ -5,7 +11,6 @@ const { doubleTap, isAndroid, isEditorVisible, - isElementVisible, longPressMiddleOfElement, setClipboard, setupDriver, @@ -168,15 +173,21 @@ class EditorPage { } async getTitleElement( options = { autoscroll: false } ) { - // TODO: Improve the identifier for this element - const elements = await this.driver.elementsByXPath( - `//*[contains(@${ this.accessibilityIdXPathAttrib }, "Post title.")]` + const titleElement = isAndroid() + ? 'Post title. Welcome to Gutenberg!, Updates the title.' + : 'post-title'; + const elements = await this.driver.elementsByAccessibilityId( + titleElement ); - if ( elements.length === 0 && options.autoscroll ) { + + if ( + ( elements.length === 0 || ! elements[ 0 ].isDisplayed() ) && + options.autoscroll + ) { await swipeDown( this.driver ); return this.getTitleElement( options ); } - return elements[ elements.length - 1 ]; + return elements[ 0 ]; } // iOS loads the block list more eagerly compared to Android. @@ -278,15 +289,32 @@ class EditorPage { } } - async openBlockSettings( block ) { - await block.click(); - - const settingsButton = await block.elementByAccessibilityId( - 'Open Settings' + async openBlockSettings() { + const settingsButtonElement = 'Open Settings'; + const settingsButton = await this.waitForElementToBeDisplayedById( + settingsButtonElement ); + await settingsButton.click(); } + async removeBlock() { + const blockActionsButtonElement = isAndroid() + ? 'Open Block Actions Menu, Double tap to open Bottom Sheet with available options' + : 'Open Block Actions Menu'; + const blockActionsMenu = await this.waitForElementToBeDisplayedById( + blockActionsButtonElement + ); + await blockActionsMenu.click(); + + const removeElement = 'Remove block'; + const removeBlockButton = await this.waitForElementToBeDisplayedById( + removeElement, + 4000 + ); + return await removeBlockButton.click(); + } + async dismissBottomSheet() { return await swipeDown( this.driver ); } @@ -296,13 +324,12 @@ class EditorPage { // ========================= async addNewBlock( blockName, relativePosition ) { - const addBlockButtonLocator = isAndroid() - ? '//android.widget.Button[@content-desc="Add block, Double tap to add a block"]' - : '//XCUIElementTypeButton[@name="add-block-button"]'; - - const addButton = await waitForVisible( - this.driver, - addBlockButtonLocator + const addBlockElement = isAndroid() + ? 'Add block, Double tap to add a block' + : 'Add block'; + const addButton = await this.waitForElementToBeDisplayedById( + addBlockElement, + 3000 ); if ( relativePosition === 'before' ) { @@ -346,14 +373,19 @@ class EditorPage { return screenHeight * 0.82; } + async waitForInserter() { + const inserterElement = isAndroid() + ? 'Blocks menu' + : 'InserterUI-Blocks'; + return await this.waitForElementToBeDisplayedById( + inserterElement, + 4000 + ); + } + // Attempts to find the given block button in the block inserter control. async findBlockButton( blockName ) { - // Wait for the first block, Paragraph block, to load before looking for other blocks - const paragraphBlockLocator = isAndroid() - ? '//android.widget.Button[@content-desc="Paragraph block"]/android.widget.TextView' - : '//XCUIElementTypeButton[@name="Paragraph block"]'; - - await waitForVisible( this.driver, paragraphBlockLocator ); + await this.waitForInserter(); const blockAccessibilityLabel = `${ blockName } block`; const blockAccessibilityLabelNewBlock = `${ blockAccessibilityLabel }, newly available`; @@ -555,11 +587,20 @@ class EditorPage { } async assertSlashInserterPresent() { - const slashInserterLocator = isAndroid() - ? '//android.widget.HorizontalScrollView[@content-desc="Slash inserter results"]/android.view.ViewGroup' - : '(//XCUIElementTypeOther[@name="Slash inserter results"])[1]'; + let isPresent = false; + const autocompleterElementId = isAndroid() + ? 'Slash inserter results' + : 'autocompleter'; + const autocompleterElement = + await this.driver.elementsByAccessibilityId( + autocompleterElementId + ); + + if ( autocompleterElement?.[ 0 ] ) { + isPresent = await autocompleterElement[ 0 ].isDisplayed(); + } - return await isElementVisible( this.driver, slashInserterLocator, 5 ); + return isPresent; } // ========================= @@ -700,8 +741,8 @@ class EditorPage { return await typeString( this.driver, textViewElement, text ); } - async toggleHideSearchLabelSetting( block ) { - await this.openBlockSettings( block ); + async toggleHideSearchLabelSetting() { + await this.openBlockSettings(); const elementName = isAndroid() ? '//*' : '//XCUIElementTypeOther'; @@ -712,8 +753,8 @@ class EditorPage { ); } - async changeSearchButtonPositionSetting( block, buttonPosition ) { - await this.openBlockSettings( block ); + async changeSearchButtonPositionSetting( buttonPosition ) { + await this.openBlockSettings(); const elementName = isAndroid() ? '//*' : '//XCUIElementTypeButton'; @@ -724,8 +765,8 @@ class EditorPage { return await clickIfClickable( this.driver, optionMenuButtonLocator ); } - async toggleSearchIconOnlySetting( block ) { - await this.openBlockSettings( block ); + async toggleSearchIconOnlySetting() { + await this.openBlockSettings(); const elementName = isAndroid() ? '//*' : '//XCUIElementTypeOther'; @@ -800,6 +841,16 @@ class EditorPage { return await waitForVisible( this.driver, blockLocator ); } + + async waitForElementToBeDisplayedById( id, timeout = 2000 ) { + await this.driver.waitForElementByAccessibilityId( + id, + wd.asserters.isDisplayed, + timeout + ); + + return await this.driver.elementByAccessibilityId( id ); + } } const blockNames = { diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index 2601a1871f580..065fe844249a9 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -108,7 +108,7 @@ "test": "cross-env NODE_ENV=test jest --verbose --config ../../test/native/jest.config.js", "test:debug": "cross-env NODE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand --verbose --config ../../test/native/jest.config.js", "test:update": "npm run test -- --updateSnapshot", - "device-tests": "cross-env NODE_ENV=test jest --forceExit --detectOpenHandles --no-cache --maxWorkers=3 --testPathIgnorePatterns=@canary --verbose --config ./jest_ui.config.js", + "device-tests": "cross-env NODE_ENV=test jest --forceExit --detectOpenHandles --no-cache --maxWorkers=3 --testPathIgnorePatterns='canary|gutenberg-editor-rendering' --verbose --config ./jest_ui.config.js", "device-tests-canary": "cross-env NODE_ENV=test jest --forceExit --detectOpenHandles --no-cache --maxWorkers=2 --testPathPattern=@canary --verbose --config ./jest_ui.config.js", "device-tests:local": "cross-env NODE_ENV=test jest --runInBand --detectOpenHandles --verbose --forceExit --config ./jest_ui.config.js", "device-tests:debug": "cross-env NODE_ENV=test node $NODE_DEBUG_OPTION --inspect-brk node_modules/jest/bin/jest --runInBand --detectOpenHandles --verbose --config ./jest_ui.config.js",