test: make clickXpath wait until it can 'see' the element

This commit is contained in:
Christopher Willis-Ford 2023-10-26 14:52:50 -07:00
parent b856a2648c
commit aceac8237a
2 changed files with 44 additions and 8 deletions

View file

@ -118,9 +118,7 @@ describe('www-integration project-page signed in', () => {
// expect(projectUrl).toBe(defined);
driver = await buildDriver('www-integration project-page signed in');
await driver.get(rootUrl);
await driver.sleep(1000);
await signIn(username, password);
await findByXpath('//span[contains(@class, "profile-name")]');
});
afterAll(() => driver.quit());
@ -184,11 +182,9 @@ describe('www-integration project-page signed in', () => {
describe('www-integration project-creation signed in', () => {
beforeAll(async () => {
// expect(projectUrl).toBe(defined);
driver = await buildDriver('www-integration project-creation signed in');
await driver.get(rootUrl);
await signIn(username, password);
await findByXpath('//span[contains(@class, "profile-name")]');
// SauceLabs doesn't have access to the sb3 used in 'load project from file' test
// https://support.saucelabs.com/hc/en-us/articles/115003685593-Uploading-Files-to-a-Sauce-Labs-Virtual-Machine-during-a-Test
@ -203,8 +199,6 @@ describe('www-integration project-creation signed in', () => {
test('make a copy of a project', async () => {
await driver.get(`${ownedUnsharedUrl}/editor`);
const gf = await findByXpath('//img[@class="green-flag_green-flag_1kiAo"]');
await gf.isDisplayed();
await clickXpath(FILE_MENU_XPATH);
await clickText('Save as a copy');
const successAlert = await findText('Project saved as a copy.');

View file

@ -276,6 +276,48 @@ class SeleniumHelper {
}
}
/**
* @param {string} xpath Wait until an element at the provided xpath is clickable.
* @returns {Promise<webdriver.WebElement>} A promise that resolves to the clickable element.
*/
waitUntilClickable (xpath) {
// @ts-ignore - TS can't tell that `driver.wait()` called this way will never return `undefined`.
return this.driver.wait(async () => {
const elementAtPath = await this.findByXpath(xpath);
if (!elementAtPath) {
return;
}
const rect = await elementAtPath.getRect();
const x = rect.x + (rect.width / 2);
const y = rect.y + (rect.height / 2);
const elementAtPoint = await this.driver.executeScript(
'return document.elementFromPoint(arguments[0], arguments[1])',
x,
y
);
if (!elementAtPoint) {
return;
}
// If we ask to click on a button and Selenium finds an image on the button, or vice versa, that's OK.
// It doesn't have to be an exact match.
const match = await this.driver.executeScript(
'return arguments[0].contains(arguments[1]) || arguments[1].contains(arguments[0])',
elementAtPath,
elementAtPoint
);
if (!match) {
return;
}
if (!await elementAtPath.isDisplayed()) {
return;
}
if (!await elementAtPath.isEnabled()) {
return;
}
return elementAtPath;
});
}
/**
* Wait until an element can be found by the provided xpath, then click on it.
* @param {string} xpath The xpath to click.
@ -284,8 +326,8 @@ class SeleniumHelper {
async clickXpath (xpath) {
const outerError = new SeleniumHelperError('clickXpath failed', [{xpath}]);
try {
const el = await this.findByXpath(xpath);
await el.click();
const element = await this.waitUntilClickable(xpath);
element.click();
} catch (cause) {
throw await outerError.chain(cause, this.driver);
}