diff --git a/app/lib/phantomasWrapper.js b/app/lib/phantomasWrapper.js
new file mode 100644
index 0000000..a33401c
--- /dev/null
+++ b/app/lib/phantomasWrapper.js
@@ -0,0 +1,67 @@
+/**
+ * Yellow Lab Tools main file
+ */
+
+var q = require ('q');
+var phantomas = require('phantomas');
+
+var PhantomasWrapper = function() {
+ 'use strict';
+
+ /**
+ * This is the phantomas launcher. It merges user chosen options into the default options
+ * Available options :
+ *
+ * - timeout : in seconds (default 60)
+ * - jsDeepAnalysis : should we inspect subrequests in the javascript execution tree (reported durations of main tasks will be slower than usual)
+ *
+ */
+ this.execute = function(task, callback) {
+
+ var options = {
+ // Cusomizable options
+ timeout: task.options.timeout || 60,
+ 'js-deep-analysis': task.options.jsDeepAnalysis || false,
+
+ // Mandatory
+ reporter: 'json:pretty',
+ 'skip-modules': [
+ 'ajaxRequests',
+ 'alerts',
+ 'cacheHits',
+ 'caching',
+ 'console',
+ 'cookies',
+ 'documentHeight',
+ 'domains',
+ 'domComplexity',
+ 'domMutations',
+ 'domQueries',
+ 'filmStrip',
+ 'jQuery',
+ 'jserrors',
+ 'har',
+ 'headers',
+ 'localStorage',
+ 'mainRequest',
+ 'pageSource',
+ 'redirects',
+ 'requestsStats',
+ 'screenshot',
+ 'staticAssets',
+ 'timeToFirst',
+ 'waitForSelector'
+ ].join(','),
+ 'include-dirs': [
+ 'phantomas_custom/core',
+ 'phantomas_custom/modules'
+ ].join(',')
+ };
+
+ // It's time to launch the test!!!
+ phantomas(task.url, options, callback);
+
+ };
+};
+
+module.exports = new PhantomasWrapper();
\ No newline at end of file
diff --git a/app/lib/testQueue.js b/app/lib/testQueue.js
new file mode 100644
index 0000000..82ebd3e
--- /dev/null
+++ b/app/lib/testQueue.js
@@ -0,0 +1,63 @@
+/**
+ * Creation of a queue and it's worker function
+ */
+
+var util = require('util');
+var EventEmitter = require('events').EventEmitter;
+var async = require('async');
+var phantomasWrapper = require('./phantomasWrapper');
+
+
+var testQueue = function() {
+ 'use strict';
+
+ var currentTask = null;
+ var self = this;
+
+ var queue = async.queue(function(task, callback) {
+ currentTask = task;
+
+ console.log('Starting test ' + task.testId);
+
+ phantomasWrapper.execute(task, function(err, json, results) {
+ console.log('Test ' + task.testId + ' complete');
+ currentTask = null;
+ callback(err, json, results);
+ self.emit('queueMoving');
+ });
+ });
+
+
+ // Use this method to add a test to the queue
+ this.push = queue.push;
+
+
+ // Gives the position of a task in the queue
+ // Returns 0 if it is the current running task
+ // Returns -1 if not found
+ this.indexOf = function(testId) {
+ if (currentTask && currentTask.testId === testId) {
+ return 0;
+ }
+
+ var position = -1;
+ if (queue.length() > 0) {
+ queue.tasks.forEach(function(task, index) {
+ if (task.data.testId === testId) {
+ position = index + 1;
+ }
+ });
+ }
+ return position;
+ };
+
+ // Forward testComplete
+ this.testComplete = function(testId) {
+ self.emit('testComplete', testId);
+ };
+};
+
+// extend the EventEmitter class using our Radio class
+util.inherits(testQueue, EventEmitter);
+
+module.exports = new testQueue();
\ No newline at end of file
diff --git a/app/node_controllers/indexController.js b/app/node_controllers/indexController.js
new file mode 100644
index 0000000..b835509
--- /dev/null
+++ b/app/node_controllers/indexController.js
@@ -0,0 +1,23 @@
+/**
+ * Yellow Lab Tools home page controller
+ */
+
+var async = require('async');
+var fs = require ('fs');
+
+var indexController = function(req, res) {
+ 'use strict';
+
+ async.parallel({
+
+ htmlTemplate: function(callback) {
+ fs.readFile('./app/node_views/index.html', {encoding: 'utf8'}, callback);
+ }
+
+ }, function(err, results) {
+ res.setHeader('Content-Type', 'text/html');
+ res.send(results.htmlTemplate);
+ });
+ };
+
+ module.exports = indexController;
\ No newline at end of file
diff --git a/app/node_controllers/launchTestController.js b/app/node_controllers/launchTestController.js
new file mode 100644
index 0000000..2b97359
--- /dev/null
+++ b/app/node_controllers/launchTestController.js
@@ -0,0 +1,82 @@
+/**
+ * Controller for the test launching page (the waiting page, after the user submited a test on the index page)
+ */
+
+var async = require('async');
+var fs = require ('fs');
+
+var launchTestController = function(req, res, testQueue) {
+ 'use strict';
+
+ // Generate test id
+ var testId = (Date.now()*1000 + Math.round(Math.random()*1000)).toString(36);
+
+ var resultsPath = 'results/' + testId;
+ var phantomasResultsPath = resultsPath + '/results.json';
+
+ var url = req.body.url;
+ if (url.indexOf('http://') !== 0 && url.indexOf('https://') !== 0) {
+ url = 'http://' + url;
+ }
+
+ var options = {};
+ if (req.body.timeout) {
+ options.timeout = req.body.timeout;
+ }
+
+ async.waterfall([
+
+ function htmlTemplate(callback) {
+ fs.readFile('./app/node_views/launchTest.html', {encoding: 'utf8'}, callback);
+ },
+
+ function sendResponse(html, callback) {
+
+ html = html.replace('%%TEST_URL%%', url);
+ html = html.replace('%%TEST_ID%%', testId);
+
+ res.setHeader('Content-Type', 'text/html');
+ res.send(html);
+
+ callback();
+ },
+
+ function createFolder(callback) {
+ // Create results folder
+ fs.mkdir(resultsPath, callback);
+ },
+
+ function executePhantomas(callback) {
+ console.log('Adding test ' + testId + ' on ' + url + ' to the queue');
+
+ var task = {
+ testId: testId,
+ url: url,
+ options: options
+ };
+
+ testQueue.push(task, callback);
+ },
+
+ function writeResults(json, resultsObject, callback) {
+ console.log('Saving Phantomas results file to ' + phantomasResultsPath);
+ fs.writeFile(phantomasResultsPath, JSON.stringify(json, null, 4), callback);
+ }
+
+ ], function(err) {
+ if (err) {
+ console.log('An error occured while launching the phantomas test : ', err);
+
+ fs.writeFile(phantomasResultsPath, JSON.stringify({url: url, error: err}, null, 4), function(err) {
+ if (err) {
+ console.log('Could not even write an error message on file ' + phantomasResultsPath);
+ console.log(err);
+ }
+ });
+ } else {
+ testQueue.testComplete(testId);
+ }
+ });
+ };
+
+ module.exports = launchTestController;
\ No newline at end of file
diff --git a/app/node_controllers/resultsController.js b/app/node_controllers/resultsController.js
new file mode 100644
index 0000000..e8920ae
--- /dev/null
+++ b/app/node_controllers/resultsController.js
@@ -0,0 +1,41 @@
+/**
+ * The page that dispays the results
+ */
+
+var async = require('async');
+var fs = require ('fs');
+
+var resultsController = function(req, res) {
+ 'use strict';
+
+ var testId = req.params.testId;
+ var resultsPath = 'results/' + testId;
+ var phantomasResultsPath = resultsPath + '/results.json';
+
+ console.log('Opening test ' + testId + ' results as HTML');
+
+ async.parallel({
+
+ htmlTemplate: function(callback) {
+ fs.readFile('./app/node_views/results.html', {encoding: 'utf8'}, callback);
+ },
+
+ phantomasResults: function(callback) {
+ fs.readFile(phantomasResultsPath, {encoding: 'utf8'}, callback);
+ }
+
+ }, function(err, results) {
+ if (err) {
+ console.log(err);
+ return res.status(404).send('Sorry, test not found...');
+ }
+
+ var html = results.htmlTemplate;
+ html = html.replace('%%RESULTS%%', results.phantomasResults);
+
+ res.setHeader('Content-Type', 'text/html');
+ res.send(html);
+ });
+};
+
+module.exports = resultsController;
\ No newline at end of file
diff --git a/app/node_controllers/waitingQueueSocket.js b/app/node_controllers/waitingQueueSocket.js
new file mode 100644
index 0000000..5fdf919
--- /dev/null
+++ b/app/node_controllers/waitingQueueSocket.js
@@ -0,0 +1,53 @@
+/**
+ * Socket.io handler
+ */
+
+var fs = require('fs');
+
+var waitingQueueSocket = function(socket, testQueue) {
+
+ socket.on('waiting', function(testId) {
+ console.log('User waiting for test id ' + testId);
+
+ sendTestStatus(testId);
+
+ testQueue.on('testComplete', function(id) {
+ if (testId === id) {
+ socket.emit('complete');
+ console.log('Sending complete event to test id ' + testId);
+ }
+ });
+
+ testQueue.on('queueMoving', function() {
+ var positionInQueue = testQueue.indexOf(testId);
+ if (positionInQueue >= 0) {
+ socket.emit('position', positionInQueue);
+ console.log('Sending position to test id ' + testId);
+ }
+ });
+ });
+
+ // Finds the status of a test and send it to the client
+ function sendTestStatus(testId) {
+ // Check task position in queue
+ var positionInQueue = testQueue.indexOf(testId);
+
+ if (positionInQueue >= 0) {
+ socket.emit('position', positionInQueue);
+ } else {
+ // Find in results files
+ var exists = fs.exists('results/' + testId + '/results.json', function(exists) {
+ if (exists) {
+ // TODO : find a way to make sure the file is completely written
+ setTimeout(function() {
+ socket.emit('complete');
+ }, 4000);
+ } else {
+ socket.emit('404');
+ }
+ });
+ }
+ }
+};
+
+module.exports = waitingQueueSocket;
\ No newline at end of file
diff --git a/app/views/index.html b/app/node_views/index.html
similarity index 100%
rename from app/views/index.html
rename to app/node_views/index.html
diff --git a/app/views/launchTest.html b/app/node_views/launchTest.html
similarity index 78%
rename from app/views/launchTest.html
rename to app/node_views/launchTest.html
index 611b776..a46c48f 100644
--- a/app/views/launchTest.html
+++ b/app/node_views/launchTest.html
@@ -21,15 +21,13 @@
function askStatus() {
socket.emit('waiting', testId);
}
-
- socket.on('running', function() {
- statusElement.innerHTML = 'Running';
- setTimeout(askStatus, 200);
- });
socket.on('position', function(position) {
- statusElement.innerHTML = 'Waiting behind ' + (position + 1) + ' other tests';
- setTimeout(askStatus, 2000);
+ if (position === 0) {
+ statusElement.innerHTML = 'Running';
+ } else {
+ statusElement.innerHTML = 'Waiting behind ' + (position) + ' other tests';
+ }
});
socket.on('complete', function() {
diff --git a/app/views/results.html b/app/node_views/results.html
similarity index 94%
rename from app/views/results.html
rename to app/node_views/results.html
index 18a4cdc..12166fb 100644
--- a/app/views/results.html
+++ b/app/node_views/results.html
@@ -16,7 +16,7 @@
Untangling and counting the spaghettis...
-
+
Error: {{phantomasResults.error}}
@@ -74,7 +74,7 @@
ng-class="{
'warningDetails': node.data.type == 'jQuery - bind' && node.data.callDetails.context.length > 5
}"
- ng-if="node.data.type != 'script loaded'">i
+ ng-if="node.data.type != 'script loaded' && node.data.type != 'jQuery loaded'">i
✖
@@ -125,14 +125,17 @@
: {{node.data.callDetails.arguments[2]}}
: {{node.data.callDetails.arguments[3]}}
-
+
{{node.data.time}} ms
-
+
+ {{node.data.time}} ms
+
+
diff --git a/phantomas_custom/core/scopeYLT/scopeYLT.js b/phantomas_custom/core/scopeYLT/scopeYLT.js
index 22af662..8139d5e 100644
--- a/phantomas_custom/core/scopeYLT/scopeYLT.js
+++ b/phantomas_custom/core/scopeYLT/scopeYLT.js
@@ -6,11 +6,11 @@
* @see http://code.jquery.com/jquery-2.0.3.js
*/
/* global document: true, window: true */
-'use strict';
exports.version = '0.1';
exports.module = function(phantomas) {
+ 'use strict';
phantomas.once('init', function() {
phantomas.evaluate(function(deepAnalysis) {
@@ -43,8 +43,7 @@ exports.module = function(phantomas) {
// Before
if (enabled) {
- var args = Array.prototype.slice.call(arguments);
- callbackBefore.apply(this, args);
+ callbackBefore.apply(this, arguments);
}
// Execute
@@ -56,8 +55,7 @@ exports.module = function(phantomas) {
// After
if (enabled && callbackAfter) {
- var args = Array.prototype.slice.call(arguments);
- callbackAfter.apply(this, args);
+ callbackAfter.apply(this, arguments);
}
}
@@ -158,7 +156,7 @@ exports.module = function(phantomas) {
var child = new ContextTreeNode(this, data);
this.children.push(child);
return child;
- }
+ };
}
phantomas.log('Adding some contextTree functions to phantomas');
diff --git a/phantomas_custom/modules/domComplexYLT/domComplexYLT.js b/phantomas_custom/modules/domComplexYLT/domComplexYLT.js
index 1b92151..1d37d9f 100644
--- a/phantomas_custom/modules/domComplexYLT/domComplexYLT.js
+++ b/phantomas_custom/modules/domComplexYLT/domComplexYLT.js
@@ -2,11 +2,11 @@
* Analyzes DOM complexity
*/
/* global document: true, Node: true, window: true */
-'use strict';
exports.version = '0.3.a';
exports.module = function(phantomas) {
+ 'use strict';
// total length of HTML comments (including brackets)
phantomas.setMetric('commentsSize'); // @desc the size of HTML comments on the page @offenders
diff --git a/phantomas_custom/modules/domQYLT/domQYLT.js b/phantomas_custom/modules/domQYLT/domQYLT.js
index 25d0068..34a095e 100644
--- a/phantomas_custom/modules/domQYLT/domQYLT.js
+++ b/phantomas_custom/modules/domQYLT/domQYLT.js
@@ -2,18 +2,19 @@
* Analyzes DOM queries done via native DOM methods
*/
/* global Element: true, Document: true, Node: true, window: true */
-'use strict';
exports.version = '0.7.a';
exports.module = function(phantomas) {
- phantomas.setMetric('DOMqueries'); // @desc number of all DOM queries @offenders
- phantomas.setMetric('DOMqueriesById'); // @desc number of document.getElementById calls
- phantomas.setMetric('DOMqueriesByClassName'); // @desc number of document.getElementsByClassName calls
- phantomas.setMetric('DOMqueriesByTagName'); // @desc number of document.getElementsByTagName calls
- phantomas.setMetric('DOMqueriesByQuerySelectorAll'); // @desc number of document.querySelector(All) calls
- phantomas.setMetric('DOMinserts'); // @desc number of DOM nodes inserts
- phantomas.setMetric('DOMqueriesDuplicated'); // @desc number of duplicated DOM queries
+ 'use strict';
+
+ phantomas.setMetric('DOMqueries'); // @desc number of all DOM queries @offenders
+ phantomas.setMetric('DOMqueriesById'); // @desc number of document.getElementById calls
+ phantomas.setMetric('DOMqueriesByClassName'); // @desc number of document.getElementsByClassName calls
+ phantomas.setMetric('DOMqueriesByTagName'); // @desc number of document.getElementsByTagName calls
+ phantomas.setMetric('DOMqueriesByQuerySelectorAll'); // @desc number of document.querySelector(All) calls
+ phantomas.setMetric('DOMinserts'); // @desc number of DOM nodes inserts
+ phantomas.setMetric('DOMqueriesDuplicated'); // @desc number of duplicated DOM queries
// fake native DOM functions
phantomas.once('init', function() {
@@ -43,6 +44,8 @@ exports.module = function(phantomas) {
// selectors by class name
function selectorClassNameSpyBefore(className) {
+ /*jshint validthis: true */
+
phantomas.incrMetric('DOMqueriesByClassName');
phantomas.addOffender('DOMqueriesByClassName', '.' + className);
querySpy('class', '.' + className, 'getElementsByClassName');
@@ -65,6 +68,8 @@ exports.module = function(phantomas) {
// selectors by tag name
function selectorTagNameSpyBefore(tagName) {
+ /*jshint validthis: true */
+
phantomas.incrMetric('DOMqueriesByTagName');
phantomas.addOffender('DOMqueriesByTagName', tagName);
querySpy('tag name', tagName, 'getElementsByTagName');
@@ -93,6 +98,8 @@ exports.module = function(phantomas) {
}
function selectorQuerySpyBefore(selector) {
+ /*jshint validthis: true */
+
selectorQuerySpy(selector);
phantomas.enterContext({
@@ -109,6 +116,8 @@ exports.module = function(phantomas) {
}
function selectorAllQuerySpyBefore(selector) {
+ /*jshint validthis: true */
+
selectorQuerySpy(selector);
phantomas.enterContext({
@@ -132,7 +141,8 @@ exports.module = function(phantomas) {
// count DOM inserts
function appendChild(child) {
- /* jshint validthis: true */
+ /*jshint validthis: true */
+
// ignore appending to the node that's not yet added to DOM tree
if (!this.parentNode) {
return;
@@ -154,6 +164,8 @@ exports.module = function(phantomas) {
}
function appendChildSpyBefore(child) {
+ /*jshint validthis: true */
+
appendChild(child);
phantomas.enterContext({
@@ -170,6 +182,8 @@ exports.module = function(phantomas) {
}
function insertBeforeSpyBefore(child) {
+ /*jshint validthis: true */
+
appendChild(child);
phantomas.enterContext({
diff --git a/phantomas_custom/modules/jQYLT/jQYLT.js b/phantomas_custom/modules/jQYLT/jQYLT.js
index c19f5fb..0f62b01 100644
--- a/phantomas_custom/modules/jQYLT/jQYLT.js
+++ b/phantomas_custom/modules/jQYLT/jQYLT.js
@@ -5,17 +5,19 @@
* @see http://code.jquery.com/jquery-2.0.3.js
*/
/* global document: true, window: true */
-'use strict';
+/* jshint -W030 */
exports.version = '0.2.a';
exports.module = function(phantomas) {
- phantomas.setMetric('jQueryVersion', ''); // @desc version of jQuery framework (if loaded) [string]
- phantomas.setMetric('jQueryOnDOMReadyFunctions'); // @desc number of functions bound to onDOMReady event
- phantomas.setMetric('jQuerySizzleCalls'); // @desc number of calls to Sizzle (including those that will be resolved using querySelectorAll)
- phantomas.setMetric('jQuerySizzleCallsDuplicated'); // @desc number of calls on the same Sizzle request
- phantomas.setMetric('jQueryBindOnMultipleElements'); //@desc number of calls to jQuery bind function on 2 or more elements
- phantomas.setMetric('jQueryDifferentVersions'); //@desc number of different jQuery versions loaded on the page (not counting iframes)
+ 'use strict';
+
+ phantomas.setMetric('jQueryVersion', ''); // @desc version of jQuery framework (if loaded) [string]
+ phantomas.setMetric('jQueryOnDOMReadyFunctions'); // @desc number of functions bound to onDOMReady event
+ phantomas.setMetric('jQuerySizzleCalls'); // @desc number of calls to Sizzle (including those that will be resolved using querySelectorAll)
+ phantomas.setMetric('jQuerySizzleCallsDuplicated'); // @desc number of calls on the same Sizzle request
+ phantomas.setMetric('jQueryBindOnMultipleElements'); //@desc number of calls to jQuery bind function on 2 or more elements
+ phantomas.setMetric('jQueryDifferentVersions'); //@desc number of different jQuery versions loaded on the page (not counting iframes)
var jQueryFunctions = [
// DOM manipulations
@@ -244,7 +246,7 @@ exports.module = function(phantomas) {
arg = 'false';
}
- if (arg == null) {
+ if (arg === null) {
arg = 'null';
}
diff --git a/phantomas_custom/modules/jsErrYLT/jsErrYLT.js b/phantomas_custom/modules/jsErrYLT/jsErrYLT.js
index 6af9ce1..53f0adb 100644
--- a/phantomas_custom/modules/jsErrYLT/jsErrYLT.js
+++ b/phantomas_custom/modules/jsErrYLT/jsErrYLT.js
@@ -1,11 +1,12 @@
/**
* Meters the number of page errors, and provides traces as offenders for "jsErrors" metric
*/
-'use strict';
exports.version = '0.3.a';
exports.module = function(phantomas) {
+ 'use strict';
+
phantomas.setMetric('jsErrors'); // @desc number of JavaScript errors
function formatTrace(trace) {
diff --git a/phantomas_custom/modules/jsFileLoadYLT/jsFileLoadYLT.js b/phantomas_custom/modules/jsFileLoadYLT/jsFileLoadYLT.js
index 50ad55e..e8bdfec 100644
--- a/phantomas_custom/modules/jsFileLoadYLT/jsFileLoadYLT.js
+++ b/phantomas_custom/modules/jsFileLoadYLT/jsFileLoadYLT.js
@@ -1,11 +1,11 @@
/**
* Meters the number of page errors, and provides traces as offenders for "jsErrors" metric
*/
-'use strict';
exports.version = '0.0';
exports.module = function(phantomas) {
+ 'use strict';
phantomas.on('recv', function(entry, res) {
if (!entry.isJS) {
diff --git a/phantomas_custom/modules/jsTreeYLT/jsTreeYLT.js b/phantomas_custom/modules/jsTreeYLT/jsTreeYLT.js
index 1129485..a9772a5 100644
--- a/phantomas_custom/modules/jsTreeYLT/jsTreeYLT.js
+++ b/phantomas_custom/modules/jsTreeYLT/jsTreeYLT.js
@@ -3,11 +3,11 @@
*
* Run phantomas with --js-execution-tree option to use this module
*/
-'use strict';
exports.version = '0.1';
exports.module = function(phantomas) {
+ 'use strict';
phantomas.setMetric('javascriptExecutionTree'); // @desc number of duplicated DOM queries
diff --git a/server.js b/server.js
index 57903a9..77429b5 100644
--- a/server.js
+++ b/server.js
@@ -1,230 +1,42 @@
-var settings = require('./server_config/settings.json');
-var fs = require('fs');
-var async = require('async');
-var express = require('express');
-var app = express();
-var server = require('http').createServer(app);
-var io = require('socket.io').listen(server);
-var bodyParser = require('body-parser');
-var phantomas = require('phantomas');
+// Config file
+var settings = require('./server_config/settings.json');
+// Libraries
+var fs = require('fs');
+var async = require('async');
+var express = require('express');
+var app = express();
+var server = require('http').createServer(app);
+var io = require('socket.io').listen(server);
+var bodyParser = require('body-parser');
+
+// Internals
+var indexController = require('./app/node_controllers/indexController');
+var launchTestController = require('./app/node_controllers/launchTestController');
+var resultsController = require('./app/node_controllers/resultsController');
+var waitingQueueSocket = require('./app/node_controllers/waitingQueueSocket');
+var testQueue = require('./app/lib/testQueue');
app.use(bodyParser.urlencoded({ extended: false }));
-// Home page
-app.get('/', function(req, res) {
- async.parallel({
-
- htmlTemplate: function(callback) {
- fs.readFile('./app/views/index.html', {encoding: 'utf8'}, callback);
- }
-
- }, function(err, results) {
- res.setHeader('Content-Type', 'text/html');
- res.send(results.htmlTemplate);
- });
-});
-
-// Waiting queue page
-app.post('/launchTest', function(req, res) {
- // Generate test id
- var testId = (Date.now()*1000 + Math.round(Math.random()*1000)).toString(36);
-
- var resultsPath = 'results/' + testId;
- var phantomasResultsPath = resultsPath + '/results.json';
-
- var url = req.body.url;
- if (url.indexOf('http://') !== 0 && url.indexOf('https://') !== 0) {
- url = 'http://' + url;
- }
-
- async.waterfall([
-
- function htmlTemplate(callback) {
- fs.readFile('./app/views/launchTest.html', {encoding: 'utf8'}, callback);
- },
-
- function sendResponse(html, callback) {
-
- html = html.replace('%%TEST_URL%%', url);
- html = html.replace('%%TEST_ID%%', testId);
-
- res.setHeader('Content-Type', 'text/html');
- res.send(html);
-
- callback();
- },
-
- function createFolder(callback) {
- // Create results folder
- fs.mkdir(resultsPath, callback);
- },
-
- function executePhantomas(callback) {
-
- var options = {
- timeout: 60,
- 'js-deep-analysis': false,
- reporter: 'json:pretty',
- 'skip-modules': [
- 'ajaxRequests',
- 'alerts',
- 'cacheHits',
- 'caching',
- 'console',
- 'cookies',
- 'documentHeight',
- 'domains',
- 'domComplexity',
- 'domMutations',
- 'domQueries',
- 'filmStrip',
- 'jQuery',
- 'jserrors',
- 'har',
- 'headers',
- 'localStorage',
- 'mainRequest',
- 'pageSource',
- 'redirects',
- 'requestsStats',
- 'screenshot',
- 'staticAssets',
- 'timeToFirst',
- 'waitForSelector'
- ].join(','),
- 'include-dirs': [
- 'phantomas_custom/core',
- 'phantomas_custom/modules'
- ].join(',')
- };
-
- console.log('Adding test ' + testId + ' on ' + url + ' to the queue');
-
- var task = {
- testId: testId,
- url: url,
- options: options
- };
-
- console.log(JSON.stringify(task, null, 4));
-
- taskQueue.push(task, callback);
- },
-
- function writeResults(json, resultsObject, callback) {
- console.log('Saving Phantomas results file to ' + phantomasResultsPath);
- fs.writeFile(phantomasResultsPath, JSON.stringify(json, null, 4), callback);
- }
-
- ], function(err) {
- if (err) {
- console.log('An error occured while launching the phantomas test : ', err);
-
- fs.writeFile(phantomasResultsPath, JSON.stringify({url: url, error: err}, null, 4), function(err) {
- if (err) {
- console.log('Could not even write an error message on file ' + phantomasResultsPath);
- console.log(err);
- }
- });
- }
- });
-});
-
-
-// Results page
-app.get('/results/:testId', function(req, res) {
-
- var testId = req.params.testId;
- var resultsPath = 'results/' + testId;
- var phantomasResultsPath = resultsPath + '/results.json';
-
- console.log('Opening test ' + testId + ' results as HTML');
-
- async.parallel({
-
- htmlTemplate: function(callback) {
- fs.readFile('./app/views/results.html', {encoding: 'utf8'}, callback);
- },
-
- phantomasResults: function(callback) {
- fs.readFile(phantomasResultsPath, {encoding: 'utf8'}, callback);
- }
-
- }, function(err, results) {
- if (err) {
- console.log(err);
- return res.status(404).send('Sorry, test not found...');
- }
-
- var html = results.htmlTemplate;
- html = html.replace('%%RESULTS%%', results.phantomasResults);
-
- res.setHeader('Content-Type', 'text/html');
- res.send(html);
- });
-});
+// Routes definition
+app.get('/', indexController);
+app.post('/launchTest', function(req, res) { launchTestController(req, res, testQueue); });
+app.get('/results/:testId', resultsController);
// Static files
-app.use('/public', express.static(__dirname + '/app/public'));
-app.use('/bower_components', express.static(__dirname + '/bower_components'));
+app.use('/public', express.static(__dirname + '/app/public'));
+app.use('/bower_components', express.static(__dirname + '/bower_components'));
// Socket.io
io.on('connection', function(socket){
- socket.on('waiting', function(testId) {
- console.log('User waiting for test id ' + testId);
- socket.testId = testId;
-
- // Check task position in queue
- var positionInQueue = -1;
- if (taskQueue.length() > 0) {
- taskQueue.tasks.forEach(function(task, index) {
- if (task.data.testId === testId) {
- positionInQueue = index;
- }
- });
- }
-
- if (positionInQueue >= 0) {
- socket.emit('position', positionInQueue);
- } else if (currentTask && currentTask.testId === testId) {
- socket.emit('running');
- } else {
- // Find in results files
- var exists = fs.exists('results/' + testId + '/results.json', function(exists) {
- if (exists) {
- // TODO : use eventEmitter to make sure the file is completly written on disk
- setTimeout(function() {
- socket.emit('complete');
- }, 1000);
- } else {
- socket.emit('404');
- }
- });
- }
- });
+ waitingQueueSocket(socket, testQueue);
});
-// Creating a queue and defining the worker function
-var currentTask = null;
-var taskQueue = async.queue(function queueWorker(task, callback) {
-
- currentTask = task;
- console.log('Starting test ' + task.testId);
-
- // It's time to launch the test!!!
- phantomas(task.url, task.options, function(err, json, results) {
- console.log('Test ' + task.testId + ' complete');
- currentTask = null;
- callback(err, json, results);
- });
-
-});
-
// Launch the server
server.listen(settings.serverPort, function() {