Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problems using nightmare in a loop #522

Closed
kokujin opened this issue Mar 1, 2016 · 3 comments
Closed

Problems using nightmare in a loop #522

kokujin opened this issue Mar 1, 2016 · 3 comments

Comments

@kokujin
Copy link

kokujin commented Mar 1, 2016

I cannot seem to get Nightmare to work in a loop. Given this quick simple example:

var nightmare = Nightmare( {
    'show': false,
    'openDevTools': {
        'detach': true
    }
} );

var allTags = [ 'Tag1', 'Tag2', 'Tag3'];

test( 'Test a few items ID tags', function ( t ) {
    t.plan( 3 );

    for ( var idx = 0; idx < allTags.length; idx++ ) {
        var selector = '#' + allTags[ idx ] + ' a';
        console.log( 'selector: ', selector ); // <--- THIS WORKS
            nightmare // <-- THIS DOESNT ITERATE
                .goto( 'http://www.my-test-domain.com' )
                .viewport( 1024, 768 )
                .click( selector )
                .wait( 'body' )
                .then( function ( result ) {
                    var msg = allTags[ idx ] ;
                    t.equal( result, 'Tag1',
                        msg2 );
                }, function ( err ) {
                    console.log( err );
                } );        
        t.equal( 'mock-result', allTags[ idx ]);
    } // END FOR LOOP
} );

On the first item nightmare instance is instantiated and tested, the second and third not. Can someone explain to me what I am doing wrong? Thanks

@rosshinkley
Copy link
Contributor

I'm guessing judging from your example you're using Tape or some variant of Tape. I'm also not clear on what you're expecting to happen: the result from the .then() call is going to be undefined as it's the result of the .wait() call.

There are other internal issues with the test, but let's set those aside. The bigger issue is with asynchrony. That loop is going to issue multiple queues against the same Nightmare instance, which is going to lead to problems. Nightmare can only handle one instruction set at a time (see #493). You'll want to let one instruction set finish before the next one begins. You could do this using Array.reduce and native promises, something like:

var allTags = [ 'Tag1', 'Tag2', 'Tag3'];

test('a few items id tags', function(t){
    allTags.reduce(function(accumulator, tag){
        return accumulator.then(function(results){
            var selector = '#' + tag + ' a';
            return nightmare.goto('http://example.com')
                .click(selector)
                .wait('body')
                .title()
                .then(function(result){
                    //assert the tag ID equals the page title
                    t.equal(result, tag);
                    results.push(result);
                    return results;
                });
        });
    }, Promise.resolve([])).then(function(results){
        //results contains the array of all of the finished promise results,
        //in this case, the titles from every page visited
        //you could do something with that here
        nightmare.end(function(){
            t.end();
        });
    });
});

If you're interested in something that looks a little closer to how the Nightmare test suite works, I'd suggest using co-tape. The example from above rewritten:

var cotape = require('co-tape');

var allTags = [ 'Tag1', 'Tag2', 'Tag3'];

test( 'Test a few items ID tags', cotape(function*(t) {
    t.plan( 3 );

    var results = [];
    for ( var idx = 0; idx < allTags.length; idx++ ) {
        var selector = '#' + allTags[ idx ] + ' a';
            var title = yield nightmare
                .goto( 'http://example.com' )
                .wait( 'body' )
                .click('selector')
                .title();
            t.equal(title, allTags[idx]);
            results.push(title);
    }
    yield nightmare.end();
    t.end();
} ));

@kokujin
Copy link
Author

kokujin commented Mar 3, 2016

Nice! Thanks for your patience and time @rosshinkley

@cue232s
Copy link

cue232s commented Dec 20, 2017

What would be the best way to handle an error that occurs inside the resolve function of the accumulator? For example in my call to return accumulator.then I'm passing a function that makes an asynchronous call and pushes the result into array like in your example above. If that asynchronous call fails, the next trip in the reduce loop, my array will be replaced by undefined. Is there a way to catch errors and pass the array along?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants