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

Add waitForVisible() to all blocks #41126

Merged
merged 14 commits into from
May 25, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ describe( 'Gutenberg Editor tests for Block insertion', () => {
testData.blockInsertionHtml.toLowerCase()
);

// wait for the block editor to load and for accessibility ids to update
await editorPage.driver.sleep( 3000 );

// Workaround for now since deleting the first element causes a crash on CI for Android
if ( isAndroid() ) {
paragraphBlockElement = await editorPage.getTextBlockAtPosition(
Expand All @@ -55,8 +52,6 @@ describe( 'Gutenberg Editor tests for Block insertion', () => {
await paragraphBlockElement.click();
await editorPage.removeBlockAtPosition( blockNames.paragraph, 3 );
for ( let i = 3; i > 0; i-- ) {
// wait for accessibility ids to update
await editorPage.driver.sleep( 1000 );
paragraphBlockElement = await editorPage.getTextBlockAtPosition(
blockNames.paragraph,
i,
Expand All @@ -72,8 +67,6 @@ describe( 'Gutenberg Editor tests for Block insertion', () => {
}
} else {
for ( let i = 4; i > 0; i-- ) {
// wait for accessibility ids to update
await editorPage.driver.sleep( 1000 );
paragraphBlockElement = await editorPage.getTextBlockAtPosition(
blockNames.paragraph
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,95 +2,49 @@
* Internal dependencies
*/
import { blockNames } from './pages/editor-page';
import { isAndroid, waitForVisible } from './helpers/utils';
import { isAndroid } from './helpers/utils';
import testData from './helpers/test-data';

describe( 'Gutenberg Editor Cover Block test', () => {
it( 'should displayed properly and have properly converted height (ios only)', async () => {
await editorPage.setHtmlContent( testData.coverHeightWithRemUnit );

const coverBlock = await editorPage.getBlockAtPosition(
blockNames.cover,
1,
{ useWaitForVisible: true }
);

// Temporarily this test is skipped on Android,due to the inconsistency of the results,
// which are related to getting values in raw pixels instead of density pixels on Android.
/* eslint-disable jest/no-conditional-expect */
if ( ! isAndroid() ) {
await editorPage.setHtmlContent( testData.coverHeightWithRemUnit );

const coverBlock = await editorPage.getBlockAtPosition(
blockNames.cover
);

const { height } = await coverBlock.getSize();
// Height is set to 20rem, where 1rem is 16.
// There is also block's vertical padding equal 32.
// Finally, the total height should be 20 * 16 + 32 = 352.
expect( height ).toBe( 352 );
}
/* eslint-enable jest/no-conditional-expect */
/* eslint-enable jest/no-conditional-expect */

await coverBlock.click();
expect( coverBlock ).toBeTruthy();
await editorPage.removeBlockAtPosition( blockNames.cover );
jostnes marked this conversation as resolved.
Show resolved Hide resolved
await coverBlock.click();
expect( coverBlock ).toBeTruthy();
await editorPage.removeBlockAtPosition( blockNames.cover );
}
} );

// Testing this for iOS on a device is valuable to ensure that it properly
// handles opening multiple modals, as only one can be open at a time.
it( 'allows modifying media from within block settings', async () => {
await editorPage.setHtmlContent( testData.coverHeightWithRemUnit );
jostnes marked this conversation as resolved.
Show resolved Hide resolved

const coverBlock = await editorPage.getBlockAtPosition(
blockNames.cover,
1,
{ useWaitForVisible: true }
);
await coverBlock.click();

// Can only add image from media library on iOS
if ( ! isAndroid() ) {
// Open block settings.
const settingsButton = await editorPage.driver.elementByAccessibilityId(
'Open Settings'
);
await settingsButton.click();
await editorPage.setHtmlContent( testData.coverHeightWithRemUnit );

// Add initial media via button within bottom sheet.
const mediaSection = await editorPage.driver.elementByAccessibilityId(
'Media Add image or video'
);
const addMediaButton = await mediaSection.elementByAccessibilityId(
'Add image or video'
const coverBlock = await editorPage.getBlockAtPosition(
blockNames.cover
);
await addMediaButton.click();
await editorPage.chooseMediaLibrary();
await editorPage.driver.sleep( 2000 ); // Await media load.

// Get Edit image button of block
const editImageButtonLocator =
'//XCUIElementTypeButton[@name="Edit image"][@enabled="true"]';
const blockEditImageButton = await waitForVisible(
editorPage.driver,
editImageButtonLocator
);

// Edit media within block settings.
await settingsButton.click();
await editorPage.driver.sleep( 2000 ); // Await media load.

// Get Edit image button of block settings.
// NOTE: Since we have multiple Edit image buttons at this
// point, we have to filter them to obtain the correct one.
const settingsEditImageButtons = await editorPage.driver.elementsByXPath(
editImageButtonLocator
);
const settingsEditImageButton = settingsEditImageButtons.find(
( element ) => element.value !== blockEditImageButton.value
);
await settingsEditImageButton.click();

// Replace image.
const replaceButton = await editorPage.driver.elementByAccessibilityId(
'Replace'
);
await replaceButton.click();
await editorPage.openBlockSettings( coverBlock );
await editorPage.clickAddMediaFromCoverBlock();
await editorPage.chooseMediaLibrary();
await editorPage.replaceMediaImage();

// First modal should no longer be presented.
const replaceButtons = await editorPage.driver.elementsByAccessibilityId(
Expand All @@ -101,9 +55,9 @@ describe( 'Gutenberg Editor Cover Block test', () => {

// Select different media.
await editorPage.chooseMediaLibrary();
}

expect( coverBlock ).toBeTruthy();
await editorPage.removeBlockAtPosition( blockNames.cover );
expect( coverBlock ).toBeTruthy();
await editorPage.removeBlockAtPosition( blockNames.cover );
}
} );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ describe( 'Gutenberg Editor Image Block tests', () => {
await editorPage.closePicker();

const imageBlock = await editorPage.getBlockAtPosition(
blockNames.image,
1,
{
useWaitForVisible: true,
}
blockNames.image
);

// Can only add image from media library on iOS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ describe( 'Gutenberg Editor Search Block tests.', () => {
searchBlock,
'Button inside'
);
await editorPage.isSearchSettingsVisible();
await editorPage.dismissBottomSheet();

// Switch to html and verify.
Expand All @@ -141,6 +142,7 @@ describe( 'Gutenberg Editor Search Block tests.', () => {
searchBlock,
'No button'
);
await editorPage.isSearchSettingsVisible();
await editorPage.dismissBottomSheet();

// Switch to html and verify.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ describe( 'Gutenberg Editor Spacer Block test', () => {
it( 'should be able to add a spacer block', async () => {
await editorPage.addNewBlock( blockNames.spacer );
const spacerBlock = await editorPage.getBlockAtPosition(
blockNames.spacer,
1,
{ useWaitForVisible: true }
blockNames.spacer
);

expect( spacerBlock ).toBeTruthy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ describe( 'Gutenberg Editor Unsupported Block Editor Tests', () => {
const editButton = await editorPage.getUnsupportedBlockBottomSheetEditButton();
await editButton.click();

await expect(
editorPage.getUnsupportedBlockWebView()
).resolves.toBeTruthy();
const webView = await editorPage.getUnsupportedBlockWebView();
await expect( webView ).toBeTruthy();
} );
} );
75 changes: 57 additions & 18 deletions packages/react-native-editor/__device-tests__/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,8 @@ const longPressMiddleOfElement = async ( driver, element ) => {
const x = location.x + size.width / 2;
const y = location.y + size.height / 2;
action.press( { x, y } );
action.wait( 2000 );
// Setting to wait a bit longer because this is failing more frequently on the CI
action.wait( 3500 );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Increasing the wait here before releasing the button press as found out that this is the culprit for flakiness on the gutenberg-editor-block-insertion-2.test.js tests.

Some examples of this failing before the fix from the past week: 1, 2, 3.

This is how the test should look like when the wait time is enough and the correct element is pressed at the right time.

action.release();
await action.perform();
};
Expand Down Expand Up @@ -419,24 +420,28 @@ const toggleHtmlMode = async ( driver, toggleOn ) => {

const showHtmlButtonXpath =
'/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.ListView/android.widget.TextView[9]';
const showHtmlButton = await driver.elementByXPath(
showHtmlButtonXpath

await clickIfClickable( driver, showHtmlButtonXpath );
} else if ( ! isAndroid() && toggleOn ) {
fluiddot marked this conversation as resolved.
Show resolved Hide resolved
await clickIfClickable(
driver,
'//XCUIElementTypeButton[@name="..."]'
);
await clickIfClickable(
driver,
'//XCUIElementTypeButton[@name="Switch to HTML"]'
);
await showHtmlButton.click();
} else {
const menuButton = await driver.elementByAccessibilityId( '...' );
await menuButton.click();
let toggleHtmlButton;
if ( toggleOn ) {
toggleHtmlButton = await driver.elementByAccessibilityId(
'Switch to HTML'
);
} else {
toggleHtmlButton = await driver.elementByAccessibilityId(
'Switch To Visual'
);
}
await toggleHtmlButton.click();
// This is to wait for the clipboard paste notification to disappear, currently it overlaps with the menu button
await driver.sleep( 3000 );
await clickIfClickable(
driver,
'//XCUIElementTypeButton[@name="..."]'
);
await clickIfClickable(
driver,
'//XCUIElementTypeButton[@name="Switch To Visual"]'
);
}
};

Expand Down Expand Up @@ -492,7 +497,7 @@ const waitForVisible = async (
}

const element = await driver.elementsByXPath( elementLocator );
if ( element.length !== 1 ) {
if ( element.length === 0 ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Certain blocks don't return exactly one element (e.g. Column and Unsupported blocks), so updating the condition to as long as it's not 0.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be interesting adding a parameter to the function to configure whether the function returns the first element or the elements array. It could be useful for those cases where we need to pick one of the elements, like for example in the getLastBlockVisible helper function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I agree, since this always returns the first element, for cases like above, we have to make the call first before making a separate call to get the list of elements.

I'll make that enhancement in a separate PR, as might take a while to test to make sure it covers all use cases.

// if locator is not visible, try again
return waitForVisible(
driver,
Expand Down Expand Up @@ -530,6 +535,39 @@ const isElementVisible = async (
return true;
};

const clickIfClickable = async (
driver,
elementLocator,
maxIteration = 25,
iteration = 0
) => {
const element = await waitForVisible(
driver,
elementLocator,
maxIteration,
iteration
);

try {
await element.click();
fluiddot marked this conversation as resolved.
Show resolved Hide resolved
} catch ( error ) {
if ( iteration >= maxIteration ) {
// eslint-disable-next-line no-console
console.error(
`Element still not clickable after "${ iteration }" retries`
fluiddot marked this conversation as resolved.
Show resolved Hide resolved
);
return '';
}

return clickIfClickable(
driver,
elementLocator,
maxIteration,
iteration + 1
);
}
};

// Only for Android
const waitIfAndroid = async () => {
if ( isAndroid() ) {
Expand All @@ -540,6 +578,7 @@ const waitIfAndroid = async () => {
module.exports = {
backspace,
clickBeginningOfElement,
clickIfClickable,
clickMiddleOfElement,
doubleTap,
isAndroid,
Expand Down
Loading