Merge pull request #59 from gmetais/phantomas1.10

Phantomas1.10
This commit is contained in:
Gaël Métais
2015-03-16 17:43:30 +01:00
9 changed files with 88 additions and 102 deletions
+13
View File
@@ -62,6 +62,19 @@
</div>
</div>
<div ng-if="policyName === 'documentWriteCalls'">
<b>{{offender.writeFn}}</b>
<span ng-if="offender.from">
called from
<span ng-if="offender.from.functionName">{{offender.from.functionName}}()</span>
<url-link url="offender.from.file" max-length="50"></url-link>
line {{offender.from.line}}
</span>
<span ng-if="!offender.from">
called from (no backtrace available)
</span>
</div>
<div ng-if="policyName === 'cssParsingErrors'">
<b>{{offender.error}}</b>
<file-and-line file="offender.file" line="offender.line" column="offender.column"></file-and-line>
+43 -1
View File
@@ -242,7 +242,49 @@ var policies = {
"isOkThreshold": 0,
"isBadThreshold": 10,
"isAbnormalThreshold": 20,
"hasOffenders": false
"hasOffenders": true,
"offendersTransformFn": function(offenders) {
return {
count: offenders.length,
list: offenders.map(function(offender) {
var parts = /^document.write(ln)?\(\) used from (.*)$/.exec(offender);
if (parts) {
var writeFn = 'document.write' + (parts[1] || '');
var methodParts = /^([^\s]+) \((.+):(\d+)\)$/.exec(parts[2]);
if (methodParts) {
return {
writeFn: writeFn,
from: {
functionName: methodParts[1],
file: methodParts[2],
line: methodParts[3]
}
};
} else {
var noMethodParts = /^(.+):(\d+)$/.exec(parts[2]);
if (noMethodParts) {
return {
writeFn: writeFn,
from: {
file: noMethodParts[1],
line: noMethodParts[2]
}
};
}
}
}
debug('documentWriteCalls offenders transform function error with "%s"', offender);
return {
parseError: offender
};
})
};
}
},
"consoleMessages": {
"tool": "phantomas",
@@ -36,7 +36,7 @@ exports.module = function(phantomas) {
phantomas.log('Overwritting phantomas spy function');
function spy(obj, fn, callbackBefore, callbackAfter) {
var origFn = obj[fn];
var origFn = obj && obj[fn];
if (typeof origFn !== 'function') {
return false;
@@ -1,79 +0,0 @@
/**
* Analyzes HTTP caching headers
*
* @see https://developers.google.com/speed/docs/best-practices/caching
*/
exports.version = '0.2.a';
exports.module = function(phantomas) {
'use strict';
var cacheControlRegExp = /max-age=(\d+)/;
function getCachingTime(url, headers) {
// false means "no caching"
var ttl = false,
headerName,
now = new Date(),
headerDate;
for (headerName in headers) {
var value = headers[headerName];
switch (headerName.toLowerCase()) {
// parse max-age=...
//
// max-age=2592000
// public, max-age=300, must-revalidate
case 'cache-control':
var matches = value.match(cacheControlRegExp);
if (matches) {
ttl = parseInt(matches[1], 10);
}
break;
// catch Expires and Pragma headers
case 'expires':
case 'pragma':
// and Varnish specific headers
case 'x-pass-expires':
case 'x-pass-cache-control':
phantomas.incrMetric('oldCachingHeaders'); // @desc number of responses with old, HTTP 1.0 caching headers (Expires and Pragma)
phantomas.addOffender('oldCachingHeaders', url + ' - ' + headerName + ': ' + value);
headerDate = Date.parse(value);
if (headerDate) ttl = Math.round((headerDate - now) / 1000);
break;
}
}
//console.log(JSON.stringify(headers)); console.log("TTL: " + ttl + ' s');
return ttl;
}
phantomas.setMetric('cachingNotSpecified'); // @desc number of responses with no caching header sent (no Cache-Control header)
phantomas.setMetric('cachingTooShort'); // @desc number of responses with too short (less than a week) caching time
phantomas.setMetric('cachingDisabled'); // @desc number of responses with caching disabled (max-age=0)
phantomas.setMetric('oldCachingHeaders');
phantomas.on('recv', function(entry, res) {
var ttl = getCachingTime(entry.url, entry.headers);
// static assets
if (entry.isImage || entry.isJS || entry.isCSS) {
if (ttl === false) {
phantomas.incrMetric('cachingNotSpecified');
phantomas.addOffender('cachingNotSpecified', entry.url);
} else if (ttl <= 0) {
phantomas.incrMetric('cachingDisabled');
phantomas.addOffender('cachingDisabled', entry.url);
} else if (ttl < 7 * 86400) {
phantomas.incrMetric('cachingTooShort');
phantomas.addOffender('cachingTooShort', entry.url + ' cached for ' + ttl + ' s');
}
}
});
};
@@ -17,7 +17,7 @@ exports.module = function(phantomas) {
phantomas.setMetric('jQueryOnDOMReadyFunctions'); // @desc number of functions bound to onDOMReady event
phantomas.setMetric('jQueryWindowOnLoadFunctions'); // @desc number of functions bound to windowOnLoad event
phantomas.setMetric('jQuerySizzleCalls'); // @desc number of calls to Sizzle (including those that will be resolved using querySelectorAll)
//phantomas.setMetric('jQueryEventTriggers'); // @desc number of jQuery event triggers
phantomas.setMetric('jQueryEventTriggers'); // @desc number of jQuery event triggers
var jQueryFunctions = [
// DOM manipulations
@@ -245,7 +245,7 @@ exports.module = function(phantomas) {
});
/*if (!jQuery.event) {
if (!jQuery.event) {
phantomas.spy(jQuery.event, 'trigger', function(ev, data, elem) {
var path = phantomas.getDOMPath(elem),
type = ev.type || ev;
@@ -255,7 +255,7 @@ exports.module = function(phantomas) {
phantomas.incrMetric('jQueryEventTriggers');
phantomas.addOffender('jQueryEventTriggers', '"%s" on "%s"', type, path);
});
}*/
}
// jQuery events bound to window' onLoad event (#451)
phantomas.spy(jQuery.fn, 'on', function(eventName, func) {
@@ -4,44 +4,56 @@
* @see http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/
* @see http://www.quirksmode.org/blog/archives/2005/06/three_javascrip_1.html
* @see http://www.stevesouders.com/blog/2012/04/10/dont-docwrite-scripts/
*
* Run phantomas with --spy-eval to count eval() calls (see issue #467)
*/
/* global document: true, window: true */
exports.version = '0.1.a';
exports.version = '0.2';
exports.module = function(phantomas) {
'use strict';
phantomas.setMetric('documentWriteCalls'); //@desc number of calls to either document.write or document.writeln @offenders
phantomas.setMetric('evalCalls'); // @desc number of calls to eval (either direct or via setTimeout / setInterval) @offenders
phantomas.setMetric('documentWriteCalls'); //@desc number of calls to either document.write or document.writeln
phantomas.setMetric('evalCalls'); // @desc number of calls to eval (either direct or via setTimeout / setInterval)
// spy calls to eval only when requested (issue #467)
var spyEval = phantomas.getParam('spy-eval') === true;
if (!spyEval) {
phantomas.log('javaScriptBottlenecks: to spy calls to eval() run phantomas with --spy-eval option');
}
phantomas.once('init', function() {
phantomas.evaluate(function() {
phantomas.evaluate(function(spyEval) {
(function(phantomas) {
function report(msg, caller, backtrace, metric) {
phantomas.log(msg + ': from ' + caller + '!');
phantomas.log('Backtrace: ' + backtrace);
phantomas.incrMetric(metric);
phantomas.addOffender(metric, "%s from %s", msg, caller);
}
// spy calls to eval()
/*phantomas.spy(window, 'eval', function(code) {
report('eval() called directly', phantomas.getCaller(), phantomas.getBacktrace(), 'evalCalls');
phantomas.log('eval\'ed code: ' + (code || '').substring(0, 150) + '(...)');
});*/
if (spyEval) {
phantomas.spy(window, 'eval', function(code) {
report('eval() called directly', phantomas.getCaller(), phantomas.getBacktrace(), 'evalCalls');
phantomas.log('eval\'ed code: ' + (code || '').substring(0, 150) + '(...)');
});
}
// spy calls to setTimeout / setInterval with string passed instead of a function
/*phantomas.spy(window, 'setTimeout', function(fn, interval) {
phantomas.spy(window, 'setTimeout', function(fn, interval) {
if (typeof fn !== 'string') return;
report('eval() called via setTimeout("' + fn + '")', phantomas.getCaller(), phantomas.getBacktrace(), 'evalCalls');
});*/
});
/*phantomas.spy(window, 'setInterval', function(fn, interval) {
phantomas.spy(window, 'setInterval', function(fn, interval) {
if (typeof fn !== 'string') return;
report('eval() called via setInterval("' + fn + '")', phantomas.getCaller(), phantomas.getBacktrace(), 'evalCalls');
});*/
});
// spy document.write(ln)
phantomas.spy(document, 'write', function(arg) {
@@ -52,6 +64,6 @@ exports.module = function(phantomas) {
report('document.writeln() used', phantomas.getCaller(), phantomas.getBacktrace(), 'documentWriteCalls');
});
})(window.__phantomas);
});
}, spyEval);
});
};
-2
View File
@@ -29,14 +29,12 @@ var PhantomasWrapper = function() {
'js-deep-analysis': task.options.jsDeepAnalysis || false,
'user-agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36',
'screenshot': task.options.screenshot || false,
'viewport': '1366x768',
// Mandatory
'reporter': 'json:pretty',
'analyze-css': true,
'skip-modules': [
'blockDomains', // not needed
'caching', // overriden
'domMutations', // not compatible with webkit
'domQueries', // overriden
'eventListeners', // overridden
+1 -1
View File
@@ -19,7 +19,7 @@
"express": "~4.10.6",
"lwip": "0.0.6",
"meow": "^3.0.0",
"phantomas": "1.9.0",
"phantomas": "1.10.0",
"ps-node": "0.0.3",
"q": "~1.1.2",
"rimraf": "~2.2.8",
+2 -2
View File
@@ -54,7 +54,7 @@ describe('index.js', function() {
data.toolsResults.phantomas.should.have.a.property('offenders').that.is.an('object');
data.toolsResults.phantomas.offenders.should.have.a.property('DOMelementMaxDepth');
data.toolsResults.phantomas.offenders.DOMelementMaxDepth.should.have.length(1);
data.toolsResults.phantomas.offenders.DOMelementMaxDepth[0].should.equal('body > h1[1]');
data.toolsResults.phantomas.offenders.DOMelementMaxDepth[0].should.equal('body > h1[0]');
// Test rules
data.should.have.a.property('rules').that.is.an('object');
@@ -79,7 +79,7 @@ describe('index.js', function() {
"count": 1,
"tree": {
"body": {
"h1[1]": 1
"h1[0]": 1
}
}
}