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

Support for running phantomas in SlimerJS #345

Merged
merged 27 commits into from
Jun 12, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
eb708f7
slimerjs: install via npm package
macbre Jun 5, 2014
aaeb364
spawn: pass phantomas options via args (not stdin)
macbre Jun 6, 2014
17bd50f
Show SlimerJS version and path
macbre Jun 6, 2014
fa261b9
Improve handling of --debug option
macbre Jun 7, 2014
5736dc7
Provide modules path when running using SlimerJS
macbre Jun 7, 2014
5959237
Improve detection of phantomas home directory
macbre Jun 7, 2014
affa32e
ipc: use stdout
macbre Jun 7, 2014
125c556
timeToFirstByte: emit "responseEdit" internally
macbre Jun 7, 2014
01c0e3e
slimerjs: exit with a proper code
macbre Jun 7, 2014
8de06ef
windowPerformance: use native window.performance (if available)
macbre Jun 7, 2014
c8b5ef1
progress: check if supported
macbre Jun 7, 2014
cdccee2
Modules: fail early on require('fast-stats')
macbre Jun 7, 2014
716eba5
IPC: long JSON-encoded messages were received in batches
macbre Jun 7, 2014
3df55d5
globalVariables: filter out 0, 1, 2, ...
macbre Jun 7, 2014
dc9e877
Fix failing unit tests
macbre Jun 7, 2014
76210b5
Abstract engines selection
macbre Jun 7, 2014
a2234e5
Abstract engines definition
macbre Jun 7, 2014
6fc7cad
SlimerJS: onInitialized is called twice
macbre Jun 8, 2014
acf85a5
onLoadStarted: trigger just once
macbre Jun 8, 2014
35971e3
requestsMonitor: bodySize for SlimerJS + offenders for "gzipRequests"
macbre Jun 8, 2014
29e9600
Update metrics metadata
macbre Jun 8, 2014
a981de0
SlimerJS: use stdout for IPC, no need for a hack here
macbre Jun 11, 2014
03a005b
SlimerJS: use xvfb to run
macbre Jun 11, 2014
bfcfb91
Remove the screenshot file
macbre Jun 11, 2014
83a0048
engines: cleanup
macbre Jun 11, 2014
5b1ad70
Remove the screenshot file - again :)
macbre Jun 11, 2014
b6e5603
engine: the name is enough
macbre Jun 11, 2014
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions core/ipc.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
/**
* Simple IPC implementation using JSON-encoded messages sent over stderr stream
* Simple IPC implementation using JSON-encoded messages sent over stdout stream
*
* Implements producer of the data (for PhantomJS environment)
*/
'use strict';

var stderr = require('system').stderr;
var stream = require('system').stdout,
SEPARATOR = "\xFF\xFF";

function ipc(event) {
/* jshint validthis: true */
this.event = event;
}

