Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b0f7d639a | ||
|
|
301766f4aa | ||
|
|
6bdbfa2657 | ||
|
|
f5dafe6d43 | ||
|
|
623f25e521 | ||
|
|
a0e8a7f935 | ||
|
|
61bd31e421 | ||
|
|
84425e1319 | ||
|
|
09615f8ec4 | ||
|
|
3d368482bb | ||
|
|
df2cec5a94 | ||
|
|
369512c6c1 | ||
|
|
10a4cc270a | ||
|
|
1eda5e5860 | ||
|
|
3fc0d9418f | ||
|
|
c4f243f4b0 | ||
|
|
e057d740d8 | ||
|
|
a20c5677b4 | ||
|
|
7f687051f8 | ||
|
|
450c5c9829 | ||
|
|
0ab019e5ab | ||
|
|
40b3fa91ac | ||
|
|
f59c8da000 | ||
|
|
3555d01b68 | ||
|
|
b201b32b46 | ||
|
|
89f79e0977 | ||
|
|
aa0233f49b | ||
|
|
df4a45afb2 | ||
|
|
def0144dab | ||
|
|
ce16fa154c | ||
|
|
063a21997b | ||
|
|
92fce802c5 | ||
|
|
f76d597cbf | ||
|
|
ecbbbc2d6b |
+2
-1
@@ -1,8 +1,9 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- "8.9"
|
||||
- "6.2"
|
||||
- "4.0"
|
||||
- "4.7"
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
|
||||
+8
-86
@@ -80,27 +80,11 @@ module.exports = function(grunt) {
|
||||
dev: {
|
||||
src: ['front/src/css']
|
||||
},
|
||||
coverage: {
|
||||
src: ['.tmp', 'coverage/']
|
||||
},
|
||||
build: {
|
||||
src: ['front/build']
|
||||
}
|
||||
},
|
||||
copy: {
|
||||
beforeCoverage: {
|
||||
files: [
|
||||
{src: ['bin/server.js'], dest: '.tmp/'}
|
||||
]
|
||||
},
|
||||
coverage: {
|
||||
files: [
|
||||
{cwd: 'test', src: '**/*', dest: 'coverage/test', expand: true},
|
||||
{cwd: 'lib/metadata', src: '**/*', dest: 'coverage/lib/metadata', expand: true},
|
||||
{cwd: 'node_modules/phantomas', src: '**/*', dest: 'coverage/node_modules/phantomas', expand: true},
|
||||
{cwd: 'lib/tools/phantomas/custom_modules', src: '**/*', dest: 'coverage/lib/tools/phantomas/custom_modules', expand: true}
|
||||
]
|
||||
},
|
||||
build: {
|
||||
files: [
|
||||
{src: ['./front/src/img/favicon.png'], dest: './front/build/img/favicon.png'},
|
||||
@@ -108,50 +92,18 @@ module.exports = function(grunt) {
|
||||
]
|
||||
}
|
||||
},
|
||||
lineremover: {
|
||||
beforeCoverage: {
|
||||
files: {
|
||||
'.tmp/bin/cli.js': 'bin/cli.js'
|
||||
},
|
||||
options: {
|
||||
exclusionPattern: /#!\/usr\/bin\/env node/
|
||||
}
|
||||
}
|
||||
},
|
||||
blanket: {
|
||||
coverageApp: {
|
||||
src: ['app/'],
|
||||
dest: 'coverage/app/'
|
||||
},
|
||||
coverageLib: {
|
||||
src: ['lib/'],
|
||||
dest: 'coverage/lib/'
|
||||
},
|
||||
coverageBin: {
|
||||
src: ['.tmp/bin/'],
|
||||
dest: 'coverage/bin/'
|
||||
}
|
||||
},
|
||||
mochaTest: {
|
||||
test: {
|
||||
options: {
|
||||
reporter: 'spec',
|
||||
},
|
||||
src: ['coverage/test/core/*.js', 'coverage/test/api/*.js']
|
||||
src: ['test/core/*.js', 'test/api/*.js']
|
||||
},
|
||||
'test-current-work': {
|
||||
options: {
|
||||
reporter: 'spec',
|
||||
},
|
||||
src: ['test/core/fontAnalyzerTest.js']
|
||||
},
|
||||
coverage: {
|
||||
options: {
|
||||
reporter: 'html-cov',
|
||||
quiet: true,
|
||||
captureFile: 'coverage/coverage.html'
|
||||
},
|
||||
src: ['coverage/test/core/*.js', 'coverage/test/api/*.js']
|
||||
src: ['test/api/apiTest.js']
|
||||
}
|
||||
},
|
||||
env: {
|
||||
@@ -182,7 +134,7 @@ module.exports = function(grunt) {
|
||||
test: {
|
||||
options: {
|
||||
port: 8387,
|
||||
server: './coverage/bin/server.js',
|
||||
server: './bin/server.js',
|
||||
showStack: true
|
||||
}
|
||||
},
|
||||
@@ -265,35 +217,11 @@ module.exports = function(grunt) {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Custom task: copies the test settings.json file to the coverage folder, and checks if there's no missing fields
|
||||
grunt.registerTask('copy-test-server-settings', function() {
|
||||
var mainSettingsFile = './server_config/settings.json';
|
||||
var testSettingsFile = './test/fixtures/settings.json';
|
||||
|
||||
var mainSettings = grunt.file.readJSON(mainSettingsFile);
|
||||
var testSettings = grunt.file.readJSON(testSettingsFile);
|
||||
|
||||
// Recursively compare keys of two objects (not the values)
|
||||
function compareKeys(original, copy, context) {
|
||||
for (var key in original) {
|
||||
if (!copy[key] && copy[key] !== '' && copy[key] !== 0) {
|
||||
grunt.fail.warn('Settings file ' + testSettingsFile + ' doesn\'t contain key ' + context + '.' + key);
|
||||
}
|
||||
if (original[key] !== null && typeof original[key] === 'object') {
|
||||
compareKeys(original[key], copy[key], context + '.' + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compareKeys(mainSettings, testSettings, 'settings');
|
||||
|
||||
var outputFile = './coverage/server_config/settings.json';
|
||||
grunt.file.write(outputFile, JSON.stringify(testSettings, null, 4));
|
||||
grunt.log.ok('File ' + outputFile + ' created');
|
||||
// Custom task that sets a variable for tests
|
||||
grunt.registerTask('test-settings', function() {
|
||||
process.env.IS_TEST = true;
|
||||
});
|
||||
|
||||
|
||||
grunt.registerTask('icons', [
|
||||
'webfont:icons',
|
||||
'less',
|
||||
@@ -333,24 +261,18 @@ module.exports = function(grunt) {
|
||||
]);
|
||||
|
||||
grunt.registerTask('test', [
|
||||
'test-settings',
|
||||
'build',
|
||||
'express:testSuite',
|
||||
'clean:coverage',
|
||||
'copy-test-server-settings',
|
||||
'lineremover:beforeCoverage',
|
||||
'copy:beforeCoverage',
|
||||
'blanket',
|
||||
'copy:coverage',
|
||||
'express:test',
|
||||
'mochaTest:test',
|
||||
'clean:tmp'
|
||||
]);
|
||||
|
||||
grunt.registerTask('test-current-work', [
|
||||
'test-settings',
|
||||
'jshint',
|
||||
'express:testSuite',
|
||||
'clean:coverage',
|
||||
'copy-test-server-settings',
|
||||
'express:test-current-work',
|
||||
'mochaTest:test-current-work',
|
||||
'clean:tmp'
|
||||
|
||||
@@ -111,6 +111,7 @@ if (cli.flags.reporter && cli.flags.reporter !== 'json' && cli.flags.reporter !=
|
||||
xmlOutput = xmlOutput.replace(/(<[a-zA-Z]*>[^<]*)\n([^<]*<\/[a-zA-Z]*>)/g, '$1$2');
|
||||
xmlOutput = xmlOutput.replace(/\0/g, '');
|
||||
xmlOutput = xmlOutput.replace(/\uFFFF/g, '');
|
||||
xmlOutput = xmlOutput.replace(/\u0002/g, '');
|
||||
|
||||
console.log(xmlOutput);
|
||||
break;
|
||||
|
||||
+3
-1
@@ -35,7 +35,9 @@ yltApp.run(['$rootScope', '$location', function($rootScope, $location) {
|
||||
|
||||
// Google Analytics
|
||||
$rootScope.$on('$routeChangeSuccess', function(){
|
||||
ga('send', 'pageview', {'page': $location.path()});
|
||||
if (ga) {
|
||||
ga('send', 'pageview', {'page': $location.path()});
|
||||
}
|
||||
});
|
||||
|
||||
// GitHub star button (asynchronously loaded iframe)
|
||||
|
||||
@@ -751,7 +751,7 @@
|
||||
}
|
||||
|
||||
function onDetailsClick(row) {
|
||||
// Close if it's alreay open
|
||||
// Close if it's already open
|
||||
if (row.classList.contains('showDetails')) {
|
||||
closeDetails(row);
|
||||
return;
|
||||
@@ -924,4 +924,4 @@
|
||||
};
|
||||
});
|
||||
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -37,6 +37,8 @@ apiService.factory('API', ['$location', 'Runs', 'Results', function($location, R
|
||||
}, function(response) {
|
||||
if (response.status === 429) {
|
||||
alert('Too many requests, you reached the max number of requests allowed in 24h');
|
||||
} else if (response.status === 403) {
|
||||
alert('This particular query was blocked due to spamming. If you think it\'s an error, please open an issue on GitHub.');
|
||||
} else {
|
||||
alert('An error occured...');
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
}
|
||||
|
||||
.totalWeightPie {
|
||||
max-width: 39em;
|
||||
max-width: 20em;
|
||||
margin: 2em auto 4em;
|
||||
|
||||
canvas {
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@
|
||||
<!-- endbuild -->
|
||||
|
||||
<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');}
|
||||
if('@@googleAnalyticsId'.indexOf('UA-')===0){(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','https://www.google-analytics.com/analytics.js','ga');ga('create','@@googleAnalyticsId','auto');}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -117,10 +117,6 @@
|
||||
<file-and-line-button file="offender.file" line="offender.line" column="offender.column"></file-and-line-button>
|
||||
</div>
|
||||
|
||||
<div ng-if="policyName === 'cssDuplicatedSelectors'">
|
||||
{{offender.rule}} (<b>x{{offender.occurrences}}</b>)
|
||||
</div>
|
||||
|
||||
<div ng-if="policyName === 'cssOldPropertyPrefixes'">
|
||||
<b>{{offender.property}} {{offender.message}}</b>
|
||||
<div ng-if="offender.rules.length" ng-click="offender.showMore = !offender.showMore" class="offenderButton">
|
||||
@@ -188,6 +184,10 @@
|
||||
<span ng-if="offender.line !== null && offender.column !== null"> @ {{offender.line}}:{{offender.column}}</span>
|
||||
</div>
|
||||
|
||||
<div ng-if="policyName === 'cssDuplicatedSelectors'">
|
||||
{{offender.rule}} (<b>x{{offender.occurrences}}</b>)
|
||||
</div>
|
||||
|
||||
<div ng-if="policyName === 'cssDuplicatedProperties'">
|
||||
Property <b>{{offender.property}}</b> duplicated in <b>{{offender.rule}} { }</b>
|
||||
<span ng-if="offender.line !== null && offender.column !== null"> @ {{offender.line}}:{{offender.column}}</span>
|
||||
@@ -455,6 +455,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="font.ligaturesOrHiddenChars > 0">
|
||||
<div><b>Ligatures or hidden chars</b></div>
|
||||
<div ng-class="{offenderProblem: (font.ligaturesOrHiddenChars > 25)}">{{font.ligaturesOrHiddenChars}} glyphs</div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+75
-71
@@ -6,18 +6,18 @@ var policies = {
|
||||
"tool": "phantomas",
|
||||
"label": "DOM elements count",
|
||||
"message": "<p>A high number of DOM elements means a lot of work for the browser to render the page.</p><p>It also slows down JavaScript DOM queries, as there are more elements to search through.</p>",
|
||||
"isOkThreshold": 1000,
|
||||
"isBadThreshold": 2500,
|
||||
"isAbnormalThreshold": 4000,
|
||||
"isOkThreshold": 1500,
|
||||
"isBadThreshold": 3000,
|
||||
"isAbnormalThreshold": 4500,
|
||||
"hasOffenders": false
|
||||
},
|
||||
"DOMelementMaxDepth": {
|
||||
"tool": "phantomas",
|
||||
"label": "DOM max depth",
|
||||
"message": "<p>A deep DOM makes the CSS matching with DOM elements difficult.</p><p>It also slows down JavaScript modifications to the DOM because changing the dimensions of an element makes the browser re-calculate the dimensions of it's parents. Same thing for JavaScript events, that bubble up to the document root.</p>",
|
||||
"isOkThreshold": 10,
|
||||
"isBadThreshold": 20,
|
||||
"isAbnormalThreshold": 28,
|
||||
"isOkThreshold": 12,
|
||||
"isBadThreshold": 22,
|
||||
"isAbnormalThreshold": 30,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
var domArrays = offenders.map(offendersHelpers.domPathToArray);
|
||||
@@ -32,7 +32,7 @@ var policies = {
|
||||
"tool": "phantomas",
|
||||
"label": "Number of iframes",
|
||||
"message": "<p>iFrames are the most complex HTML elements. They are pages, just like the main page, and the browser needs to create a new page context, which has a cost.</p>",
|
||||
"isOkThreshold": 2,
|
||||
"isOkThreshold": 3,
|
||||
"isBadThreshold": 15,
|
||||
"isAbnormalThreshold": 30,
|
||||
"hasOffenders": false
|
||||
@@ -70,9 +70,9 @@ var policies = {
|
||||
"tool": "jsExecutionTransformer",
|
||||
"label": "DOM access",
|
||||
"message": "<p>This metric counts the number of calls to DOM related functions (both native DOM functions and jQuery functions) on page load.</p><p>The more your JavaScript code accesses the DOM, the slower the page will load.</p><p>Try, as much as possible, to have an HTML page fully generated by the server instead of making changes with JS.</p><p>Try to reduce the number of queries by refactoring your JavaScript code.</p><p>Binding too many events also has a cost. Try to use <a href=\"https://learn.jquery.com/events/event-delegation/\" target=\"_blank\">event delegation</a> as much as possible.</p>",
|
||||
"isOkThreshold": 50,
|
||||
"isBadThreshold": 1500,
|
||||
"isAbnormalThreshold": 3000,
|
||||
"isOkThreshold": 200,
|
||||
"isBadThreshold": 2000,
|
||||
"isAbnormalThreshold": 4000,
|
||||
"hasOffenders": false
|
||||
},
|
||||
"queriesWithoutResults": {
|
||||
@@ -80,8 +80,8 @@ var policies = {
|
||||
"label": "Queries without result",
|
||||
"message": "<p>Number of queries that return no result. Both native and jQuery DOM requests are counted.</p><p>It suggests the query is not used on the page, probably because it is some dead code.</p><p>Or maybe the code is trying to find an HTML block that is not always here. Look at the JS Timeline to see if the scripts correctly figures out the HTML block is not here and immediatly stops interacting further with the DOM.</p>",
|
||||
"isOkThreshold": 0,
|
||||
"isBadThreshold": 100,
|
||||
"isAbnormalThreshold": 200,
|
||||
"isBadThreshold": 150,
|
||||
"isAbnormalThreshold": 250,
|
||||
"hasOffenders": false
|
||||
},
|
||||
"DOMqueriesAvoidable": {
|
||||
@@ -89,8 +89,8 @@ var policies = {
|
||||
"label": "Duplicated DOM queries",
|
||||
"message": "<p>This is the number of queries that could be avoided by removing all duplicated queries.</p><p>Simply save the result of a query in a variable. Ok it is not always simple, especially with third-party scripts, but at least do it with your own code.</p>",
|
||||
"isOkThreshold": 0,
|
||||
"isBadThreshold": 200,
|
||||
"isAbnormalThreshold": 500,
|
||||
"isBadThreshold": 300,
|
||||
"isAbnormalThreshold": 600,
|
||||
"hasOffenders": true,
|
||||
"takeOffendersFrom": "DOMqueriesDuplicated",
|
||||
"offendersTransformFn": function(offenders) {
|
||||
@@ -121,7 +121,7 @@ var policies = {
|
||||
"label": "Scroll events bound",
|
||||
"message": "<p>Number of 'scroll' event listeners binded to 'window' or 'document'.</p><p>Asking too much work to the browser on scroll hurts the smoothness of the scroll. Merging all your event listeners into an unique listener can help you factorize their code and reduce their footprint on scroll.</p>",
|
||||
"isOkThreshold": 1,
|
||||
"isBadThreshold": 7,
|
||||
"isBadThreshold": 8,
|
||||
"isAbnormalThreshold": 15,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
@@ -195,8 +195,8 @@ var policies = {
|
||||
"label": "document.write calls",
|
||||
"message": "<p>They slow down the page construction, especially if they are used to insert scripts in the page. Remove them ASAP.</p><p>If you cannot remove them because they come from a third-party script (such as ads), have a look at <a href=\"https://github.com/krux/postscribe\" target=\"_blank\">PostScribe</a>.</p>",
|
||||
"isOkThreshold": 0,
|
||||
"isBadThreshold": 5,
|
||||
"isAbnormalThreshold": 10,
|
||||
"isBadThreshold": 3,
|
||||
"isAbnormalThreshold": 8,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
return {
|
||||
@@ -254,8 +254,8 @@ var policies = {
|
||||
"tool": "phantomas",
|
||||
"label": "Console messages",
|
||||
"message": "<p>Try to keep your console clean when in production. Debugging is good for development only.</p><p>Writing in the console has a cost, especially when dumping large object variables.</p><p>There is also a problem with Internet Explorer 8, not knowing the console object.</p>",
|
||||
"isOkThreshold": 0,
|
||||
"isBadThreshold": 10,
|
||||
"isOkThreshold": 3,
|
||||
"isBadThreshold": 20,
|
||||
"isAbnormalThreshold": 50,
|
||||
"hasOffenders": false
|
||||
},
|
||||
@@ -276,7 +276,7 @@ var policies = {
|
||||
},
|
||||
"jQueryVersion": {
|
||||
"label": "jQuery version",
|
||||
"message": "<p>The current latest version of jQuery is 3.0</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>The current latest version of jQuery is 3.3</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;
|
||||
@@ -288,28 +288,32 @@ var policies = {
|
||||
var value = data.toolsResults.phantomas.metrics.jQueryVersion;
|
||||
var score;
|
||||
|
||||
if (value.indexOf('3.1.') === 0 ||
|
||||
value.indexOf('3.2.') === 0 ||
|
||||
value.indexOf('3.3.') === 0) {
|
||||
if (value.indexOf('3.3.') === 0 ||
|
||||
value.indexOf('3.4.') === 0 ||
|
||||
value.indexOf('3.5.') === 0 ||
|
||||
value.indexOf('4.0.') === 0) {
|
||||
score = 100;
|
||||
} else if (value.indexOf('3.0.') === 0) {
|
||||
} else if (value.indexOf('3.2.') === 0) {
|
||||
score = 90;
|
||||
} else if (value.indexOf('3.1.') === 0) {
|
||||
score = 70;
|
||||
} else if (value.indexOf('3.0.') === 0) {
|
||||
score = 50;
|
||||
} else if (value.indexOf('1.12.') === 0 ||
|
||||
value.indexOf('2.2.') === 0) {
|
||||
score = 70;
|
||||
score = 40;
|
||||
} else if (value.indexOf('1.11.') === 0 ||
|
||||
value.indexOf('2.1.') === 0) {
|
||||
score = 50;
|
||||
score = 30;
|
||||
} else if (value.indexOf('1.10.') === 0 ||
|
||||
value.indexOf('2.0.') === 0) {
|
||||
score = 40;
|
||||
} else if (value.indexOf('1.9.') === 0) {
|
||||
score = 30;
|
||||
} else if (value.indexOf('1.8.') === 0) {
|
||||
score = 20;
|
||||
} else if (value.indexOf('1.7') === 0) {
|
||||
} else if (value.indexOf('1.9') === 0) {
|
||||
score = 10;
|
||||
} else if (value.indexOf('1.6') === 0 ||
|
||||
} else if (value.indexOf('1.9') === 0 ||
|
||||
value.indexOf('1.8') === 0 ||
|
||||
value.indexOf('1.7') === 0 ||
|
||||
value.indexOf('1.6') === 0 ||
|
||||
value.indexOf('1.5') === 0 ||
|
||||
value.indexOf('1.4') === 0 ||
|
||||
value.indexOf('1.3') === 0 ||
|
||||
@@ -419,7 +423,7 @@ var policies = {
|
||||
"tool": "phantomas",
|
||||
"label": "Rules count",
|
||||
"message": "<p>Having a huge number of CSS rules hurts performances. If the number of CSS rules is higher than the number of DOM elements, there is clearly a problem.</p><p>Huge stylesheets generally occur when the different pages of a website load all the CSS, concatenated in a single stylesheet, even if a large part of the rules are page-specific. Solution is to create one main CSS file with global rules and one custom file per page.</p>",
|
||||
"isOkThreshold": 750,
|
||||
"isOkThreshold": 1000,
|
||||
"isBadThreshold": 3000,
|
||||
"isAbnormalThreshold": 4500,
|
||||
"hasOffenders": true,
|
||||
@@ -462,7 +466,7 @@ var policies = {
|
||||
"label": "Complex selectors",
|
||||
"message": "<p>Complex selectors are CSS selectors with 4 or more expressions, like \"#header ul li .foo\".</p><p>They are adding more work for the browser, and this could be avoided by simplifying selectors. The <a href=\"http://getbem.com\" target=\"_blank\">B.E.M. methodology</a> is an useful way to simplify your CSS.</p>",
|
||||
"isOkThreshold": 0,
|
||||
"isBadThreshold": 600,
|
||||
"isBadThreshold": 800,
|
||||
"isAbnormalThreshold": 2000,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
@@ -478,7 +482,7 @@ var policies = {
|
||||
"tool": "phantomas",
|
||||
"label": "Colors count",
|
||||
"message": "<p>This is the number of different colors defined in CSS.</p><p>Your CSS will be easier to maintain if you keep a small color set.</p>",
|
||||
"isOkThreshold": 30,
|
||||
"isOkThreshold": 50,
|
||||
"isBadThreshold": 150,
|
||||
"isAbnormalThreshold": 400,
|
||||
"hasOffenders": true,
|
||||
@@ -561,8 +565,8 @@ var policies = {
|
||||
"tool": "mediaQueriesChecker",
|
||||
"label": "Not mobile-first media queries",
|
||||
"message": "<p>This is the number of CSS rules inside media queries that address small screens.</p><p>The common good practice, when creating a responsive website, is to write it \"mobile-first\". More explanation in <a href=\"http://www.sitepoint.com/introduction-mobile-first-media-queries\" target=\"_blank\">this great article</a>.</p>",
|
||||
"isOkThreshold": 25,
|
||||
"isBadThreshold": 200,
|
||||
"isOkThreshold": 50,
|
||||
"isBadThreshold": 250,
|
||||
"isAbnormalThreshold": 1000,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
@@ -596,24 +600,24 @@ var policies = {
|
||||
"isAbnormalThreshold": 100,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
return {
|
||||
count: offenders.length,
|
||||
list: offenders.map(function(offender) {
|
||||
var parts = /^(.*) \((\d+) times\)$/.exec(offender);
|
||||
|
||||
if (!parts) {
|
||||
debug('cssDuplicatedSelectors offenders transform function error with "%s"', offender);
|
||||
return {
|
||||
parseError: offender
|
||||
};
|
||||
}
|
||||
var parsedOffenders = offenders.map(function(offender) {
|
||||
var parts = /^(.*) \((\d+) times\) ?<(.*)>$/.exec(offender);
|
||||
|
||||
if (!parts) {
|
||||
debug('cssDuplicatedSelectors offenders transform function error with "%s"', offender);
|
||||
return {
|
||||
rule: parts[1],
|
||||
occurrences: parseInt(parts[2], 10)
|
||||
parseError: offender
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
rule: parts[1],
|
||||
occurrences: parseInt(parts[2], 10),
|
||||
file: parts[3]
|
||||
};
|
||||
});
|
||||
|
||||
return offendersHelpers.orderByFile(parsedOffenders);
|
||||
}
|
||||
},
|
||||
"cssDuplicatedProperties": {
|
||||
@@ -735,8 +739,8 @@ var policies = {
|
||||
"label": "Old IE fixes",
|
||||
"message": "<p>What browser do you need to support? Once you've got the answer, take a look at these old rules that pollute your CSS code and remove them.</p><p>IE6:<ul><li>* html</li><li>html > body (everything but IE6)</li></ul><p><p>IE7:<ul><li><b>*</b>height: 123px;</li><li>height: 123px <b>!ie</b>;</li></ul><p><p>IE9:<ul><li>-ms-filter</li><li>progid:DXImageTransform.Microsoft</li></ul></p>",
|
||||
"isOkThreshold": 0,
|
||||
"isBadThreshold": 75,
|
||||
"isAbnormalThreshold": 300,
|
||||
"isBadThreshold": 50,
|
||||
"isAbnormalThreshold": 150,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
var parsedOffenders = offenders.map(function(offender) {
|
||||
@@ -791,8 +795,8 @@ var policies = {
|
||||
"label": "Old prefixes",
|
||||
"message": "<p>Many property prefixes such as -moz- or -webkit- are not needed anymore, or by very few people. Sometimes, they have never even existed. You can remove them or replace them with the non-prefixed version. This will help reducing your stylesheets weight.</p><p>The prefixes database comes from <a href=\"http://caniuse.com/\" target=\"_blank\">Can I Use</a>.</p>",
|
||||
"isOkThreshold": 0,
|
||||
"isBadThreshold": 75,
|
||||
"isAbnormalThreshold": 300,
|
||||
"isBadThreshold": 50,
|
||||
"isAbnormalThreshold": 200,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
var properties = {};
|
||||
@@ -893,10 +897,10 @@ var policies = {
|
||||
"totalWeight": {
|
||||
"tool": "redownload",
|
||||
"label": "Total weight",
|
||||
"message": "<p>The weight is of course very important if you want the page to load fast. Try to stay under 1MB, which is alreay very long to download over a slow connection.</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": 716800,
|
||||
"isBadThreshold": 2097152,
|
||||
"isAbnormalThreshold": 3145728,
|
||||
"message": "<p>The weight is of course very important if you want the page to load fast. Try to stay under 1MB, which is already very long to download over a slow connection.</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": 1048576,
|
||||
"isBadThreshold": 2621440,
|
||||
"isAbnormalThreshold": 4194304,
|
||||
"hasOffenders": true,
|
||||
"unit": 'bytes'
|
||||
},
|
||||
@@ -904,8 +908,8 @@ var policies = {
|
||||
"tool": "redownload",
|
||||
"label": "Image optimization",
|
||||
"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,
|
||||
"isOkThreshold": 20480,
|
||||
"isBadThreshold": 204800,
|
||||
"isAbnormalThreshold": 307200,
|
||||
"hasOffenders": true,
|
||||
"unit": 'bytes'
|
||||
@@ -934,9 +938,9 @@ var policies = {
|
||||
"tool": "redownload",
|
||||
"label": "Requests number",
|
||||
"message": "<p>This is one of the most important performance rule. Every request is slowing down the page loading.</p><p>There are several technics to reduce their number:<ul><li>Concatenate JS files</li><li>Concatenate CSS files</li><li>Embed or inline small JS or CSS files in the HTML</li><li>Create sprites</li><li>Base64 encode small images in HTML or stylesheets</li><li>Use lazyloading for images</li></ul></p>",
|
||||
"isOkThreshold": 15,
|
||||
"isBadThreshold": 100,
|
||||
"isAbnormalThreshold": 180,
|
||||
"isOkThreshold": 20,
|
||||
"isBadThreshold": 120,
|
||||
"isAbnormalThreshold": 200,
|
||||
"hasOffenders": true
|
||||
},
|
||||
"domains": {
|
||||
@@ -982,8 +986,8 @@ var policies = {
|
||||
"label": "Connections closed",
|
||||
"message": "<p>This counts the number of requests not keeping the connection alive (specifying \"Connection: close\" in the response headers). It is only counting a request if it is followed by another request on the same domain.</p><p>This is slowing down the next request, because the brower needs to open a new connection to the server, which means an additional round-trip.</p><p>Correct the problem by setting a Keep-Alive header on the guilty server.</p>",
|
||||
"isOkThreshold": 0,
|
||||
"isBadThreshold": 7,
|
||||
"isAbnormalThreshold": 20,
|
||||
"isBadThreshold": 4,
|
||||
"isAbnormalThreshold": 15,
|
||||
"hasOffenders": true
|
||||
},
|
||||
"identicalFiles": {
|
||||
@@ -1011,9 +1015,9 @@ var policies = {
|
||||
"tool": "redownload",
|
||||
"label": "Small requests",
|
||||
"message": "<p>List of all requests that are less than 2 KB. Try to merge them with other files.</p>",
|
||||
"isOkThreshold": 4,
|
||||
"isBadThreshold": 30,
|
||||
"isAbnormalThreshold": 50,
|
||||
"isOkThreshold": 10,
|
||||
"isBadThreshold": 50,
|
||||
"isAbnormalThreshold": 80,
|
||||
"hasOffenders": true
|
||||
},
|
||||
"lazyLoadableImagesBelowTheFold": {
|
||||
@@ -1062,7 +1066,7 @@ var policies = {
|
||||
"unusedUnicodeRanges": {
|
||||
"tool": "redownload",
|
||||
"label": "Unused Unicode ranges",
|
||||
"message": "<p>This metric counts the number of unused Unicode ranges inside each font. For example, one font could include Cyrillic glyphs but none of them are used on the page.</p><p>Because of technical limitations, Yellow Lab Tools checks each font against the glyphs of the entire page. As a result, estimated use is >= to reality. For example, if you read that 10 glyphs are \"possibly used\", it means that these 10 glyphs are used on the page but nothing guaranties that they are displayed using this font.</p><p>Tools such as <a href=\"https://www.fontsquirrel.com/tools/webfont-generator\" target=\"_blank\">Font Squirrel</a> can remove some unicode ranges from a font.</p><p>In the case of an icon font, make sure you only keep the icons that are used on the website and to remove the others. Several tools are able to extract SVG images from a font, then some other tools can generate a font from the SVGs you want to keep.</p>",
|
||||
"message": "<p>This metric counts the number of unused Unicode ranges inside each font. For example, one font could include Cyrillic glyphs but none of them are used on the page.</p><p>It also reveals the number of ligatures (letters that are represented differently when close to each other) and hidden chars (glyphs not linked to the unicode system that can't be displayed on the web).</p><p>Because of technical limitations, Yellow Lab Tools checks each font against the glyphs of the entire page. As a result, estimated use is >= to reality. For example, if you read that 10 glyphs are \"possibly used\", it means that these 10 glyphs are used on the page but nothing guaranties that they are displayed using this font.</p><p>Tools such as <a href=\"https://www.fontsquirrel.com/tools/webfont-generator\" target=\"_blank\">Font Squirrel</a> can remove some unicode ranges from a font.</p><p>In the case of an icon font, make sure you only keep the icons that are used on the website and to remove the others. Several tools are able to extract SVG images from a font, then some other tools can generate a font from the SVGs you want to keep.</p>",
|
||||
"isOkThreshold": 0,
|
||||
"isBadThreshold": 8,
|
||||
"isAbnormalThreshold": 12,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var debug = require('debug')('ylt:screenshotHandler');
|
||||
var lwip = require('lwip');
|
||||
var Jimp = require('jimp');
|
||||
var tmp = require('temporary');
|
||||
var Q = require('q');
|
||||
var fs = require('fs');
|
||||
@@ -57,7 +57,7 @@ var screenshotHandler = function() {
|
||||
this.openImage = function(imagePath) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
lwip.open(imagePath, function(err, image){
|
||||
Jimp.read(imagePath, function(err, image){
|
||||
if (err) {
|
||||
debug('Could not open imagePath %s', imagePath);
|
||||
debug(err);
|
||||
@@ -76,7 +76,7 @@ var screenshotHandler = function() {
|
||||
this.resizeImage = function(image, newWidth) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
var currentWidth = image.width();
|
||||
var currentWidth = image.bitmap.width;
|
||||
var ratio = newWidth / currentWidth;
|
||||
|
||||
image.scale(ratio, function(err, image){
|
||||
@@ -101,7 +101,7 @@ var screenshotHandler = function() {
|
||||
var deferred = Q.defer();
|
||||
|
||||
// Create a canvas with the same dimensions as your image:
|
||||
lwip.create(image.width(), image.height(), 'white', function(err, canvas){
|
||||
new Jimp(image.bitmap.width, image.bitmap.height, 0xFFFFFF, function(err, canvas){
|
||||
if (err) {
|
||||
debug('Could not create a white canvas');
|
||||
debug(err);
|
||||
@@ -109,7 +109,7 @@ var screenshotHandler = function() {
|
||||
deferred.reject(err);
|
||||
} else {
|
||||
// Paste original image on top of the canvas
|
||||
canvas.paste(0, 0, image, function(err, image){
|
||||
canvas.composite(image, 0, 0, function(err, image){
|
||||
if (err) {
|
||||
debug('Could not paste image on the white canvas');
|
||||
debug(err);
|
||||
@@ -131,7 +131,7 @@ var screenshotHandler = function() {
|
||||
this.toBuffer = function(image) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
image.toBuffer('jpg', {quality: 90}, function(err, buffer){
|
||||
image.quality(90).getBuffer(Jimp.MIME_JPEG, function(err, buffer){
|
||||
if (err) {
|
||||
debug('Could not save image to buffer');
|
||||
debug(err);
|
||||
@@ -143,7 +143,7 @@ var screenshotHandler = function() {
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
|
||||
@@ -167,4 +167,4 @@ var screenshotHandler = function() {
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = new screenshotHandler();
|
||||
module.exports = new screenshotHandler();
|
||||
|
||||
@@ -6,7 +6,8 @@ var ScreenshotHandler = require('../../screenshotHandler');
|
||||
var RunsQueue = require('../datastores/runsQueue');
|
||||
var RunsDatastore = require('../datastores/runsDatastore');
|
||||
var ResultsDatastore = require('../datastores/resultsDatastore');
|
||||
var serverSettings = require('../../../server_config/settings.json');
|
||||
|
||||
var serverSettings = (process.env.IS_TEST) ? require('../../../test/fixtures/settings.json') : require('../../../server_config/settings.json');
|
||||
|
||||
var ApiController = function(app) {
|
||||
'use strict';
|
||||
@@ -23,6 +24,13 @@ var ApiController = function(app) {
|
||||
req.body.url = 'http://' + req.body.url;
|
||||
}
|
||||
|
||||
// Block requests to unwanted websites (=spam)
|
||||
if (req.body.url && isBlocked(req.body.url)) {
|
||||
console.error('Test blocked for URL: %s', req.body.url);
|
||||
res.status(403).send('Forbidden');
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the test parameters and generate a random run ID
|
||||
var run = {
|
||||
runId: (Date.now()*1000 + Math.round(Math.random()*1000)).toString(36),
|
||||
@@ -86,6 +94,7 @@ var ApiController = function(app) {
|
||||
return ylt(run.params.url, runOptions);
|
||||
|
||||
})
|
||||
|
||||
// Phantomas completed, let's save the screenshot if any
|
||||
.then(function(data) {
|
||||
|
||||
@@ -327,6 +336,15 @@ var ApiController = function(app) {
|
||||
});
|
||||
});
|
||||
|
||||
function isBlocked(url) {
|
||||
if (!serverSettings.blockedUrls) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return serverSettings.blockedUrls.some(function(blockedUrl) {
|
||||
return (url.indexOf(blockedUrl) === 0);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ApiController;
|
||||
|
||||
@@ -14,7 +14,7 @@ var FrontController = function(app) {
|
||||
app.get(route, function(req, res) {
|
||||
res.setHeader('Cache-Control', 'public, max-age=20');
|
||||
res.render(path.join(__dirname, assetsPath, 'main.html'), {
|
||||
baseUrl: app.locals.baseUrl
|
||||
baseUrl: app.locals.baseUrl || '/'
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -23,7 +23,7 @@ var FrontController = function(app) {
|
||||
app.get('/views/:viewName', function(req, res) {
|
||||
res.setHeader('Cache-Control', 'public, max-age=' + cacheDuration);
|
||||
res.render(path.join(__dirname, assetsPath, 'views/' + req.params.viewName), {
|
||||
baseUrl: app.locals.baseUrl
|
||||
baseUrl: app.locals.baseUrl || '/'
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var config = require('../../../server_config/settings.json');
|
||||
var config = (process.env.IS_TEST) ? require('../../../test/fixtures/settings.json') : require('../../../server_config/settings.json');
|
||||
|
||||
var debug = require('debug')('apiLimitsMiddleware');
|
||||
var debug = require('debug')('apiLimitsMiddleware');
|
||||
|
||||
|
||||
var apiLimitsMiddleware = function(req, res, next) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var config = require('../../../server_config/settings.json');
|
||||
var config = (process.env.IS_TEST) ? require('../../../test/fixtures/settings.json') : require('../../../server_config/settings.json');
|
||||
|
||||
var debug = require('debug')('authMiddleware');
|
||||
var debug = require('debug')('authMiddleware');
|
||||
|
||||
|
||||
var authMiddleware = function(req, res, next) {
|
||||
|
||||
@@ -296,7 +296,7 @@ var FileMinifier = function() {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Avoid loosing time trying to compress some JS libraries known as already compressed
|
||||
// Avoid losing time trying to compress some JS libraries known as already compressed
|
||||
function isKnownAsMinified(url) {
|
||||
var result = false;
|
||||
|
||||
@@ -322,7 +322,7 @@ var FileMinifier = function() {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Avoid loosing some trying to compress JS files if they alreay look minified
|
||||
// Avoid losing some trying to compress JS files if they already look minified
|
||||
// by counting the number of lines compared to the total size.
|
||||
// Less than 2KB per line is suspicious
|
||||
function looksAlreadyMinified(code) {
|
||||
@@ -351,4 +351,4 @@ var FileMinifier = function() {
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = new FileMinifier();
|
||||
module.exports = new FileMinifier();
|
||||
|
||||
@@ -57,8 +57,17 @@ var FontAnalyzer = function() {
|
||||
var endTime = Date.now();
|
||||
debug('Font analysis took %dms', endTime - startTime);
|
||||
|
||||
deferred.resolve(result);
|
||||
// Mark fonts that are not used on the page (#224)
|
||||
var fontIsUsed = false;
|
||||
for (var range in result.unicodeRanges) {
|
||||
if (result.unicodeRanges[range].numGlyphsInCommonWithPageContent > 0) {
|
||||
fontIsUsed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.isUsed = fontIsUsed;
|
||||
|
||||
deferred.resolve(result);
|
||||
} catch(error) {
|
||||
deferred.reject(error);
|
||||
}
|
||||
|
||||
@@ -103,6 +103,14 @@ var Redownload = function() {
|
||||
var metrics = {};
|
||||
var offenders = {};
|
||||
|
||||
// Remove unused fonts that a normal browser would not download (fix #224)
|
||||
results = results.filter(function(result) {
|
||||
if (result && result.fontMetrics) {
|
||||
return result.fontMetrics.isUsed !== false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Count requests
|
||||
offenders.totalRequests = listRequestsByType(results);
|
||||
metrics.totalRequests = offenders.totalRequests.total;
|
||||
@@ -500,6 +508,8 @@ var Redownload = function() {
|
||||
var ranges = [];
|
||||
var others = null;
|
||||
var rangeNames = Object.keys(req.fontMetrics.unicodeRanges);
|
||||
var unicodePointsCount = 0;
|
||||
var unusedRangesInFont = 0;
|
||||
|
||||
rangeNames.forEach(function(rangeName) {
|
||||
var range = req.fontMetrics.unicodeRanges[rangeName];
|
||||
@@ -508,18 +518,20 @@ var Redownload = function() {
|
||||
if (rangeName === 'Others') {
|
||||
if (range.numGlyphsInCommonWithPageContent === 0 && range.charset.length > 50) {
|
||||
range.underused = true;
|
||||
unusedUnicodeRanges ++;
|
||||
unusedRangesInFont ++;
|
||||
}
|
||||
|
||||
unicodePointsCount += range.charset.length;
|
||||
others = range;
|
||||
} else if (range.charset.length > 0) {
|
||||
// Now lets detect if the current Unicode range is unused.
|
||||
// Reminder: range.coverage = glyphs declared in this range, divided by the range size
|
||||
if (range.coverage > 0.25 && range.numGlyphsInCommonWithPageContent === 0) {
|
||||
range.underused = true;
|
||||
unusedUnicodeRanges ++;
|
||||
unusedRangesInFont ++;
|
||||
}
|
||||
|
||||
unicodePointsCount += range.charset.length;
|
||||
ranges.push(range);
|
||||
}
|
||||
});
|
||||
@@ -539,7 +551,7 @@ var Redownload = function() {
|
||||
|
||||
// And if less than 5% of the icons are used, let's report it as underused
|
||||
if (others && others.numGlyphsInCommonWithPageContent / others.charset.length <= 0.05) {
|
||||
unusedUnicodeRanges ++;
|
||||
unusedRangesInFont = 1;
|
||||
}
|
||||
|
||||
// Not an icons font
|
||||
@@ -549,13 +561,21 @@ var Redownload = function() {
|
||||
ranges.push(others);
|
||||
}
|
||||
|
||||
var ligaturesOrHiddenChars = req.fontMetrics.numGlyphs - unicodePointsCount;
|
||||
if (ligaturesOrHiddenChars > 25) {
|
||||
unusedUnicodeRanges ++;
|
||||
}
|
||||
|
||||
list.push({
|
||||
url: req.url,
|
||||
weight: req.weightCheck.bodySize,
|
||||
isIconFont: false,
|
||||
unicodeRanges: ranges
|
||||
unicodeRanges: ranges,
|
||||
ligaturesOrHiddenChars: ligaturesOrHiddenChars
|
||||
});
|
||||
}
|
||||
|
||||
unusedUnicodeRanges += unusedRangesInFont;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+46
-46
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yellowlabtools",
|
||||
"version": "1.12.0",
|
||||
"version": "1.13.0",
|
||||
"description": "Online tool to audit a webpage for performance and front-end quality issues",
|
||||
"license": "GPL-2.0",
|
||||
"author": {
|
||||
@@ -20,80 +20,80 @@
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"dependencies": {
|
||||
"angular": "1.6.1",
|
||||
"angular-animate": "1.6.1",
|
||||
"angular": "1.6.8",
|
||||
"angular-animate": "1.6.8",
|
||||
"angular-chart.js": "1.1.1",
|
||||
"angular-local-storage": "0.5.0",
|
||||
"angular-resource": "1.6.1",
|
||||
"angular-route": "1.6.1",
|
||||
"angular-sanitize": "1.6.1",
|
||||
"async": "1.5.2",
|
||||
"body-parser": "1.15.2",
|
||||
"chart.js": "2.4.0",
|
||||
"clean-css": "3.4.23",
|
||||
"color-diff": "1.0.0",
|
||||
"compression": "1.6.2",
|
||||
"cors": "2.8.1",
|
||||
"angular-local-storage": "0.7.1",
|
||||
"angular-resource": "1.6.8",
|
||||
"angular-route": "1.6.8",
|
||||
"angular-sanitize": "1.6.8",
|
||||
"async": "2.6.0",
|
||||
"body-parser": "1.18.2",
|
||||
"chart.js": "2.7.1",
|
||||
"clean-css": "4.1.9",
|
||||
"color-diff": "1.1.0",
|
||||
"compression": "1.7.1",
|
||||
"cors": "2.8.4",
|
||||
"css-mq-parser": "0.0.3",
|
||||
"debug": "2.6.0",
|
||||
"debug": "3.1.0",
|
||||
"easyxml": "2.0.1",
|
||||
"ejs": "2.5.5",
|
||||
"express": "4.14.0",
|
||||
"fontkit": "1.5.1",
|
||||
"imagemin": "5.2.2",
|
||||
"imagemin-jpegoptim": "5.0.0",
|
||||
"ejs": "2.5.7",
|
||||
"express": "4.16.2",
|
||||
"fontkit": "1.7.7",
|
||||
"imagemin": "5.3.1",
|
||||
"imagemin-jpegoptim": "5.2.0",
|
||||
"imagemin-jpegtran": "5.0.2",
|
||||
"imagemin-optipng": "5.2.1",
|
||||
"imagemin-svgo": "5.2.0",
|
||||
"imagemin-svgo": "6.0.0",
|
||||
"is-eot": "1.0.0",
|
||||
"is-gif": "1.0.0",
|
||||
"is-http2": "1.1.0",
|
||||
"is-http2": "1.2.0",
|
||||
"is-jpg": "1.0.0",
|
||||
"is-otf": "0.1.2",
|
||||
"is-png": "1.0.0",
|
||||
"is-png": "1.1.0",
|
||||
"is-svg": "2.1.0",
|
||||
"is-ttf": "0.2.2",
|
||||
"is-woff": "1.0.3",
|
||||
"is-woff2": "1.0.0",
|
||||
"lwip": "0.0.9",
|
||||
"jimp": "0.2.28",
|
||||
"md5": "2.2.1",
|
||||
"meow": "3.7.0",
|
||||
"minimize": "2.0.0",
|
||||
"minimize": "2.1.0",
|
||||
"parse-color": "1.0.0",
|
||||
"phantomas": "1.18.0",
|
||||
"ps-node": "0.1.4",
|
||||
"q": "1.4.1",
|
||||
"request": "2.79.0",
|
||||
"rimraf": "2.5.4",
|
||||
"phantomas": "1.19.0",
|
||||
"ps-node": "0.1.6",
|
||||
"q": "1.5.1",
|
||||
"request": "2.83.0",
|
||||
"rimraf": "2.6.2",
|
||||
"temporary": "0.0.8",
|
||||
"try-thread-sleep": "1.0.0",
|
||||
"uglify-js": "2.7.5"
|
||||
"try-thread-sleep": "1.0.2",
|
||||
"uglify-js": "2.8.29"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "~3.5.0",
|
||||
"chai": "~4.1.2",
|
||||
"grunt": "~1.0.1",
|
||||
"grunt-blanket": "~0.0.10",
|
||||
"grunt-contrib-clean": "~1.0.0",
|
||||
"grunt-contrib-clean": "~1.1.0",
|
||||
"grunt-contrib-concat": "~1.0.1",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-cssmin": "~1.0.2",
|
||||
"grunt-contrib-htmlmin": "~2.0.0",
|
||||
"grunt-contrib-cssmin": "~2.2.1",
|
||||
"grunt-contrib-htmlmin": "~2.4.0",
|
||||
"grunt-contrib-jshint": "~1.1.0",
|
||||
"grunt-contrib-less": "~1.4.0",
|
||||
"grunt-contrib-uglify": "~2.0.0",
|
||||
"grunt-contrib-less": "~1.4.1",
|
||||
"grunt-contrib-uglify": "~3.3.0",
|
||||
"grunt-contrib-watch": "~1.0.0",
|
||||
"grunt-env": "~0.4.4",
|
||||
"grunt-express": "~1.4.1",
|
||||
"grunt-filerev": "~2.3.1",
|
||||
"grunt-inline-angular-templates": "~0.1.5",
|
||||
"grunt-line-remover": "~0.0.2",
|
||||
"grunt-mocha-test": "~0.13.2",
|
||||
"grunt-mocha-test": "~0.13.3",
|
||||
"grunt-parallel": "~0.5.1",
|
||||
"grunt-replace": "~1.0.1",
|
||||
"grunt-usemin": "~3.1.1",
|
||||
"grunt-webfont": "~1.4.0",
|
||||
"matchdep": "~1.0.1",
|
||||
"mocha": "~3.2.0",
|
||||
"sinon": "~1.17.4",
|
||||
"sinon-chai": "~2.8.0"
|
||||
"grunt-webfont": "~1.6.0",
|
||||
"matchdep": "~2.0.0",
|
||||
"mocha": "~4.1.0",
|
||||
"sinon": "~4.1.6",
|
||||
"sinon-chai": "~2.14.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test"
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
|
||||
},
|
||||
"maxAnonymousRunsPerDay": 99999999,
|
||||
"maxAnonymousCallsPerDay": 99999999
|
||||
"maxAnonymousCallsPerDay": 99999999,
|
||||
"blockedUrls": []
|
||||
}
|
||||
@@ -8,5 +8,6 @@
|
||||
|
||||
},
|
||||
"maxAnonymousRunsPerDay": 99999999,
|
||||
"maxAnonymousCallsPerDay": 99999999
|
||||
"maxAnonymousCallsPerDay": 99999999,
|
||||
"blockedUrls": []
|
||||
}
|
||||
@@ -687,4 +687,28 @@ describe('api', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should refuse a query on a blocked Url', function(done) {
|
||||
this.timeout(5000);
|
||||
|
||||
request({
|
||||
method: 'POST',
|
||||
url: serverUrl + '/api/runs',
|
||||
body: {
|
||||
url: 'http://www.test.com/something.html',
|
||||
waitForResponse: false
|
||||
},
|
||||
json: true,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
||||
}
|
||||
}, function(error, response, body) {
|
||||
if (!error && response.statusCode === 403) {
|
||||
done();
|
||||
} else {
|
||||
done(error || response.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -7,17 +7,17 @@ var path = require('path');
|
||||
describe('screenshotHandler', function() {
|
||||
|
||||
var imagePath = path.join(__dirname, '../fixtures/logo-large.png');
|
||||
var screenshot, lwipImage;
|
||||
var screenshot, jimpImage;
|
||||
|
||||
|
||||
it('should open an image and return an lwip object', function(done) {
|
||||
|
||||
it('should open an image and return an jimp object', function(done) {
|
||||
ScreenshotHandler.openImage(imagePath)
|
||||
.then(function(image) {
|
||||
lwipImage = image;
|
||||
jimpImage = image;
|
||||
|
||||
lwipImage.should.be.an('object');
|
||||
lwipImage.width().should.equal(620);
|
||||
lwipImage.height().should.equal(104);
|
||||
jimpImage.should.be.an('object');
|
||||
jimpImage.bitmap.width.should.equal(620);
|
||||
jimpImage.bitmap.height.should.equal(104);
|
||||
|
||||
done();
|
||||
})
|
||||
@@ -26,14 +26,14 @@ describe('screenshotHandler', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should resize an lwip image', function(done) {
|
||||
ScreenshotHandler.resizeImage(lwipImage, 310)
|
||||
|
||||
it('should resize an jimp image', function(done) {
|
||||
ScreenshotHandler.resizeImage(jimpImage, 310)
|
||||
.then(function(image) {
|
||||
lwipImage = image;
|
||||
jimpImage = image;
|
||||
|
||||
lwipImage.width().should.equal(310);
|
||||
lwipImage.height().should.equal(52);
|
||||
jimpImage.bitmap.width.should.equal(310);
|
||||
jimpImage.bitmap.height.should.equal(52);
|
||||
|
||||
done();
|
||||
})
|
||||
@@ -43,8 +43,8 @@ describe('screenshotHandler', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should transform a lwip image into a buffer', function(done) {
|
||||
ScreenshotHandler.toBuffer(lwipImage)
|
||||
it('should transform a jimp image into a buffer', function(done) {
|
||||
ScreenshotHandler.toBuffer(jimpImage)
|
||||
.then(function(buffer) {
|
||||
buffer.should.be.an.instanceof(Buffer);
|
||||
done();
|
||||
@@ -125,4 +125,4 @@ describe('screenshotHandler', function() {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
var path = require('path');
|
||||
var srcDir = path.join(__dirname, '..', 'server');
|
||||
|
||||
require('blanket')({
|
||||
// Only files that match the pattern will be instrumented
|
||||
pattern: srcDir
|
||||
});
|
||||
@@ -182,20 +182,19 @@ describe('customPolicies', function() {
|
||||
it('should grade correctly jQuery versions', function() {
|
||||
|
||||
var versions = {
|
||||
'1.2.9': 0,
|
||||
'1.6.3': 0,
|
||||
'1.7.0': 10,
|
||||
'1.8.3a': 20,
|
||||
'1.9.2': 30,
|
||||
'1.10.1': 40,
|
||||
'2.0.0-rc1': 40,
|
||||
'1.11.1': 50,
|
||||
'2.1.1-beta1': 50,
|
||||
'1.12.1': 70,
|
||||
'2.2.1': 70,
|
||||
'3.0.1': 90,
|
||||
'3.1.0': 100,
|
||||
'3.2.1': 100
|
||||
'1.7.0': 0,
|
||||
'1.10.1': 20,
|
||||
'1.10.3a': 20,
|
||||
'2.0.0-rc1': 20,
|
||||
'1.11.1': 30,
|
||||
'2.1.1-beta1': 30,
|
||||
'1.12.1': 40,
|
||||
'2.2.1': 40,
|
||||
'3.0.1': 50,
|
||||
'3.1.0': 70,
|
||||
'3.2.1': 90,
|
||||
'3.3.1': 100,
|
||||
'3.5.0': 100
|
||||
};
|
||||
|
||||
for (var version in versions) {
|
||||
|
||||
@@ -66,9 +66,9 @@ describe('index.js', function() {
|
||||
"tool": "phantomas",
|
||||
"label": "DOM max depth",
|
||||
"message": "<p>A deep DOM makes the CSS matching with DOM elements difficult.</p><p>It also slows down JavaScript modifications to the DOM because changing the dimensions of an element makes the browser re-calculate the dimensions of it's parents. Same thing for JavaScript events, that bubble up to the document root.</p>",
|
||||
"isOkThreshold": 10,
|
||||
"isBadThreshold": 20,
|
||||
"isAbnormalThreshold": 28,
|
||||
"isOkThreshold": 12,
|
||||
"isBadThreshold": 22,
|
||||
"isAbnormalThreshold": 30,
|
||||
"hasOffenders": true
|
||||
},
|
||||
"value": 1,
|
||||
|
||||
@@ -228,14 +228,14 @@ describe('redownload', function() {
|
||||
newEntry.weightCheck.bodySize.should.equal(4193);
|
||||
newEntry.weightCheck.bodyBuffer.should.deep.equal(fileContent);
|
||||
|
||||
// Opening the image in lwip to check if the format is good
|
||||
var lwip = require('lwip');
|
||||
lwip.open(newEntry.weightCheck.bodyBuffer, 'png', function(err, image) {
|
||||
image.width().should.equal(620);
|
||||
image.height().should.equal(104);
|
||||
// Opening the image in jimp to check if the format is good
|
||||
var Jimp = require('jimp');
|
||||
Jimp.read(newEntry.weightCheck.bodyBuffer, function(err, image) {
|
||||
image.bitmap.width.should.equal(620);
|
||||
image.bitmap.height.should.equal(104);
|
||||
done(err);
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
|
||||
.fail(function(err) {
|
||||
|
||||
Vendored
+4
-1
@@ -8,5 +8,8 @@
|
||||
"1234567890": "contact@gaelmetais.com"
|
||||
},
|
||||
"maxAnonymousRunsPerDay": 10,
|
||||
"maxAnonymousCallsPerDay": 1000
|
||||
"maxAnonymousCallsPerDay": 1000,
|
||||
"blockedUrls": [
|
||||
"http://www.test.com"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user