autobench 0.0.1alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +16 -0
- data/README.md +123 -0
- data/bin/autobench +180 -0
- data/bin/autobench-config +162 -0
- data/lib/autobench.rb +28 -0
- data/lib/autobench/client.rb +78 -0
- data/lib/autobench/common.rb +49 -0
- data/lib/autobench/config.rb +62 -0
- data/lib/autobench/render.rb +102 -0
- data/lib/autobench/version.rb +3 -0
- data/lib/autobench/yslow.rb +75 -0
- data/lib/phantomas/README.md +296 -0
- data/lib/phantomas/core/formatter.js +65 -0
- data/lib/phantomas/core/helper.js +64 -0
- data/lib/phantomas/core/modules/requestsMonitor/requestsMonitor.js +214 -0
- data/lib/phantomas/core/pads.js +16 -0
- data/lib/phantomas/core/phantomas.js +418 -0
- data/lib/phantomas/lib/args.js +27 -0
- data/lib/phantomas/lib/modules/_coffee-script.js +2 -0
- data/lib/phantomas/lib/modules/assert.js +326 -0
- data/lib/phantomas/lib/modules/events.js +216 -0
- data/lib/phantomas/lib/modules/http.js +55 -0
- data/lib/phantomas/lib/modules/path.js +441 -0
- data/lib/phantomas/lib/modules/punycode.js +510 -0
- data/lib/phantomas/lib/modules/querystring.js +214 -0
- data/lib/phantomas/lib/modules/tty.js +7 -0
- data/lib/phantomas/lib/modules/url.js +625 -0
- data/lib/phantomas/lib/modules/util.js +520 -0
- data/lib/phantomas/modules/ajaxRequests/ajaxRequests.js +15 -0
- data/lib/phantomas/modules/assetsTypes/assetsTypes.js +21 -0
- data/lib/phantomas/modules/cacheHits/cacheHits.js +28 -0
- data/lib/phantomas/modules/caching/caching.js +66 -0
- data/lib/phantomas/modules/cookies/cookies.js +54 -0
- data/lib/phantomas/modules/domComplexity/domComplexity.js +130 -0
- data/lib/phantomas/modules/domQueries/domQueries.js +148 -0
- data/lib/phantomas/modules/domains/domains.js +49 -0
- data/lib/phantomas/modules/globalVariables/globalVariables.js +44 -0
- data/lib/phantomas/modules/headers/headers.js +48 -0
- data/lib/phantomas/modules/localStorage/localStorage.js +14 -0
- data/lib/phantomas/modules/requestsStats/requestsStats.js +71 -0
- data/lib/phantomas/modules/staticAssets/staticAssets.js +40 -0
- data/lib/phantomas/modules/waterfall/waterfall.js +62 -0
- data/lib/phantomas/modules/windowPerformance/windowPerformance.js +36 -0
- data/lib/phantomas/package.json +27 -0
- data/lib/phantomas/phantomas.js +35 -0
- data/lib/phantomas/run-multiple.js +177 -0
- data/lib/yslow.js +5 -0
- metadata +135 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
/**
|
2
|
+
* Analyzes AJAX requests
|
3
|
+
*/
|
4
|
+
exports.version = '0.1';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
phantomas.setMetric('ajaxRequests');
|
8
|
+
|
9
|
+
phantomas.on('send', function(entry, res) {
|
10
|
+
if (entry.requestHeaders['X-Requested-With'] === 'XMLHttpRequest') {
|
11
|
+
phantomas.addNotice('AJAX request: ' + entry.url);
|
12
|
+
phantomas.incrMetric('ajaxRequests');
|
13
|
+
}
|
14
|
+
});
|
15
|
+
};
|
@@ -0,0 +1,21 @@
|
|
1
|
+
/**
|
2
|
+
* Analyze number of requests and sizes of differet assets types
|
3
|
+
*/
|
4
|
+
exports.version = '0.1';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
['html', 'css', 'js', 'image', 'base64', 'other'].forEach(function(key) {
|
8
|
+
phantomas.setMetric(key + 'Count');
|
9
|
+
phantomas.setMetric(key + 'Size');
|
10
|
+
});
|
11
|
+
|
12
|
+
phantomas.on('recv', function(entry, res) {
|
13
|
+
phantomas.incrMetric(entry.type + 'Count');
|
14
|
+
phantomas.incrMetric(entry.type + 'Size', entry.bodySize);
|
15
|
+
});
|
16
|
+
|
17
|
+
phantomas.on('base64recv', function(entry, res) {
|
18
|
+
phantomas.incrMetric('base64Count');
|
19
|
+
phantomas.incrMetric('base64Size', entry.bodySize);
|
20
|
+
});
|
21
|
+
};
|
@@ -0,0 +1,28 @@
|
|
1
|
+
/**
|
2
|
+
* Analyzes X-Cache headers from caching servers like Squid or Varnish
|
3
|
+
*/
|
4
|
+
exports.version = '0.1';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
phantomas.setMetric('cacheHits');
|
8
|
+
phantomas.setMetric('cacheMisses');
|
9
|
+
|
10
|
+
var re = /miss|hit/i;
|
11
|
+
|
12
|
+
// examples:
|
13
|
+
// X-Cache:HIT, HIT
|
14
|
+
// X-Cache:arsenic miss (0)
|
15
|
+
phantomas.on('recv', function(entry,res) {
|
16
|
+
var header = entry.headers['X-Cache'] || '',
|
17
|
+
isHit;
|
18
|
+
|
19
|
+
if (re.test(header)) {
|
20
|
+
isHit = header.toLowerCase().indexOf('hit') > -1;
|
21
|
+
phantomas.incrMetric(isHit ? 'cacheHits' : 'cacheMisses');
|
22
|
+
|
23
|
+
if (!isHit) {
|
24
|
+
phantomas.addNotice('Cache miss on ' + entry.url + ' (X-Cache: ' + header + ')');
|
25
|
+
}
|
26
|
+
}
|
27
|
+
});
|
28
|
+
};
|
@@ -0,0 +1,66 @@
|
|
1
|
+
/**
|
2
|
+
* Analyzes HTTP caching headers
|
3
|
+
*
|
4
|
+
* @see https://developers.google.com/speed/docs/best-practices/caching
|
5
|
+
*/
|
6
|
+
exports.version = '0.1';
|
7
|
+
|
8
|
+
exports.module = function(phantomas) {
|
9
|
+
var cacheControlRegExp = /max-age=(\d+)/;
|
10
|
+
|
11
|
+
function getCachingTime(headers) {
|
12
|
+
// false means "no caching"
|
13
|
+
var ttl = false,
|
14
|
+
headerName;
|
15
|
+
|
16
|
+
for (headerName in headers) {
|
17
|
+
var value = headers[headerName];
|
18
|
+
|
19
|
+
switch(headerName.toLowerCase()) {
|
20
|
+
// parse max-age=...
|
21
|
+
//
|
22
|
+
// max-age=2592000
|
23
|
+
// public, max-age=300, must-revalidate
|
24
|
+
case 'cache-control':
|
25
|
+
var matches = value.match(cacheControlRegExp);
|
26
|
+
|
27
|
+
if (matches) {
|
28
|
+
ttl = parseInt(matches[1], 10);
|
29
|
+
}
|
30
|
+
break;
|
31
|
+
|
32
|
+
// TODO: parse date
|
33
|
+
case 'expires':
|
34
|
+
break;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
//console.log(JSON.stringify(headers)); console.log("TTL: " + ttl + ' s');
|
39
|
+
|
40
|
+
return ttl;
|
41
|
+
}
|
42
|
+
|
43
|
+
phantomas.setMetric('cachingNotSpecified');
|
44
|
+
phantomas.setMetric('cachingTooShort');
|
45
|
+
phantomas.setMetric('cachingDisabled');
|
46
|
+
|
47
|
+
phantomas.on('recv', function(entry, res) {
|
48
|
+
var ttl = getCachingTime(entry.headers);
|
49
|
+
|
50
|
+
// static assets
|
51
|
+
if (entry.isImage || entry.isJS || entry.isCSS) {
|
52
|
+
if (ttl === false) {
|
53
|
+
phantomas.addNotice("No caching specified for <" + entry.url + ">");
|
54
|
+
phantomas.incrMetric('cachingNotSpecified');
|
55
|
+
}
|
56
|
+
else if (ttl === 0) {
|
57
|
+
phantomas.addNotice("Caching disabled for <" + entry.url + ">");
|
58
|
+
phantomas.incrMetric('cachingDisabled');
|
59
|
+
}
|
60
|
+
else if (ttl < 7 * 86400) {
|
61
|
+
phantomas.addNotice("Caching period is less than a week for <" + entry.url + "> (set to " + ttl + " s)");
|
62
|
+
phantomas.incrMetric('cachingTooShort');
|
63
|
+
}
|
64
|
+
}
|
65
|
+
});
|
66
|
+
};
|
@@ -0,0 +1,54 @@
|
|
1
|
+
/**
|
2
|
+
* cookies metrics
|
3
|
+
*/
|
4
|
+
exports.version = '0.1';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
// monitor cookies in HTTP headers
|
8
|
+
var cookiesSent = 0,
|
9
|
+
cookiesRecv = 0;
|
10
|
+
cookiesDomains = {};
|
11
|
+
|
12
|
+
phantomas.on('send', function(entry, res) {
|
13
|
+
res.headers.forEach(function(header) {
|
14
|
+
switch (header.name) {
|
15
|
+
case 'Cookie':
|
16
|
+
cookiesSent += header.value.length;
|
17
|
+
cookiesDomains[entry.domain] = true;
|
18
|
+
break;
|
19
|
+
}
|
20
|
+
});
|
21
|
+
});
|
22
|
+
|
23
|
+
phantomas.on('recv', function(entry, res) {
|
24
|
+
res.headers.forEach(function(header) {
|
25
|
+
switch (header.name) {
|
26
|
+
case 'Set-Cookie':
|
27
|
+
cookiesRecv += header.value.length;
|
28
|
+
cookiesDomains[entry.domain] = true;
|
29
|
+
break;
|
30
|
+
}
|
31
|
+
});
|
32
|
+
});
|
33
|
+
|
34
|
+
// domain cookies (accessible by the browser)
|
35
|
+
phantomas.on('report', function() {
|
36
|
+
phantomas.setMetric('cookiesSent', cookiesSent);
|
37
|
+
phantomas.setMetric('cookiesRecv', cookiesRecv);
|
38
|
+
|
39
|
+
// domains with cookies
|
40
|
+
var domainsWithCookies = 0;
|
41
|
+
for (var domain in cookiesDomains) {
|
42
|
+
domainsWithCookies++;
|
43
|
+
}
|
44
|
+
phantomas.setMetric('domainsWithCookies', domainsWithCookies);
|
45
|
+
|
46
|
+
phantomas.setMetricEvaluate('documentCookiesLength', function() {
|
47
|
+
return document.cookie.length;
|
48
|
+
});
|
49
|
+
|
50
|
+
phantomas.setMetricEvaluate('documentCookiesCount', function() {
|
51
|
+
return document.cookie.split(';').length;
|
52
|
+
});
|
53
|
+
});
|
54
|
+
};
|
@@ -0,0 +1,130 @@
|
|
1
|
+
/**
|
2
|
+
* Analyzes DOM complexity
|
3
|
+
*/
|
4
|
+
exports.version = '0.2';
|
5
|
+
|
6
|
+
exports.module = function(phantomas) {
|
7
|
+
|
8
|
+
// HTML size
|
9
|
+
phantomas.on('report', function() {
|
10
|
+
phantomas.setMetricEvaluate('bodyHTMLSize', function() {
|
11
|
+
return document.body.innerHTML.length;
|
12
|
+
});
|
13
|
+
|
14
|
+
phantomas.evaluate(function() {
|
15
|
+
(function(phantomas) {
|
16
|
+
var runner = new phantomas.nodeRunner(),
|
17
|
+
whitespacesRegExp = /^\s+$/;
|
18
|
+
|
19
|
+
var metrics = {
|
20
|
+
nodes: 0,
|
21
|
+
comments: 0,
|
22
|
+
hiddenContent: 0,
|
23
|
+
whitespaces: 0,
|
24
|
+
maxDepth: 0,
|
25
|
+
nodesWithCSS: 0
|
26
|
+
};
|
27
|
+
|
28
|
+
// include all nodes
|
29
|
+
runner.isSkipped = function(node) {
|
30
|
+
return false;
|
31
|
+
};
|
32
|
+
|
33
|
+
runner.walk(document.body, function(node, depth) {
|
34
|
+
switch (node.nodeType) {
|
35
|
+
case Node.COMMENT_NODE:
|
36
|
+
metrics.comments += node.textContent.length + 7; // '<!--' + '-->'.length
|
37
|
+
break;
|
38
|
+
|
39
|
+
case Node.ELEMENT_NODE:
|
40
|
+
metrics.nodes++;
|
41
|
+
metrics.maxDepth = Math.max(metrics.maxDepth, depth);
|
42
|
+
|
43
|
+
// ignore inline <script> tags
|
44
|
+
if (node.nodeName === 'SCRIPT') {
|
45
|
+
return false;
|
46
|
+
}
|
47
|
+
|
48
|
+
// @see https://developer.mozilla.org/en/DOM%3awindow.getComputedStyle
|
49
|
+
var styles = window.getComputedStyle(node);
|
50
|
+
|
51
|
+
if (styles && styles.getPropertyValue('display') === 'none') {
|
52
|
+
//console.log(node.innerHTML);
|
53
|
+
metrics.hiddenContent += node.innerHTML.length;
|
54
|
+
|
55
|
+
// don't run for child nodes as they're hidden as well
|
56
|
+
return false;
|
57
|
+
}
|
58
|
+
|
59
|
+
// count nodes with inline CSS
|
60
|
+
if (node.hasAttribute('style')) {
|
61
|
+
metrics.nodesWithCSS++;
|
62
|
+
}
|
63
|
+
|
64
|
+
break;
|
65
|
+
|
66
|
+
case Node.TEXT_NODE:
|
67
|
+
if (whitespacesRegExp.test(node.textContent)) {
|
68
|
+
metrics.whitespaces += node.textContent.length;
|
69
|
+
}
|
70
|
+
break;
|
71
|
+
}
|
72
|
+
});
|
73
|
+
|
74
|
+
// store metrics
|
75
|
+
phantomas.DOMmetrics = metrics;
|
76
|
+
|
77
|
+
}(window.phantomas));
|
78
|
+
});
|
79
|
+
|
80
|
+
// total length of HTML comments (including <!-- --> brackets)
|
81
|
+
phantomas.setMetricEvaluate('commentsSize', function() {
|
82
|
+
return window.phantomas.DOMmetrics.comments;
|
83
|
+
});
|
84
|
+
|
85
|
+
// total length of HTML of hidden elements (i.e. display: none)
|
86
|
+
phantomas.setMetricEvaluate('hiddenContentSize', function() {
|
87
|
+
return window.phantomas.DOMmetrics.hiddenContent;
|
88
|
+
});
|
89
|
+
|
90
|
+
// total length of text nodes with whitespaces only (i.e. pretty formatting of HTML)
|
91
|
+
phantomas.setMetricEvaluate('whiteSpacesSize', function() {
|
92
|
+
return window.phantomas.DOMmetrics.whitespaces;
|
93
|
+
});
|
94
|
+
|
95
|
+
// count all tags
|
96
|
+
phantomas.setMetricEvaluate('DOMelementsCount', function() {
|
97
|
+
return window.phantomas.DOMmetrics.nodes;
|
98
|
+
});
|
99
|
+
|
100
|
+
phantomas.setMetricEvaluate('DOMelementMaxDepth', function() {
|
101
|
+
return window.phantomas.DOMmetrics.maxDepth;
|
102
|
+
});
|
103
|
+
|
104
|
+
// count <iframe> tags
|
105
|
+
phantomas.setMetricEvaluate('iframesCount', function() {
|
106
|
+
return document.querySelectorAll('iframe').length;
|
107
|
+
});
|
108
|
+
|
109
|
+
// nodes with inlines CSS (style attribute)
|
110
|
+
phantomas.setMetricEvaluate('nodesWithInlineCSS', function() {
|
111
|
+
return window.phantomas.DOMmetrics.nodesWithCSS;
|
112
|
+
});
|
113
|
+
|
114
|
+
// <img> nodes without dimensions (one of width / height missing)
|
115
|
+
phantomas.setMetricEvaluate('imagesWithoutDimensions', function() {
|
116
|
+
var imgNodes = document.body.querySelectorAll('img'),
|
117
|
+
node,
|
118
|
+
imagesWithoutDimensions = 0;
|
119
|
+
|
120
|
+
for (i=0, len=imgNodes.length; i<len; i++) {
|
121
|
+
node = imgNodes[i];
|
122
|
+
if (!node.hasAttribute('width') || !node.hasAttribute('height')) {
|
123
|
+
imagesWithoutDimensions++;
|
124
|
+
}
|
125
|
+
};
|
126
|
+
|
127
|
+
return imagesWithoutDimensions;
|
128
|
+
});
|
129
|
+
});
|
130
|
+
};
|
@@ -0,0 +1,148 @@
|
|
1
|
+
/**
|
2
|
+
* Analyzes DOM queries done via native DOM methods & jQuery
|
3
|
+
*
|
4
|
+
*/
|
5
|
+
exports.version = '0.2';
|
6
|
+
|
7
|
+
exports.module = function(phantomas) {
|
8
|
+
|
9
|
+
// fake native DOM functions
|
10
|
+
phantomas.once('init', function() {
|
11
|
+
phantomas.evaluate(function() {
|
12
|
+
(function() {
|
13
|
+
var originalGetElementById = window.document.getElementById,
|
14
|
+
originalGetElementsByClassName = window.document.getElementsByClassName,
|
15
|
+
originalAppendChild = Node.prototype.appendChild,
|
16
|
+
originalInsertBefore = Node.prototype.insertBefore;
|
17
|
+
|
18
|
+
// metrics storage
|
19
|
+
window.phantomas.domQueries = 0;
|
20
|
+
window.phantomas.domInserts = 0;
|
21
|
+
window.phantomas.domInsertsBacktrace = [];
|
22
|
+
|
23
|
+
window.phantomas.jQueryOnDOMReadyFunctions = 0;
|
24
|
+
window.phantomas.jQueryOnDOMReadyFunctionsBacktrace = [];
|
25
|
+
|
26
|
+
window.phantomas.jQuerySelectors = 0;
|
27
|
+
window.phantomas.jQuerySelectorsBacktrace = [];
|
28
|
+
|
29
|
+
// hook into DOM methods
|
30
|
+
document.getElementById = function(id) {
|
31
|
+
// log calls
|
32
|
+
console.log('document.getElementById("' + id + '")');
|
33
|
+
window.phantomas.domQueries++;
|
34
|
+
|
35
|
+
return originalGetElementById.call(document, id);
|
36
|
+
};
|
37
|
+
|
38
|
+
// count DOM inserts
|
39
|
+
Node.prototype.appendChild = function(child) {
|
40
|
+
var hasParent = typeof this.parentNode !== 'undefined';
|
41
|
+
|
42
|
+
// ignore appending to the node that's not yet added to DOM tree
|
43
|
+
if (!hasParent) {
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
|
47
|
+
var caller = window.phantomas.getCaller();
|
48
|
+
|
49
|
+
window.phantomas.domInserts++;
|
50
|
+
window.phantomas.domInsertsBacktrace.push({
|
51
|
+
url: caller.sourceURL,
|
52
|
+
line: caller.line
|
53
|
+
});
|
54
|
+
|
55
|
+
return originalAppendChild.call(this, child);
|
56
|
+
};
|
57
|
+
|
58
|
+
Node.prototype.insertBefore = function(child) {
|
59
|
+
var hasParent = typeof this.parentNode !== 'undefined';
|
60
|
+
|
61
|
+
// ignore appending to the node that's not yet added to DOM tree
|
62
|
+
if (!hasParent) {
|
63
|
+
return;
|
64
|
+
}
|
65
|
+
|
66
|
+
var caller = window.phantomas.getCaller();
|
67
|
+
|
68
|
+
window.phantomas.domInserts++;
|
69
|
+
window.phantomas.domInsertsBacktrace.push({
|
70
|
+
url: caller.sourceURL,
|
71
|
+
line: caller.line
|
72
|
+
});
|
73
|
+
|
74
|
+
return originalInsertBefore.call(this, child);
|
75
|
+
};
|
76
|
+
|
77
|
+
return;
|
78
|
+
|
79
|
+
// hook into $.fn.init to catch DOM queries
|
80
|
+
//
|
81
|
+
// TODO: use a better approach here:
|
82
|
+
// @see https://github.com/osteele/jquery-profile
|
83
|
+
var jQuery;
|
84
|
+
|
85
|
+
window.__defineSetter__('jQuery', function(val) {
|
86
|
+
jQuery = val;
|
87
|
+
console.log('Mocked jQuery v' + val.fn.jquery + ' object');
|
88
|
+
});
|
89
|
+
|
90
|
+
window.__defineGetter__('jQuery', function() {
|
91
|
+
return jQuery;
|
92
|
+
});
|
93
|
+
})();
|
94
|
+
});
|
95
|
+
});
|
96
|
+
|
97
|
+
phantomas.on('loadFinished', function() {
|
98
|
+
phantomas.setMetricEvaluate('DOMqueries', function() {
|
99
|
+
return window.phantomas.domQueries;
|
100
|
+
});
|
101
|
+
|
102
|
+
phantomas.setMetricEvaluate('DOMinserts', function() {
|
103
|
+
return window.phantomas.domInserts;
|
104
|
+
});
|
105
|
+
|
106
|
+
phantomas.setMetricEvaluate('jQuerySelectors', function() {
|
107
|
+
return window.phantomas.jQuerySelectors;
|
108
|
+
});
|
109
|
+
|
110
|
+
phantomas.setMetricEvaluate('jQueryOnDOMReadyFunctions', function() {
|
111
|
+
return window.phantomas.jQueryOnDOMReadyFunctions;
|
112
|
+
});
|
113
|
+
|
114
|
+
// list all selectors
|
115
|
+
var selectorsBacktrace = phantomas.evaluate(function() {
|
116
|
+
return window.phantomas.jQuerySelectorsBacktrace;
|
117
|
+
});
|
118
|
+
/**
|
119
|
+
phantomas.addNotice('jQuery selectors:');
|
120
|
+
selectorsBacktrace.forEach(function(item) {
|
121
|
+
phantomas.addNotice('* $("' + item.selector + '") called from ' + item.url + ' @ ' + item.line);
|
122
|
+
});
|
123
|
+
phantomas.addNotice();
|
124
|
+
|
125
|
+
// list all onDOMReady functions
|
126
|
+
var onDOMReadyBacktrace = phantomas.evaluate(function() {
|
127
|
+
return window.phantomas.jQueryOnDOMReadyFunctionsBacktrace;
|
128
|
+
});
|
129
|
+
|
130
|
+
phantomas.addNotice('jQuery onDOMReady functions (' + onDOMReadyBacktrace.length + '):');
|
131
|
+
onDOMReadyBacktrace.forEach(function(item) {
|
132
|
+
phantomas.addNotice('* bound from ' + item.url + ' @ ' + item.line);
|
133
|
+
});
|
134
|
+
phantomas.addNotice();
|
135
|
+
|
136
|
+
// list all DOM inserts
|
137
|
+
var domInsertsBacktrace = phantomas.evaluate(function() {
|
138
|
+
return window.phantomas.domInsertsBacktrace;
|
139
|
+
});
|
140
|
+
|
141
|
+
phantomas.addNotice('DOM inserts (' + domInsertsBacktrace.length + '):');
|
142
|
+
domInsertsBacktrace.forEach(function(item) {
|
143
|
+
phantomas.addNotice('* from ' + item.url + ' @ ' + item.line);
|
144
|
+
});
|
145
|
+
phantomas.addNotice();
|
146
|
+
**/
|
147
|
+
});
|
148
|
+
};
|