question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

waitFor does not work in nested then statement

See original GitHub issue

@awlayton as per request I’m opening a new issue for this problem. The code I’m working on is scraping a protected resource and I can’t make the access info publicly known (though Alex should have the connection info). Essentially it’s an Angular site for vehicle management, some vehicles have pictures, some do not (depending on if there was damage reported). In the former case it takes less time to load a vehicle than the latter case, and thus I need to conditionally wait for img tags or not.

My environment: Linux host. NodeJS: v5.7.0 PhantomJS: v2.1.1 Horseman: v2.8.2

//Broken:
var Horseman = require('/home/automationScripts/node_modules/node-horseman');

var valueStore = {};

valueStore.loginURL = "";
valueStore.username = "";
valueStore.password = "";
valueStore.VIN = "";

new Horseman()
    .userAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36")
    .viewport(1080, 1920)
    .then(function(){
        console.log("Opening Login Page");
    })
    .open(valueStore.loginURL)
    .type('input#IDToken1', valueStore.username)
    .type('input#IDToken2', valueStore.password)
    .click('[name="Login.Submit"]')
    .then(function(){
        console.log("Logged in!");
        console.log("Loading Search Page");
    })
    .waitForSelector('[ng-model="vm.searchCriteria.vins"]')
    .waitForSelector('button[ng-click*="vm.search()"]')
    .screenshot('horseSearchPage.png')
    .type('[ng-model="vm.searchCriteria.vins"]', valueStore.VIN)
    .click('button[ng-click*="vm.search()"]')
    .then(function(){
        console.log("Searched for vin!");
        console.log("Returning results");
    })
    .waitForSelector('role="listitem"')
    .evaluate(function(){
        var hasPictures = !!$('.fa-camera');
        $('[role="listitem"] [role="button"]:first').click(); //click on item
        return hasPictures;
    })
    .then(function(hasPictures){
        console.log("Has pictures: "+hasPictures);
        return this.waitFor(function(hasPictures){
            if(hasPictures){
                return $('.image-viewer-thumbnails').length > 0;            //wait for images to load
            }else{
                return $('[ng-include*="image-viewer.html"]').length > 0;   //only wait for image container (this should be present even if there aren't images)
            }
        }, hasPictures, true);
    })
    .screenshot('vehicle.png') //no pictures are loaded
    .then(function(){
        console.log("Closing horseman!");
    })
    .close();
//Working:

var Horseman = require('/home/automationScripts/node_modules/node-horseman');

var valueStore = {};

valueStore.loginURL = "";
valueStore.username = "";
valueStore.password = "";
valueStore.VIN = ""; 

new Horseman()
    .userAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36")
    .viewport(1080, 1920)
    .then(function(){
        console.log("Opening Login Page");
    })
    .open(valueStore.loginURL)
    .type('input#IDToken1', valueStore.username)
    .type('input#IDToken2', valueStore.password)
    .click('[name="Login.Submit"]')
    .then(function(){
        console.log("Logged in!");
        console.log("Loading Search Page");
    })
    .waitForSelector('[ng-model="vm.searchCriteria.vins"]')
    .waitForSelector('button[ng-click*="vm.search()"]')
    .screenshot('horseSearchPage.png')
    .type('[ng-model="vm.searchCriteria.vins"]', valueStore.VIN)
    .click('button[ng-click*="vm.search()"]')
    .then(function(){
        console.log("Searched for vin!");
        console.log("Returning results");
    })
    .waitForSelector('role="listitem"')
    .evaluate(function(){
        var hasPictures = !!$('.fa-camera');
        $('[role="listitem"] [role="button"]:first').click(); //click on item
        return hasPictures;
    })
    .then(function(hasPictures){
        console.log("Has pictures: "+hasPictures);
        valueStore.hasPictures = hasPictures;
    })
    .waitFor(function(hasPictures){
            if(hasPictures){
                return $('.image-viewer-thumbnails').length > 0;            //wait for images to load
            }else{
                return $('[ng-include*="image-viewer.html"]').length > 0;   //only wait for image container (this should be present even if there aren't images)
            }
        }, valueStore.hasPictures, true)
    .screenshot('vehicle.png') //pictures are loaded!
    .then(function(){
        console.log("Closing horseman!");
    })
    .close();

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:5

github_iconTop GitHub Comments

1reaction
awlaytoncommented, Apr 11, 2016

The .waitFor* actions will timeout after a certain period (based on the timeout constructor option).

In v2 the chain would still continue after a timeout though. In v3, the Promise will reject on timeout stopping the actions (unless you have a .catch that handles the timeout).

1reaction
awlaytoncommented, Apr 11, 2016

With debugging on I can see that horseman is waiting for the selector in both cases. It seems the version without the .then just waits slightly longer for some reason.

The problem is the <img> being present does not mean the image is loaded. You need to wait for the tag to be present (which is what you are doing), and the image to be loaded. I changed the .waitFor() as shown below.

    .then(function(hasPictures){
        console.log("Has pictures: "+hasPictures);
        return this.waitFor(function(hasPictures){
            if(hasPictures){
                var p = $('.image-viewer-thumbnails');
                var loaded = p.length > 0; // Check tags are present
                p.each(function(i, p) {
                    loaded = loaded && p.complete; // Check each image is complete
                });
                return loaded;
            }else{
                return $('[ng-include*="image-viewer.html"]').length > 0;   //only wait for image container (this should be present even if there aren't images)
            }
        }, hasPictures, true);
    })

Secondly, 'role="listitem"' is not a valid selector. I changed it to '[role="listitem"]', which seems like what you wanted.

With the above two changes, the top code works for me. Please note, the setup I had was:

  • Linux host
  • NodeJS: v5.10.1
  • PhantomJS: v2.1.1
  • Horseman: v3.0.0

I have not tested my changes with horseman 2.8.2, but ideally you would just use v3 since it is out of beta now.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Promise.all does not wait for nested promise? - Stack Overflow
I tried to use Promise.all to add each statement as a Promise, but Promise.all never waits for the nested Promise to complete. Similar...
Read more >
`waitFor` with nested `expect` timeout in v9 · Issue #898 - GitHub
Hey guys, I've been banging my head for hours following a migration to v9. I had some timeout issues with a waitFor and...
Read more >
Conditions Task - Apache Ant
Conditions are nested elements of the <condition> and <waitfor> tasks. There are core conditions and custom conditions. Custom conditions are described in ...
Read more >
Async Await JavaScript Tutorial – How to Wait for a Function to ...
Alright, so we know that JavaScript is synchronous and lazy. It doesn't want to do all of the work itself, so it farms...
Read more >
Promises chaining - The Modern JavaScript Tutorial
A Promise keeps an internal state. It calls the handler wrapped in a try...catch block and stores the error. That is why asynchronous...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found