Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mobile] - Add E2E tests for the Drag & Drop blocks feature #41368

Merged
merged 6 commits into from
May 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/**
* Internal dependencies
*/
import { blockNames } from './pages/editor-page';
import {
clearClipboard,
clickElementOutsideOfTextInput,
dragAndDropAfterElement,
isAndroid,
setClipboard,
tapPasteAboveElement,
} from './helpers/utils';
import testData from './helpers/test-data';

describe( 'Gutenberg Editor Drag & Drop blocks tests', () => {
beforeEach( async () => {
await clearClipboard( editorPage.driver );
} );

it( 'should be able to drag & drop a block', async () => {
// Initialize the editor with a Spacer and Paragraph block
await editorPage.setHtmlContent(
[ testData.spacerBlock, testData.paragraphBlockShortText ].join(
'\n\n'
)
);

// Get elements for both blocks
const spacerBlock = await editorPage.getBlockAtPosition(
blockNames.spacer
);
const paragraphBlock = await editorPage.getParagraphBlockWrapperAtPosition(
2
);

// Drag & drop the Spacer block after the Paragraph block
await dragAndDropAfterElement(
editorPage.driver,
spacerBlock,
paragraphBlock
);

// Get the first block, in this case the Paragraph block
// and check the text value is the expected one
const firstBlockText = await editorPage.getTextForParagraphBlockAtPosition(
1
);
expect( firstBlockText ).toMatch( testData.shortText );

// Remove the blocks
await spacerBlock.click();
await editorPage.removeBlockAtPosition( blockNames.spacer, 2 );
await editorPage.removeBlockAtPosition( blockNames.paragraph, 1 );
} );

it( 'should be able to long-press on a text-based block to paste a text in a focused textinput', async () => {
// Add a Paragraph block
await editorPage.addNewBlock( blockNames.paragraph );
const paragraphBlockElement = await editorPage.getTextBlockAtPosition(
blockNames.paragraph
);

// Set clipboard text
await setClipboard( editorPage.driver, testData.shortText );

// Dismiss auto-suggestion popup
if ( isAndroid() ) {
// On Andrdoid 10 a new auto-suggestion popup is appearing to let the user paste text recently put in the clipboard. Let's dismiss it.
await editorPage.dismissAndroidClipboardSmartSuggestion();
}

// Paste into the Paragraph block
await tapPasteAboveElement( editorPage.driver, paragraphBlockElement );
const paragraphText = await editorPage.getTextForParagraphBlockAtPosition(
1
);

// Expect to have the pasted text in the Paragraph block
expect( paragraphText ).toMatch( testData.shortText );

// Remove the block
await editorPage.removeBlockAtPosition( blockNames.paragraph );
} );

it( 'should be able to long-press on a text-based block using the PlainText component to paste a text in a focused textinput', async () => {
// Add a Shortcode block
await editorPage.addNewBlock( blockNames.shortcode );
const shortcodeBlockElement = await editorPage.getShortBlockTextInputAtPosition(
blockNames.shortcode
);

// Set clipboard text
await setClipboard( editorPage.driver, testData.shortText );

// Dismiss auto-suggestion popup
if ( isAndroid() ) {
// On Andrdoid 10 a new auto-suggestion popup is appearing to let the user paste text recently put in the clipboard. Let's dismiss it.
await editorPage.dismissAndroidClipboardSmartSuggestion();
}

// Paste into the Shortcode block
await tapPasteAboveElement( editorPage.driver, shortcodeBlockElement );
const shortcodeText = await shortcodeBlockElement.text();

// Expect to have the pasted text in the Shortcode block
expect( shortcodeText ).toMatch( testData.shortText );

// Remove the block
await editorPage.removeBlockAtPosition( blockNames.shortcode );
} );

it( 'should be able to drag & drop a text-based block when the textinput is not focused', async () => {
// Initialize the editor with two Paragraph blocks
await editorPage.setHtmlContent(
[
testData.paragraphBlockShortText,
testData.paragraphBlockEmpty,
].join( '\n\n' )
);

// Get elements for both blocks
const firstParagraphBlock = await editorPage.getParagraphBlockWrapperAtPosition(
1
);
const secondParagraphBlock = await editorPage.getParagraphBlockWrapperAtPosition(
2
);

// Tap on the first Paragraph block outside of the textinput
await clickElementOutsideOfTextInput(
editorPage.driver,
firstParagraphBlock
);

// Drag & drop the first Paragraph block after the second Paragraph block
await dragAndDropAfterElement(
editorPage.driver,
firstParagraphBlock,
secondParagraphBlock
);

// Get the current second Paragraph block in the editor after dragging & dropping
const secondBlockText = await editorPage.getTextForParagraphBlockAtPosition(
2
);

// Expect the second Paragraph block to have the expected content
expect( secondBlockText ).toMatch( testData.shortText );

// Remove the block
await editorPage.removeBlockAtPosition( blockNames.paragraph );
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import { blockNames } from './pages/editor-page';
import {
clearClipboard,
longPressMiddleOfElement,
tapSelectAllAboveElement,
tapCopyAboveElement,
Expand All @@ -21,7 +22,7 @@ describe( 'Gutenberg Editor paste tests', () => {
}

beforeAll( async () => {
await editorPage.driver.setClipboard( '', 'plaintext' );
await clearClipboard( editorPage.driver );
} );

it( 'copies plain text from one paragraph block and pastes in another', async () => {
Expand Down Expand Up @@ -58,10 +59,6 @@ describe( 'Gutenberg Editor paste tests', () => {
);

// Paste into second paragraph block.
await longPressMiddleOfElement(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this to tapPasteAboveElement so it handles all of the logic.

editorPage.driver,
paragraphBlockElement2
);
await tapPasteAboveElement( editorPage.driver, paragraphBlockElement2 );

const text = await editorPage.getTextForParagraphBlockAtPosition( 2 );
Expand Down Expand Up @@ -101,10 +98,6 @@ describe( 'Gutenberg Editor paste tests', () => {
);

// Paste into second paragraph block.
await longPressMiddleOfElement(
editorPage.driver,
paragraphBlockElement2
);
await tapPasteAboveElement( editorPage.driver, paragraphBlockElement2 );

// Check styled text by verifying html contents.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ exports.paragraphBlockEmpty = `<!-- wp:paragraph -->
<p></p>
<!-- /wp:paragraph -->`;

exports.paragraphBlockShortText = `<!-- wp:paragraph -->
<p>Rock music approaches at high velocity.</p>
<!-- /wp:paragraph -->`;

exports.multiLinesParagraphBlock = `<!-- wp:paragraph -->
<p>multiple lines<br>multiple lines<br>multiple lines</p>
<!-- /wp:paragraph -->`;
Expand All @@ -177,3 +181,7 @@ exports.unknownElementParagraphBlock = `<!-- wp:paragraph -->
exports.lettersInParagraphBlock = `<!-- wp:paragraph -->
<p>ABCD</p>
<!-- /wp:paragraph -->`;

exports.spacerBlock = `<!-- wp:spacer -->
<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->`;
100 changes: 88 additions & 12 deletions packages/react-native-editor/__device-tests__/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const strToKeycode = {
[ backspace ]: 67,
};

// $block-edge-to-content value
const blockEdgeToContent = 16;

const timer = ( ms ) => new Promise( ( res ) => setTimeout( res, ms ) );

const isAndroid = () => {
Expand Down Expand Up @@ -301,18 +304,27 @@ const clickBeginningOfElement = async ( driver, element ) => {
await action.perform();
};

// Clicks in the top left of a text-based element outside of the TextInput
const clickElementOutsideOfTextInput = async ( driver, element ) => {
const location = await element.getLocation();
const y = isAndroid() ? location.y - blockEdgeToContent : location.y;
const x = isAndroid() ? location.x - blockEdgeToContent : location.x;

const action = new wd.TouchAction( driver ).press( { x, y } ).release();
await action.perform();
};

// Long press to activate context menu.
const longPressMiddleOfElement = async ( driver, element ) => {
const location = await element.getLocation();
const size = await element.getSize();

const action = await new wd.TouchAction( driver );
const x = location.x + size.width / 2;
const y = location.y + size.height / 2;
action.press( { x, y } );
// Setting to wait a bit longer because this is failing more frequently on the CI
action.wait( 5000 );
action.release();
const action = new wd.TouchAction( driver )
.longPress( { x, y } )
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this from press to longPress since it was causing random fails on iOS.

.wait( 5000 ) // Setting to wait a bit longer because this is failing more frequently on the CI
.release();
await action.perform();
};

Expand Down Expand Up @@ -342,13 +354,21 @@ const tapCopyAboveElement = async ( driver, element ) => {

// Press "Paste" in floating context menu.
const tapPasteAboveElement = async ( driver, element ) => {
const location = await element.getLocation();
const action = await new wd.TouchAction( driver );
action.wait( 2000 );
action.press( { x: location.x + 100, y: location.y - 50 } );
action.wait( 2000 );
action.release();
await action.perform();
await longPressMiddleOfElement( driver, element );

if ( isAndroid() ) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tapPasteAboveElement was only being used on Android, so I added support for iOS.

const location = await element.getLocation();
const action = await new wd.TouchAction( driver );
action.wait( 2000 );
action.press( { x: location.x + 100, y: location.y - 50 } );
action.wait( 2000 );
action.release();
await action.perform();
} else {
const pasteButtonLocator = '//XCUIElementTypeMenuItem[@name="Paste"]';
await clickIfClickable( driver, pasteButtonLocator );
await driver.sleep( 3000 ); // Wait for paste notification to disappear.
}
};

// Starts from the middle of the screen or the element(if specified)
Expand Down Expand Up @@ -413,6 +433,29 @@ const swipeDown = async ( driver, delay = 3000 ) => {
);
};

// Drag & Drop after element
const dragAndDropAfterElement = async ( driver, element, nextElement ) => {
// Element to drag & drop
const elementLocation = await element.getLocation();
const elementSize = await element.getSize();
const x = elementLocation.x + elementSize.width / 2;
const y = elementLocation.y + elementSize.height / 2;

// Element to drag & drop to
const nextElementLocation = await nextElement.getLocation();
const nextElementSize = await nextElement.getSize();
const nextYPosition = isAndroid()
? elementLocation.y + nextElementLocation.y + nextElementSize.height
: nextElementLocation.y + nextElementSize.height;

const action = new wd.TouchAction( driver )
.press( { x, y } )
Copy link
Member Author

@geriux geriux May 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up using press since longPress was not triggering correctly for the Drag & Drop feature on iOS.

.wait( 5000 )
.moveTo( { x, y: nextYPosition } )
.release();
await action.perform();
};

const toggleHtmlMode = async ( driver, toggleOn ) => {
if ( isAndroid() ) {
// Hit the "Menu" key.
Expand Down Expand Up @@ -575,17 +618,50 @@ const waitIfAndroid = async () => {
}
};

/**
* Content type definitions.
* Note: Android only supports plaintext.
*
* @typedef {"plaintext" | "image" | "url"} ClipboardContentType
*/

/**
* Helper to set content in the clipboard.
*
* @param {Object} driver Driver
* @param {string} content Content to set in the clipboard
* @param {ClipboardContentType} contentType Type of the content
*/
const setClipboard = async ( driver, content, contentType = 'plaintext' ) => {
const base64String = Buffer.from( content ).toString( 'base64' );
await driver.setClipboard( base64String, contentType );
};

/**
* Helper to clear the clipboard
*
* @param {Object} driver Driver
* @param {ClipboardContentType} contentType Type of the content
*/
const clearClipboard = async ( driver, contentType = 'plaintext' ) => {
await driver.setClipboard( '', contentType );
};

module.exports = {
backspace,
clearClipboard,
clickBeginningOfElement,
clickElementOutsideOfTextInput,
clickIfClickable,
clickMiddleOfElement,
doubleTap,
dragAndDropAfterElement,
isAndroid,
isEditorVisible,
isElementVisible,
isLocalEnvironment,
longPressMiddleOfElement,
setClipboard,
setupDriver,
stopDriver,
swipeDown,
Expand Down
Loading