How to avoid using await on every single line

Viewed 1615

When using Protractor for E2E Testing, it seems as though every single line of code requires await when SELENIUM_PROMISE_MANAGER: false.

Is there a better way to do this example below?

Originally I was using the SELENIUM_PROMISE_MANAGER: false but ran into issues when needing to use conditionals WebElement.isPresent(). The promise manager was not able to resolve the promises of the isPresent() and just continued execution. So now considering using async/await.

describe('Home Page', async () => {

  beforeAll(async () => {
    await loginPage.loadPage();  // methods defined in Page Object
    await loginPage.login();     // uses WebElement.sendKeys() and Webelement.submit()
    await homePage.closeModal();
  });

  it('should fill a form', async () => {
    await $('#field1').clear();
    await $('#field1').sendKeys('hello');
    await $('#field2').clear();
    await $('#field2').sendKeys('world');
    $('form').submit();
  }
}

If almost every line needs an await, is there something i'm missing here?

2 Answers

When using async/await there really is no other way than to put await before calling each function, since you need to make sure everything runs in that particular order, when it comes to E2E testing.
In your particular case, you could also declare all inside 1 single function.

//declare this somewhere else:
this.formFill = async () => {
    await $('#field1').clear();
    await $('#field1').sendKeys('hello');
    await $('#field2').clear();
    await $('#field2').sendKeys('world');
    await $('form').submit();
};  

Then call it inside the it block:

it('should fill a form', async () => {
    await this.formFill();
});

We can not avoid it because there is strong reason for it. First of all we need to understand that JavaScript is asynchronous programming language. When protractor was initially used then We had to use Promises() for writing end-to-end test cases and it was difficult to understand and debugging the test code because of promises chaining(multiple 'then'). In order to overcome this problem Protractor introduced Promise Manger. It solved the problem of promise chaining but it was difficult to debug test code and we needed to do some explicit action to debugging test code. When ES2017 introduced async/await then it actually solved the problem of Promises chaining so instead of writing

 Using Promises 
        A().then( () => {
             B().then( () => {
                   C().then( () => {
                          });
                       });
                   });

Using async/await
     async func() {
       await A();
       await B();
       await C();
     }

What is role of await here? function A(), B(), C() still returns the object of Promise but await internally wait for Promise to resolve/reject before moving to next line. Once Promise resolve then it get the value then execute next line. Therefore async/await is important to write before every function which return Promise.

Let's take the code, you mentioned

await loginPage.login(); - > WebElement.sendKeys(); and Webelement.submit();

If we check the definition of sendKeys() and submit() functions in IWebelement class then it looks like

    sendKeys(...var_args: Array<string|number|promise.Promise<string|number>>): promise.Promise<void>;

    submit(): promise.Promise<void>;

In preceding code snippet we can see both functions return Promise. If we don't use await before these then it would not execute code properly and move to next line.

Conclusion is to avoid or not to avoid 'await' depends on return type of function. If it not promise then you can avoid and if function returns promise then never avoid it.

Related