Work in progress: switch to Phantomas v2
This commit is contained in:
@@ -2,7 +2,6 @@ language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- "8.9"
|
||||
- "6.2"
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
|
||||
+5
-2
@@ -71,7 +71,10 @@ module.exports = function(grunt) {
|
||||
'test/core/*.js',
|
||||
'test/fixtures/*.js',
|
||||
'front/src/js/**/*.js'
|
||||
]
|
||||
],
|
||||
options: {
|
||||
esversion: 6
|
||||
}
|
||||
},
|
||||
clean: {
|
||||
tmp: {
|
||||
@@ -103,7 +106,7 @@ module.exports = function(grunt) {
|
||||
options: {
|
||||
reporter: 'spec',
|
||||
},
|
||||
src: ['test/api/apiTest.js']
|
||||
src: ['test/core/mediaQueriesCheckerTest.js']
|
||||
}
|
||||
},
|
||||
env: {
|
||||
|
||||
+9
-17
@@ -172,23 +172,15 @@ var OffendersHelpers = function() {
|
||||
};
|
||||
|
||||
this.cssOffenderPattern = function(offender) {
|
||||
// Remove any line breaks
|
||||
offender = offender.replace(/(\r\n|\n|\r)/gm, '');
|
||||
|
||||
var parts = /^(.*) (?:<([^ \(]*)>|\[inline CSS\]) ?@ ?(\d+):(\d+)$/.exec(offender);
|
||||
|
||||
if (!parts) {
|
||||
return {
|
||||
offender: offender
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
css: parts[1],
|
||||
file: parts[2] || null,
|
||||
line: parseInt(parts[3], 10),
|
||||
column: parseInt(parts[4], 10)
|
||||
};
|
||||
}
|
||||
// Used to work with strings
|
||||
// As of Phantomas v2, offender is now in JSON format.
|
||||
// Let's just adapt this for now and we'll see later if we remove completely this function
|
||||
return {
|
||||
css: offender.value.message,
|
||||
file: offender.url,
|
||||
line: offender.value.position.start.line,
|
||||
column: offender.value.position.start.column
|
||||
};
|
||||
};
|
||||
|
||||
this.fileWithSizePattern = function(fileWithSize) {
|
||||
|
||||
@@ -56,12 +56,6 @@ var Runner = function(params) {
|
||||
generic : scoreCalculator.calculate(data, scoreProfileGeneric)
|
||||
};
|
||||
|
||||
|
||||
delete data.toolsResults.phantomas.metrics.javascriptExecutionTree;
|
||||
delete data.toolsResults.phantomas.offenders.javascriptExecutionTree;
|
||||
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;
|
||||
|
||||
@@ -87,91 +87,55 @@ var PhantomasWrapper = function() {
|
||||
debug('node node_modules/phantomas/bin/phantomas.js --url=' + task.url + optionsString + ' --verbose');
|
||||
|
||||
|
||||
var phantomasPid;
|
||||
var isKilled = false;
|
||||
|
||||
// Kill phantomas if nothing happens
|
||||
var killer = setTimeout(function() {
|
||||
console.log('Killing phantomas because the test on ' + task.url + ' was launched ' + 5*options.timeout + ' seconds ago');
|
||||
|
||||
if (phantomasPid) {
|
||||
ps.kill(phantomasPid, function(err) {
|
||||
|
||||
if (err) {
|
||||
debug('Could not kill Phantomas process %s', phantomasPid);
|
||||
|
||||
// Suicide
|
||||
process.exit(1);
|
||||
// If in server mode, forever will restart the server
|
||||
}
|
||||
|
||||
debug('Phantomas process %s was correctly killed', phantomasPid);
|
||||
|
||||
// Then mark the test as failed
|
||||
// Error 1003 = Phantomas not answering
|
||||
deferred.reject(1003);
|
||||
isKilled = true;
|
||||
});
|
||||
} else {
|
||||
// Suicide
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
}, 5*options.timeout*1000);
|
||||
|
||||
|
||||
// It's time to launch the test!!!
|
||||
var triesNumber = 2;
|
||||
var currentTry = 0;
|
||||
|
||||
const promise = phantomas(task.url, {
|
||||
'analyze-css': true
|
||||
});
|
||||
|
||||
async.retry(triesNumber, function(cb) {
|
||||
// handle the promise
|
||||
promise.
|
||||
then(results => {
|
||||
var json = {
|
||||
generator: results.getGenerator(),
|
||||
url: results.getUrl(),
|
||||
metrics: results.getMetrics(),
|
||||
offenders: results.getAllOffenders()
|
||||
};
|
||||
|
||||
currentTry ++;
|
||||
|
||||
var process = phantomas(task.url, options, function(err, json, results) {
|
||||
var errorCode = err ? parseInt(err.message, 10) : null;
|
||||
|
||||
if (isKilled) {
|
||||
debug('Process was killed, too late Phantomas, sorry...');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
debug('Returning from Phantomas with error %s', errorCode);
|
||||
|
||||
// Adding some YellowLabTools errors here
|
||||
if (json && json.metrics && (!json.metrics.javascriptExecutionTree || !json.offenders.javascriptExecutionTree)) {
|
||||
errorCode = 1001;
|
||||
}
|
||||
|
||||
if (!errorCode && (!json || !json.metrics)) {
|
||||
errorCode = 1002;
|
||||
}
|
||||
|
||||
// Don't cancel test if it is a timeout and we've got some results
|
||||
if (errorCode === 252 && json) {
|
||||
debug('Timeout after ' + options.timeout + ' seconds. But it\'s not a problem, the test is valid.');
|
||||
errorCode = null;
|
||||
}
|
||||
|
||||
if (errorCode) {
|
||||
debug('Attempt failed. Error code ' + errorCode);
|
||||
}
|
||||
|
||||
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');
|
||||
deferred.resolve(json);
|
||||
}).
|
||||
catch(res => {
|
||||
console.error(res);
|
||||
deferred.reject('Phantomas failed: ' + res.message);
|
||||
});
|
||||
|
||||
phantomasPid = process.pid;
|
||||
|
||||
/*var process = phantomas(task.url, options, function(err, json, results) {
|
||||
var errorCode = err ? parseInt(err.message, 10) : null;
|
||||
|
||||
debug('Returning from Phantomas with error %s', errorCode);
|
||||
|
||||
// Adding some YellowLabTools errors here
|
||||
if (json && json.metrics && (!json.metrics.javascriptExecutionTree || !json.offenders.javascriptExecutionTree)) {
|
||||
errorCode = 1001;
|
||||
}
|
||||
|
||||
if (!errorCode && (!json || !json.metrics)) {
|
||||
errorCode = 1002;
|
||||
}
|
||||
|
||||
// Don't cancel test if it is a timeout and we've got some results
|
||||
if (errorCode === 252 && json) {
|
||||
debug('Timeout after ' + options.timeout + ' seconds. But it\'s not a problem, the test is valid.');
|
||||
errorCode = null;
|
||||
}
|
||||
|
||||
if (errorCode) {
|
||||
debug('Attempt failed. Error code ' + errorCode);
|
||||
}
|
||||
|
||||
}, function(err, json) {
|
||||
|
||||
clearTimeout(killer);
|
||||
|
||||
if (err) {
|
||||
debug('All ' + triesNumber + ' attemps failed for the test');
|
||||
deferred.reject(err);
|
||||
@@ -182,6 +146,7 @@ var PhantomasWrapper = function() {
|
||||
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ var isJpg = require('is-jpg');
|
||||
var isPng = require('is-png');
|
||||
var isSvg = require('is-svg');
|
||||
var isGif = require('is-gif');
|
||||
var isWebp = require('is-webp');
|
||||
var isWoff = require('is-woff');
|
||||
var isWoff2 = require('is-woff2');
|
||||
var isOtf = require('is-otf');
|
||||
@@ -17,6 +18,39 @@ var ContentTypeChecker = function() {
|
||||
|
||||
debug('Entering contentTypeChecker');
|
||||
|
||||
// Setting isSomething values:
|
||||
switch(entry.type) {
|
||||
case 'html':
|
||||
entry.isHTML = true;
|
||||
break;
|
||||
case 'xml':
|
||||
entry.isXML = true;
|
||||
break;
|
||||
case 'css':
|
||||
entry.isCSS = true;
|
||||
break;
|
||||
case 'js':
|
||||
entry.isJS = true;
|
||||
break;
|
||||
case 'json':
|
||||
entry.isJSON = true;
|
||||
break;
|
||||
case 'image':
|
||||
entry.isImage = true;
|
||||
break;
|
||||
case 'webfont':
|
||||
entry.isWebFont = true;
|
||||
break;
|
||||
case 'video':
|
||||
entry.isVideo = true;
|
||||
break;
|
||||
case 'favicon':
|
||||
entry.isFavicon = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Now let's check for mistakes by analysing body content. It happens more often then we think!
|
||||
|
||||
// Ignore very small files as they are generally tracking pixels
|
||||
if (entry.weightCheck && entry.weightCheck.bodyBuffer && entry.weightCheck.bodySize > 100) {
|
||||
var foundType;
|
||||
@@ -24,6 +58,9 @@ var ContentTypeChecker = function() {
|
||||
try {
|
||||
foundType = findContentType(entry.weightCheck.bodyBuffer);
|
||||
|
||||
// Note: as of Phantomas v2, entry.contentType is always undefined
|
||||
// It could change, so let's keep the following code in place:
|
||||
|
||||
if (!entry.contentType || entry.contentType === '') {
|
||||
if (foundType === null) {
|
||||
debug('ContentType is empty for file %s', entry.url);
|
||||
@@ -71,6 +108,10 @@ var ContentTypeChecker = function() {
|
||||
return contentTypes.gif;
|
||||
}
|
||||
|
||||
if (isWebp(bodyBuffer)) {
|
||||
return contentTypes.webp;
|
||||
}
|
||||
|
||||
if (isWoff(bodyBuffer)) {
|
||||
return contentTypes.woff;
|
||||
}
|
||||
@@ -142,6 +183,13 @@ var ContentTypeChecker = function() {
|
||||
entry.isImage = true;
|
||||
}
|
||||
},
|
||||
webp: {
|
||||
mimes: ['image/webp'],
|
||||
updateFn: function(entry) {
|
||||
entry.type = 'image';
|
||||
entry.isImage = true;
|
||||
}
|
||||
},
|
||||
woff: {
|
||||
mimes: ['application/x-font-woff', 'application/font-woff', 'font/woff'],
|
||||
updateFn: function(entry) {
|
||||
|
||||
@@ -34,9 +34,13 @@ var Redownload = function() {
|
||||
debug('Redownload started');
|
||||
var deferred = Q.defer();
|
||||
|
||||
var requestsList = JSON.parse(data.toolsResults.phantomas.offenders.requestsList);
|
||||
delete data.toolsResults.phantomas.metrics.requestsList;
|
||||
delete data.toolsResults.phantomas.offenders.requestsList;
|
||||
var requestsOffenders = data.toolsResults.phantomas.offenders.requests;
|
||||
var gzipOffenders = data.toolsResults.phantomas.offenders.gzipRequests;
|
||||
var postOffenders = data.toolsResults.phantomas.offenders.postRequests;
|
||||
var notFoundOffenders = data.toolsResults.phantomas.offenders.notFound;
|
||||
var redirectOffenders = data.toolsResults.phantomas.offenders.redirects;
|
||||
|
||||
var requestsList = mergeOffenders(requestsOffenders, gzipOffenders, postOffenders, notFoundOffenders, redirectOffenders);
|
||||
|
||||
var httpAuth = null;
|
||||
if (data.params && data.params.options && data.params.options.authUser && data.params.options.authPass) {
|
||||
@@ -194,6 +198,57 @@ var Redownload = function() {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function mergeOffenders(requests, compressedOffenders, postOffenders, notFoundOffenders, redirectOffenders) {
|
||||
|
||||
// Parse each request and check if it can be found in other offenders
|
||||
requests.forEach(function(request) {
|
||||
|
||||
// Is it compressed?
|
||||
if (compressedOffenders) {
|
||||
compressedOffenders.some(function(entry) {
|
||||
if (entry.url === request.url) {
|
||||
request.compressed = true;
|
||||
request.bodySize = entry.bodySize;
|
||||
request.transferedSize = entry.transferedSize;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Is it a POST request?
|
||||
if (postOffenders) {
|
||||
postOffenders.some(function(url) {
|
||||
if (url === request.url) {
|
||||
request.post = true;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Is it a 404?
|
||||
if (notFoundOffenders) {
|
||||
notFoundOffenders.some(function(url) {
|
||||
if (url === request.url) {
|
||||
request.notFound = true;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Is it a redirection?
|
||||
if (redirectOffenders) {
|
||||
redirectOffenders.some(function(message) {
|
||||
if (message.split(' ')[0] === request.url) {
|
||||
request.redirect = true;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return requests;
|
||||
}
|
||||
|
||||
function listIncorrectContentTypes(requests) {
|
||||
var results = [];
|
||||
|
||||
@@ -639,13 +694,15 @@ var Redownload = function() {
|
||||
deferred.resolve(entry);
|
||||
}
|
||||
|
||||
if (entry.method !== 'GET') {
|
||||
if (entry.post) {
|
||||
notDownloadableFile('only downloading GET');
|
||||
// ... at least trying to
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
if (entry.status !== 200) {
|
||||
if (entry.notFound || entry.redirect) {
|
||||
unwantedFile('only downloading requests with status code 200');
|
||||
// ...at least trying to
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
@@ -654,12 +711,12 @@ var Redownload = function() {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
||||
debug('Downloading %s', entry.url);
|
||||
|
||||
// Always add a gzip header before sending, in case the server listens to it
|
||||
var reqHeaders = entry.requestHeaders;
|
||||
reqHeaders['Accept-Encoding'] = 'gzip, deflate';
|
||||
// Always add gzip and webp headers before sending, in case the server listens to them
|
||||
var reqHeaders = [];
|
||||
reqHeaders['Accept'] = '*/*,image/webp';
|
||||
reqHeaders['Accept-Encoding'] = 'gzip, deflate, br';
|
||||
reqHeaders['Connection'] = 'keep-alive';
|
||||
|
||||
var requestOptions = {
|
||||
|
||||
+3
-2
@@ -16,7 +16,7 @@
|
||||
"yellowlabtools": "./bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6.0"
|
||||
"node": ">= 8.0"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"dependencies": {
|
||||
@@ -54,6 +54,7 @@
|
||||
"is-png": "1.1.0",
|
||||
"is-svg": "3.0.0",
|
||||
"is-ttf": "0.2.2",
|
||||
"is-webp": "1.0.1",
|
||||
"is-woff": "1.0.3",
|
||||
"is-woff2": "1.0.0",
|
||||
"jimp": "0.6.0",
|
||||
@@ -61,7 +62,7 @@
|
||||
"meow": "5.0.0",
|
||||
"minimize": "2.2.0",
|
||||
"parse-color": "1.0.0",
|
||||
"phantomas": "1.19.0",
|
||||
"phantomas": "git://github.com/macbre/phantomas.git#phantomas-v2",
|
||||
"ps-node": "0.1.6",
|
||||
"q": "1.5.1",
|
||||
"request": "2.88.0",
|
||||
|
||||
@@ -171,9 +171,6 @@ describe('api', function() {
|
||||
body.should.have.a.property('rules').that.is.an('object');
|
||||
body.should.have.a.property('toolsResults').that.is.an('object');
|
||||
|
||||
body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
|
||||
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('*');
|
||||
@@ -410,10 +407,6 @@ describe('api', function() {
|
||||
body.should.have.a.property('toolsResults').that.is.an('object');
|
||||
body.toolsResults.should.have.a.property('phantomas').that.is.an('object');
|
||||
|
||||
body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
|
||||
body.javascriptExecutionTree.should.have.a.property('data').that.is.an('object');
|
||||
body.javascriptExecutionTree.data.should.have.a.property('type').that.equals('main');
|
||||
|
||||
done();
|
||||
|
||||
} else {
|
||||
@@ -507,28 +500,6 @@ describe('api', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should return the javascript execution tree', function(done) {
|
||||
this.timeout(5000);
|
||||
|
||||
request({
|
||||
method: 'GET',
|
||||
url: serverUrl + '/api/results/' + asyncRunId + '/javascriptExecutionTree',
|
||||
json: true,
|
||||
}, function(error, response, body) {
|
||||
if (!error && response.statusCode === 200) {
|
||||
|
||||
body.should.have.a.property('data').that.is.an('object');
|
||||
body.data.should.have.a.property('type').that.equals('main');
|
||||
|
||||
done();
|
||||
|
||||
} else {
|
||||
done(error || response.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should return the phantomas results', function(done) {
|
||||
this.timeout(5000);
|
||||
|
||||
@@ -565,7 +536,6 @@ describe('api', function() {
|
||||
body.should.have.a.property('params').that.is.an('object');
|
||||
body.should.have.a.property('scoreProfiles').that.is.an('object');
|
||||
body.should.have.a.property('rules').that.is.an('object');
|
||||
body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
|
||||
|
||||
body.should.not.have.a.property('toolsResults').that.is.an('object');
|
||||
|
||||
@@ -591,7 +561,6 @@ describe('api', function() {
|
||||
body.should.have.a.property('runId').that.equals(asyncRunId);
|
||||
body.should.have.a.property('scoreProfiles').that.is.an('object');
|
||||
body.should.have.a.property('rules').that.is.an('object');
|
||||
body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
|
||||
|
||||
body.should.not.have.a.property('params').that.is.an('object');
|
||||
body.should.not.have.a.property('toolsResults').that.is.an('object');
|
||||
@@ -617,7 +586,6 @@ describe('api', function() {
|
||||
body.should.have.a.property('runId').that.equals(asyncRunId);
|
||||
body.should.have.a.property('scoreProfiles').that.is.an('object');
|
||||
body.should.have.a.property('rules').that.is.an('object');
|
||||
body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
|
||||
body.should.have.a.property('params').that.is.an('object');
|
||||
body.should.have.a.property('toolsResults').that.is.an('object');
|
||||
|
||||
@@ -642,7 +610,6 @@ describe('api', function() {
|
||||
body.should.have.a.property('runId').that.equals(asyncRunId);
|
||||
body.should.have.a.property('scoreProfiles').that.is.an('object');
|
||||
body.should.have.a.property('rules').that.is.an('object');
|
||||
body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
|
||||
body.should.have.a.property('params').that.is.an('object');
|
||||
body.should.have.a.property('toolsResults').that.is.an('object');
|
||||
|
||||
|
||||
@@ -87,17 +87,6 @@ describe('index.js', function() {
|
||||
}
|
||||
});
|
||||
|
||||
// Test javascriptExecutionTree
|
||||
data.toolsResults.phantomas.metrics.should.not.have.a.property('javascriptExecutionTree');
|
||||
data.toolsResults.phantomas.offenders.should.not.have.a.property('javascriptExecutionTree');
|
||||
data.should.have.a.property('javascriptExecutionTree').that.is.an('object');
|
||||
data.javascriptExecutionTree.should.have.a.property('data');
|
||||
data.javascriptExecutionTree.data.should.have.a.property('type').that.equals('main');
|
||||
data.javascriptExecutionTree.data.should.have.a.property('domInteractive').that.is.a('number');
|
||||
data.javascriptExecutionTree.data.should.have.a.property('domContentLoaded').that.is.a('number');
|
||||
data.javascriptExecutionTree.data.should.have.a.property('domContentLoadedEnd').that.is.a('number');
|
||||
data.javascriptExecutionTree.data.should.have.a.property('domComplete').that.is.a('number');
|
||||
|
||||
/*jshint expr: true*/
|
||||
console.log.should.not.have.been.called;
|
||||
|
||||
|
||||
@@ -2,54 +2,74 @@ var should = require('chai').should();
|
||||
var mediaQueriesChecker = require('../../lib/tools/mediaQueriesChecker');
|
||||
|
||||
describe('mediaQueriesChecker', function() {
|
||||
|
||||
function wrap(message) {
|
||||
return {
|
||||
url: 'http://domain.com/css/stylesheet.css',
|
||||
value: {
|
||||
message: message,
|
||||
position: {
|
||||
start: {
|
||||
line: 269,
|
||||
column: 1
|
||||
},
|
||||
end: {
|
||||
line: 269,
|
||||
column: 182
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
it('should parse mediaQueryes correctly', function() {
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (max-width: 1024px) (1 rules) <http://domain.com/css/stylesheet.css> @ 269:1').should.deep.equal({
|
||||
mediaQuery: {
|
||||
query: 'screen and (max-width: 1024px)',
|
||||
rules: 1,
|
||||
file: 'http://domain.com/css/stylesheet.css',
|
||||
line: 269,
|
||||
column: 1
|
||||
},
|
||||
isForMobile: true,
|
||||
breakpoints: [{string: '1024px', pixels: 1024}]
|
||||
});
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (max-width: 1024px) (1 rules)'))
|
||||
.should.deep.equal({
|
||||
mediaQuery: {
|
||||
query: 'screen and (max-width: 1024px)',
|
||||
rules: 1,
|
||||
file: 'http://domain.com/css/stylesheet.css',
|
||||
line: 269,
|
||||
column: 1
|
||||
},
|
||||
isForMobile: true,
|
||||
breakpoints: [{string: '1024px', pixels: 1024}]
|
||||
});
|
||||
});
|
||||
|
||||
it('should determine if it\'s for mobile correctly', function() {
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (max-width: 1024px) (1 rules) <file> @ 1:1').isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media (max-width:1024px) (1 rules) <file> @ 1:1').isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (max-width: 320px) (1 rules) <file> @ 1:1').isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (max-width: 290px) (1 rules) <file> @ 1:1').isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (min-width: 320px) (1 rules) <file> @ 1:1').isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (min-width: 600px) (1 rules) <file> @ 1:1').isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (max-width: 20em) (1 rules) <file> @ 1:1').isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (min-width: 40em) (1 rules) <file> @ 1:1').isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media (min-width: 600px) and (max-width: 1000px) (1 rules) <file> @ 1:1').isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media (min-width: 180px) and (max-width: 400px) (1 rules) <file> @ 1:1').isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media (min-width: 180px) and (max-width: 290px) (1 rules) <file> @ 1:1').isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media not all and (min-width: 600px) (1 rules) <file> @ 1:1').isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media not all and (min-width: 180px) (1 rules) <file> @ 1:1').isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media not all and (min-width: 1000px) and (max-width: 600px) (1 rules) <file> @ 1:1').isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media not all and (min-width: 400px) and (max-width: 180px) (1 rules) <file> @ 1:1').isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (max-width: 1024px) (1 rules)')).isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media (max-width:1024px) (1 rules)')).isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (max-width: 320px) (1 rules)')).isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (max-width: 290px) (1 rules)')).isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (min-width: 320px) (1 rules)')).isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (min-width: 600px) (1 rules)')).isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (max-width: 20em) (1 rules)')).isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (min-width: 40em) (1 rules)')).isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media (min-width: 600px) and (max-width: 1000px) (1 rules)')).isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media (min-width: 180px) and (max-width: 400px) (1 rules)')).isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media (min-width: 180px) and (max-width: 290px) (1 rules)')).isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media not all and (min-width: 600px) (1 rules)')).isForMobile.should.equal(true);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media not all and (min-width: 180px) (1 rules)')).isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media not all and (min-width: 1000px) and (max-width: 600px) (1 rules)')).isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media not all and (min-width: 400px) and (max-width: 180px) (1 rules)')).isForMobile.should.equal(true);
|
||||
});
|
||||
|
||||
it('should count breakpoints correctly', function() {
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (max-width: 1024px) (1 rules) <file> @ 1:1').breakpoints.should.deep.equal([{string: '1024px', pixels: 1024}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media (max-width:1024px) (1 rules) <file> @ 1:1').breakpoints.should.deep.equal([{string: '1024px', pixels: 1024}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (max-width: 320px) (1 rules) <file> @ 1:1').breakpoints.should.deep.equal([{string: '320px', pixels: 320}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media (min-width: 600px) and (max-width: 1000px) (1 rules) <file> @ 1:1').breakpoints.should.deep.equal([{string: '600px', pixels: 600}, {string: '1000px', pixels: 1000}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media not all and (min-width: 180px) (1 rules) <file> @ 1:1').breakpoints.should.deep.equal([{string: '180px', pixels: 180}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media not all and (min-width: 1000px) and (max-width: 600px) (1 rules) <file> @ 1:1').breakpoints.should.deep.equal([{string: '1000px', pixels: 1000}, {string: '600px', pixels: 600}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media (max-height:500px) (1 rules) <file> @ 1:1').breakpoints.should.deep.equal([]);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (max-width: 100em) (1 rules) <file> @ 1:1').breakpoints.should.deep.equal([{string: '100em', pixels: 1600}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (max-width: 120pt) (1 rules) <file> @ 1:1').breakpoints.should.deep.equal([{string: '120pt', pixels: 160}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media screen and (max-width: 40.2em) (1 rules) <file> @ 1:1').breakpoints.should.deep.equal([{string: '40.2em', pixels: 643.2}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (max-width: 1024px) (1 rules)')).breakpoints.should.deep.equal([{string: '1024px', pixels: 1024}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media (max-width:1024px) (1 rules)')).breakpoints.should.deep.equal([{string: '1024px', pixels: 1024}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (max-width: 320px) (1 rules)')).breakpoints.should.deep.equal([{string: '320px', pixels: 320}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media (min-width: 600px) and (max-width: 1000px) (1 rules)')).breakpoints.should.deep.equal([{string: '600px', pixels: 600}, {string: '1000px', pixels: 1000}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media not all and (min-width: 180px) (1 rules)')).breakpoints.should.deep.equal([{string: '180px', pixels: 180}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media not all and (min-width: 1000px) and (max-width: 600px) (1 rules)')).breakpoints.should.deep.equal([{string: '1000px', pixels: 1000}, {string: '600px', pixels: 600}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media (max-height:500px) (1 rules)')).breakpoints.should.deep.equal([]);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (max-width: 100em) (1 rules)')).breakpoints.should.deep.equal([{string: '100em', pixels: 1600}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (max-width: 120pt) (1 rules)')).breakpoints.should.deep.equal([{string: '120pt', pixels: 160}]);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media screen and (max-width: 40.2em) (1 rules)')).breakpoints.should.deep.equal([{string: '40.2em', pixels: 643.2}]);
|
||||
});
|
||||
|
||||
it('should fail silently', function() {
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media bad syntax (1 rules) <file> @ 1:1').isForMobile.should.equal(false);
|
||||
mediaQueriesChecker.parseOneMediaQuery(wrap('@media bad syntax (1 rules)')).isForMobile.should.equal(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -307,52 +307,6 @@ describe('offendersHelpers', function() {
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('cssOffenderPattern', function() {
|
||||
|
||||
it('should transform a css offender into an object', function() {
|
||||
var result = offendersHelpers.cssOffenderPattern('.pagination .plus ul li <http://www.pouet.com/css/main.css> @ 30:31862');
|
||||
|
||||
result.should.deep.equal({
|
||||
css: '.pagination .plus ul li',
|
||||
file: 'http://www.pouet.com/css/main.css',
|
||||
line: 30,
|
||||
column: 31862
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with an inline css', function() {
|
||||
var result = offendersHelpers.cssOffenderPattern('.pagination .plus ul li [inline CSS] @ 1:32');
|
||||
|
||||
result.should.deep.equal({
|
||||
css: '.pagination .plus ul li',
|
||||
file: null,
|
||||
line: 1,
|
||||
column: 32
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle the case where line and char are not here', function() {
|
||||
var result = offendersHelpers.cssOffenderPattern('.pagination .plus ul li');
|
||||
|
||||
result.should.deep.equal({
|
||||
offender: '.pagination .plus ul li'
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle line breaks inside the string', function() {
|
||||
var result = offendersHelpers.cssOffenderPattern('.card-mask-wrap { -moz-transform: translate3d(0, \n-288px\n, 0) } // was required by firefox 15 and earlier [inline CSS] @ 29:3');
|
||||
|
||||
result.should.deep.equal({
|
||||
css: '.card-mask-wrap { -moz-transform: translate3d(0, -288px, 0) } // was required by firefox 15 and earlier',
|
||||
file: null,
|
||||
line: 29,
|
||||
column: 3
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('fileWithSizePattern', function() {
|
||||
|
||||
it('should return an object', function() {
|
||||
|
||||
@@ -26,7 +26,6 @@ describe('phantomasWrapper', function() {
|
||||
data.should.have.a.property('url').that.equals(url);
|
||||
data.should.have.a.property('metrics').that.is.an('object').not.empty;
|
||||
data.should.have.a.property('offenders').that.is.an('object').not.empty;
|
||||
data.offenders.should.have.a.property('javascriptExecutionTree').that.is.a('array').not.empty;
|
||||
|
||||
done();
|
||||
}).fail(function(err) {
|
||||
@@ -34,7 +33,7 @@ describe('phantomasWrapper', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail with error 254', function(done) {
|
||||
it('should fail when page cannot be fetched', function(done) {
|
||||
var url = 'http://localhost:8389/not-existing.html';
|
||||
|
||||
this.timeout(15000);
|
||||
@@ -52,8 +51,8 @@ describe('phantomasWrapper', function() {
|
||||
|
||||
}).fail(function(err) {
|
||||
try {
|
||||
console.log(err);
|
||||
should.exist(err);
|
||||
err.should.equal(254);
|
||||
|
||||
done();
|
||||
} catch(error) {
|
||||
|
||||
+15
-74
@@ -10,101 +10,42 @@ describe('redownload', function() {
|
||||
|
||||
var requestsList = [
|
||||
{
|
||||
method: 'GET',
|
||||
url: 'http://localhost:8388/simple-page.html',
|
||||
requestHeaders: {
|
||||
'User-Agent': 'something',
|
||||
Referer: 'http://www.google.fr/',
|
||||
Accept: '*/*'
|
||||
},
|
||||
status: 200,
|
||||
isHTML: true,
|
||||
type: 'html'
|
||||
type: 'html',
|
||||
size: 30000
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
url: 'http://localhost:8388/jquery1.8.3.js',
|
||||
requestHeaders: {
|
||||
'User-Agent': 'something',
|
||||
Referer: 'http://www.google.fr/',
|
||||
Accept: '*/*'
|
||||
},
|
||||
status: 200,
|
||||
isJS: true,
|
||||
type: 'js'
|
||||
type: 'js',
|
||||
size: 30000
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
url: 'http://localhost:8388/jpeg-image.jpg',
|
||||
requestHeaders: {
|
||||
'User-Agent': 'something',
|
||||
Referer: 'http://www.google.fr/',
|
||||
Accept: '*/*'
|
||||
},
|
||||
status: 200,
|
||||
isImage: true,
|
||||
type: 'image',
|
||||
contentType: 'image/jpeg'
|
||||
size: 30000
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
url: 'http://localhost:8388/svg-image.svg',
|
||||
requestHeaders: {
|
||||
'User-Agent': 'something',
|
||||
Referer: 'http://www.google.fr/',
|
||||
Accept: '*/*'
|
||||
},
|
||||
status: 200,
|
||||
isImage: true,
|
||||
isSVG: true,
|
||||
type: 'image',
|
||||
contentType: 'image/svg+xml'
|
||||
size: 30000
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
url: 'http://localhost:8388/unminified-script.js',
|
||||
requestHeaders: {
|
||||
'User-Agent': 'something',
|
||||
Referer: 'http://www.google.fr/',
|
||||
Accept: '*/*'
|
||||
},
|
||||
status: 200,
|
||||
isJS: true,
|
||||
type: 'js'
|
||||
type: 'js',
|
||||
size: 30000
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
url: 'http://localhost:8388/unminified-stylesheet.css',
|
||||
requestHeaders: {
|
||||
'User-Agent': 'something',
|
||||
Referer: 'http://www.google.fr/',
|
||||
Accept: '*/*'
|
||||
},
|
||||
status: 200,
|
||||
isCSS: true,
|
||||
type: 'css'
|
||||
type: 'css',
|
||||
size: 30000
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
url: 'http://localhost:8388/xml.xml',
|
||||
requestHeaders: {
|
||||
'User-Agent': 'something',
|
||||
Referer: 'http://www.google.fr/',
|
||||
Accept: '*/*'
|
||||
},
|
||||
status: 200,
|
||||
isXML: true,
|
||||
type: 'xml'
|
||||
type: 'xml',
|
||||
size: 30000
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
url: 'about:blank',
|
||||
requestHeaders: {
|
||||
'User-Agent': 'something',
|
||||
Referer: 'http://www.google.fr/',
|
||||
Accept: '*/*'
|
||||
},
|
||||
status: 200
|
||||
url: 'about:blank'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -117,10 +58,10 @@ describe('redownload', function() {
|
||||
toolsResults: {
|
||||
phantomas: {
|
||||
metrics: {
|
||||
requestsList: true
|
||||
requests: 8
|
||||
},
|
||||
offenders: {
|
||||
requestsList: JSON.stringify(requestsList)
|
||||
requests: requestsList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user