+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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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,9 +106,6 @@ 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 its context
|
||||
function pushContext(data) {
|
||||
@@ -129,9 +126,7 @@ exports.module = function(phantomas) {
|
||||
}
|
||||
}
|
||||
|
||||
if (depth === 0 || deepAnalysis) {
|
||||
currentContext.addChild(data);
|
||||
}
|
||||
currentContext.addChild(data);
|
||||
}
|
||||
|
||||
// Add a child to the current context and enter its context
|
||||
@@ -153,9 +148,7 @@ exports.module = function(phantomas) {
|
||||
}
|
||||
}
|
||||
|
||||
if (depth === 0 || deepAnalysis) {
|
||||
currentContext = currentContext.addChild(data);
|
||||
}
|
||||
currentContext = currentContext.addChild(data);
|
||||
|
||||
depth ++;
|
||||
}
|
||||
@@ -178,21 +171,18 @@ exports.module = function(phantomas) {
|
||||
}
|
||||
}
|
||||
|
||||
if (depth === 1 || deepAnalysis) {
|
||||
|
||||
// Merge previous data with moreData (ovewrites if exists)
|
||||
if (moreData) {
|
||||
for (var key in moreData) {
|
||||
currentContext.data[key] = moreData[key];
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
var parent = currentContext.parent;
|
||||
if (parent === null) {
|
||||
console.error('Error: trying to close root context in ContextTree');
|
||||
} else {
|
||||
currentContext = parent;
|
||||
}
|
||||
|
||||
depth --;
|
||||
@@ -255,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,7 +138,7 @@ 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 oldJQuery;
|
||||
|
||||
+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;
|
||||
|
||||
+35
-34
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yellowlabtools",
|
||||
"version": "1.9.3",
|
||||
"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.8",
|
||||
"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.2",
|
||||
"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.1.0",
|
||||
"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": {
|
||||
|
||||
@@ -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