0
votes

I have written a few functions for browser.wait. My problem is when I run my tests, they inconsistently pass due to protractor running too fast. To combat this, I was using browser.sleep. This works, but is not efficient. If I am on a slow connection for example, these browser.sleep statements can become unreliable. So I have started to use browser.wait. Here is an example of a wait function that I use.

EC = Expected Conditions;
myForm = element(by.id('myForm');
button1 = element(by.id('btn1');
waitForBtn = this.EC.elementToBeClickable(button1)
formIsVisible = this.EC.visibilityOf(myForm)

//Test
it(should click the button and bring up the form), () => {
    browser.wait(page.waitForBtn, 3000);
    page.button1.click()
    browser.wait(page.formIsVisible, 3000);
    expect(page.getMyForm().isDisplayed).toBeTruthy(); 

This test would work in isoluation, but when combinded with other tests, sometimes a test will fail and return an error"element" is not clickable at point.... Even with these wait functions, I will receive this error. It always seems to be for when an element is being clicked.

My question is this: When running multiple tests, what is the best way for protractor to wait so that protractor does not execute the test too fast and miss the element? Thank you. EDIT: Here is the solution.

Helper function for clicking elements.

 async function sendClick(el: ElementFinder): Promise<void> {
    try {

      let result = await forCondition(function() { return el.isDisplayed() });
      if(!result || !await el.isEnabled()) {
        throw new Error('element disabled');
      }

      await browser.executeScript('arguments[0].click();', el.getWebElement());
    } catch(error) {
      console.warn('element:', el.locator().toString(), 'is not clickable', error.message);
    }
  }

export async function forCondition(condition: () => any | PromiseLike<boolean>, timeout = 20000): Promise<boolean> {
try {
  return await browser.wait(condition, timeout);
} catch (err) {
  console.log('Condition: false');
  return false;
}

}

1

1 Answers

1
votes

I cannot tell you the best way, but this is one way :)

Usually, errors like element" is not clickable at point... are caused when the element is overlapped by another element OR if it has not finished rendering yet.

1) For preventing element is not clickable error, you should use javascript for clicking. One disadvantage is that elements always get clicked. Means you have to check manually if the element is clickable and should only click if it is. A 'proper' click function could look as follows:

async function sendClick(el: ElementFinder): Promise<void> {
  try {

    // forCondition() is a custom function and listed below..
    let result = await forCondition(function() { return el.isDisplayed() });
    if(!result || !await el.isEnabled()) {
      throw new Error('element disabled');
    }

    // click via javascript
    await browser.executeScript('arguments[0].click();', el.getWebElement());
  } catch(error) {
    console.warn('element:', el.locator().toString(), 'is not clickable', error.message);
  }
}

Above function waits for the element to be displayed and clicks it if it's enabled.

Here is the helper function::

forCondition() which waits for a condition to fullfill. You cann add more logic to forCondition() for making sure the page is loaded (let's say until spinner disappear or something similar.)

export async function forCondition(condition: () => boolean | PromiseLike<boolean>, timeout = 20000): Promise<boolean> {
  try {
    // add function which waits until page is loaded here..
    return await browser.wait(condition, timeout);
  } catch (err) {
    logger.silly('Condition: false');
    return false;
  }
}

In tests it would look as follows:

it('should click element', async function() {
  let button = element(by.id('myButton');

  // it's waited until button appears and clicks it if possible.
  await sendClick(button);
});

If you always use these helper functions you can easily add checks for waiting for the page to be loaded or other custom logic.

Let me know if anything is unclear.