gruntjs / grunt-lib-phantomjs

Grunt and PhantomJS, sitting in a tree.
http://gruntjs.com/
MIT License
93 stars 63 forks source link

Make it easier to run multiple pages in parallel #65

Closed Krinkle closed 7 years ago

Krinkle commented 10 years ago

I'm working on a Grunt task that opens one or more pages/urls up (e.g. the demo pages) in phantomjs and does various performance and integration tests (no uncaught exceptions or 404 errors etc.).

The way the events are emitted makes it difficult to keep context, so one is basically required to, when inside a multitask, attach the event handlers in the outer scope.

However when using something like async.forEachLimit to iterate through the pages, it's near impossible to deal with the events.

In theory I could move require( 'grunt-lib-phantomjs' ).init( grunt ); and the event handlers to inside the main work loop. However this still poses issues with the output.

Abstract example:

module.exports = function ( grunt ) {
    var phantomjs = require( 'grunt-lib-phantomjs' ).init( grunt );
    ...

    phantomjs.on( 'onInitialized', function () {
        grunt.log.write( '...' );
    } );

    phantomjs.on( 'error.onError', function ( msg, stackTrace ) {
        phantomjs.halt();
        grunt.log.writeln();
        grunt.log.error( 'PhantomJS>\nUncaught ' + msg );
        console.dir( stackTrace );
        failures++;
    });

    phantomjs.on( 'fail.timeout', function () {
        phantomjs.halt();
        grunt.log.writeln();
        grunt.log.error('PhantomJS timed out.');
        failures++;
    } );

    ...

    grunt.registerMultiTask( 'phantomcheck', function () {
        var done = this.async(),
            urls = options.urls.concat( this.filesSrc );

        grunt.util.async.forEachSeries( urls, function ( url, next ) {
            grunt.log.write( 'Checking ' + url );
            phantomjs.spawn(url, {
                options: options,
                done: function ( err ) {
                    ran++;
                    if ( err ) {
                        next( err ); // Abort
                    } else {
                        next(); // Done
                    }
                }
            });
        // Async loop finished
        }, function ( err ) {
            if ( failure ) {
                grunt.log.error( failures + ' pages failed.' );
                done( false );
            } else {
                grunt.log.ok( ran + ' pages checked.' );
                done();
            }
        } );
    } );
};

It would cut down the execution time a lot if I could use async.forEachLimit( urls, 3, .. so that we'd run three of them in parallel. I'm curious if other people have run into this.

I'm considering to do it and solve the output issue by buffering it in a wrapper object instead of using the grunt.log methods directly (e.g. mylog = { text: '', error: '', ok: '', dirobjects: [] }) and pass it on to the grunt.log methods at the end of each iteration.

Krinkle commented 7 years ago

Switched to Karma.