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.
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
+ };