Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execution sequence with selenium-webdriver (for Node JS)

I'm a bit confused with the execution sequence of selenium-webdriver commands.

What is the correct coding for the following example. I want to:

  1. FIRST, Enter text in the text box
  2. THEN, Click on the button

Does the following code garantee the correct sequence?

   // Enter the text
    driver.findElement(webdriver.By.id('txt'))
      .sendKeys('bla bla')
      .then(
        function() {},
        function(err) {
          console.log(err);
          done(err); 
        }
      );

    // Press the button
    driver.findElement(webdriver.By.id('btn'))
      .click()
      .then(
        function() {
          done();
        },
        function(err) {
          console.log(err);
          done(err); 
        }
      );

or should I do something like this?

// Enter the pass
driver.findElement(webdriver.By.id('txt_pass'))    
  .sendKeys('sifra')
  .then(
    function() {
      // Press the login button
      driver.findElement(webdriver.By.id('btn_login'))
        .click()
        .then(
          function() {
            done();
          },
          function(err) {
            console.log(err);
            done(err); 
          }
        );
    },
    function(err) {
      console.log(err);
      done(err); 
    }
  );

If the second case is correct, how should I write the code when more than 2 actions in a row are needed? Iw would be very complicated and had to maintain.

UPDATE:

Let's say now I want to clear the element "txt", found in the first findElement, and do it just before I type the text in. Based on the answer from Louis, I suspect it should be something like this:

   // Clear the textbox
    driver.findElement(webdriver.By.id('txt'))
      .clear()
      .then(
        function() {},
        function(err) {
          console.log(err);
          done(err); 
        }
      );

    // Enter the text
    driver.findElement(webdriver.By.id('txt'))
      .sendKeys('bla bla')
      .then(
        function() {},
        function(err) {
          console.log(err);
          done(err); 
        }
      );

    // go on with the further sequence...

I don't find this very nice, it's just too long and clumsy, especially if there are more operations on the same element. Is there a common way to shorten this, to make it compacter?

Something like chaining:

findElement('txt').clear().sendKeys('bla, bla'); // This does not work, as clear is a void
like image 840
Aleks Avatar asked May 06 '26 05:05

Aleks


1 Answers

The first way you are doing it is correct. The piece of information which you appear to be missing is that the JavaScript implementation of WebDriver uses a "promise manager" to sequence promises for you. This is explained in the "Control Flows" section of the guide. Here is an example:

var webdriver = require('selenium-webdriver');

var driver = new webdriver.Builder().
   withCapabilities(webdriver.Capabilities.chrome()).
   build();

// Do not implicitly wait for anything.
driver.manage().timeouts().implicitlyWait(0);

// Set the script wait timeout to 10 seconds.
driver.manage().timeouts().setScriptTimeout(10 * 1000);

driver.get('http://www.example.com');

function err() {
    console.log("ERR", arguments);
}

var start = Date.now();
driver.executeAsyncScript(
    "var done = arguments[0];" +
    "setTimeout(function () { document.body.innerHTML = '<p id=\"foo\"></p>'; done() }, 5000)")
    .then(function () {
        console.log("created foo!", Date.now() - start);
    },
          err);

driver.findElement(webdriver.By.tagName("body")).then(function () {
    console.log("here", Date.now() - start);
});

driver.findElement(webdriver.By.id("foo")).then(function () {
    console.log("found foo!", Date.now() - start);
}, err);

This will output to the console the following lines, always in the same order:

created foo! 5639
here 5657
found foo! 5670

The numbers will vary but they will always be > 5000 and follow one another pretty closely (unless running on an overloaded or ridiculously slow system). The executeAsyncScript that is executed first is there to illustrate how an operation that takes a while to be completed is not going to allow subsequent operations on the same driver to go forward. Except for the fact that it executes a script rather than find an element, it is not any different from findElement: the promise manager waits until the operation is completed before the next operation can go forward, even if the two operations are not explicitly sequenced using .then().

If the promise manager did not sequence the operations one after the other, then .findElement(webdriver.By.tagName("body")) would execute before the async script was done, and it would print "here" before "created foo!" is printed to the console because the page does have a body element.

Also, if the promise manager did not sequence the operations one after the other, then the last .findElement would fail to find an element with id value of foo. Early in the script, I turn off implicit waits so the findElement call is certainly not waiting for foo to appear.

There are cases where you would want to do it more like you do it in your second snippet but you do not include in your question any clear reason it should be done that way.

Also, if it were not for Selenium's promise manager, then you'd have to do something like your second snippet, because in general there is nothing sequencing promises for you.

Regarding your question as to how to sequence clear and sendKeys, you can do:

driver.findElement(webdriver.By.id('txt')).then(function (el) {
    el.clear();
    el.sendKeys('bla bla');
});

This structure uses the "framing" feature of the "control flows" functionality.

like image 134
Louis Avatar answered May 08 '26 15:05

Louis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!