Back to a clean phantomas version + modules
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* Analyzes DOM complexity
|
||||
*/
|
||||
/* global document: true, Node: true, window: true */
|
||||
'use strict';
|
||||
|
||||
exports.version = '0.3.a';
|
||||
|
||||
exports.module = function(phantomas) {
|
||||
|
||||
// total length of HTML comments (including <!-- --> brackets)
|
||||
phantomas.setMetric('commentsSize'); // @desc the size of HTML comments on the page @offenders
|
||||
|
||||
// total length of HTML of hidden elements (i.e. display: none)
|
||||
phantomas.setMetric('hiddenContentSize'); // @desc the size of content of hidden elements on the page (with CSS display: none) @offenders
|
||||
|
||||
// total length of text nodes with whitespaces only (i.e. pretty formatting of HTML)
|
||||
phantomas.setMetric('whiteSpacesSize'); // @desc the size of text nodes with whitespaces only
|
||||
|
||||
// count all tags
|
||||
phantomas.setMetric('DOMelementsCount'); // @desc total number of HTML element nodes
|
||||
phantomas.setMetric('DOMelementMaxDepth'); // @desc maximum level on nesting of HTML element node
|
||||
|
||||
phantomas.setMetric('DOMidDuplicated'); // @desc duplicated id found in DOM
|
||||
|
||||
// nodes with inlines CSS (style attribute)
|
||||
phantomas.setMetric('nodesWithInlineCSS'); // @desc number of nodes with inline CSS styling (with style attribute) @offenders
|
||||
|
||||
// HTML size
|
||||
phantomas.on('report', function() {
|
||||
phantomas.setMetricEvaluate('bodyHTMLSize', function() { // @desc the size of body tag content (document.body.innerHTML.length)
|
||||
return document.body && document.body.innerHTML.length || 0;
|
||||
});
|
||||
|
||||
phantomas.evaluate(function() {
|
||||
(function(phantomas) {
|
||||
var runner = new phantomas.nodeRunner(),
|
||||
whitespacesRegExp = /^\s+$/,
|
||||
DOMelementMaxDepth = 0,
|
||||
size = 0;
|
||||
|
||||
// include all nodes
|
||||
runner.isSkipped = function(node) {
|
||||
return false;
|
||||
};
|
||||
|
||||
runner.walk(document.body, function(node, depth) {
|
||||
switch (node.nodeType) {
|
||||
case Node.COMMENT_NODE:
|
||||
size = node.textContent.length + 7; // '<!--' + '-->'.length
|
||||
phantomas.incrMetric('commentsSize', size);
|
||||
|
||||
// log HTML comments bigger than 64 characters
|
||||
if (size > 64) {
|
||||
phantomas.addOffender('commentsSize', phantomas.getDOMPath(node) + ' (' + size + ' characters)');
|
||||
}
|
||||
break;
|
||||
|
||||
case Node.ELEMENT_NODE:
|
||||
phantomas.incrMetric('DOMelementsCount');
|
||||
DOMelementMaxDepth = Math.max(DOMelementMaxDepth, depth);
|
||||
|
||||
if (node.id) {
|
||||
// Send id to a collection so that duplicated ids can be counted
|
||||
phantomas.emit('domId', node.id);
|
||||
}
|
||||
|
||||
// ignore inline <script> tags
|
||||
if (node.nodeName === 'SCRIPT') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @see https://developer.mozilla.org/en/DOM%3awindow.getComputedStyle
|
||||
var styles = window.getComputedStyle(node);
|
||||
|
||||
if (styles && styles.getPropertyValue('display') === 'none') {
|
||||
if (typeof node.innerHTML === 'string') {
|
||||
size = node.innerHTML.length;
|
||||
phantomas.incrMetric('hiddenContentSize', size);
|
||||
|
||||
// log hidden containers bigger than 1 kB
|
||||
if (size > 1024) {
|
||||
phantomas.addOffender('hiddenContentSize', phantomas.getDOMPath(node) + ' (' + size + ' characters)');
|
||||
}
|
||||
}
|
||||
|
||||
// don't run for child nodes as they're hidden as well
|
||||
return false;
|
||||
}
|
||||
|
||||
// count nodes with inline CSS
|
||||
if (node.hasAttribute('style')) {
|
||||
phantomas.incrMetric('nodesWithInlineCSS');
|
||||
phantomas.addOffender('nodesWithInlineCSS', phantomas.getDOMPath(node) + ' (' + node.getAttribute('style') + ')');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Node.TEXT_NODE:
|
||||
if (whitespacesRegExp.test(node.textContent)) {
|
||||
phantomas.incrMetric('whiteSpacesSize', node.textContent.length);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
phantomas.setMetric('DOMelementMaxDepth', DOMelementMaxDepth);
|
||||
|
||||
phantomas.spyEnabled(false, 'counting iframes and images');
|
||||
|
||||
// count <iframe> tags
|
||||
phantomas.setMetric('iframesCount', document.querySelectorAll('iframe').length); // @desc number of iframe nodes
|
||||
|
||||
// <img> nodes without dimensions (one of width / height missing)
|
||||
phantomas.setMetric('imagesWithoutDimensions', (function() { // @desc number of <img> nodes without both width and height attribute @offenders
|
||||
var imgNodes = document.body && document.body.querySelectorAll('img') || [],
|
||||
node,
|
||||
imagesWithoutDimensions = 0;
|
||||
|
||||
for (var i=0, len=imgNodes.length; i<len; i++) {
|
||||
node = imgNodes[i];
|
||||
if (!node.hasAttribute('width') || !node.hasAttribute('height')) {
|
||||
phantomas.addOffender('imagesWithoutDimensions', phantomas.getDOMPath(node));
|
||||
imagesWithoutDimensions++;
|
||||
}
|
||||
}
|
||||
|
||||
return imagesWithoutDimensions;
|
||||
})());
|
||||
|
||||
phantomas.spyEnabled(true);
|
||||
}(window.__phantomas));
|
||||
});
|
||||
});
|
||||
|
||||
// count ids in DOM to detect duplicated ids
|
||||
// @see https://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-doctype
|
||||
var Collection = require('../../../node_modules/phantomas/lib/collection'),
|
||||
DOMids = new Collection();
|
||||
|
||||
phantomas.on('domId', function(id) {
|
||||
DOMids.push(id);
|
||||
});
|
||||
|
||||
phantomas.on('report', function() {
|
||||
phantomas.log('Looking for duplicated DOM ids');
|
||||
DOMids.sort().forEach(function(id, cnt) {
|
||||
if (cnt > 1) {
|
||||
phantomas.incrMetric('DOMidDuplicated');
|
||||
phantomas.addOffender('DOMidDuplicated', '%s: %d', id, cnt);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
// fake native DOM functions
|
||||
phantomas.once('init', function() {
|
||||
phantomas.evaluate(function() {
|
||||
(function(phantomas) {
|
||||
function querySpy(type, query, fnName) {
|
||||
phantomas.emit('domQuery', type, query, fnName); // @desc DOM query has been made
|
||||
}
|
||||
|
||||
phantomas.spy(Document.prototype, 'getElementById', function(id) {
|
||||
phantomas.incrMetric('DOMqueriesById');
|
||||
querySpy('id', '#' + id, 'getElementById');
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'getElementById',
|
||||
callDetails: {
|
||||
context: {
|
||||
domElement: '#document'
|
||||
},
|
||||
arguments: ['#' + id]
|
||||
},
|
||||
caller: phantomas.getCaller(1),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
|
||||
}, phantomas.leaveContext);
|
||||
|
||||
// selectors by class name
|
||||
function selectorClassNameSpyBefore(className) {
|
||||
phantomas.incrMetric('DOMqueriesByClassName');
|
||||
phantomas.addOffender('DOMqueriesByClassName', '.' + className);
|
||||
querySpy('class', '.' + className, 'getElementsByClassName');
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'getElementsByClassName',
|
||||
callDetails: {
|
||||
context: {
|
||||
domElement: phantomas.getDOMPath(this)
|
||||
},
|
||||
arguments: ['.' + className]
|
||||
},
|
||||
caller: phantomas.getCaller(1),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
}
|
||||
|
||||
phantomas.spy(Document.prototype, 'getElementsByClassName', selectorClassNameSpyBefore, phantomas.leaveContext);
|
||||
phantomas.spy(Element.prototype, 'getElementsByClassName', selectorClassNameSpyBefore, phantomas.leaveContext);
|
||||
|
||||
// selectors by tag name
|
||||
function selectorTagNameSpyBefore(tagName) {
|
||||
phantomas.incrMetric('DOMqueriesByTagName');
|
||||
phantomas.addOffender('DOMqueriesByTagName', tagName);
|
||||
querySpy('tag name', tagName, 'getElementsByTagName');
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'getElementsByTagName',
|
||||
callDetails: {
|
||||
context: {
|
||||
domElement: phantomas.getDOMPath(this)
|
||||
},
|
||||
arguments: [tagName]
|
||||
},
|
||||
caller: phantomas.getCaller(1),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
}
|
||||
|
||||
phantomas.spy(Document.prototype, 'getElementsByTagName', selectorTagNameSpyBefore, phantomas.leaveContext);
|
||||
phantomas.spy(Element.prototype, 'getElementsByTagName', selectorTagNameSpyBefore, phantomas.leaveContext);
|
||||
|
||||
// selector queries
|
||||
function selectorQuerySpy(selector) {
|
||||
phantomas.incrMetric('DOMqueriesByQuerySelectorAll');
|
||||
phantomas.addOffender('DOMqueriesByQuerySelectorAll', selector);
|
||||
querySpy('selector', selector, 'querySelectorAll');
|
||||
}
|
||||
|
||||
function selectorQuerySpyBefore(selector) {
|
||||
selectorQuerySpy(selector);
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'querySelector',
|
||||
callDetails: {
|
||||
context: {
|
||||
domElement: phantomas.getDOMPath(this)
|
||||
},
|
||||
arguments: [selector]
|
||||
},
|
||||
caller: phantomas.getCaller(1),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
}
|
||||
|
||||
function selectorAllQuerySpyBefore(selector) {
|
||||
selectorQuerySpy(selector);
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'querySelectorAll',
|
||||
callDetails: {
|
||||
context: {
|
||||
domElement: phantomas.getDOMPath(this)
|
||||
},
|
||||
arguments: [selector]
|
||||
},
|
||||
caller: phantomas.getCaller(1),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
}
|
||||
|
||||
phantomas.spy(Document.prototype, 'querySelector', selectorQuerySpyBefore, phantomas.leaveContext);
|
||||
phantomas.spy(Document.prototype, 'querySelectorAll', selectorAllQuerySpyBefore, phantomas.leaveContext);
|
||||
phantomas.spy(Element.prototype, 'querySelector', selectorQuerySpyBefore, phantomas.leaveContext);
|
||||
phantomas.spy(Element.prototype, 'querySelectorAll', selectorAllQuerySpyBefore, phantomas.leaveContext);
|
||||
|
||||
|
||||
// count DOM inserts
|
||||
function appendChild(child) {
|
||||
/* jshint validthis: true */
|
||||
// ignore appending to the node that's not yet added to DOM tree
|
||||
if (!this.parentNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
var destNodePath = phantomas.getDOMPath(this),
|
||||
appendedNodePath = phantomas.getDOMPath(child);
|
||||
|
||||
// don't count elements added to fragments as a DOM inserts (issue #350)
|
||||
// DocumentFragment > div[0]
|
||||
if (destNodePath.indexOf('DocumentFragment') === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
phantomas.incrMetric('DOMinserts');
|
||||
phantomas.addOffender('DOMinserts', '"%s" appended to "%s"', appendedNodePath, destNodePath);
|
||||
|
||||
phantomas.log('DOM insert: node "%s" appended to "%s"', appendedNodePath, destNodePath);
|
||||
}
|
||||
|
||||
function appendChildSpyBefore(child) {
|
||||
appendChild(child);
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'appendChild',
|
||||
callDetails: {
|
||||
context: {
|
||||
domElement: phantomas.getDOMPath(this)
|
||||
},
|
||||
arguments: [phantomas.getDOMPath(child)]
|
||||
},
|
||||
caller: phantomas.getCaller(1),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
}
|
||||
|
||||
function insertBeforeSpyBefore(child) {
|
||||
appendChild(child);
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'insertBefore',
|
||||
callDetails: {
|
||||
context: {
|
||||
domElement: phantomas.getDOMPath(this)
|
||||
},
|
||||
arguments: [phantomas.getDOMPath(child)]
|
||||
},
|
||||
caller: phantomas.getCaller(1),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
}
|
||||
|
||||
phantomas.spy(Node.prototype, 'appendChild', appendChildSpyBefore, phantomas.leaveContext);
|
||||
phantomas.spy(Node.prototype, 'insertBefore', insertBeforeSpyBefore, phantomas.leaveContext);
|
||||
})(window.__phantomas);
|
||||
});
|
||||
});
|
||||
|
||||
// count DOM queries by either ID, tag name, class name and selector query
|
||||
// @see https://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-doctype
|
||||
var Collection = require('../../../node_modules/phantomas/lib/collection'),
|
||||
DOMqueries = new Collection();
|
||||
|
||||
phantomas.on('domQuery', function(type, query, fnName) {
|
||||
phantomas.log('DOM query: by %s - "%s" (using %s)', type, query, fnName);
|
||||
phantomas.incrMetric('DOMqueries');
|
||||
|
||||
DOMqueries.push(type + ' "' + query + '"');
|
||||
});
|
||||
|
||||
phantomas.on('report', function() {
|
||||
DOMqueries.sort().forEach(function(query, cnt) {
|
||||
if (cnt > 1) {
|
||||
phantomas.incrMetric('DOMqueriesDuplicated');
|
||||
phantomas.addOffender('DOMqueriesDuplicated', '%s: %d queries', query, cnt);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* Analyzes jQuery activity
|
||||
*
|
||||
* @see http://code.jquery.com/jquery-1.10.2.js
|
||||
* @see http://code.jquery.com/jquery-2.0.3.js
|
||||
*/
|
||||
/* global document: true, window: true */
|
||||
'use strict';
|
||||
|
||||
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)
|
||||
|
||||
var jQueryFunctions = [
|
||||
// DOM manipulations
|
||||
'html',
|
||||
'append',
|
||||
'appendTo',
|
||||
'prepend',
|
||||
'prependTo',
|
||||
'before',
|
||||
'insertBefore',
|
||||
'after',
|
||||
'insertAfter',
|
||||
'remove',
|
||||
'detach',
|
||||
'empty',
|
||||
'clone',
|
||||
'replaceWith',
|
||||
'replaceAll',
|
||||
'text',
|
||||
'wrap',
|
||||
'wrapAll',
|
||||
'wrapInner',
|
||||
'unwrap',
|
||||
|
||||
// Style manipulations
|
||||
'css',
|
||||
'offset',
|
||||
'position',
|
||||
'height',
|
||||
'innerHeight',
|
||||
'outerHeight',
|
||||
'width',
|
||||
'innerWidth',
|
||||
'outerWidth',
|
||||
'scrollLeft',
|
||||
'scrollTop',
|
||||
|
||||
// generic events
|
||||
'on',
|
||||
'off',
|
||||
'live',
|
||||
'die',
|
||||
'delegate',
|
||||
'undelegate',
|
||||
'one',
|
||||
'trigger',
|
||||
'triggerHandler',
|
||||
'unbind',
|
||||
|
||||
// more events
|
||||
'blur',
|
||||
'change',
|
||||
'click',
|
||||
'dblclick',
|
||||
'error',
|
||||
'focus',
|
||||
'focusin',
|
||||
'focusout',
|
||||
'hover',
|
||||
'keydown',
|
||||
'keypress',
|
||||
'keyup',
|
||||
'load',
|
||||
'mousedown',
|
||||
'mouseenter',
|
||||
'mouseleave',
|
||||
'mousemove',
|
||||
'mouseout',
|
||||
'mouseover',
|
||||
'mouseup',
|
||||
'resize',
|
||||
'scroll',
|
||||
'select',
|
||||
'submit',
|
||||
'toggle',
|
||||
'unload',
|
||||
|
||||
// attributes
|
||||
'attr',
|
||||
'prop',
|
||||
'removeAttr',
|
||||
'removeProp',
|
||||
'val',
|
||||
'hasClass',
|
||||
'addClass',
|
||||
'removeClass',
|
||||
'toggleClass'
|
||||
];
|
||||
|
||||
// spy calls to jQuery functions
|
||||
phantomas.once('init', function() {
|
||||
phantomas.evaluate(function(jQueryFunctions) {
|
||||
(function(phantomas) {
|
||||
var jQuery;
|
||||
|
||||
// TODO: create a helper - phantomas.spyGlobalVar() ?
|
||||
window.__defineSetter__('jQuery', function(val) {
|
||||
var version;
|
||||
var oldJQuery = jQuery;
|
||||
|
||||
if (!val || !val.fn) {
|
||||
phantomas.log('jQuery: unable to detect version!');
|
||||
return;
|
||||
}
|
||||
|
||||
version = val.fn.jquery;
|
||||
jQuery = val;
|
||||
jQueryFn = val.fn;
|
||||
// Older jQuery (v?.?) compatibility
|
||||
if (!jQueryFn) {
|
||||
jQueryFn = jQuery;
|
||||
}
|
||||
|
||||
phantomas.log('jQuery: loaded v' + version);
|
||||
phantomas.setMetric('jQueryVersion', version);
|
||||
phantomas.emit('jQueryLoaded', version);
|
||||
|
||||
phantomas.pushContext({
|
||||
type: (oldJQuery) ? 'jQuery version change' : 'jQuery loaded',
|
||||
callDetails: {
|
||||
arguments: ['version ' + version]
|
||||
},
|
||||
caller: phantomas.getCaller(1),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
|
||||
// jQuery.ready.promise
|
||||
// works for jQuery 1.8.0+ (released Aug 09 2012)
|
||||
phantomas.spy(val.ready, 'promise', function(func) {
|
||||
phantomas.incrMetric('jQueryOnDOMReadyFunctions');
|
||||
|
||||
phantomas.pushContext({
|
||||
type: 'jQuery - onDOMReady',
|
||||
callDetails: {
|
||||
arguments: [func]
|
||||
},
|
||||
caller: phantomas.getCaller(1),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
|
||||
}) || phantomas.log('jQuery: can not measure jQueryOnDOMReadyFunctions (jQuery used on the page is too old)!');
|
||||
|
||||
|
||||
// Sizzle calls - jQuery.find
|
||||
// works for jQuery 1.3+ (released Jan 13 2009)
|
||||
phantomas.spy(val, 'find', function(selector, context) {
|
||||
phantomas.incrMetric('jQuerySizzleCalls');
|
||||
phantomas.emit('onSizzleCall', selector + ' (context: ' + (phantomas.getDOMPath(context) || 'unknown') + ')');
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'jQuery - find',
|
||||
callDetails: {
|
||||
context: {
|
||||
length: this.length,
|
||||
firstElementPath: phantomas.getDOMPath(context)
|
||||
},
|
||||
arguments: [selector]
|
||||
},
|
||||
caller: phantomas.getCaller(3),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
|
||||
}, phantomas.leaveContext) || phantomas.log('jQuery: can not measure jQuerySizzleCalls (jQuery used on the page is too old)!');
|
||||
|
||||
|
||||
// $().bind - jQuery.bind
|
||||
// works for jQuery v?.?
|
||||
phantomas.spy(jQueryFn, 'bind', function(eventTypes, func) {
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'jQuery - bind',
|
||||
callDetails: {
|
||||
context: {
|
||||
length: this.length,
|
||||
firstElementPath: phantomas.getDOMPath(this[0]),
|
||||
selector: this.selector
|
||||
},
|
||||
arguments: [eventTypes, func]
|
||||
},
|
||||
caller: phantomas.getCaller(3),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
|
||||
}, function(eventTypes, func) {
|
||||
phantomas.leaveContext();
|
||||
|
||||
if (this.length > 1) {
|
||||
phantomas.incrMetric('jQueryBindOnMultipleElements');
|
||||
phantomas.addOffender('jQueryBindOnMultipleElements', '%s (%s on %d elements)', this.selector, eventTypes, this.length);
|
||||
}
|
||||
|
||||
}) || phantomas.log('jQuery: can not measure jQueryBindCalls (jQuery used on the page is too old)!');
|
||||
|
||||
|
||||
|
||||
// Add spys on many jQuery functions
|
||||
jQueryFunctions.forEach(function(functionName) {
|
||||
var capitalizedName = functionName.substring(0,1).toUpperCase() + functionName.substring(1);
|
||||
|
||||
phantomas.spy(jQueryFn, functionName, function(args) {
|
||||
|
||||
// Clean args
|
||||
args = [].slice.call(arguments);
|
||||
args.forEach(function(arg, index) {
|
||||
if (arg instanceof jQuery) {
|
||||
arg = phantomas.getDOMPath(arg[0]) || 'unknown';
|
||||
}
|
||||
|
||||
if (arg instanceof Object) {
|
||||
try {
|
||||
arg = JSON.stringify(arg);
|
||||
} catch(e) {
|
||||
arg = '[Object]';
|
||||
}
|
||||
}
|
||||
|
||||
if ((typeof arg == 'string' || arg instanceof String) && arg.length > 200) {
|
||||
arg = arg.substring(0, 200) + '...';
|
||||
}
|
||||
|
||||
if (arg === true) {
|
||||
arg = 'true';
|
||||
}
|
||||
|
||||
if (arg === false) {
|
||||
arg = 'false';
|
||||
}
|
||||
|
||||
if (arg == null) {
|
||||
arg = 'null';
|
||||
}
|
||||
|
||||
if (typeof arg !== 'number' && typeof arg !== 'string' && !(arg instanceof String)) {
|
||||
arg = 'undefined';
|
||||
}
|
||||
|
||||
args[index] = arg;
|
||||
});
|
||||
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'jQuery - ' + functionName,
|
||||
callDetails: {
|
||||
context: {
|
||||
length: this.length,
|
||||
firstElementPath: phantomas.getDOMPath(this[0])
|
||||
},
|
||||
arguments: args
|
||||
},
|
||||
caller: phantomas.getCaller(3),
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
|
||||
}, phantomas.leaveContext) || phantomas.log('jQuery: can not track jQuery - ' + capitalizedName + ' (this version of jQuery doesn\'t support it)');
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
window.__defineGetter__('jQuery', function() {
|
||||
return jQuery;
|
||||
});
|
||||
})(window.__phantomas);
|
||||
}, jQueryFunctions);
|
||||
});
|
||||
|
||||
|
||||
// count Sizzle calls to detect duplicated queries
|
||||
var Collection = require('../../../node_modules/phantomas/lib/collection'),
|
||||
sizzleCalls = new Collection(),
|
||||
jQueryLoading = new Collection();
|
||||
|
||||
phantomas.on('onSizzleCall', function(request) {
|
||||
sizzleCalls.push(request);
|
||||
});
|
||||
|
||||
phantomas.on('jQueryLoaded', function(version) {
|
||||
jQueryLoading.push(version);
|
||||
});
|
||||
|
||||
phantomas.on('report', function() {
|
||||
sizzleCalls.sort().forEach(function(id, cnt) {
|
||||
if (cnt > 1) {
|
||||
phantomas.incrMetric('jQuerySizzleCallsDuplicated');
|
||||
phantomas.addOffender('jQuerySizzleCallsDuplicated', '%s: %d', id, cnt);
|
||||
}
|
||||
});
|
||||
|
||||
jQueryLoading.forEach(function(version) {
|
||||
phantomas.incrMetric('jQueryDifferentVersions');
|
||||
phantomas.addOffender('jQueryDifferentVersions', '%s', version);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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) {
|
||||
phantomas.setMetric('jsErrors'); // @desc number of JavaScript errors
|
||||
|
||||
function formatTrace(trace) {
|
||||
var ret = [];
|
||||
|
||||
if(Array.isArray(trace)) {
|
||||
trace.forEach(function(entry) {
|
||||
ret.push((entry.function ? entry.function + '(): ' : 'unknown fn: ') + (entry.sourceURL || entry.file) + ' @ ' + entry.line);
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
phantomas.on('jserror', function(msg, trace) {
|
||||
trace = formatTrace(trace);
|
||||
|
||||
phantomas.log(msg);
|
||||
phantomas.log('Backtrace: ' + trace.join(' / '));
|
||||
|
||||
phantomas.incrMetric('jsErrors');
|
||||
phantomas.addOffender('jsErrors', msg + ' - ' + trace.join(' / '));
|
||||
|
||||
// TODO : send the error back to the browser ?
|
||||
/*phantomas.pushContext({
|
||||
type: 'error',
|
||||
callDetails: {
|
||||
arguments: [msg]
|
||||
},
|
||||
caller: trace[0],
|
||||
backtrace: trace.join(' / ')
|
||||
});*/
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Saves the javascript interractions with the DOM
|
||||
*
|
||||
* Run phantomas with --js-execution-tree option to use this module
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
exports.version = '0.1';
|
||||
|
||||
exports.module = function(phantomas) {
|
||||
|
||||
phantomas.setMetric('javascriptExecutionTree'); // @desc number of duplicated DOM queries
|
||||
|
||||
// save data
|
||||
phantomas.on('report', function() {
|
||||
phantomas.log('Reading execution tree JSON');
|
||||
|
||||
phantomas.evaluate(function() {(function(phantomas) {
|
||||
var fullTree = phantomas.readFullTree();
|
||||
|
||||
if (fullTree === null) {
|
||||
phantomas.log('JS execution tree: error, the execution tree is not correctly closed');
|
||||
return;
|
||||
}
|
||||
|
||||
phantomas.setMetric('javascriptExecutionTree', true, true);
|
||||
phantomas.addOffender('javascriptExecutionTree', JSON.stringify(fullTree));
|
||||
|
||||
})(window.__phantomas);});
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user