1
votes

I'm trying to understand how The WebDriver Control Flow works exactly.

According to the linked documentation (https://github.com/angular/protractor/blob/master/docs/control-flow.md) no callback method / call is needed in jasmine:

Protractor adapts Jasmine so that each spec automatically waits until the control flow is empty before exiting.

However, I have to use cucumber. I'm using the library protractor-cucumber-framework as described here: https://github.com/angular/protractor/blob/master/docs/frameworks.md#using-cucumber

It works well, but for some reason it works better when I skip the callback variable then when I try using it. For instance, this code fails:

this.Given(/^the login page is active$/, function (callback) {
  browser.get('/').then(callback);
});

With the error ...

TypeError: text.split is not a function

[launcher] Process exited with error code 1

On the other hand, this codes works as I want it to work and cucumber / protractor seems to be waiting until the page is loaded, before executing further functions:

me.Given(/^the login page is active$/, function () {
  browser.get('/');
});

But I couldn't find any documentation confirming that I really can omit the callback function.

Currently the page I tried to test doesn't use Angular and therefore I have the following code in my config file:

onPrepare: function() {
  browser.ignoreSynchronization = true;
}
1

1 Answers

3
votes

Protractor uses WebDriverJS underneath. And WebDriverJS uses a promise manager where it queues its commands. Here is some excerpts from their wiki page here

Internally, the promise manager maintains a call stack. Upon each turn of the manager's execution loop, it will pull a task to execute from the queue of the top-most frame. Any commands scheduled within the callback of a previous command will be scheduled in a new frame, ensuring they run before any tasks previously scheduled. The end result is that if your test is written-in line, with all callbacks defined by function literals, commands should execute in the order they are read vertically on the screen. For example, consider the following WebDriverJS test case:

driver.get(MY_APP_URL);
driver.getTitle().then(function(title) {
  if (title === 'Login page') {
    driver.findElement(webdriver.By.id('user')).sendKeys('bugs');
    driver.findElement(webdriver.By.id('pw')).sendKeys('bunny');
    driver.findElement(webdriver.By.id('login')).click();
  }
});
driver.findElement(webdriver.By.id('userPreferences')).click();

This test case could be rewritten using !WebDriver's Java API as follows:

driver.get(MY_APP_URL);
if ("Login Page".equals(driver.getTitle())) {
  driver.findElement(By.id("user")).sendKeys("bugs");
  driver.findElement(By.id("pw")).sendKeys("bunny");
  driver.findElement(By.id("login")).click();
}
driver.findElement(By.id("userPreferences")).click();

Now going back to your question, since you are omitting callback from your steps, cucumber is treating your test code as synchronous. See documentation here. And because the way protractor/WebdriverJS handles promise manager the way described above, everything works as expected for you.

As far as the error you are getting when using callback, I'm not sure. I do it exactly the same way you are doing. See here. I'm using cucumber ^0.9.2. It could be that your cucumber version has issues.

On a side note, I found that you could return promises instead of using callbacks to let cucumber know that you are done executing. So something like this works as well (assuming you are using ^0.9.2). I tested it,

me.Given(/^the login page is active$/, function () {
  return browser.get('/');
});