autobench 0.0.1alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +16 -0
  3. data/README.md +123 -0
  4. data/bin/autobench +180 -0
  5. data/bin/autobench-config +162 -0
  6. data/lib/autobench.rb +28 -0
  7. data/lib/autobench/client.rb +78 -0
  8. data/lib/autobench/common.rb +49 -0
  9. data/lib/autobench/config.rb +62 -0
  10. data/lib/autobench/render.rb +102 -0
  11. data/lib/autobench/version.rb +3 -0
  12. data/lib/autobench/yslow.rb +75 -0
  13. data/lib/phantomas/README.md +296 -0
  14. data/lib/phantomas/core/formatter.js +65 -0
  15. data/lib/phantomas/core/helper.js +64 -0
  16. data/lib/phantomas/core/modules/requestsMonitor/requestsMonitor.js +214 -0
  17. data/lib/phantomas/core/pads.js +16 -0
  18. data/lib/phantomas/core/phantomas.js +418 -0
  19. data/lib/phantomas/lib/args.js +27 -0
  20. data/lib/phantomas/lib/modules/_coffee-script.js +2 -0
  21. data/lib/phantomas/lib/modules/assert.js +326 -0
  22. data/lib/phantomas/lib/modules/events.js +216 -0
  23. data/lib/phantomas/lib/modules/http.js +55 -0
  24. data/lib/phantomas/lib/modules/path.js +441 -0
  25. data/lib/phantomas/lib/modules/punycode.js +510 -0
  26. data/lib/phantomas/lib/modules/querystring.js +214 -0
  27. data/lib/phantomas/lib/modules/tty.js +7 -0
  28. data/lib/phantomas/lib/modules/url.js +625 -0
  29. data/lib/phantomas/lib/modules/util.js +520 -0
  30. data/lib/phantomas/modules/ajaxRequests/ajaxRequests.js +15 -0
  31. data/lib/phantomas/modules/assetsTypes/assetsTypes.js +21 -0
  32. data/lib/phantomas/modules/cacheHits/cacheHits.js +28 -0
  33. data/lib/phantomas/modules/caching/caching.js +66 -0
  34. data/lib/phantomas/modules/cookies/cookies.js +54 -0
  35. data/lib/phantomas/modules/domComplexity/domComplexity.js +130 -0
  36. data/lib/phantomas/modules/domQueries/domQueries.js +148 -0
  37. data/lib/phantomas/modules/domains/domains.js +49 -0
  38. data/lib/phantomas/modules/globalVariables/globalVariables.js +44 -0
  39. data/lib/phantomas/modules/headers/headers.js +48 -0
  40. data/lib/phantomas/modules/localStorage/localStorage.js +14 -0
  41. data/lib/phantomas/modules/requestsStats/requestsStats.js +71 -0
  42. data/lib/phantomas/modules/staticAssets/staticAssets.js +40 -0
  43. data/lib/phantomas/modules/waterfall/waterfall.js +62 -0
  44. data/lib/phantomas/modules/windowPerformance/windowPerformance.js +36 -0
  45. data/lib/phantomas/package.json +27 -0
  46. data/lib/phantomas/phantomas.js +35 -0
  47. data/lib/phantomas/run-multiple.js +177 -0
  48. data/lib/yslow.js +5 -0
  49. 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
+ };