Merge pull request #128 from gmetais/develop
Pushing to production soon
This commit is contained in:
@@ -75,14 +75,6 @@ You can use [ngrok](https://ngrok.com/), a tool that creates a secure tunnel bet
|
||||
If your project is not accessible from outside, or if you want to fork and improve the tool, you can build your own instance. The documentation is [here](https://github.com/gmetais/YellowLabTools/wiki/Install-your-private-server).
|
||||
|
||||
|
||||
## Help needed!
|
||||
There are so many things left to do, **your help would be greatly appreciated**! Please report bugs, ask for evolutions and come code with me.
|
||||
|
||||
|
||||
## License
|
||||
Please read the [license](LICENSE). Icons are the property of [IcoMoon.io](https://icomoon.io/)
|
||||
|
||||
|
||||
## Author
|
||||
Gaël Métais. I'm a webperf freelance. Follow me on Twitter [@gaelmetais](https://twitter.com/gaelmetais), I tweet about Web Performances, Front-end and new versions of YellowLabTools!
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
.checker {
|
||||
/* Checkerboard background */
|
||||
background-color: #ddd;
|
||||
background-image: linear-gradient(45deg, #aaaaaa 25%, transparent 25%, transparent 75%, #aaaaaa 75%, #aaaaaa), linear-gradient(45deg, #aaaaaa 25%, transparent 25%, transparent 75%, #aaaaaa 75%, #aaaaaa);
|
||||
background-image: linear-gradient(45deg, #AAA 25%, transparent 25%, transparent 75%, #AAA 75%, #AAA), linear-gradient(45deg, #AAA 25%, transparent 25%, transparent 75%, #AAA 75%, #AAA);
|
||||
background-size: 1em 1em;
|
||||
background-position: 0 0, 0.5em 0.5em;
|
||||
}
|
||||
|
||||
+15
-15
@@ -62,34 +62,34 @@
|
||||
background: #F04DA7;
|
||||
}
|
||||
.timeline .domComplete.interval {
|
||||
background: #ede3ff;
|
||||
background: #EDE3FF;
|
||||
}
|
||||
.timeline .domComplete .color {
|
||||
background: #c2a3ff;
|
||||
background: #C2A3FF;
|
||||
}
|
||||
.timeline .domContentLoadedEnd.interval {
|
||||
background: #d8f0f0;
|
||||
background: #D8F0F0;
|
||||
}
|
||||
.timeline .domContentLoadedEnd .color {
|
||||
background: #7ecccc;
|
||||
background: #7ECCCC;
|
||||
}
|
||||
.timeline .domContentLoaded.interval {
|
||||
background: #e0ffd1;
|
||||
background: #E0FFD1;
|
||||
}
|
||||
.timeline .domContentLoaded .color {
|
||||
background: #a7e846;
|
||||
background: #A7E846;
|
||||
}
|
||||
.timeline .domInteractive.interval {
|
||||
background: #fffccc;
|
||||
background: #FFFCCC;
|
||||
}
|
||||
.timeline .domInteractive .color {
|
||||
background: #ffe433;
|
||||
background: #FFE433;
|
||||
}
|
||||
.timeline .domCreation.interval {
|
||||
background: #ffe0cc;
|
||||
background: #FFE0CC;
|
||||
}
|
||||
.timeline .domCreation .color {
|
||||
background: #ff6600;
|
||||
background: #FF6600;
|
||||
}
|
||||
.timeline .tooltip.detailsOverlay {
|
||||
position: absolute;
|
||||
@@ -294,19 +294,19 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
.table > div > .startTime.domComplete {
|
||||
background: #ede3ff;
|
||||
background: #EDE3FF;
|
||||
}
|
||||
.table > div > .startTime.domContentLoadedEnd {
|
||||
background: #d8f0f0;
|
||||
background: #D8F0F0;
|
||||
}
|
||||
.table > div > .startTime.domContentLoaded {
|
||||
background: #e0ffd1;
|
||||
background: #E0FFD1;
|
||||
}
|
||||
.table > div > .startTime.domInteractive {
|
||||
background: #fffccc;
|
||||
background: #FFFCCC;
|
||||
}
|
||||
.table > div > .startTime.domCreation {
|
||||
background: #ffe0cc;
|
||||
background: #FFE0CC;
|
||||
}
|
||||
.execution .icon-warning {
|
||||
color: #e74c3c;
|
||||
|
||||
@@ -48,6 +48,54 @@ ruleCtrl.controller('RuleCtrl', ['$scope', '$rootScope', '$routeParams', '$locat
|
||||
tooltipTemplate: '<%=label%>: <%=value%> KB'
|
||||
};
|
||||
}
|
||||
|
||||
// Init "Breakpoints" chart
|
||||
if ($scope.policyName === 'cssBreakpoints' && $scope.rule.value > 0) {
|
||||
|
||||
// Seek for the biggest breakpoint
|
||||
var max = 0;
|
||||
$scope.rule.offendersObj.forEach(function(offender) {
|
||||
if (offender.pixels > max) {
|
||||
max = offender.pixels;
|
||||
}
|
||||
});
|
||||
max = Math.max(max + 100, 1400);
|
||||
|
||||
// We group offenders 10px by 10px
|
||||
var GROUP_SIZE = 20;
|
||||
|
||||
// Generate an empty array of values
|
||||
$scope.breakpointsLabels = [];
|
||||
$scope.breakpointsData = [[]];
|
||||
for (var i = 0; i <= max / GROUP_SIZE; i++) {
|
||||
$scope.breakpointsLabels[i] = '';
|
||||
$scope.breakpointsData[0][i] = 0;
|
||||
}
|
||||
|
||||
// Fill it with results
|
||||
$scope.rule.offendersObj.forEach(function(offender) {
|
||||
var group = Math.floor((offender.pixels + 1) / GROUP_SIZE);
|
||||
|
||||
if ($scope.breakpointsLabels[group] !== '') {
|
||||
$scope.breakpointsLabels[group] += '/';
|
||||
}
|
||||
$scope.breakpointsLabels[group] += offender.breakpoint;
|
||||
|
||||
$scope.breakpointsData[0][group] += offender.count;
|
||||
});
|
||||
|
||||
$scope.breakpointsColours = ['#9c4274'];
|
||||
$scope.breakpointsOptions = {
|
||||
scaleShowGridLines: false,
|
||||
barShowStroke: false,
|
||||
showTooltips: false,
|
||||
pointDot: false,
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
strokeColor: 'rgba(20, 200, 20, 1)',
|
||||
scaleFontSize: 9
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
$scope.backToDashboard = function() {
|
||||
|
||||
@@ -100,6 +100,11 @@
|
||||
<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>
|
||||
|
||||
<div ng-if="policyName === 'cssMobileFirst'">
|
||||
<b>{{offender.query}}</b> for <ng-pluralize count="offender.rules" when="{'one':'1 rule','other':'{} rules'}"></ng-pluralize>
|
||||
<file-and-line-button file="offender.file" line="offender.line" column="offender.column"></file-and-line-button>
|
||||
</div>
|
||||
|
||||
<div ng-if="policyName === 'cssParsingErrors'">
|
||||
<b>{{offender.error}}</b>
|
||||
<file-and-line file="offender.file" line="offender.line" column="offender.column"></file-and-line>
|
||||
@@ -348,6 +353,22 @@
|
||||
Please open the <a href="/result/{{runId}}/timeline#filter=eventNotDelegated">JS timeline, filtered by "Events not delegated"</a>
|
||||
</div>
|
||||
|
||||
<div ng-if="policyName === 'cssBreakpoints'">
|
||||
<div ng-if="rule.value > 0" class="cssBreakpointsGraph">
|
||||
<h3>Breakpoints distribution graph</h3>
|
||||
<canvas class="chart chart-line" chart-data="breakpointsData" chart-labels="breakpointsLabels" chart-options="breakpointsOptions" chart-legend="true" chart-colours="breakpointsColours" width="600" height="250"></canvas>
|
||||
<h3>Breakpoints list</h3>
|
||||
<div class="offendersTable">
|
||||
<div ng-repeat="offender in rule.offendersObj | orderBy:'pixels'">
|
||||
<div>Breakpoint <b>{{offender.breakpoint}}</b> involves <ng-pluralize count="offender.count" when="{'one': '1 rule', 'other': '{} rules'}"></ng-pluralize></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="rule.value === 0">
|
||||
No breakpoint
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="!rule && rule !== null" class="notFound">
|
||||
<h2>404</h2>
|
||||
Rule "{{policyName}}"" not found
|
||||
|
||||
@@ -464,7 +464,7 @@ var policies = {
|
||||
"cssColors": {
|
||||
"tool": "phantomas",
|
||||
"label": "Colors count",
|
||||
"message": "<p>This is the number of different colors defined in CSS.</p><p>Your CSS project will be easier to maintain if you keep a small color set.</p>",
|
||||
"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,
|
||||
"isBadThreshold": 150,
|
||||
"isAbnormalThreshold": 400,
|
||||
@@ -522,6 +522,43 @@ var policies = {
|
||||
};
|
||||
}
|
||||
},
|
||||
"cssBreakpoints": {
|
||||
"tool": "mediaQueriesChecker",
|
||||
"label": "Breakpoints count",
|
||||
"message": "<p>This is the number of different breakpoints found in the stylesheets' media queries.</p><p>Please note this rule is based on <i>min-width</i>, <i>max-width</i>, <i>min-device-width</i> and <i>max-device-width</i> media queries only.</p><p>Your CSS will be easier to maintain if you keep a reasonable number of breakpoints. Try to make a fluid design - using percents - to avoid the creation of numerous breakpoints.</p>",
|
||||
"isOkThreshold": 6,
|
||||
"isBadThreshold": 40,
|
||||
"isAbnormalThreshold": 60,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
var offendersTable = [];
|
||||
|
||||
for (var offender in offenders) {
|
||||
offendersTable.push({
|
||||
breakpoint: offender,
|
||||
count: offenders[offender].count,
|
||||
pixels: offenders[offender].pixels
|
||||
});
|
||||
}
|
||||
|
||||
return offendersTable;
|
||||
}
|
||||
},
|
||||
"cssMobileFirst": {
|
||||
"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,
|
||||
"isAbnormalThreshold": 1000,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders) {
|
||||
return {
|
||||
count: offenders.length,
|
||||
list: offenders
|
||||
};
|
||||
}
|
||||
},
|
||||
"cssImports": {
|
||||
"tool": "phantomas",
|
||||
"label": "Uses of @import",
|
||||
|
||||
@@ -71,7 +71,9 @@
|
||||
"cssComplexSelectors": 2,
|
||||
"cssComplexSelectorsByAttribute": 1.5,
|
||||
"cssColors": 0.5,
|
||||
"similarColors": 0.5
|
||||
"similarColors": 0.5,
|
||||
"cssBreakpoints": 1,
|
||||
"cssMobileFirst": 1
|
||||
}
|
||||
},
|
||||
"badCSS": {
|
||||
|
||||
@@ -4,6 +4,7 @@ var debug = require('debug')('ylt:runner');
|
||||
var phantomasWrapper = require('./tools/phantomas/phantomasWrapper');
|
||||
var jsExecutionTransformer = require('./tools/jsExecutionTransformer');
|
||||
var colorDiff = require('./tools/colorDiff');
|
||||
var mediaQueriesChecker = require('./tools/mediaQueriesChecker');
|
||||
var weightChecker = require('./tools/weightChecker/weightChecker');
|
||||
var rulesChecker = require('./rulesChecker');
|
||||
var scoreCalculator = require('./scoreCalculator');
|
||||
@@ -32,6 +33,9 @@ var Runner = function(params) {
|
||||
// Compare colors
|
||||
data = colorDiff.compareAllColors(data);
|
||||
|
||||
// Check media queries
|
||||
data = mediaQueriesChecker.analyzeMediaQueries(data);
|
||||
|
||||
// Redownload every file
|
||||
return weightChecker.recheckAllFiles(data);
|
||||
|
||||
|
||||
@@ -46,7 +46,9 @@ var colorDiff = function() {
|
||||
var deduplicatedColors = {};
|
||||
|
||||
parsedOffenders.forEach(function(color) {
|
||||
deduplicatedColors[color] = color;
|
||||
if (color !== null) {
|
||||
deduplicatedColors[color] = color;
|
||||
}
|
||||
});
|
||||
|
||||
return Object.keys(deduplicatedColors).map(this.parseColor);
|
||||
@@ -54,7 +56,7 @@ var colorDiff = function() {
|
||||
|
||||
this.parseOffender = function(offender) {
|
||||
var regexResult = /^(.*) \(\d+ times\)$/.exec(offender);
|
||||
return regexResult[1];
|
||||
return regexResult ? regexResult[1] : null;
|
||||
};
|
||||
|
||||
this.parseColor = function(color) {
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
var debug = require('debug')('ylt:mediaQueriesChecker');
|
||||
var parseMediaQuery = require('css-mq-parser');
|
||||
var offendersHelpers = require('../offendersHelpers');
|
||||
|
||||
|
||||
var mediaQueriesChecker = function() {
|
||||
'use strict';
|
||||
|
||||
var MOBILE_MIN_BREAKPOINT = 200;
|
||||
var MOBILE_MAX_BREAKPOINT = 300;
|
||||
|
||||
this.analyzeMediaQueries = function(data) {
|
||||
debug('Starting to check all media queries...');
|
||||
|
||||
var offenders = data.toolsResults.phantomas.offenders.cssMediaQueries;
|
||||
var mediaQueries = (offenders) ? this.parseAllMediaQueries(offenders) : [];
|
||||
|
||||
var notMobileFirstCount = 0;
|
||||
var notMobileFirstOffenders = [];
|
||||
|
||||
var breakpointsOffenders = {};
|
||||
|
||||
for (var i = 0; i < mediaQueries.length; i++) {
|
||||
var item = mediaQueries[i];
|
||||
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.isForMobile) {
|
||||
notMobileFirstCount += item.mediaQuery.rules;
|
||||
notMobileFirstOffenders.push(item.mediaQuery);
|
||||
}
|
||||
|
||||
for (var j = 0; j < item.breakpoints.length; j++) {
|
||||
var breakpointString = item.breakpoints[j].string;
|
||||
if (!breakpointsOffenders[breakpointString]) {
|
||||
breakpointsOffenders[breakpointString] = {
|
||||
count: 1,
|
||||
pixels: item.breakpoints[j].pixels
|
||||
};
|
||||
} else {
|
||||
breakpointsOffenders[breakpointString].count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.toolsResults.mediaQueriesChecker = {
|
||||
metrics: {
|
||||
cssMobileFirst: notMobileFirstCount,
|
||||
cssBreakpoints: Object.keys(breakpointsOffenders).length
|
||||
},
|
||||
offenders: {
|
||||
cssMobileFirst: notMobileFirstOffenders,
|
||||
cssBreakpoints: breakpointsOffenders
|
||||
}
|
||||
};
|
||||
|
||||
debug('End of media queries check');
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
this.parseAllMediaQueries = function(offenders) {
|
||||
return offenders.map(this.parseOneMediaQuery);
|
||||
};
|
||||
|
||||
this.parseOneMediaQuery = function(offender) {
|
||||
var splittedOffender = offendersHelpers.cssOffenderPattern(offender);
|
||||
var parts = /^@media (.*) \((\d+ rules)\)$/.exec(splittedOffender.css);
|
||||
|
||||
if (!parts) {
|
||||
debug('Failed to parse media query ' + offender);
|
||||
return false;
|
||||
}
|
||||
|
||||
var rulesCount = parseInt(parts[2], 10);
|
||||
var query = parts[1];
|
||||
|
||||
var isForMobile = false;
|
||||
var breakpoints = [];
|
||||
|
||||
try {
|
||||
|
||||
var ast = parseMediaQuery(query);
|
||||
|
||||
var min = 0;
|
||||
var max = Infinity;
|
||||
var pixels;
|
||||
|
||||
ast.forEach(function(astItem) {
|
||||
astItem.expressions.forEach(function(expression) {
|
||||
if (expression.feature === 'width' || expression.feature === 'device-width') {
|
||||
if (astItem.inverse === false) {
|
||||
if (expression.modifier === 'max') {
|
||||
pixels = toPixels(expression.value);
|
||||
max = Math.min(max, pixels);
|
||||
breakpoints.push({
|
||||
string: expression.value,
|
||||
pixels: pixels
|
||||
});
|
||||
} else if (expression.modifier === 'min') {
|
||||
pixels = toPixels(expression.value);
|
||||
min = Math.max(min, pixels);
|
||||
breakpoints.push({
|
||||
string: expression.value,
|
||||
pixels: pixels
|
||||
});
|
||||
}
|
||||
} else if (astItem.inverse === true) {
|
||||
if (expression.modifier === 'max') {
|
||||
pixels = toPixels(expression.value);
|
||||
min = Math.max(min, pixels);
|
||||
breakpoints.push({
|
||||
string: expression.value,
|
||||
pixels: pixels
|
||||
});
|
||||
} else if (expression.modifier === 'min') {
|
||||
pixels = toPixels(expression.value);
|
||||
max = Math.min(max, pixels);
|
||||
breakpoints.push({
|
||||
string: expression.value,
|
||||
pixels: pixels
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
isForMobile = (min <= MOBILE_MIN_BREAKPOINT && max >= MOBILE_MAX_BREAKPOINT && max !== Infinity);
|
||||
|
||||
} catch(error) {
|
||||
debug('Failed to parse media query ' + offender);
|
||||
}
|
||||
|
||||
return {
|
||||
mediaQuery: {
|
||||
query: query,
|
||||
rules: rulesCount,
|
||||
file: splittedOffender.file,
|
||||
line: splittedOffender.line,
|
||||
column: splittedOffender.column
|
||||
},
|
||||
isForMobile: isForMobile,
|
||||
breakpoints: breakpoints
|
||||
};
|
||||
};
|
||||
|
||||
// Parses a size in em, pt (or px) and returns it in px
|
||||
function toPixels(size) {
|
||||
var splittedSize = /^([\d\.]+)(.*)/.exec(size);
|
||||
var value = parseFloat(splittedSize[1]);
|
||||
var unit = splittedSize[2];
|
||||
|
||||
if (unit === 'em') {
|
||||
return value * 16;
|
||||
}
|
||||
|
||||
if (unit === 'pt') {
|
||||
return value / 12 * 16;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = new mediaQueriesChecker();
|
||||
+20
-19
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yellowlabtools",
|
||||
"version": "1.8.2",
|
||||
"version": "1.9.0",
|
||||
"description": "Online tool to audit a webpage for performance and front-end quality issues",
|
||||
"license": "GPL-2.0",
|
||||
"author": {
|
||||
@@ -20,50 +20,51 @@
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"dependencies": {
|
||||
"angular": "1.4.7",
|
||||
"angular-animate": "1.4.7",
|
||||
"angular-chart.js": "0.8.5",
|
||||
"angular": "1.4.8",
|
||||
"angular-animate": "1.4.8",
|
||||
"angular-chart.js": "0.8.6",
|
||||
"angular-local-storage": "0.2.2",
|
||||
"angular-resource": "1.4.7",
|
||||
"angular-route": "1.4.7",
|
||||
"angular-sanitize": "1.4.7",
|
||||
"angular-resource": "1.4.8",
|
||||
"angular-route": "1.4.8",
|
||||
"angular-sanitize": "1.4.8",
|
||||
"async": "1.5.0",
|
||||
"body-parser": "1.14.1",
|
||||
"chart.js": "1.0.2",
|
||||
"clean-css": "3.4.6",
|
||||
"clean-css": "3.4.8",
|
||||
"color-diff": "0.1.7",
|
||||
"compression": "1.6.0",
|
||||
"cors": "2.7.1",
|
||||
"css-mq-parser": "0.0.3",
|
||||
"debug": "2.2.0",
|
||||
"express": "4.13.3",
|
||||
"imagemin": "3.2.2",
|
||||
"imagemin-jpegoptim": "4.0.0",
|
||||
"imagemin": "4.0.0",
|
||||
"imagemin-jpegoptim": "4.1.0",
|
||||
"jstoxml": "0.2.3",
|
||||
"lwip": "0.0.8",
|
||||
"meow": "3.4.2",
|
||||
"meow": "3.6.0",
|
||||
"minimize": "1.7.4",
|
||||
"parse-color": "1.0.0",
|
||||
"phantomas": "1.13.0",
|
||||
"ps-node": "0.0.5",
|
||||
"q": "1.4.1",
|
||||
"request": "2.65.0",
|
||||
"rimraf": "2.4.3",
|
||||
"request": "2.67.0",
|
||||
"rimraf": "2.4.4",
|
||||
"temporary": "0.0.8",
|
||||
"try-thread-sleep": "1.0.0",
|
||||
"uglify-js": "2.5.0"
|
||||
"uglify-js": "2.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "~3.4.0",
|
||||
"grunt": "~0.4.5",
|
||||
"grunt-blanket": "~0.0.8",
|
||||
"grunt-contrib-clean": "~0.6.0",
|
||||
"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.0.1",
|
||||
"grunt-contrib-uglify": "~0.10.0",
|
||||
"grunt-contrib-less": "~1.1.0",
|
||||
"grunt-contrib-uglify": "~0.11.0",
|
||||
"grunt-env": "~0.4.4",
|
||||
"grunt-express": "~1.4.1",
|
||||
"grunt-filerev": "~2.3.1",
|
||||
@@ -72,7 +73,7 @@
|
||||
"grunt-mocha-test": "~0.12.7",
|
||||
"grunt-replace": "~0.11.0",
|
||||
"grunt-usemin": "~3.1.1",
|
||||
"grunt-webfont": "~0.5.4",
|
||||
"grunt-webfont": "~1.0.2",
|
||||
"matchdep": "~1.0.0",
|
||||
"mocha": "~2.3.2",
|
||||
"sinon": "~1.17.2",
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
var should = require('chai').should();
|
||||
var mediaQueriesChecker = require('../../lib/tools/mediaQueriesChecker');
|
||||
|
||||
describe('mediaQueriesChecker', function() {
|
||||
|
||||
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}]
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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}]);
|
||||
});
|
||||
|
||||
it('should fail silently', function() {
|
||||
mediaQueriesChecker.parseOneMediaQuery('@media bad syntax (1 rules) <file> @ 1:1').isForMobile.should.equal(false);
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user