From b9c285a8f430ecc75d44096070af97df7c880062 Mon Sep 17 00:00:00 2001 From: Mark Trostler Date: Tue, 8 Nov 2011 21:42:34 -0800 Subject: [PATCH] phantomjs stuff --- backend/nodejute/jute/actions/phantomJS.js | 114 ------------------ .../nodejute/jute/actions/startPhantomjs.js | 21 +++- backend/nodejute/jute/phantomJUTE.js | 53 ++++++++ 3 files changed, 68 insertions(+), 120 deletions(-) delete mode 100644 backend/nodejute/jute/actions/phantomJS.js create mode 100644 backend/nodejute/jute/phantomJUTE.js diff --git a/backend/nodejute/jute/actions/phantomJS.js b/backend/nodejute/jute/actions/phantomJS.js deleted file mode 100644 index acd36a0..0000000 --- a/backend/nodejute/jute/actions/phantomJS.js +++ /dev/null @@ -1,114 +0,0 @@ -/* -Copyright (c) 2011, Yahoo! Inc. -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of Yahoo! Inc. nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of Yahoo! Inc. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - - -module.exports = { - Create: function(hub) { - // Javascript is single threaded! We don't have to worry about concurrency! - var path = require('path'), - sys = require('sys'), - fs = require('fs'), - cache = hub.cache - ; - - // Events I care about - hub.addListener('action:seleniumStart', startSelenium); - - function startSelenium(selID, req, res) { - var soda = require('soda'), cb, - body = req.body, - browser - ; - - try { - browser = soda.createClient({ - url: 'http://' + (hub.config.host ? hub.config.host + ':' + hub.config.port : req.headers.host), - host: body.sel_host, - browser: body.sel_browser - }) - } catch(e) { - hub.emit('action:seleniumDone', 'Cannot connect to Selenium server at ' + body.sel_host + ': ' + e, selID); - return; - } - - // Give Selenium 1000 minutes to finish - should be good - 16 hours baby! - req.socket.setTimeout(60000000, function() { - hub.emit(hub.LOG, hub.ERROR, 'Selenium taking too long - giving up'); - cb(); - }); - - cache.connections[selID] = res; // our link back to the requesting client for status messages - - // called when all Selenium tests are complete for this browser - // && keep track of requesting client for debug messages back... - // Callback for when the Selenium session is done - cb = function(err) { - if (!err) { - browser.chain.testComplete().end(function(err) { - delete cache.connections[selID]; // done with status updates - hub.emit('action:seleniumDone', err, selID); - }); - } else { - hub.emit('action:seleniumDone', err, selID); - } - }; - cb = hub.once(selID + 'finished', cb); - - browser. - chain. - session(). - open('/?selenium=' + selID). - waitForPageToLoad(10000). - end(function(err) { - if (err) { - var msg = 'Error starting/waiting for Selenium page to load: ' + err; - hub.emit('seleniumTestsFinished', err); - } else { - hub.emit(hub.LOG, hub.INFO, "Selenium up and running: " + browser.sid); - // If this is one of the tests that are going to run in the - // Selenium session, tag it with the Selenium token - cache.tests_to_run.forEach(function(test) { - if (test.browser === selID) { - test.seleniumID = browser.sid; - } - }); - - } - }); - } - } - -}; - diff --git a/backend/nodejute/jute/actions/startPhantomjs.js b/backend/nodejute/jute/actions/startPhantomjs.js index ee9d705..bde5f16 100644 --- a/backend/nodejute/jute/actions/startPhantomjs.js +++ b/backend/nodejute/jute/actions/startPhantomjs.js @@ -47,20 +47,26 @@ module.exports = { // Events I care about hub.addListener('action:phantomjsStart', startPhantomjs); - function Phantomjs(selID, phantomjs, screen, req, res) { + function startPhantomjs(selID, phantomjs, screen, req, res) { var cb, phantom, body = req.body, url = 'http://' + (hub.config.host ? hub.config.host + ':' + hub.config.port : req.headers.host) + '/?selenium=' + selID ; try { - phantom = child.spawn(phantomjs, [url]); + hub.emit(hub.LOG, hub.INFO, "DISPLAY=:" + screen + ' ' + phantomjs + ' ' + path.join(__dirname, '..', "phantomJUTE.js") + ' ' + url); + process.env.DISPLAY = ':' + screen; + phantom = child.spawn(phantomjs, [ path.join(__dirname, '..', "phantomJUTE.js"), url]); phantom.stdout.on('data', function(data) { - hub.emit(hub.LOG, hub.INFO, "PhantomJS up and running: " + data); + hub.emit(hub.LOG, hub.INFO, "PhantomJS sez: " + data); }); phantom.stderr.on('data', function(data) { + hub.emit(hub.LOG, hub.INFO, "PhantomJS stderr: " + data); }); phantom.on('exit', function() { - hub.emit(hub.LOG, hub.INFO, "PhantomJS up done"); + if (!phantom.done) { + hub.emit(hub.LOG, hub.ERROR, "PhantomJS exited unexpectedly"); + cb('PhantomJS executable exited unexpectedly'); + } }); } catch(e) { @@ -69,9 +75,9 @@ module.exports = { } // Give Selenium 1000 minutes to finish - should be good - 16 hours baby! - req.socket.setTimeout(60000000, function() { + req.socket.setTimeout(6000000, function() { hub.emit(hub.LOG, hub.ERROR, 'Phantomjs taking too long - giving up'); - cb(); + cb('took too long!'); }); cache.connections[selID] = res; // our link back to the requesting client for status messages @@ -80,6 +86,9 @@ module.exports = { // && keep track of requesting client for debug messages back... // Callback for when the phantomjs process is done cb = function(err) { + hub.emit(hub.LOG, hub.INFO, 'Phantomjs done!'); + phantom.done = true; + phantom.kill() delete cache.connections[selID]; // done with status updates hub.emit('action:phantomjsDone', err, selID); }; diff --git a/backend/nodejute/jute/phantomJUTE.js b/backend/nodejute/jute/phantomJUTE.js new file mode 100644 index 0000000..1e96bc2 --- /dev/null +++ b/backend/nodejute/jute/phantomJUTE.js @@ -0,0 +1,53 @@ +/** + * Wait until the test condition is true or a timeout occurs. Useful for waiting + * on a server response or for a ui change (fadeIn, etc.) to occur. + * + * @param testFx javascript condition that evaluates to a boolean, + * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or + * as a callback function. + * @param onReady what to do when testFx condition is fulfilled, + * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or + * as a callback function. + * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used. + */ +function waitFor(testFx, onReady, timeOutMillis) { + var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3000000, //< Default Max Timout is 3s + start = new Date().getTime(), + condition = false, + interval = setInterval(function() { + if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) { + // If not time-out yet and condition not yet fulfilled + condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code + } else { + if(!condition) { + // If condition still not fulfilled (timeout but condition is 'false') + console.log("'waitFor()' timeout"); + phantom.exit(1); + } else { + // Condition fulfilled (timeout and/or condition is 'true') + console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms."); + typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled + clearInterval(interval); //< Stop this interval + } + } + }, 1000); //< repeat check every 1000ms +}; + + +var page = new WebPage(); + +page.onAlert = function(msg) { console.log("ALERT: " + msg); }; +page.onConsoleMessage = function (msg) { console.log("CONSOLE: " + msg); }; +#page.onResourceRequested = function (msg) { console.log("Request: " + msg.url); }; +#page.onResourceReceived = function (msg) { console.log("Received: " + msg.url); }; + +// Open Twitter on 'sencha' profile and, onPageLoad, do... +page.open(phantom.args[0], function (status) { + // Check for page load success + if (status !== "success") { + console.log("Unable to access network"); + } else { + waitFor(function(){}, function(){}); + } +}); +