ipc.prototype.push = function() {
stderr.writeLine(JSON.stringify({
stream.writeLine(JSON.stringify({
event: this.event,
data: Array.prototype.slice.apply(arguments)
}));
stream.writeLine(SEPARATOR);
};

module.exports = ipc;
4 changes: 4 additions & 0 deletions core/modules/requestsMonitor/requestsMonitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ exports.module = function(phantomas) {

// the end of response
case 'end':
// SlimerJS sets res.bodySize at stage = end
entry.bodySize = entry.bodySize || res.bodySize;

// timing
entry.recvEndTime = res.time;
entry.timeToLastByte = res.time - entry.sendTime;
Expand Down Expand Up @@ -282,6 +285,7 @@ exports.module = function(phantomas) {

if (entry.gzip) {
phantomas.incrMetric('gzipRequests');
phantomas.addOffender('gzipRequests', '%s (gzip: %s kB / uncompressed: %s kB)', entry.url, (entry.contentLength/1024).toFixed(2), (entry.bodySize/1024).toFixed(2));
}

if (entry.isSSL) {
Expand Down
2 changes: 1 addition & 1 deletion core/modules/timeToFirstByte/timeToFirstByte.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ exports.module = function(phantomas) {
phantomas.log('Time to first byte: set to %d ms for #%d request to <%s> (HTTP %d)', entry.timeToFirstByte, entry.id, entry.url, entry.status);
phantomas.log('Time to last byte: set to %d ms', entry.timeToLastByte);

phantomas.emit('responseEnd', entry, res); // @desc the first response (that was not a redirect) fully received
phantomas.emitInternal('responseEnd', entry, res); // @desc the first response (that was not a redirect) fully received
}
});
};
61 changes: 39 additions & 22 deletions core/phantomas.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,9 @@ var EXIT_SUCCESS = 0,
// get phantomas version from package.json file
var VERSION = require('../package').version;

var getDefaultUserAgent = function() {
var version = phantom.version,
system = require('system'),
os = system.os;

return "phantomas/" + VERSION + " (PhantomJS/" + version.major + "." + version.minor + "." + version.patch + "; " + os.name + " " + os.architecture + ")";
};

var phantomas = function(params) {
var fs = require('fs');

// store script CLI parameters
this.params = params;

Expand Down Expand Up @@ -93,11 +87,18 @@ var phantomas = function(params) {
beSilent: this.silentMode
});

// detect phantomas working directory
if (typeof module.dirname !== 'undefined') {
this.dir = module.dirname.replace(/core$/, '');
this.log('phantomas v%s: running with the following parameters: %j', this.getVersion(), this.params);
}
else if (typeof slimer !== 'undefined') {
var args = require('system').args;
this.dir = fs.dirname(args[0]).replace(/scripts$/, '');
}

this.log('phantomas v%s: %s', this.getVersion(), this.dir);
this.log('Options: %j', this.params);

// queue of jobs that needs to be done before report can be generated
var Queue = require('../lib/simple-queue');
this.reportQueue = new Queue();
Expand Down Expand Up @@ -132,9 +133,7 @@ var phantomas = function(params) {
this.addCoreModule('timeToFirstByte');

// load 3rd party modules
var modules = (this.modules.length > 0) ? this.modules : this.listModules(),
fs = require('fs');

var modules = (this.modules.length > 0) ? this.modules : this.listModules();
modules.forEach(this.addModule, this);

this.includeDirs.forEach(function(dirName) {
Expand Down Expand Up @@ -301,7 +300,7 @@ phantomas.prototype = {

// returns list of 3rd party modules located in modules directory
listModules: function() {
return this.listModulesInDir(module.dirname + '/../modules');
return this.listModulesInDir(this.dir + 'modules');
},

// returns list of 3rd party modules located in modules directory
Expand Down Expand Up @@ -343,7 +342,12 @@ phantomas.prototype = {
this.emit('progress', currentProgress, inc); // @desc loading progress has changed
}

setInterval(pollFn.bind(this), 50);
if (typeof this.page.loadingProgress !== 'undefined') {
setInterval(pollFn.bind(this), 50);
}
else {
this.log('Loading progress: not available!');
}
},

// runs phantomas
Expand All @@ -366,7 +370,7 @@ phantomas.prototype = {
}

// setup user agent / --user-agent=custom-agent
this.page.settings.userAgent = this.getParam('user-agent', getDefaultUserAgent(), 'string');
this.page.settings.userAgent = this.getParam('user-agent');

// disable JavaScript on the page that will be loaded
if (this.disableJs) {
Expand Down Expand Up @@ -473,10 +477,8 @@ phantomas.prototype = {
this.log('Returning results with %d metric(s)...', this.results.getMetricsNames().length);

// emit results in JSON
var formatter = require('./formatter'),
stdout = require('system').stdout;

stdout.write(formatter(this.results));
var formatter = require('./formatter');
this.emit('json', formatter(this.results));

// handle timeouts (issue #129)
if (this.timedOut) {
Expand Down Expand Up @@ -515,17 +517,32 @@ phantomas.prototype = {

// core events
onInitialized: function() {
// SlimerJS triggers this event twice
// @see https://github.com/laurentj/slimerjs/blob/master/docs/api/webpage.rst#oninitialized
if (this.page.url === '') {
this.log('onInit: webpage.url is empty, waiting for the second trigger...');
return;
}

// add helper tools into window.__phantomas "namespace"
if (!this.page.injectJs(module.dirname + '/scope.js')) {
if (!this.page.injectJs(this.dir + 'core/scope.js')) {
this.tearDown(EXIT_ERROR, 'Scope script injection failed');
return;
}

this.log('Page object initialized');
this.log('onInit: page object initialized');
this.emitInternal('init'); // @desc page has been initialized, scripts can be injected
},

onLoadStarted: function() {
onLoadStarted: function(url, isFrame) {
if (this.onLoadStartedEmitted) {
return;
}

// onLoadStarted is called for the page and each iframe
// tigger "loadStarted" event just once
this.onLoadStartedEmitted = true;

this.log('Page loading started');
this.emitInternal('loadStarted'); // @desc page loading has started
},
Expand Down
2 changes: 1 addition & 1 deletion lib/ansicolors.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var colors = require('ansicolors'),
keys = Object.keys(colors),
disableColors;

if (typeof process.stdout !== 'undefined') {
if (typeof process !== 'undefined' && typeof process.stdout !== 'undefined') {
// nodejs
disableColors = !!process.env.BW;
}
Expand Down
106 changes: 106 additions & 0 deletions lib/engines.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* Provides abstractor layer for using different engines to run phantomas
*/
'use strict';

var debug = require('debug')('phantomas:engines'),
spawn = require('child_process').spawn,
VERSION = require('../package').version;

function engines(options) {
/* jshint validthis: true */
this.options = options;

//--debug can be either 'true' or 'false'
this.options.debug = (this.options.debug === true) ? 'true' : 'false';

// engines storage
this.engines = {};
['webkit', 'gecko'].forEach(this.registerEngine, this);
}

engines.prototype.registerEngine = function(engine) {
var def = require('./engines/' + engine);
this.engines[engine] = def;

debug('%s v%s installed in %s', def.name, def.version, def.path);
};

engines.prototype.getEngines = function() {
return Object.keys(this.engines);
};

engines.prototype.getEngine = function(engine) {
return this.engines[engine];
};

engines.prototype.run = function(scriptFile) {
// select the engine
var engines = this.getEngines(),
engine = this.getEngine(engines[0]);

engines.forEach(function(key) {
if (this.options[key] === true) {
engine = this.getEngine(key);
}
}, this);

// customize user agent
if (typeof this.options['user-agent'] === 'undefined') {
this.options['user-agent'] = "phantomas/" + VERSION + " (" + engine.getUserAgent() + "; " + process.platform + " " + process.arch + ")";
}

// build engine specific command-line arguments
var engineArgs = [];

Object.keys(this.options).forEach(function(key) {
var val = this.options[key],
nativeOptions = [
'cookies-file',
'debug',
'ignore-ssl-errors',
'proxy',
'proxy-auth',
'proxy-type'
];

if (val === false) {
return;
}

// handle native PhantomJS options (#163)
// @see http://phantomjs.org/api/command-line.html
if (nativeOptions.indexOf(key) > -1) {
engineArgs.push('--' + key + '=' + val);

delete this.options[key];
}
}, this);

// add a script to run
engineArgs.push(scriptFile);

// pass JSON encoded options
engineArgs.push(JSON.stringify(this.options));

debug('Running %s (using %s)', scriptFile, engine.name);
debug('Passing phantomas options: %j', this.options);

// spawn the process
var proc;

if (typeof engine.spawn === 'function') {
debug('Using custom spawn function');
proc = engine.spawn(engine.path, engineArgs);
}
else {
proc = spawn(engine.path, engineArgs, {
env: process.env
});
}

return proc;
};

// public API
module.exports = engines;
41 changes: 41 additions & 0 deletions lib/engines/gecko.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Defines SlimerJS engine
*/
var pkg = require('slimerjs'),
debug = require('debug')('phantomas:slimerjs'),
spawn = require('child_process').spawn;

module.exports = {
name: 'SlimerJS',
engine: 'gecko',
path: pkg.path,
version: pkg.version,
getUserAgent: function() {
return 'SlimerJS/' + pkg.version
},
spawn: function(path, engineArgs) {
// xvfb arguments
var args = [
'--auto-servernum',
'--server-num=1',
//'--server-args=\':1 -noreset -screen 1 1600x1200x24\''
];

// SlimerJS binary
args.push(path);

// pass runner and phantomas options
args = args.concat(engineArgs);

debug('xvfb-run %s', args.join(' '));

// spawn SlimerJS using xvfb
//
// sudo aptitude install xvfb libasound2 libgtk2.0-0
//
// @see https://gist.github.com/macbre/e7e2e35caf9d91af5ecf
return spawn('xvfb-run', args, {
env: process.env
});
}
};
14 changes: 14 additions & 0 deletions lib/engines/webkit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Defines PhantomJS engine
*/
var pkg = require('phantomjs');

module.exports = {
name: 'PhantomJS',
engine: 'webkit',
path: pkg.path,
version: pkg.version,
getUserAgent: function() {
return 'PhantomJS/' + pkg.version
}
};
Loading