Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51e4265109 | ||
|
|
eaebb3adfa | ||
|
|
76b4ca6ea5 | ||
|
|
b8560c6e15 | ||
|
|
8eec2549a4 | ||
|
|
aaa1ad7eaa | ||
|
|
74c57fc11c | ||
|
|
b7e25c4e1c | ||
|
|
3f97333423 | ||
|
|
71152f64a5 | ||
|
|
531d125dcc | ||
|
|
299e79e890 | ||
|
|
14f60d8dd3 | ||
|
|
a00b1a96c7 | ||
|
|
2847e02c5d | ||
|
|
57b30a1838 | ||
|
|
f2a637ada7 | ||
|
|
b3804586dc | ||
|
|
113c1f0565 | ||
|
|
3948eeaad4 | ||
|
|
f843184970 | ||
|
|
2aa9edeb8f | ||
|
|
5e37cf8224 | ||
|
|
8bd3a1edde | ||
|
|
c68962e2f2 | ||
|
|
6a9a247a87 | ||
|
|
e238370bc8 | ||
|
|
178f2fd135 | ||
|
|
55cd1160db | ||
|
|
06bdbd929c | ||
|
|
100eb01f27 | ||
|
|
00ade4ad29 | ||
|
|
f3a5779102 | ||
|
|
6f0b97e760 | ||
|
|
28f9ed64ff | ||
|
|
b6924f70d4 | ||
|
|
79ecae34c8 | ||
|
|
f4843baa0e | ||
|
|
cd97a275fe |
+13
-4
@@ -1,9 +1,18 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- "0.12.4"
|
||||
- "5.8"
|
||||
- "0.12"
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
before_install:
|
||||
- "npm install -g npm"
|
||||
- "npm install -g grunt-cli"
|
||||
- "npm install -g npm"
|
||||
- "npm install -g grunt-cli"
|
||||
install:
|
||||
- "npm install"
|
||||
- "npm install"
|
||||
+3
-8
@@ -3,7 +3,7 @@
|
||||
var debug = require('debug')('ylt:cli');
|
||||
var meow = require('meow');
|
||||
var path = require('path');
|
||||
var jstoxml = require('jstoxml');
|
||||
var EasyXml = require('easyxml');
|
||||
|
||||
var ylt = require('../lib/index');
|
||||
|
||||
@@ -15,7 +15,6 @@ var cli = meow({
|
||||
'Options:',
|
||||
' --device Use "phone" or "tablet" to simulate a mobile device (by user-agent and viewport size).',
|
||||
' --screenshot Will take a screenshot and use this value as the output path. It needs to end with ".png".',
|
||||
' --js-deep-analysis When activated, the javascriptExecutionTree will contain sub-requests.',
|
||||
' --wait-for-selector Once the page is loaded, Phantomas will wait until the given CSS selector matches some elements.',
|
||||
' --cookie Adds a cookie on the main domain.',
|
||||
' --auth-user Basic HTTP authentication username.',
|
||||
@@ -51,11 +50,6 @@ if (screenshot) {
|
||||
options.screenshot = cli.flags.screenshot;
|
||||
}
|
||||
|
||||
// Deep JS analysis option
|
||||
if (cli.flags.jsDeepAnalysis === true || cli.flags.jsDeepAnalysis === 'true') {
|
||||
options.jsDeepAnalysis = true;
|
||||
}
|
||||
|
||||
// Device simulation
|
||||
options.device = cli.flags.device || 'desktop';
|
||||
|
||||
@@ -86,7 +80,8 @@ if (cli.flags.reporter && cli.flags.reporter !== 'json' && cli.flags.reporter !=
|
||||
debug('Success');
|
||||
switch(cli.flags.reporter) {
|
||||
case 'xml':
|
||||
console.log(jstoxml.toXML(data, {indent: ' '}));
|
||||
var serializer = new EasyXml();
|
||||
console.log(serializer.render(data));
|
||||
break;
|
||||
default:
|
||||
console.log(JSON.stringify(data, null, 2));
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
font-size: 1em;
|
||||
}
|
||||
.settings input[type=text],
|
||||
.settings input[type=password] {
|
||||
.settings input[type=password],
|
||||
.settings textarea {
|
||||
width: 100%;
|
||||
min-width: 4em;
|
||||
}
|
||||
|
||||
@@ -9,25 +9,26 @@ apiService.factory('API', ['$location', 'Runs', 'Results', function($location, R
|
||||
url: url,
|
||||
waitForResponse: false,
|
||||
screenshot: true,
|
||||
jsTimeline: true,
|
||||
device: settings.device,
|
||||
waitForSelector: settings.waitForSelector,
|
||||
cookie: settings.cookie,
|
||||
authUser: settings.authUser,
|
||||
authPass: settings.authPass
|
||||
authPass: settings.authPass,
|
||||
blockDomain: settings.blockDomain,
|
||||
allowedDomains: settings.allowedDomains,
|
||||
noExternals: settings.noExternals
|
||||
};
|
||||
|
||||
if (settings.waitForSelector && settings.waitForSelector !== '') {
|
||||
runObject.waitForSelector = settings.waitForSelector;
|
||||
}
|
||||
|
||||
if (settings.cookie && settings.cookie !== '') {
|
||||
runObject.cookie = settings.cookie;
|
||||
}
|
||||
|
||||
if (settings.authUser && settings.authUser !== '' && settings.authPass && settings.authPass !== '') {
|
||||
runObject.authUser = settings.authUser;
|
||||
runObject.authPass = settings.authPass;
|
||||
|
||||
if (settings.domainsBlackOrWhite === 'black') {
|
||||
runObject.blockDomain = this.parseDomains(settings.domains);
|
||||
} else if (settings.domainsBlackOrWhite === 'white') {
|
||||
var allowedDomains = this.parseDomains(settings.domains);
|
||||
if (allowedDomains.length > 0) {
|
||||
runObject.allowDomain = allowedDomains;
|
||||
} else {
|
||||
runObject.noExternals = true;
|
||||
}
|
||||
}
|
||||
|
||||
Runs.save(runObject, function(data) {
|
||||
@@ -43,6 +44,17 @@ apiService.factory('API', ['$location', 'Runs', 'Results', function($location, R
|
||||
|
||||
relaunchTest: function(result) {
|
||||
this.launchTest(result.params.url, result.params.options);
|
||||
},
|
||||
|
||||
parseDomains: function(textareaContent) {
|
||||
var lines = textareaContent.split('\n');
|
||||
|
||||
function removeEmptyLines (line) {
|
||||
return line.trim() !== '';
|
||||
}
|
||||
|
||||
// Remove empty lines
|
||||
return lines.filter(removeEmptyLines).join(',');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
input[type=text], input[type=password] {
|
||||
input[type=text], input[type=password], textarea {
|
||||
width: 100%;
|
||||
min-width: 4em;
|
||||
}
|
||||
|
||||
+18
-10
@@ -19,6 +19,24 @@
|
||||
<link rel="stylesheet" type="text/css" href="/node_modules/angular-chart.js/dist/angular-chart.css">
|
||||
<!-- endbuild -->
|
||||
|
||||
<link rel="preconnect" href="//www.google-analytics.com">
|
||||
<link rel="dns-prefetch" href="//www.google-analytics.com">
|
||||
<link rel="preconnect" href="//ghbtns.com">
|
||||
<link rel="dns-prefetch" href="//ghbtns.com">
|
||||
<link rel="preconnect" href="https://api.github.com">
|
||||
<link rel="dns-prefetch" href="https://api.github.com">
|
||||
|
||||
<head>
|
||||
|
||||
<body ng-app="YellowLabTools">
|
||||
<div id="header"><h1>Yellow Lab <span class="icon-lab"></span> Tools</h1></div>
|
||||
<div id="body" ng-view autoscroll="true"></div>
|
||||
<div class="footer">
|
||||
<span class="version">@@version</span>
|
||||
<br><a href="/about">More about Yellow Lab Tools</a><br>
|
||||
<div class="github"><iframe id="ghbtn" frameborder="0" scrolling="0" width="145px" height="30px"></iframe></div>
|
||||
</div>
|
||||
|
||||
<!-- build:js /js/all.js -->
|
||||
<script src="/node_modules/angular/angular.min.js"></script>
|
||||
<script src="/node_modules/chart.js/Chart.min.js"></script>
|
||||
@@ -44,16 +62,6 @@
|
||||
<script src="/js/directives/gradeDirective.js"></script>
|
||||
<script src="/js/directives/offendersDirectives.js"></script>
|
||||
<!-- endbuild -->
|
||||
<head>
|
||||
|
||||
<body ng-app="YellowLabTools">
|
||||
<div id="header"><h1>Yellow Lab <span class="icon-lab"></span> Tools</h1></div>
|
||||
<div id="body" ng-view autoscroll="true"></div>
|
||||
<div class="footer">
|
||||
<span class="version">@@version</span>
|
||||
<br><a href="/about">More about Yellow Lab Tools</a><br>
|
||||
<div class="github"><iframe id="ghbtn" frameborder="0" scrolling="0" width="145px" height="30px"></iframe></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga');if('@@googleAnalyticsId'.indexOf('UA-')===0){ga('create','@@googleAnalyticsId','auto');}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
<div ng-include="'views/resultSubHeader.html'"></div>
|
||||
<div class="summary board">
|
||||
|
||||
<div ng-if="result.blockedRequests">
|
||||
<b><ng-pluralize count="result.blockedRequests.length" when="{'0': 'No blocked request', 'one': '1 blocked request', 'other': '{} blocked requests'}"></ng-pluralize>:</b>
|
||||
<div ng-repeat="request in result.blockedRequests">
|
||||
{{request}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="globalScore" ng-if="globalScore === 0 || globalScore > 0">
|
||||
<div>
|
||||
<h2>Global score</h2>
|
||||
|
||||
@@ -61,15 +61,22 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--<div>
|
||||
<div class="label">Blocked domains</div>
|
||||
<div>
|
||||
<div class="label">
|
||||
Block domains
|
||||
<span class="settingsTooltip">
|
||||
<span class="icon-question"></span>
|
||||
<div><b>Block some domains</b><br><br>One line per domain or subdomain.<br><br><i><b>Example:</b><br>google-analytics.com<br>ads.yahoo.com<br>ajax.googleapis.com</i><br><br>An empty whitelist will block all domains except the main domain.</div>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
Blacklist / Whitelist
|
||||
<input type="radio" name="blackOrWhite" ng-model="settings.domainsBlackOrWhite" value="black" />blacklist
|
||||
<input type="radio" name="blackOrWhite" ng-model="settings.domainsBlackOrWhite" value="white" />whitelist
|
||||
</div>
|
||||
<textarea name=""></textarea>
|
||||
<textarea name="domains" ng-model="settings.domains" rows="5"></textarea>
|
||||
</div>
|
||||
</div>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -96,6 +96,12 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div ng-if="policyName === 'cssRules'">
|
||||
<span ng-if="offender.file === 'inline CSS'">inline CSS</span>
|
||||
<span ng-if="offender.file !== 'inline CSS'"><url-link url="offender.file" max-length="80"></url-link></span>
|
||||
: <ng-pluralize count="offender.rules" when="{'0': '0 rule', 'one':'1 rule','other':'{} rules'}"></ng-pluralize>
|
||||
</div>
|
||||
|
||||
<div ng-if="policyName === 'similarColors'">
|
||||
<div class="similarColors checker"><div ng-style="{'background-color': offender.color1, 'color': offender.isDark ? '#FFF' : '#000'}">{{offender.color1}}</div><div ng-style="{'background-color': offender.color2, 'color': offender.isDark ? '#FFF' : '#000'}">{{offender.color2}}</div></div>
|
||||
</div>
|
||||
@@ -356,7 +362,7 @@
|
||||
<div ng-if="policyName === 'cssBreakpoints'">
|
||||
<div ng-if="rule.value > 0" class="cssBreakpointsGraph">
|
||||
<h3>Breakpoints distribution graph</h3>
|
||||
<canvas class="chart chart-line" chart-data="breakpointsData" chart-labels="breakpointsLabels" chart-options="breakpointsOptions" chart-legend="true" chart-colours="breakpointsColours" width="600" height="250"></canvas>
|
||||
<canvas class="chart chart-line" chart-data="breakpointsData" chart-labels="breakpointsLabels" chart-options="breakpointsOptions" chart-legend="true" chart-colours="breakpointsColours" chart-series="breakpointsSeries" width="600" height="250"></canvas>
|
||||
<h3>Breakpoints list</h3>
|
||||
<div class="offendersTable">
|
||||
<div ng-repeat="offender in rule.offendersObj | orderBy:'pixels'">
|
||||
@@ -369,6 +375,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="policyName === 'http2'">
|
||||
<h3>Protocols advertised by the server</h3>
|
||||
<div class="offendersTable">
|
||||
<div ng-repeat="protocol in rule.offendersObj.list">
|
||||
<div>{{protocol}}</div>
|
||||
</div>
|
||||
<div ng-if="!rule.offendersObj || rule.offendersObj.count == 0">
|
||||
<div>none</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="!rule && rule !== null" class="notFound">
|
||||
<h2>404</h2>
|
||||
Rule "{{policyName}}"" not found
|
||||
|
||||
+81
-15
@@ -267,7 +267,7 @@ var policies = {
|
||||
},
|
||||
"jQueryVersion": {
|
||||
"label": "jQuery version",
|
||||
"message": "<p>Current latest versions of jQuery are 1.11 (with support for old IE versions) and 2.1 (without).</p><p>Each new version of jQuery optimizes performances. Do not keep an old version of jQuery. Updating can sometimes break a few things, but it is generally quite easy to fix them up. So don't hesitate.</p>",
|
||||
"message": "<p>Current latest versions of jQuery are 1.12 (with support for old IE versions) and 2.2 (without).</p><p>Each new version of jQuery optimizes performances. Do not keep an old version of jQuery. Updating can sometimes break a few things, but it is generally quite easy to fix them up. So don't hesitate.</p>",
|
||||
"hasOffenders": false,
|
||||
"scoreFn": function(data) {
|
||||
var differentVersions = data.toolsResults.phantomas.metrics.jQueryVersionsLoaded;
|
||||
@@ -279,27 +279,31 @@ var policies = {
|
||||
var value = data.toolsResults.phantomas.metrics.jQueryVersion;
|
||||
var score;
|
||||
|
||||
if (value.indexOf('1.11.') === 0 ||
|
||||
value.indexOf('1.12.') === 0 ||
|
||||
value.indexOf('2.1.') === 0 ||
|
||||
if (value.indexOf('1.12.') === 0 ||
|
||||
value.indexOf('2.2.') === 0 ||
|
||||
value.indexOf('3.0.') === 0) {
|
||||
value.indexOf('1.13.') === 0 ||
|
||||
value.indexOf('2.3.') === 0 ||
|
||||
value.indexOf('3.0.') === 0 ||
|
||||
value.indexOf('3.1.') === 0) {
|
||||
score = 100;
|
||||
} else if (value.indexOf('1.11.') === 0 ||
|
||||
value.indexOf('2.1.') === 0) {
|
||||
score = 90;
|
||||
} else if (value.indexOf('1.10.') === 0 ||
|
||||
value.indexOf('2.0.') === 0) {
|
||||
score = 90;
|
||||
} else if (value.indexOf('1.9.') === 0) {
|
||||
score = 70;
|
||||
} else if (value.indexOf('1.8.') === 0) {
|
||||
} else if (value.indexOf('1.9.') === 0) {
|
||||
score = 50;
|
||||
} else if (value.indexOf('1.7') === 0) {
|
||||
} else if (value.indexOf('1.8.') === 0) {
|
||||
score = 40;
|
||||
} else if (value.indexOf('1.6') === 0) {
|
||||
} else if (value.indexOf('1.7') === 0) {
|
||||
score = 30;
|
||||
} else if (value.indexOf('1.5') === 0) {
|
||||
} else if (value.indexOf('1.6') === 0) {
|
||||
score = 20;
|
||||
} else if (value.indexOf('1.4') === 0) {
|
||||
} else if (value.indexOf('1.5') === 0) {
|
||||
score = 10;
|
||||
} else if (value.indexOf('1.4') === 0) {
|
||||
score = 0;
|
||||
} else if (value.indexOf('1.3') === 0) {
|
||||
score = 0;
|
||||
} else if (value.indexOf('1.2') === 0) {
|
||||
@@ -420,7 +424,40 @@ var policies = {
|
||||
"isOkThreshold": 750,
|
||||
"isBadThreshold": 3000,
|
||||
"isAbnormalThreshold": 4500,
|
||||
"hasOffenders": false
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
var hasInline = false;
|
||||
var inlineCount = 0;
|
||||
var files = [];
|
||||
|
||||
offenders.forEach(function(line) {
|
||||
if (line.indexOf('[inline CSS]: ') === 0) {
|
||||
hasInline = true;
|
||||
inlineCount += parseInt(line.substr(14));
|
||||
} else {
|
||||
var parts = /^<(.*)>: (\d+)$/.exec(line);
|
||||
|
||||
if (parts) {
|
||||
files.push({
|
||||
file: parts[1],
|
||||
rules: parseInt(parts[2], 10)
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (hasInline) {
|
||||
files.push({
|
||||
file: 'inline CSS',
|
||||
rules: inlineCount
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
count: files.length,
|
||||
list: files
|
||||
};
|
||||
}
|
||||
},
|
||||
"cssComplexSelectors": {
|
||||
"tool": "phantomas",
|
||||
@@ -925,7 +962,7 @@ var policies = {
|
||||
"imageOptimization": {
|
||||
"tool": "weightChecker",
|
||||
"label": "Image optimization",
|
||||
"message": "<p>This metric mesures the number of bytes that could be saved by optimizing images.</p><p>Image optimization is generally one of the easiest way to reduce a page weight, and as a result, the page load time. Don't use Photoshop or other image editing tools, they're not very good for optimization. Use specialized tools such as <a href=\"https://kraken.io/\" target=\"_blank\">Kraken.io</a> or the excellent <a href=\"https://imageoptim.com/\" target=\"_blank\">ImageOptim</a> on Mac. For SVG images, you can use <a href=\"https://jakearchibald.github.io/svgomg/\" target=\"_blank\">SVGOMG</a></p><p>The tools in use in YellowLabTools are not set to their maximum optimization power (JPEG quality 85), so you might be able to compress even more!</p><p>Please note that Yellow Lab Tools' engine (PhantomJS) is not compatible with image srcset (unless you use a polyfill). This can lead to incorrect page weight.</p>",
|
||||
"message": "<p>This metric measures the number of bytes that could be saved by optimizing images.</p><p>Image optimization is generally one of the easiest way to reduce a page weight, and as a result, the page load time. Don't use Photoshop or other image editing tools, they're not very good for optimization. Use specialized tools such as <a href=\"https://kraken.io/\" target=\"_blank\">Kraken.io</a> or the excellent <a href=\"https://imageoptim.com/\" target=\"_blank\">ImageOptim</a> on Mac. For SVG images, you can use <a href=\"https://jakearchibald.github.io/svgomg/\" target=\"_blank\">SVGOMG</a></p><p>The tools in use in YellowLabTools are not set to their maximum optimization power (JPEG quality 85), so you might be able to compress even more!</p><p>Please note that Yellow Lab Tools' engine (PhantomJS) is not compatible with image srcset (unless you use a polyfill). This can lead to incorrect page weight.</p>",
|
||||
"isOkThreshold": 10240,
|
||||
"isBadThreshold": 122880,
|
||||
"isAbnormalThreshold": 307200,
|
||||
@@ -935,7 +972,7 @@ var policies = {
|
||||
"gzipCompression": {
|
||||
"tool": "weightChecker",
|
||||
"label": "Gzip compression",
|
||||
"message": "<p>Mesures the number of bytes that could be saved by compressing file transfers.</p><p>Gzip is a powerfull weight reducer and should be enabled on text-based assets in your server's configuration. Note that gzipping small files (< 1 KB) is arguable, and that some assets such as images should not be gzipped as they are already compressed. <a href=\"https://gist.github.com/gmetais/971ce13a1fbeebd88445\" target=\"_blank\">Here</a> is a list of Content-Types that should be gzipped.</p>",
|
||||
"message": "<p>Measures the number of bytes that could be saved by compressing file transfers.</p><p>Gzip is a powerfull weight reducer and should be enabled on text-based assets in your server's configuration. Note that gzipping small files (< 1 KB) is arguable, and that some assets such as images should not be gzipped as they are already compressed. <a href=\"https://gist.github.com/gmetais/971ce13a1fbeebd88445\" target=\"_blank\">Here</a> is a list of Content-Types that should be gzipped.</p>",
|
||||
"isOkThreshold": 5125,
|
||||
"isBadThreshold": 81920,
|
||||
"isAbnormalThreshold": 153600,
|
||||
@@ -1044,6 +1081,35 @@ var policies = {
|
||||
"isAbnormalThreshold": 30,
|
||||
"hasOffenders": true
|
||||
},
|
||||
"http2": {
|
||||
"label": "HTTP/2 or SPDY",
|
||||
"message": "<p>HTTP/2 is the latest version of the HTTP protocol and is designed to optimize load speed. SPDY is deprecated but still very well supported.</p><p>The latest versions of all major browsers are now compatible. The difficulty is on the server side, where technologies are not quite ready yet.</p>",
|
||||
"hasOffenders": true,
|
||||
"scoreFn": function(data) {
|
||||
if (!data.toolsResults.http2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var isHttp2 = data.toolsResults.http2.metrics.http2;
|
||||
|
||||
var result = {
|
||||
value: isHttp2 ? 'Yes' : 'No',
|
||||
score: isHttp2 ? 100 : 0,
|
||||
bad: !isHttp2,
|
||||
abnormal: false,
|
||||
abnormalityScore: 0
|
||||
};
|
||||
|
||||
if (data.toolsResults.http2.offenders) {
|
||||
result.offendersObj = {
|
||||
count: data.toolsResults.http2.offenders.http2.length,
|
||||
list: data.toolsResults.http2.offenders.http2
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
"cachingDisabled": {
|
||||
"tool": "phantomas",
|
||||
"label": "Caching disabled",
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"serverConfig": {
|
||||
"label": "Server config",
|
||||
"policies": {
|
||||
"http2": 2,
|
||||
"closedConnections": 2,
|
||||
"cachingNotSpecified": 1,
|
||||
"cachingDisabled": 1,
|
||||
|
||||
+12
-1
@@ -5,6 +5,7 @@ var phantomasWrapper = require('./tools/phantomas/phantomasWrapper');
|
||||
var jsExecutionTransformer = require('./tools/jsExecutionTransformer');
|
||||
var colorDiff = require('./tools/colorDiff');
|
||||
var mediaQueriesChecker = require('./tools/mediaQueriesChecker');
|
||||
var isHttp2 = require('./tools/isHttp2');
|
||||
var weightChecker = require('./tools/weightChecker/weightChecker');
|
||||
var rulesChecker = require('./rulesChecker');
|
||||
var scoreCalculator = require('./scoreCalculator');
|
||||
@@ -41,6 +42,11 @@ var Runner = function(params) {
|
||||
|
||||
})
|
||||
|
||||
.then(function(data) {
|
||||
// Check if HTTP2
|
||||
return isHttp2.check(data);
|
||||
})
|
||||
|
||||
.then(function(data) {
|
||||
|
||||
// Rules checker
|
||||
@@ -60,9 +66,14 @@ var Runner = function(params) {
|
||||
delete data.toolsResults.phantomas.metrics.scrollExecutionTree;
|
||||
delete data.toolsResults.phantomas.offenders.scrollExecutionTree;
|
||||
|
||||
|
||||
if (data.toolsResults.phantomas.offenders.blockedRequests) {
|
||||
data.blockedRequests = data.toolsResults.phantomas.offenders.blockedRequests;
|
||||
}
|
||||
|
||||
|
||||
// Finished!
|
||||
deferred.resolve(data);
|
||||
|
||||
})
|
||||
|
||||
.fail(function(err) {
|
||||
|
||||
@@ -34,12 +34,14 @@ var ApiController = function(app) {
|
||||
waitForResponse: req.body.waitForResponse !== false && req.body.waitForResponse !== 'false' && req.body.waitForResponse !== 0,
|
||||
partialResult: req.body.partialResult || null,
|
||||
screenshot: req.body.screenshot || false,
|
||||
jsTimeline: req.body.jsTimeline || false,
|
||||
device: req.body.device || 'desktop',
|
||||
waitForSelector: req.body.waitForSelector || null,
|
||||
cookie: req.body.cookie || null,
|
||||
authUser: req.body.authUser || null,
|
||||
authPass: req.body.authPass || null
|
||||
authPass: req.body.authPass || null,
|
||||
blockDomain: req.body.blockDomain || null,
|
||||
allowDomain: req.body.allowDomain || null,
|
||||
noExternals: req.body.noExternals || false
|
||||
}
|
||||
};
|
||||
|
||||
@@ -71,12 +73,14 @@ var ApiController = function(app) {
|
||||
|
||||
var runOptions = {
|
||||
screenshot: run.params.screenshot ? screenshot.getTmpFilePath() : false,
|
||||
jsDeepAnalysis: run.params.jsTimeline,
|
||||
device: run.params.device,
|
||||
waitForSelector: run.params.waitForSelector,
|
||||
cookie: run.params.cookie,
|
||||
authUser: run.params.authUser,
|
||||
authPass: run.params.authPass,
|
||||
blockDomain: run.params.blockDomain,
|
||||
allowDomain: run.params.allowDomain,
|
||||
noExternals: run.params.noExternals,
|
||||
phantomasEngine: serverSettings.phantomasEngine
|
||||
};
|
||||
|
||||
@@ -96,7 +100,7 @@ var ApiController = function(app) {
|
||||
if (run.params.screenshot) {
|
||||
|
||||
// Replace the empty promise created earlier with Q.resolve()
|
||||
screenshotPromise = screenshot.toThumbnail(400)
|
||||
screenshotPromise = screenshot.toThumbnail(serverSettings.screenshotWidth || 400)
|
||||
|
||||
// Read screenshot
|
||||
.then(function(screenshotBuffer) {
|
||||
@@ -129,13 +133,7 @@ var ApiController = function(app) {
|
||||
// Remove uneeded temp screenshot path
|
||||
delete data.params.options.screenshot;
|
||||
|
||||
// Empty javascriptExecutionTree if not needed
|
||||
if (!run.params.jsTimeline) {
|
||||
data.javascriptExecutionTree = {};
|
||||
data.scrollExecutionTree = {};
|
||||
}
|
||||
|
||||
// Remove tools results if not needed
|
||||
// Here we can remove tools results if not needed
|
||||
|
||||
return resultsDatastore.saveResult(data);
|
||||
})
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
var debug = require('debug')('ylt:isHttp2');
|
||||
var url = require('url');
|
||||
var Q = require('q');
|
||||
var http2 = require('is-http2');
|
||||
|
||||
var isHttp2 = function() {
|
||||
'use strict';
|
||||
|
||||
this.check = function(data) {
|
||||
debug('Starting to check for HTTP2 support...');
|
||||
|
||||
return this.checkHttp2(data)
|
||||
|
||||
.then(function(result) {
|
||||
|
||||
if (result.isHttp) {
|
||||
debug('The website is not even in HTTPS');
|
||||
|
||||
data.toolsResults.http2 = {
|
||||
metrics: {
|
||||
http2: false
|
||||
}
|
||||
};
|
||||
|
||||
} else if (result.isHttp2) {
|
||||
debug('HTTP/2 (or SPDY) is supported');
|
||||
|
||||
data.toolsResults.http2 = {
|
||||
metrics: {
|
||||
http2: true
|
||||
}
|
||||
};
|
||||
|
||||
} else {
|
||||
debug('HTTP/2 is not supported');
|
||||
|
||||
data.toolsResults.http2 = {
|
||||
metrics: {
|
||||
http2: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Add the supported protocols as offenders
|
||||
if (result.supportedProtocols) {
|
||||
debug('Supported protocols: ' + result.supportedProtocols.join(' '));
|
||||
data.toolsResults.http2.offenders = {
|
||||
http2: result.supportedProtocols
|
||||
};
|
||||
}
|
||||
|
||||
debug('End of HTTP2 support check');
|
||||
|
||||
return data;
|
||||
})
|
||||
|
||||
.fail(function() {
|
||||
return data;
|
||||
});
|
||||
};
|
||||
|
||||
this.getParsedUrl = function(data) {
|
||||
return url.parse(data.toolsResults.phantomas.url);
|
||||
};
|
||||
|
||||
this.getProtocol = function(data) {
|
||||
return this.getParsedUrl(data).protocol;
|
||||
};
|
||||
|
||||
this.getDomain = function(data) {
|
||||
return this.getParsedUrl(data).hostname;
|
||||
};
|
||||
|
||||
this.checkHttp2 = function(data) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
// Check if it's HTTPS first
|
||||
if (this.getProtocol(data) === 'http:') {
|
||||
|
||||
deferred.resolve({
|
||||
isHttp: true
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
// To make is-http2 work, you need to have openssl in a version greater than 1.0.0 installed and available in your $path.
|
||||
http2(this.getDomain(data), {includeSpdy: true})
|
||||
|
||||
.then(function(result) {
|
||||
deferred.resolve(result);
|
||||
})
|
||||
|
||||
.catch(function(error) {
|
||||
debug('Error while checking HTTP2 support:');
|
||||
debug(error);
|
||||
deferred.reject('Error while checking for HTTP2 support');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = new isHttp2();
|
||||
@@ -18,8 +18,8 @@ exports.module = function(phantomas) {
|
||||
responseEndTime = Date.now();
|
||||
});
|
||||
|
||||
phantomas.once('init', function() {
|
||||
phantomas.evaluate(function(responseEndTime, deepAnalysis) {
|
||||
phantomas.on('init', function() {
|
||||
phantomas.evaluate(function(responseEndTime) {
|
||||
(function(phantomas) {
|
||||
|
||||
// Overwritting phantomas spy function
|
||||
@@ -106,48 +106,85 @@ exports.module = function(phantomas) {
|
||||
var currentContext = root;
|
||||
var depth = 0;
|
||||
|
||||
if (deepAnalysis) {
|
||||
phantomas.log('Entering deep Javascript analysis mode');
|
||||
}
|
||||
|
||||
// Add a child but don't enter his context
|
||||
// Add a child but don't enter its context
|
||||
function pushContext(data) {
|
||||
if (depth === 0 || deepAnalysis) {
|
||||
|
||||
// Some data is not needed on subchildren
|
||||
if (depth === 0) {
|
||||
data.timestamp = Date.now() - responseEndTime;
|
||||
data.loadingStep = phantomas.currentStep || '';
|
||||
currentContext.addChild(data);
|
||||
}
|
||||
|
||||
// Some data is not needed on subchildren
|
||||
if (depth > 0) {
|
||||
if (data.backtrace) {
|
||||
delete data.backtrace;
|
||||
}
|
||||
if (data.resultsNumber) {
|
||||
delete data.resultsNumber;
|
||||
}
|
||||
}
|
||||
|
||||
currentContext.addChild(data);
|
||||
}
|
||||
|
||||
// Add a child to the current context and enter his context
|
||||
// Add a child to the current context and enter its context
|
||||
function enterContext(data) {
|
||||
if (depth === 0 || deepAnalysis) {
|
||||
|
||||
// Some data is not needed on subchildren
|
||||
if (depth === 0) {
|
||||
data.timestamp = Date.now() - responseEndTime;
|
||||
data.loadingStep = phantomas.currentStep || '';
|
||||
currentContext = currentContext.addChild(data);
|
||||
}
|
||||
|
||||
// Some data is not needed on subchildren
|
||||
if (depth > 0) {
|
||||
if (data.backtrace) {
|
||||
delete data.backtrace;
|
||||
}
|
||||
if (data.resultsNumber) {
|
||||
delete data.resultsNumber;
|
||||
}
|
||||
}
|
||||
|
||||
currentContext = currentContext.addChild(data);
|
||||
|
||||
depth ++;
|
||||
}
|
||||
|
||||
// Save given data in the current context and jump change current context to its parent
|
||||
function leaveContext(moreData) {
|
||||
if (depth === 1 || deepAnalysis) {
|
||||
|
||||
// Some data is not needed on subchildren
|
||||
if (depth === 1) {
|
||||
currentContext.data.time = Date.now() - currentContext.data.timestamp - responseEndTime;
|
||||
}
|
||||
|
||||
// Merge previous data with moreData (ovewrites if exists)
|
||||
if (moreData) {
|
||||
for (var key in moreData) {
|
||||
currentContext.data[key] = moreData[key];
|
||||
}
|
||||
// Some data is not needed on subchildren
|
||||
if (depth > 1) {
|
||||
if (moreData && moreData.backtrace) {
|
||||
delete moreData.backtrace;
|
||||
}
|
||||
|
||||
var parent = currentContext.parent;
|
||||
if (parent === null) {
|
||||
console.error('Error: trying to close root context in ContextTree');
|
||||
} else {
|
||||
currentContext = parent;
|
||||
if (moreData && moreData.resultsNumber) {
|
||||
delete moreData.resultsNumber;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge previous data with moreData (ovewrites if exists)
|
||||
if (moreData) {
|
||||
for (var key in moreData) {
|
||||
currentContext.data[key] = moreData[key];
|
||||
}
|
||||
}
|
||||
|
||||
var parent = currentContext.parent;
|
||||
if (parent === null) {
|
||||
console.error('Error: trying to close root context in ContextTree');
|
||||
} else {
|
||||
currentContext = parent;
|
||||
}
|
||||
|
||||
depth --;
|
||||
}
|
||||
|
||||
@@ -161,7 +198,6 @@ exports.module = function(phantomas) {
|
||||
if (root !== currentContext) {
|
||||
return null;
|
||||
}
|
||||
var current = currentContext;
|
||||
|
||||
function recusiveRead(node) {
|
||||
if (node.children.length === 0) {
|
||||
@@ -209,6 +245,6 @@ exports.module = function(phantomas) {
|
||||
})();
|
||||
|
||||
})(window.__phantomas);
|
||||
}, responseEndTime, phantomas.getParam('js-deep-analysis'));
|
||||
}, responseEndTime);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ exports.module = function(phantomas) {
|
||||
phantomas.setMetric('DOMqueriesAvoidable'); // @desc number of repeated uses of a duplicated query
|
||||
|
||||
// fake native DOM functions
|
||||
phantomas.once('init', function() {
|
||||
phantomas.on('init', function() {
|
||||
phantomas.evaluate(function() {
|
||||
(function(phantomas) {
|
||||
function querySpy(type, query, fnName, context, hasNoResults) {
|
||||
|
||||
@@ -12,7 +12,7 @@ exports.module = function(phantomas) {
|
||||
phantomas.setMetric('eventsDispatched'); // @desc number of EventTarget.dispatchEvent calls
|
||||
phantomas.setMetric('eventsScrollBound'); // @desc number of scroll event bounds
|
||||
|
||||
phantomas.once('init', function() {
|
||||
phantomas.on('init', function() {
|
||||
phantomas.evaluate(function() {
|
||||
(function(phantomas) {
|
||||
// spy calls to EventTarget.addEventListener
|
||||
|
||||
@@ -138,10 +138,9 @@ exports.module = function(phantomas) {
|
||||
jQueryFunctions = jQueryFunctions.concat(jQueryTraversalFunctions);
|
||||
|
||||
// spy calls to jQuery functions
|
||||
phantomas.once('init', function() {
|
||||
phantomas.on('init', function() {
|
||||
phantomas.evaluate(function(jQueryFunctions, jQueryTraversalFunctions) {
|
||||
(function(phantomas) {
|
||||
var jQuery;
|
||||
var oldJQuery;
|
||||
|
||||
phantomas.spyGlobalVar('jQuery', function(jQuery) {
|
||||
@@ -371,14 +370,6 @@ exports.module = function(phantomas) {
|
||||
});
|
||||
|
||||
|
||||
// store the last resource that was received
|
||||
// try to report where given jQuery version was loaded from
|
||||
phantomas.on('recv', function(entry) {
|
||||
if (entry.isJS) {
|
||||
lastUrl = entry.url;
|
||||
}
|
||||
});
|
||||
|
||||
phantomas.on('jQueryLoaded', function(version) {
|
||||
phantomas.log('jQuery: loaded v' + version);
|
||||
phantomas.setMetric('jQueryVersion', version);
|
||||
@@ -386,7 +377,5 @@ exports.module = function(phantomas) {
|
||||
// report multiple jQuery "instances" (issue #435)
|
||||
phantomas.incrMetric('jQueryVersionsLoaded');
|
||||
phantomas.addOffender('jQueryVersionsLoaded', 'v%s', version);
|
||||
|
||||
phantomas.log('jQuery: v%s (probably loaded from <%s>)', version, lastUrl);
|
||||
});
|
||||
};
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ exports.module = function(phantomas) {
|
||||
phantomas.log('javaScriptBottlenecks: to spy calls to eval() run phantomas with --spy-eval option');
|
||||
}
|
||||
|
||||
phantomas.once('init', function() {
|
||||
phantomas.on('init', function() {
|
||||
phantomas.evaluate(function(spyEval) {
|
||||
(function(phantomas) {
|
||||
function report(msg, caller, backtrace, metric) {
|
||||
|
||||
@@ -22,7 +22,6 @@ var PhantomasWrapper = function() {
|
||||
// Cusomizable options
|
||||
'engine': task.options.phantomasEngine || 'webkit',
|
||||
'timeout': task.options.timeout || 30,
|
||||
'js-deep-analysis': task.options.jsDeepAnalysis || false,
|
||||
'user-agent': (task.options.device === 'desktop') ? 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) YLT Chrome/27.0.1453.110 Safari/537.36' : null,
|
||||
'tablet': (task.options.device === 'tablet'),
|
||||
'phone': (task.options.device === 'phone'),
|
||||
@@ -31,12 +30,14 @@ var PhantomasWrapper = function() {
|
||||
'cookie': task.options.cookie,
|
||||
'auth-user': task.options.authUser,
|
||||
'auth-pass': task.options.authPass,
|
||||
'block-domain': task.options.blockDomain,
|
||||
'allow-domain': task.options.allowDomain,
|
||||
'no-externals': task.options.noExternals,
|
||||
|
||||
// Mandatory
|
||||
'reporter': 'json:pretty',
|
||||
'analyze-css': true,
|
||||
'skip-modules': [
|
||||
'blockDomains', // not needed
|
||||
'domHiddenContent', // overriden
|
||||
'domMutations', // not compatible with webkit
|
||||
'domQueries', // overriden
|
||||
@@ -118,11 +119,6 @@ var PhantomasWrapper = function() {
|
||||
async.retry(triesNumber, function(cb) {
|
||||
|
||||
currentTry ++;
|
||||
// Fix for https://github.com/gmetais/YellowLabTools/issues/114
|
||||
if (currentTry === 2 && options.engine === 'webkit2') {
|
||||
debug('Launching a second try with the old webkit v1 engine');
|
||||
options.engine = 'webkit';
|
||||
}
|
||||
|
||||
var process = phantomas(task.url, options, function(err, json, results) {
|
||||
var errorCode = err ? parseInt(err.message, 10) : null;
|
||||
@@ -155,6 +151,11 @@ var PhantomasWrapper = function() {
|
||||
}
|
||||
|
||||
cb(errorCode, json);
|
||||
|
||||
}).fail(function() {
|
||||
// This function is useless, but the failing promise needs to be handled,
|
||||
// otherwise the module meow writes in the console in case of a timeout (error code 252).
|
||||
debug('Failing promise handled');
|
||||
});
|
||||
|
||||
phantomasPid = process.pid;
|
||||
|
||||
@@ -408,113 +408,121 @@ var WeightChecker = function() {
|
||||
|
||||
var statusCode;
|
||||
|
||||
request(requestOptions)
|
||||
try {
|
||||
|
||||
.on('response', function(res) {
|
||||
|
||||
// Raw headers were added in NodeJS v0.12
|
||||
// (https://github.com/joyent/node/issues/4844), but let's
|
||||
// reconstruct them for backwards compatibility.
|
||||
var rawHeaders = ('HTTP/' + res.httpVersion + ' ' + res.statusCode +
|
||||
' ' + http.STATUS_CODES[res.statusCode] + '\r\n');
|
||||
Object.keys(res.headers).forEach(function(headerKey) {
|
||||
rawHeaders += headerKey + ': ' + res.headers[headerKey] + '\r\n';
|
||||
});
|
||||
rawHeaders += '\r\n';
|
||||
request(requestOptions)
|
||||
|
||||
var uncompressedSize = 0; // size after uncompression
|
||||
var bodySize = 0; // bytes size over the wire
|
||||
var body = ''; // plain text body (after uncompressing gzip/deflate)
|
||||
var isCompressed = false;
|
||||
.on('response', function(res) {
|
||||
|
||||
// Raw headers were added in NodeJS v0.12
|
||||
// (https://github.com/joyent/node/issues/4844), but let's
|
||||
// reconstruct them for backwards compatibility.
|
||||
var rawHeaders = ('HTTP/' + res.httpVersion + ' ' + res.statusCode +
|
||||
' ' + http.STATUS_CODES[res.statusCode] + '\r\n');
|
||||
Object.keys(res.headers).forEach(function(headerKey) {
|
||||
rawHeaders += headerKey + ': ' + res.headers[headerKey] + '\r\n';
|
||||
});
|
||||
rawHeaders += '\r\n';
|
||||
|
||||
function tally() {
|
||||
var uncompressedSize = 0; // size after uncompression
|
||||
var bodySize = 0; // bytes size over the wire
|
||||
var body = ''; // plain text body (after uncompressing gzip/deflate)
|
||||
var isCompressed = false;
|
||||
|
||||
if (statusCode !== 200) {
|
||||
callback({code: statusCode});
|
||||
return;
|
||||
}
|
||||
function tally() {
|
||||
|
||||
var result = {
|
||||
body: body,
|
||||
headersSize: Buffer.byteLength(rawHeaders, 'utf8'),
|
||||
bodySize: bodySize,
|
||||
isCompressed: isCompressed,
|
||||
uncompressedSize: uncompressedSize
|
||||
};
|
||||
|
||||
callback(null, result);
|
||||
}
|
||||
|
||||
switch (res.headers['content-encoding']) {
|
||||
case 'gzip':
|
||||
|
||||
var gzip = zlib.createGunzip();
|
||||
|
||||
gzip.on('data', function (data) {
|
||||
body += data;
|
||||
uncompressedSize += data.length;
|
||||
}).on('end', function () {
|
||||
isCompressed = true;
|
||||
tally();
|
||||
}).on('error', function(err) {
|
||||
debug('Error while decoding %s', requestOptions.url);
|
||||
debug(err);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
res.on('data', function (data) {
|
||||
bodySize += data.length;
|
||||
}).pipe(gzip);
|
||||
|
||||
break;
|
||||
case 'deflate':
|
||||
res.setEncoding('utf8');
|
||||
|
||||
var deflate = zlib.createInflate();
|
||||
|
||||
deflate.on('data', function (data) {
|
||||
body += data;
|
||||
uncompressedSize += data.length;
|
||||
}).on('end', function () {
|
||||
isCompressed = true;
|
||||
tally();
|
||||
}).on('error', function(err) {
|
||||
debug('Error while decoding %s', requestOptions.url);
|
||||
debug(err);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
res.on('data', function (data) {
|
||||
bodySize += data.length;
|
||||
}).pipe(deflate);
|
||||
|
||||
break;
|
||||
default:
|
||||
if (contentType === 'image/jpeg' || contentType === 'image/png') {
|
||||
res.setEncoding('binary');
|
||||
if (statusCode !== 200) {
|
||||
callback({code: statusCode});
|
||||
return;
|
||||
}
|
||||
|
||||
res.on('data', function (data) {
|
||||
body += data;
|
||||
uncompressedSize += data.length;
|
||||
bodySize += data.length;
|
||||
}).on('end', function () {
|
||||
tally();
|
||||
});
|
||||
var result = {
|
||||
body: body,
|
||||
headersSize: Buffer.byteLength(rawHeaders, 'utf8'),
|
||||
bodySize: bodySize,
|
||||
isCompressed: isCompressed,
|
||||
uncompressedSize: uncompressedSize
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
})
|
||||
callback(null, result);
|
||||
}
|
||||
|
||||
.on('response', function(response) {
|
||||
statusCode = response.statusCode;
|
||||
})
|
||||
switch (res.headers['content-encoding']) {
|
||||
case 'gzip':
|
||||
|
||||
.on('error', function(err) {
|
||||
var gzip = zlib.createGunzip();
|
||||
|
||||
gzip.on('data', function (data) {
|
||||
body += data;
|
||||
uncompressedSize += data.length;
|
||||
}).on('end', function () {
|
||||
isCompressed = true;
|
||||
tally();
|
||||
}).on('error', function(err) {
|
||||
debug('Error while decoding %s', requestOptions.url);
|
||||
debug(err);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
res.on('data', function (data) {
|
||||
bodySize += data.length;
|
||||
}).pipe(gzip);
|
||||
|
||||
break;
|
||||
case 'deflate':
|
||||
res.setEncoding('utf8');
|
||||
|
||||
var deflate = zlib.createInflate();
|
||||
|
||||
deflate.on('data', function (data) {
|
||||
body += data;
|
||||
uncompressedSize += data.length;
|
||||
}).on('end', function () {
|
||||
isCompressed = true;
|
||||
tally();
|
||||
}).on('error', function(err) {
|
||||
debug('Error while decoding %s', requestOptions.url);
|
||||
debug(err);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
res.on('data', function (data) {
|
||||
bodySize += data.length;
|
||||
}).pipe(deflate);
|
||||
|
||||
break;
|
||||
default:
|
||||
if (contentType === 'image/jpeg' || contentType === 'image/png') {
|
||||
res.setEncoding('binary');
|
||||
}
|
||||
|
||||
res.on('data', function (data) {
|
||||
body += data;
|
||||
uncompressedSize += data.length;
|
||||
bodySize += data.length;
|
||||
}).on('end', function () {
|
||||
tally();
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
||||
.on('response', function(response) {
|
||||
statusCode = response.statusCode;
|
||||
})
|
||||
|
||||
.on('error', function(err) {
|
||||
debug('Error while downloading %s', requestOptions.url);
|
||||
debug(err);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
} catch(err) {
|
||||
debug('Error while downloading %s', requestOptions.url);
|
||||
debug(err);
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
+35
-34
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yellowlabtools",
|
||||
"version": "1.9.1",
|
||||
"version": "1.10.0",
|
||||
"description": "Online tool to audit a webpage for performance and front-end quality issues",
|
||||
"license": "GPL-2.0",
|
||||
"author": {
|
||||
@@ -20,51 +20,52 @@
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"dependencies": {
|
||||
"angular": "1.4.8",
|
||||
"angular-animate": "1.4.8",
|
||||
"angular-chart.js": "0.8.6",
|
||||
"angular-local-storage": "0.2.2",
|
||||
"angular-resource": "1.4.8",
|
||||
"angular-route": "1.4.8",
|
||||
"angular-sanitize": "1.4.8",
|
||||
"async": "1.5.0",
|
||||
"body-parser": "1.14.1",
|
||||
"angular": "1.5.2",
|
||||
"angular-animate": "1.5.2",
|
||||
"angular-chart.js": "0.9.0",
|
||||
"angular-local-storage": "0.2.7",
|
||||
"angular-resource": "1.5.2",
|
||||
"angular-route": "1.5.2",
|
||||
"angular-sanitize": "1.5.2",
|
||||
"async": "1.5.2",
|
||||
"body-parser": "1.15.0",
|
||||
"chart.js": "1.0.2",
|
||||
"clean-css": "3.4.8",
|
||||
"color-diff": "0.1.7",
|
||||
"compression": "1.6.0",
|
||||
"clean-css": "3.4.10",
|
||||
"color-diff": "1.0.0",
|
||||
"compression": "1.6.1",
|
||||
"cors": "2.7.1",
|
||||
"css-mq-parser": "0.0.3",
|
||||
"debug": "2.2.0",
|
||||
"express": "4.13.3",
|
||||
"easyxml": "2.0.1",
|
||||
"express": "4.13.4",
|
||||
"imagemin": "4.0.0",
|
||||
"imagemin-jpegoptim": "4.1.0",
|
||||
"jstoxml": "0.2.3",
|
||||
"is-http2": "1.0.4",
|
||||
"lwip": "0.0.8",
|
||||
"meow": "3.6.0",
|
||||
"minimize": "1.7.4",
|
||||
"meow": "3.7.0",
|
||||
"minimize": "1.8.1",
|
||||
"parse-color": "1.0.0",
|
||||
"phantomas": "1.13.0",
|
||||
"phantomas": "1.15.1",
|
||||
"ps-node": "0.0.5",
|
||||
"q": "1.4.1",
|
||||
"request": "2.67.0",
|
||||
"rimraf": "2.4.4",
|
||||
"request": "2.69.0",
|
||||
"rimraf": "2.5.2",
|
||||
"temporary": "0.0.8",
|
||||
"try-thread-sleep": "1.0.0",
|
||||
"uglify-js": "2.6.1"
|
||||
"uglify-js": "2.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "~3.4.0",
|
||||
"chai": "~3.5.0",
|
||||
"grunt": "~0.4.5",
|
||||
"grunt-blanket": "~0.0.10",
|
||||
"grunt-contrib-clean": "~0.7.0",
|
||||
"grunt-contrib-concat": "~0.5.1",
|
||||
"grunt-contrib-copy": "~0.8.2",
|
||||
"grunt-contrib-cssmin": "~0.14.0",
|
||||
"grunt-contrib-htmlmin": "~0.6.0",
|
||||
"grunt-contrib-jshint": "~0.11.3",
|
||||
"grunt-contrib-less": "~1.1.0",
|
||||
"grunt-contrib-uglify": "~0.11.0",
|
||||
"grunt-contrib-clean": "~1.0.0",
|
||||
"grunt-contrib-concat": "~1.0.0",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-cssmin": "~1.0.1",
|
||||
"grunt-contrib-htmlmin": "~1.1.0",
|
||||
"grunt-contrib-jshint": "~1.0.0",
|
||||
"grunt-contrib-less": "~1.2.0",
|
||||
"grunt-contrib-uglify": "~1.0.1",
|
||||
"grunt-env": "~0.4.4",
|
||||
"grunt-express": "~1.4.1",
|
||||
"grunt-filerev": "~2.3.1",
|
||||
@@ -73,10 +74,10 @@
|
||||
"grunt-mocha-test": "~0.12.7",
|
||||
"grunt-replace": "~0.11.0",
|
||||
"grunt-usemin": "~3.1.1",
|
||||
"grunt-webfont": "~1.0.2",
|
||||
"matchdep": "~1.0.0",
|
||||
"mocha": "~2.3.2",
|
||||
"sinon": "~1.17.2",
|
||||
"grunt-webfont": "~1.2.0",
|
||||
"matchdep": "~1.0.1",
|
||||
"mocha": "~2.4.5",
|
||||
"sinon": "~1.17.3",
|
||||
"sinon-chai": "~2.8.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
var server = require('http').createServer(app);
|
||||
|
||||
var settings = require('./settings.json');
|
||||
|
||||
app.all('*', function(req, res) {
|
||||
res.status(500).send('YellowLabTools is in maintenance. It should come back soon with a new version!');
|
||||
});
|
||||
|
||||
server.listen(settings.serverPort, function() {
|
||||
console.log('Maintenance mode started on port %d', server.address().port);
|
||||
});
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
cd /space/YellowLabTools
|
||||
|
||||
# Stop the server
|
||||
# Stop the server and start the maintenance mode
|
||||
forever stopall
|
||||
forever start server_config/maintenance.js
|
||||
|
||||
# Keep the settings.json file
|
||||
git stash
|
||||
@@ -18,5 +19,6 @@ npm install || exit 1
|
||||
rm -rf front/build
|
||||
grunt build
|
||||
|
||||
# Restart the server
|
||||
# Stop the maintenance mode and restart the server
|
||||
forever stopall
|
||||
NODE_ENV=production forever start -c "node --stack-size=262000" bin/server.js
|
||||
@@ -2,6 +2,7 @@
|
||||
"serverPort": 80,
|
||||
"phantomasEngine": "webkit",
|
||||
"googleAnalyticsId": "",
|
||||
"screenshotWidth": 400,
|
||||
|
||||
"authorizedKeys": {
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"serverPort": 8383,
|
||||
"phantomasEngine": "webkit",
|
||||
"googleAnalyticsId": "",
|
||||
"screenshotWidth": 400,
|
||||
|
||||
"authorizedKeys": {
|
||||
|
||||
|
||||
+10
-4
@@ -31,6 +31,7 @@ describe('api', function() {
|
||||
},
|
||||
json: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Api-Key': 'invalid'
|
||||
}
|
||||
}, function(error, response, body) {
|
||||
@@ -53,6 +54,7 @@ describe('api', function() {
|
||||
},
|
||||
json: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
||||
}
|
||||
}, function(error, response, body) {
|
||||
@@ -76,6 +78,7 @@ describe('api', function() {
|
||||
},
|
||||
json: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
||||
}
|
||||
}, function(error, response, body) {
|
||||
@@ -99,12 +102,13 @@ describe('api', function() {
|
||||
screenshot: true,
|
||||
device: 'tablet',
|
||||
//waitForSelector: '*',
|
||||
cookie: 'foo=bar',
|
||||
cookie: 'foo=bar;domain=google.com',
|
||||
authUser: 'joe',
|
||||
authPass: 'secret'
|
||||
},
|
||||
json: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
||||
}
|
||||
}, function(error, response, body) {
|
||||
@@ -133,6 +137,7 @@ describe('api', function() {
|
||||
},
|
||||
json: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
||||
}
|
||||
}, function(error, response, body) {
|
||||
@@ -165,14 +170,13 @@ describe('api', function() {
|
||||
body.should.have.a.property('rules').that.is.an('object');
|
||||
body.should.have.a.property('toolsResults').that.is.an('object');
|
||||
|
||||
// javascriptExecutionTree should only be filled if option jsTimeline is true
|
||||
body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
|
||||
body.javascriptExecutionTree.should.deep.equal({});
|
||||
body.javascriptExecutionTree.should.not.deep.equal({});
|
||||
|
||||
// Check if settings are correctly sent and retrieved
|
||||
body.params.options.should.have.a.property('device').that.equals('tablet');
|
||||
//body.params.options.should.have.a.property('waitForSelector').that.equals('*');
|
||||
body.params.options.should.have.a.property('cookie').that.equals('foo=bar');
|
||||
body.params.options.should.have.a.property('cookie').that.equals('foo=bar;domain=google.com');
|
||||
body.params.options.should.have.a.property('authUser').that.equals('joe');
|
||||
body.params.options.should.have.a.property('authPass').that.equals('secret');
|
||||
|
||||
@@ -208,6 +212,7 @@ describe('api', function() {
|
||||
},
|
||||
json: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
||||
}
|
||||
}, function(error, response, body) {
|
||||
@@ -232,6 +237,7 @@ describe('api', function() {
|
||||
url: serverUrl + '/api/runs/' + asyncRunId,
|
||||
json: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
||||
}
|
||||
}, function(error, response, body) {
|
||||
|
||||
@@ -183,18 +183,19 @@ describe('customPolicies', function() {
|
||||
|
||||
var versions = {
|
||||
'1.2.9': 0,
|
||||
'1.3.9': 0,
|
||||
'1.4.4': 10,
|
||||
'1.5.0': 20,
|
||||
'1.6.3': 30,
|
||||
'1.7.0': 40,
|
||||
'1.8.3a': 50,
|
||||
'1.9.2': 70,
|
||||
'1.10.1': 90,
|
||||
'2.0.0-rc1': 90,
|
||||
'1.11.1': 100,
|
||||
'2.1.1-beta1': 100,
|
||||
'3.0.0': 100
|
||||
'1.4.4': 0,
|
||||
'1.5.0': 10,
|
||||
'1.6.3': 20,
|
||||
'1.7.0': 30,
|
||||
'1.8.3a': 40,
|
||||
'1.9.2': 50,
|
||||
'1.10.1': 70,
|
||||
'2.0.0-rc1': 70,
|
||||
'1.11.1': 90,
|
||||
'2.1.1-beta1': 90,
|
||||
'1.12.1': 100,
|
||||
'2.3.1': 100,
|
||||
'3.1.0': 100
|
||||
};
|
||||
|
||||
for (var version in versions) {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
var should = require('chai').should();
|
||||
var isHttp2 = require('../../lib/tools/isHttp2');
|
||||
|
||||
describe('isHttp2', function() {
|
||||
|
||||
it('should parse the protocol correctly', function() {
|
||||
isHttp2.getProtocol({
|
||||
toolsResults: {
|
||||
phantomas: {
|
||||
url: 'http://www.yahoo.com'
|
||||
}
|
||||
}
|
||||
}).should.equal('http:');
|
||||
|
||||
|
||||
isHttp2.getProtocol({
|
||||
toolsResults: {
|
||||
phantomas: {
|
||||
url: 'https://www.yahoo.com'
|
||||
}
|
||||
}
|
||||
}).should.equal('https:');
|
||||
});
|
||||
|
||||
it('should parse the domain correctly', function() {
|
||||
isHttp2.getDomain({
|
||||
toolsResults: {
|
||||
phantomas: {
|
||||
url: 'http://www.yahoo.com'
|
||||
}
|
||||
}
|
||||
}).should.equal('www.yahoo.com');
|
||||
|
||||
|
||||
isHttp2.getDomain({
|
||||
toolsResults: {
|
||||
phantomas: {
|
||||
url: 'https://www.yahoo.com'
|
||||
}
|
||||
}
|
||||
}).should.equal('www.yahoo.com');
|
||||
});
|
||||
|
||||
it('should have a function checkHttp2', function() {
|
||||
isHttp2.should.have.a.property('checkHttp2').that.is.a('function');
|
||||
});
|
||||
|
||||
});
|
||||
Vendored
+1
@@ -2,6 +2,7 @@
|
||||
"serverPort": "8387",
|
||||
"phantomasEngine": "webkit",
|
||||
"googleAnalyticsId": "",
|
||||
"screenshotWidth": 400,
|
||||
|
||||
"authorizedKeys": {
|
||||
"1234567890": "contact@gaelmetais.com"
|
||||
|
||||
Reference in New Issue
Block a user