├── .editorconfig
├── .gitattributes
├── .gitignore
├── .jshintignore
├── .jshintrc
├── .travis.yml
├── CHANGELOG
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE
├── README.md
├── demos
├── .csslintrc
├── CSSLintDemo.htm
├── demo.css
└── demo.js
├── dist
├── cli.js
├── csslint-node.js
├── csslint-rhino.js
├── csslint-tests.js
├── csslint-worker.js
├── csslint-wsh.js
└── csslint.js
├── lib
├── js.jar
├── yuitest-rhino-cli.js
└── yuitest.js
├── package.json
├── src
├── cli
│ ├── common.js
│ ├── node.js
│ ├── rhino.js
│ └── wsh.js
├── core
│ ├── CSSLint.js
│ ├── Reporter.js
│ └── Util.js
├── formatters
│ ├── checkstyle-xml.js
│ ├── compact.js
│ ├── csslint-xml.js
│ ├── json.js
│ ├── junit-xml.js
│ ├── lint-xml.js
│ └── text.js
├── rules
│ ├── adjoining-classes.js
│ ├── box-model.js
│ ├── box-sizing.js
│ ├── bulletproof-font-face.js
│ ├── compatible-vendor-prefixes.js
│ ├── display-property-grouping.js
│ ├── duplicate-background-images.js
│ ├── duplicate-properties.js
│ ├── empty-rules.js
│ ├── errors.js
│ ├── fallback-colors.js
│ ├── floats.js
│ ├── font-faces.js
│ ├── font-sizes.js
│ ├── gradients.js
│ ├── ids.js
│ ├── import-ie-limit.js
│ ├── import.js
│ ├── important.js
│ ├── known-properties.js
│ ├── order-alphabetical.js
│ ├── outline-none.js
│ ├── overqualified-elements.js
│ ├── performant-transitions.js
│ ├── qualified-headings.js
│ ├── regex-selectors.js
│ ├── rules-count.js
│ ├── selector-max-approaching.js
│ ├── selector-max.js
│ ├── selector-newline.js
│ ├── shorthand.js
│ ├── star-property-hack.js
│ ├── text-indent.js
│ ├── underscore-property-hack.js
│ ├── unique-headings.js
│ ├── universal-selector.js
│ ├── unqualified-attributes.js
│ ├── vendor-prefix.js
│ └── zero-units.js
└── worker
│ └── Worker.js
├── tasks
├── changelog.js
├── test_rhino.js
└── yuitest.js
└── tests
├── .jshintrc
├── all-rules.js
├── cli
├── assets
│ ├── apiStub.js
│ └── data.js
└── cli-common.js
├── core
├── CSSLint.js
└── Reporter.js
├── css
└── width-100.html
├── formatters
├── checkstyle-xml.js
├── compact.js
├── csslint-xml.js
├── json.js
├── junit-xml.js
├── lint-xml.js
└── text.js
├── rules
├── adjoining-classes.js
├── box-model.js
├── box-sizing.js
├── bulletproof-font-face.js
├── compatible-vendor-prefixes.js
├── display-property-grouping.js
├── duplicate-background-images.js
├── duplicate-properties.js
├── empty-rules.js
├── errors.js
├── fallback-colors.js
├── floats.js
├── font-faces.js
├── font-sizes.js
├── gradients.js
├── ids.js
├── import-ie-limit.js
├── import.js
├── important.js
├── known-properties.js
├── order-alphabetical.js
├── outline-none.js
├── overqualified-elements.js
├── performant-transitions.js
├── qualified-headings.js
├── regex-selectors.js
├── selector-max-approaching.js
├── selector-max.js
├── selector-newline.js
├── shorthand.js
├── star-property-hack.js
├── text-indent.js
├── underscore-property-hack.js
├── unique-headings.js
├── universal-selector.js
├── unqualified-attributes.js
├── vendor-prefix.js
└── zero-units.js
├── testrunner.htm
└── testrunner.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 | indent_style = space
11 | indent_size = 4
12 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text eol=lf
3 | *.jar binary
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /npm-debug.log
3 | csslint.pnproj
4 |
5 | # Diff files
6 | *.orig
7 |
--------------------------------------------------------------------------------
/.jshintignore:
--------------------------------------------------------------------------------
1 | lib/
2 | node_modules/
3 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "camelcase": true,
3 | "curly": true,
4 | "eqeqeq": true,
5 | "es3": true,
6 | "forin": true,
7 | "immed": true,
8 | "indent": 4,
9 | "latedef": true,
10 | "newcap": true,
11 | "noarg": true,
12 | "noempty": true,
13 | "nonbsp": true,
14 | "quotmark": "double",
15 | "strict": true,
16 | "undef": true,
17 | "unused": true,
18 | "globals": {
19 | "CSSLint": true
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: node_js
4 |
5 | node_js:
6 | - "0.10"
7 | - "4"
8 | - "6"
9 | - "7"
10 |
11 | matrix:
12 | fast_finish: true
13 |
14 | cache:
15 | directories:
16 | - node_modules
17 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributor guidelines - CSSLint
2 |
3 | Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
4 |
5 | # Contributor License Agreement
6 |
7 | A CLA is a document that specifies how a project is allowed to use your code. We've put a lot of work into creating a CLA that is simple, effective, and as clear as possible so that it doesn't disrupt contributions to CSS Lint.
8 |
9 | When you make a contribution to the CSS Lint project, you agree:
10 |
11 | * The code you wrote is your original work (you own the copyright) or you otherwise have the right to submit the work.
12 | * To grant the CSS Lint project a nonexclusive, irrevocable license to use your submitted code in any way.
13 | * You are capable of granting these rights for the contribution.
14 |
15 | By submitting a fix to CSS Lint you agree to the above statements.
16 |
17 | # Contributor Code of Conduct
18 |
19 | As contributors and maintainers of this project, and in the interest of
20 | fostering an open and welcoming community, we pledge to respect all people who
21 | contribute through reporting issues, posting feature requests, updating
22 | documentation, submitting pull requests or patches, and other activities.
23 |
24 | We are committed to making participation in this project a harassment-free
25 | experience for everyone, regardless of level of experience, gender, gender
26 | identity and expression, sexual orientation, disability, personal appearance,
27 | body size, race, ethnicity, age, religion, or nationality.
28 |
29 | Examples of unacceptable behavior by participants include:
30 |
31 | * The use of sexualized language or imagery
32 | * Personal attacks
33 | * Trolling or insulting/derogatory comments
34 | * Public or private harassment
35 | * Publishing other's private information, such as physical or electronic
36 | addresses, without explicit permission
37 | * Other unethical or unprofessional conduct
38 |
39 | Project maintainers have the right and responsibility to remove, edit, or
40 | reject comments, commits, code, wiki edits, issues, and other contributions
41 | that are not aligned to this Code of Conduct, or to ban temporarily or
42 | permanently any contributor for other behaviors that they deem inappropriate,
43 | threatening, offensive, or harmful.
44 |
45 | By adopting this Code of Conduct, project maintainers commit themselves to
46 | fairly and consistently applying these principles to every aspect of managing
47 | this project. Project maintainers who do not follow or enforce the Code of
48 | Conduct may be permanently removed from the project team.
49 |
50 | This Code of Conduct applies both within project spaces and in public spaces
51 | when an individual is representing the project or its community.
52 |
53 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
54 | reported by contacting a project maintainer at nicole AT stubbornella DOT org. All
55 | complaints will be reviewed and investigated and will result in a response that
56 | is deemed necessary and appropriate to the circumstances. Maintainers are
57 | obligated to maintain confidentiality with regard to the reporter of an
58 | incident.
59 |
60 |
61 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
62 | version 1.3.0, available at
63 | [http://contributor-covenant.org/version/1/3/0/][version]
64 |
65 | [homepage]: http://contributor-covenant.org
66 | [version]: http://contributor-covenant.org/version/1/3/0/
67 |
68 | # Contributing technically
69 |
70 | * Check out the [Contributing wiki](https://github.com/CSSLint/csslint/wiki/Contributing) and [Developer Guidelines](https://github.com/CSSLint/csslint/wiki/Developer-Guide) first
71 |
72 | * To add properties that CSSLint recognizes, submit a patch to [CSSLint/parser-lib](https://github.com/CSSLint/parser-lib)
73 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | CSSLint
2 | Copyright (c) 2011 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in
12 | all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.npmjs.com/package/csslint)
2 | [](https://travis-ci.org/CSSLint/csslint)
3 | [](https://david-dm.org/CSSLint/csslint)
4 | [](https://david-dm.org/CSSLint/csslint#info=devDependencies)
5 |
6 | # CSSLint
7 |
8 | CSSLint is an open source CSS code quality tool originally written by
9 | [Nicholas C. Zakas](http://www.nczonline.net/) and
10 | [Nicole Sullivan](http://www.stubbornella.org/). It was released in June 2011 at
11 | the Velocity conference.
12 |
13 | A [lint](http://en.wikipedia.org/wiki/Lint_programming_tool) tool performs
14 | [static analysis](http://en.wikipedia.org/wiki/Static_code_analysis) of source
15 | code and flags patterns that might be errors or otherwise cause problems for the
16 | developer.
17 |
18 | CSSLint is a tool to help point out problems with your CSS code. It does basic
19 | syntax checking as well as applying a set of rules to the code that look for
20 | problematic patterns or signs of inefficiency. The rules are all pluggable, so
21 | you can easily write your own or omit ones you don't want.
22 |
23 | ## Integration
24 |
25 | ### Command Line Interface
26 |
27 | All about the command line interface for CSSLint. If you'd rather use a CLI
28 | program to verify your CSS instead of using the web site, then this guide is
29 | your best friend.
30 | https://github.com/CSSLint/csslint/wiki/Command-line-interface
31 |
32 | ### Build System
33 |
34 | Once you're familiar with the CSSLint command line interface, the next step is
35 | to integrate it into your build system. This guide walks through using CSSLint
36 | as part of your build.
37 | https://github.com/CSSLint/csslint/wiki/Build-System-Integration
38 |
39 | ### IDE
40 |
41 | You can integrate CSSLint into your favorite IDE to make checking your CSS code
42 | quality easy. In fact, some IDEs already have CSSLint built in.
43 | https://github.com/CSSLint/csslint/wiki/IDE-integration
44 |
45 | ## Rules
46 |
47 | Not sure why a rule is important? This guide talks about each of the CSSLint
48 | rules and explains how the rule is designed to improve your CSS.
49 | https://github.com/CSSLint/csslint/wiki/Rules
50 |
51 | ## Developer Guide
52 |
53 | If you want to contribute to the project, or even just tinker on your own,
54 | this guide explains how to get the source and work with it.
55 | https://github.com/CSSLint/csslint/wiki/Developer-Guide
56 |
57 | ## Contributors
58 |
59 | 1. Samori Gorse, https://twitter.com/shinuza (Rules, Non-zero Exit Code for CLI)
60 | 1. Eitan Konigsburg, https://twitter.com/eitanmk (Rhino CLI)
61 | 1. Ben Barber (Compatible Vendor Prefix Rule)
62 | 1. Eric Wendelin, http://eriwen.com (Output formatters)
63 | 1. Kasper Garnaes, http://reload.dk (Checkstyle XML format)
64 | 1. Gord Tanner, http://www.tinyhippos.com (CLI quiet option)
65 | 1. Hans-Peter Buniat, https://github.com/hpbuniat (Duplicate background image rule)
66 | 1. Dino Chiesa, https://github.com/DinoChiesa (Windows Script Host CLI)
67 | 1. Tomasz Oponowicz, https://github.com/tomasz-oponowicz (XML format and CLI fixes)
68 | 1. Julien Kernec'h, https://github.com/parallel (Fixed a rule)
69 | 1. Cillian de Róiste, https://plus.google.com/116480676247600483573/posts (Node CLI fixes)
70 | 1. Damien Sennm, https://github.com/topaxi (README fixes)
71 | 1. Jonathan Barnett, http://twitter.com/indieisaconcept (JUnit formatter)
72 | 1. Zach Leatherman, http://www.zachleat.com/ (bug fixes)
73 | 1. Philip Walton, http://philipwalton.com (Rules fixes, bug fixes)
74 | 1. Jeff Beck, http://www.jeffbeck.info (Rules fixes, bug fixes)
75 | 1. Jay Merrifield, https://github.com/fracmak (Rules fixes)
76 | 1. Michael Mattiacci, http://mattiacci.com (Rules fixes)
77 | 1. Jonathan Klein, http://www.jonathanklein.net (Bulletproof font-face rule)
78 | 1. Shannon Moeller, http://shannonmoeller.com (Embedded rulesets)
79 | 1. Nick Schonning, https://github.com/nschonni (Contributing.md, grunt build)
80 | 1. Jared Wyles, https://github.com/jaredwy (Managing pull requests, endless advice)
81 | 1. Scott Gonzalez, https://github.com/scottgonzalez (JSON config)
82 |
--------------------------------------------------------------------------------
/demos/.csslintrc:
--------------------------------------------------------------------------------
1 | --errors=important
2 | --warnings=import
--------------------------------------------------------------------------------
/demos/CSSLintDemo.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CSSLint Demo
6 |
11 |
12 |
13 |
14 |
15 |
CSSLint Demo
16 |
77 |
78 |
79 |
(You may want to keep the CSS kinda small, this could take a while.)
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/demos/demo.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | @import url("booya.css") print,screen;
4 | @import "whatup.css" screen;
5 | @import "wicked.css";
6 |
7 | @namespace "http://www.w3.org/1999/xhtml";
8 | @namespace svg "http://www.w3.org/2000/svg";
9 |
10 | li.inline #foo {
11 | background: rgba(234, 212, 200, 0.5) url("something.png");
12 | display: inline;
13 | padding-left: 3px;
14 | padding-right: 7px;
15 | border-right: 1px dotted #066;
16 | }
17 |
18 | li.last.first {
19 | display: inline;
20 | padding-left: 3px !important;
21 | padding-right: 3px;
22 | border-right: 0px;
23 | }
24 |
25 | @media print {
26 | li.inline {
27 | color: black;
28 | }
29 |
30 |
31 | @charset "UTF-8";
32 |
33 | @page {
34 | margin: 10%;
35 | counter-increment: page;
36 |
37 | @top-center {
38 | font-family: sans-serif;
39 | font-weight: bold;
40 | font-size: 2em;
41 | content: counter(page);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/demos/demo.js:
--------------------------------------------------------------------------------
1 | /* jshint browser:true */
2 |
3 | (function() {
4 | "use strict";
5 |
6 | window.onload = function() {
7 | document.body.onclick = function(event) {
8 | event = event || window.event;
9 | var target = event.target || event.srcElement,
10 | results,
11 | messages,
12 | i,
13 | len;
14 |
15 | function log(value, level) {
16 | var output = document.getElementById("output");
17 | output.innerHTML += "" + value.replace(/ /g, " ") + " ";
18 | }
19 |
20 | if (target.id === "lint-btn") {
21 | document.getElementById("output").innerHTML = "";
22 | results = CSSLint.verify(document.getElementById("input").value);
23 | messages = results.messages;
24 | for (i = 0, len = messages.length; i < len; i++) {
25 | log(messages[i].message + " (line " + messages[i].line + ", col " + messages[i].col + ")", messages[i].type);
26 | }
27 |
28 | }
29 |
30 | };
31 | };
32 | })();
33 |
--------------------------------------------------------------------------------
/lib/js.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CSSLint/csslint/49a748126ec0a6e4f6905a03c0eaece47ece04d3/lib/js.jar
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "csslint",
3 | "version": "1.0.5",
4 | "description": "CSSLint",
5 | "author": "Nicole Sullivan",
6 | "contributors": [
7 | "Nicholas C. Zakas"
8 | ],
9 | "homepage": "http://csslint.net/",
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/CSSLint/csslint.git"
13 | },
14 | "bugs": {
15 | "url": "https://github.com/CSSLint/csslint/issues"
16 | },
17 | "license": "MIT",
18 | "main": "./dist/csslint-node.js",
19 | "bin": {
20 | "csslint": "./dist/cli.js"
21 | },
22 | "scripts": {
23 | "test": "grunt test"
24 | },
25 | "dependencies": {
26 | "clone": "~2.1.0",
27 | "parserlib": "~1.1.1"
28 | },
29 | "devDependencies": {
30 | "grunt": "~1.0.1",
31 | "grunt-contrib-clean": "~1.0.0",
32 | "grunt-contrib-concat": "~1.0.0",
33 | "grunt-contrib-jshint": "~1.1.0",
34 | "grunt-contrib-watch": "~1.0.0",
35 | "grunt-include-replace": "~5.0.0",
36 | "load-grunt-tasks": "~3.5.0",
37 | "time-grunt": "~1.4.0",
38 | "yuitest": "~0.7.9"
39 | },
40 | "engines": {
41 | "node": ">=0.10.0"
42 | },
43 | "files": [
44 | "dist/cli.js",
45 | "dist/csslint-node.js",
46 | "dist/csslint.js",
47 | "CHANGELOG",
48 | "LICENSE"
49 | ]
50 | }
51 |
--------------------------------------------------------------------------------
/src/cli/node.js:
--------------------------------------------------------------------------------
1 | /*
2 | * CSSLint Node.js Command Line Interface
3 | */
4 |
5 | /* jshint node:true */
6 | /* global cli */
7 | /* exported CSSLint */
8 | "use strict";
9 |
10 | var fs = require("fs"),
11 | path = require("path"),
12 | CSSLint = require("./csslint-node").CSSLint;
13 |
14 | cli({
15 | args: process.argv.slice(2),
16 |
17 | print: function(message) {
18 | fs.writeSync(1, message + "\n");
19 | },
20 |
21 | quit: function(code) {
22 | process.exit(code || 0);
23 | },
24 |
25 | isDirectory: function(name) {
26 | try {
27 | return fs.statSync(name).isDirectory();
28 | } catch (ex) {
29 | return false;
30 | }
31 | },
32 |
33 | getFiles: function(dir) {
34 | var files = [];
35 |
36 | try {
37 | fs.statSync(dir);
38 | } catch (ex) {
39 | return [];
40 | }
41 |
42 | function traverse(dir, stack) {
43 | stack.push(dir);
44 | fs.readdirSync(stack.join("/")).forEach(function(file) {
45 | var path = stack.concat([file]).join("/"),
46 | stat = fs.statSync(path);
47 |
48 | if (file[0] === ".") {
49 | return;
50 | } else if (stat.isFile() && /\.css$/.test(file)) {
51 | files.push(path);
52 | } else if (stat.isDirectory()) {
53 | traverse(file, stack);
54 | }
55 | });
56 | stack.pop();
57 | }
58 |
59 | traverse(dir, []);
60 |
61 | return files;
62 | },
63 |
64 | getWorkingDirectory: function() {
65 | return process.cwd();
66 | },
67 |
68 | getFullPath: function(filename) {
69 | return path.resolve(process.cwd(), filename);
70 | },
71 |
72 | readFile: function(filename) {
73 | try {
74 | return fs.readFileSync(filename, "utf-8");
75 | } catch (ex) {
76 | return "";
77 | }
78 | }
79 | });
80 |
--------------------------------------------------------------------------------
/src/cli/rhino.js:
--------------------------------------------------------------------------------
1 | /*
2 | * CSSLint Rhino Command Line Interface
3 | */
4 |
5 | /* jshint rhino:true */
6 | /* global cli, File */
7 |
8 | importPackage(java.io);
9 |
10 | cli({
11 |
12 | args: Array.prototype.concat.call(arguments),
13 | print: print,
14 | quit: quit,
15 |
16 | isDirectory: function(name) {
17 | "use strict";
18 | var dir = new File(name);
19 | return dir.isDirectory();
20 | },
21 |
22 | getFiles: function(dir) {
23 | "use strict";
24 | var files = [];
25 |
26 | function traverse(dir) {
27 | var dirList = dir.listFiles();
28 | dirList.forEach(function (file) {
29 | if (/\.css$/.test(file)) {
30 | files.push(file.toString());
31 | } else if (file.isDirectory()) {
32 | traverse(file);
33 | }
34 | });
35 | }
36 |
37 | traverse(new File(dir));
38 |
39 | return files;
40 | },
41 |
42 | getWorkingDirectory: function() {
43 | "use strict";
44 | return (new File(".")).getCanonicalPath();
45 | },
46 |
47 | getFullPath: function(filename) {
48 | "use strict";
49 | return (new File(filename)).getCanonicalPath();
50 | },
51 |
52 | readFile: function(filename) {
53 | "use strict";
54 | try {
55 | return readFile(filename);
56 | } catch (ex) {
57 | return "";
58 | }
59 | }
60 | });
61 |
--------------------------------------------------------------------------------
/src/core/Util.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Utility functions that make life easier.
3 | */
4 | CSSLint.Util = {
5 | /*
6 | * Adds all properties from supplier onto receiver,
7 | * overwriting if the same name already exists on
8 | * receiver.
9 | * @param {Object} The object to receive the properties.
10 | * @param {Object} The object to provide the properties.
11 | * @return {Object} The receiver
12 | */
13 | mix: function(receiver, supplier) {
14 | "use strict";
15 | var prop;
16 |
17 | for (prop in supplier) {
18 | if (supplier.hasOwnProperty(prop)) {
19 | receiver[prop] = supplier[prop];
20 | }
21 | }
22 |
23 | return prop;
24 | },
25 |
26 | /*
27 | * Polyfill for array indexOf() method.
28 | * @param {Array} values The array to search.
29 | * @param {Variant} value The value to search for.
30 | * @return {int} The index of the value if found, -1 if not.
31 | */
32 | indexOf: function(values, value) {
33 | "use strict";
34 | if (values.indexOf) {
35 | return values.indexOf(value);
36 | } else {
37 | for (var i=0, len=values.length; i < len; i++) {
38 | if (values[i] === value) {
39 | return i;
40 | }
41 | }
42 | return -1;
43 | }
44 | },
45 |
46 | /*
47 | * Polyfill for array forEach() method.
48 | * @param {Array} values The array to operate on.
49 | * @param {Function} func The function to call on each item.
50 | * @return {void}
51 | */
52 | forEach: function(values, func) {
53 | "use strict";
54 | if (values.forEach) {
55 | return values.forEach(func);
56 | } else {
57 | for (var i=0, len=values.length; i < len; i++) {
58 | func(values[i], i, values);
59 | }
60 | }
61 | }
62 | };
63 |
--------------------------------------------------------------------------------
/src/formatters/checkstyle-xml.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 |
4 | /**
5 | * Replace special characters before write to output.
6 | *
7 | * Rules:
8 | * - single quotes is the escape sequence for double-quotes
9 | * - & is the escape sequence for &
10 | * - < is the escape sequence for <
11 | * - > is the escape sequence for >
12 | *
13 | * @param {String} message to escape
14 | * @return escaped message as {String}
15 | */
16 | var xmlEscape = function(str) {
17 | if (!str || str.constructor !== String) {
18 | return "";
19 | }
20 |
21 | return str.replace(/["&><]/g, function(match) {
22 | switch (match) {
23 | case "\"":
24 | return """;
25 | case "&":
26 | return "&";
27 | case "<":
28 | return "<";
29 | case ">":
30 | return ">";
31 | }
32 | });
33 | };
34 |
35 | CSSLint.addFormatter({
36 | // format information
37 | id: "checkstyle-xml",
38 | name: "Checkstyle XML format",
39 |
40 | /**
41 | * Return opening root XML tag.
42 | * @return {String} to prepend before all results
43 | */
44 | startFormat: function() {
45 | return "";
46 | },
47 |
48 | /**
49 | * Return closing root XML tag.
50 | * @return {String} to append after all results
51 | */
52 | endFormat: function() {
53 | return "";
54 | },
55 |
56 | /**
57 | * Returns message when there is a file read error.
58 | * @param {String} filename The name of the file that caused the error.
59 | * @param {String} message The error message
60 | * @return {String} The error message.
61 | */
62 | readError: function(filename, message) {
63 | return "";
64 | },
65 |
66 | /**
67 | * Given CSS Lint results for a file, return output for this format.
68 | * @param results {Object} with error and warning messages
69 | * @param filename {String} relative file path
70 | * @param options {Object} (UNUSED for now) specifies special handling of output
71 | * @return {String} output for results
72 | */
73 | formatResults: function(results, filename/*, options*/) {
74 | var messages = results.messages,
75 | output = [];
76 |
77 | /**
78 | * Generate a source string for a rule.
79 | * Checkstyle source strings usually resemble Java class names e.g
80 | * net.csslint.SomeRuleName
81 | * @param {Object} rule
82 | * @return rule source as {String}
83 | */
84 | var generateSource = function(rule) {
85 | if (!rule || !("name" in rule)) {
86 | return "";
87 | }
88 | return "net.csslint." + rule.name.replace(/\s/g, "");
89 | };
90 |
91 |
92 | if (messages.length > 0) {
93 | output.push("");
94 | CSSLint.Util.forEach(messages, function (message) {
95 | // ignore rollups for now
96 | if (!message.rollup) {
97 | output.push("");
99 | }
100 | });
101 | output.push("");
102 | }
103 |
104 | return output.join("");
105 | }
106 | });
107 |
108 | }());
109 |
--------------------------------------------------------------------------------
/src/formatters/compact.js:
--------------------------------------------------------------------------------
1 | CSSLint.addFormatter({
2 | // format information
3 | id: "compact",
4 | name: "Compact, 'porcelain' format",
5 |
6 | /**
7 | * Return content to be printed before all file results.
8 | * @return {String} to prepend before all results
9 | */
10 | startFormat: function() {
11 | "use strict";
12 | return "";
13 | },
14 |
15 | /**
16 | * Return content to be printed after all file results.
17 | * @return {String} to append after all results
18 | */
19 | endFormat: function() {
20 | "use strict";
21 | return "";
22 | },
23 |
24 | /**
25 | * Given CSS Lint results for a file, return output for this format.
26 | * @param results {Object} with error and warning messages
27 | * @param filename {String} relative file path
28 | * @param options {Object} (Optional) specifies special handling of output
29 | * @return {String} output for results
30 | */
31 | formatResults: function(results, filename, options) {
32 | "use strict";
33 | var messages = results.messages,
34 | output = "";
35 | options = options || {};
36 |
37 | /**
38 | * Capitalize and return given string.
39 | * @param str {String} to capitalize
40 | * @return {String} capitalized
41 | */
42 | var capitalize = function(str) {
43 | return str.charAt(0).toUpperCase() + str.slice(1);
44 | };
45 |
46 | if (messages.length === 0) {
47 | return options.quiet ? "" : filename + ": Lint Free!";
48 | }
49 |
50 | CSSLint.Util.forEach(messages, function(message) {
51 | if (message.rollup) {
52 | output += filename + ": " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
53 | } else {
54 | output += filename + ": line " + message.line +
55 | ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
56 | }
57 | });
58 |
59 | return output;
60 | }
61 | });
62 |
--------------------------------------------------------------------------------
/src/formatters/csslint-xml.js:
--------------------------------------------------------------------------------
1 | CSSLint.addFormatter({
2 | // format information
3 | id: "csslint-xml",
4 | name: "CSSLint XML format",
5 |
6 | /**
7 | * Return opening root XML tag.
8 | * @return {String} to prepend before all results
9 | */
10 | startFormat: function() {
11 | "use strict";
12 | return "";
13 | },
14 |
15 | /**
16 | * Return closing root XML tag.
17 | * @return {String} to append after all results
18 | */
19 | endFormat: function() {
20 | "use strict";
21 | return "";
22 | },
23 |
24 | /**
25 | * Given CSS Lint results for a file, return output for this format.
26 | * @param results {Object} with error and warning messages
27 | * @param filename {String} relative file path
28 | * @param options {Object} (UNUSED for now) specifies special handling of output
29 | * @return {String} output for results
30 | */
31 | formatResults: function(results, filename/*, options*/) {
32 | "use strict";
33 | var messages = results.messages,
34 | output = [];
35 |
36 | /**
37 | * Replace special characters before write to output.
38 | *
39 | * Rules:
40 | * - single quotes is the escape sequence for double-quotes
41 | * - & is the escape sequence for &
42 | * - < is the escape sequence for <
43 | * - > is the escape sequence for >
44 | *
45 | * @param {String} message to escape
46 | * @return escaped message as {String}
47 | */
48 | var escapeSpecialCharacters = function(str) {
49 | if (!str || str.constructor !== String) {
50 | return "";
51 | }
52 | return str.replace(/"/g, "'").replace(/&/g, "&").replace(//g, ">");
53 | };
54 |
55 | if (messages.length > 0) {
56 | output.push("");
57 | CSSLint.Util.forEach(messages, function (message) {
58 | if (message.rollup) {
59 | output.push("");
60 | } else {
61 | output.push("");
63 | }
64 | });
65 | output.push("");
66 | }
67 |
68 | return output.join("");
69 | }
70 | });
71 |
--------------------------------------------------------------------------------
/src/formatters/json.js:
--------------------------------------------------------------------------------
1 | /* globals JSON: true */
2 |
3 | CSSLint.addFormatter({
4 | // format information
5 | id: "json",
6 | name: "JSON",
7 |
8 | /**
9 | * Return content to be printed before all file results.
10 | * @return {String} to prepend before all results
11 | */
12 | startFormat: function() {
13 | "use strict";
14 | this.json = [];
15 | return "";
16 | },
17 |
18 | /**
19 | * Return content to be printed after all file results.
20 | * @return {String} to append after all results
21 | */
22 | endFormat: function() {
23 | "use strict";
24 | var ret = "";
25 | if (this.json.length > 0) {
26 | if (this.json.length === 1) {
27 | ret = JSON.stringify(this.json[0]);
28 | } else {
29 | ret = JSON.stringify(this.json);
30 | }
31 | }
32 | return ret;
33 | },
34 |
35 | /**
36 | * Given CSS Lint results for a file, return output for this format.
37 | * @param results {Object} with error and warning messages
38 | * @param filename {String} relative file path (Unused)
39 | * @return {String} output for results
40 | */
41 | formatResults: function(results, filename, options) {
42 | "use strict";
43 | if (results.messages.length > 0 || !options.quiet) {
44 | this.json.push({
45 | filename: filename,
46 | messages: results.messages,
47 | stats: results.stats
48 | });
49 | }
50 | return "";
51 | }
52 | });
53 |
--------------------------------------------------------------------------------
/src/formatters/junit-xml.js:
--------------------------------------------------------------------------------
1 | CSSLint.addFormatter({
2 | // format information
3 | id: "junit-xml",
4 | name: "JUNIT XML format",
5 |
6 | /**
7 | * Return opening root XML tag.
8 | * @return {String} to prepend before all results
9 | */
10 | startFormat: function() {
11 | "use strict";
12 | return "";
13 | },
14 |
15 | /**
16 | * Return closing root XML tag.
17 | * @return {String} to append after all results
18 | */
19 | endFormat: function() {
20 | "use strict";
21 | return "";
22 | },
23 |
24 | /**
25 | * Given CSS Lint results for a file, return output for this format.
26 | * @param results {Object} with error and warning messages
27 | * @param filename {String} relative file path
28 | * @param options {Object} (UNUSED for now) specifies special handling of output
29 | * @return {String} output for results
30 | */
31 | formatResults: function(results, filename/*, options*/) {
32 | "use strict";
33 |
34 | var messages = results.messages,
35 | output = [],
36 | tests = {
37 | "error": 0,
38 | "failure": 0
39 | };
40 |
41 | /**
42 | * Generate a source string for a rule.
43 | * JUNIT source strings usually resemble Java class names e.g
44 | * net.csslint.SomeRuleName
45 | * @param {Object} rule
46 | * @return rule source as {String}
47 | */
48 | var generateSource = function(rule) {
49 | if (!rule || !("name" in rule)) {
50 | return "";
51 | }
52 | return "net.csslint." + rule.name.replace(/\s/g, "");
53 | };
54 |
55 | /**
56 | * Replace special characters before write to output.
57 | *
58 | * Rules:
59 | * - single quotes is the escape sequence for double-quotes
60 | * - < is the escape sequence for <
61 | * - > is the escape sequence for >
62 | *
63 | * @param {String} message to escape
64 | * @return escaped message as {String}
65 | */
66 | var escapeSpecialCharacters = function(str) {
67 |
68 | if (!str || str.constructor !== String) {
69 | return "";
70 | }
71 |
72 | return str.replace(/"/g, "'").replace(//g, ">");
73 |
74 | };
75 |
76 | if (messages.length > 0) {
77 |
78 | messages.forEach(function (message) {
79 |
80 | // since junit has no warning class
81 | // all issues as errors
82 | var type = message.type === "warning" ? "error" : message.type;
83 |
84 | // ignore rollups for now
85 | if (!message.rollup) {
86 |
87 | // build the test case separately, once joined
88 | // we'll add it to a custom array filtered by type
89 | output.push("");
90 | output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">" + type + ">");
91 | output.push("");
92 |
93 | tests[type] += 1;
94 |
95 | }
96 |
97 | });
98 |
99 | output.unshift("");
100 | output.push("");
101 |
102 | }
103 |
104 | return output.join("");
105 |
106 | }
107 | });
108 |
--------------------------------------------------------------------------------
/src/formatters/lint-xml.js:
--------------------------------------------------------------------------------
1 | CSSLint.addFormatter({
2 | // format information
3 | id: "lint-xml",
4 | name: "Lint XML format",
5 |
6 | /**
7 | * Return opening root XML tag.
8 | * @return {String} to prepend before all results
9 | */
10 | startFormat: function() {
11 | "use strict";
12 | return "";
13 | },
14 |
15 | /**
16 | * Return closing root XML tag.
17 | * @return {String} to append after all results
18 | */
19 | endFormat: function() {
20 | "use strict";
21 | return "";
22 | },
23 |
24 | /**
25 | * Given CSS Lint results for a file, return output for this format.
26 | * @param results {Object} with error and warning messages
27 | * @param filename {String} relative file path
28 | * @param options {Object} (UNUSED for now) specifies special handling of output
29 | * @return {String} output for results
30 | */
31 | formatResults: function(results, filename/*, options*/) {
32 | "use strict";
33 | var messages = results.messages,
34 | output = [];
35 |
36 | /**
37 | * Replace special characters before write to output.
38 | *
39 | * Rules:
40 | * - single quotes is the escape sequence for double-quotes
41 | * - & is the escape sequence for &
42 | * - < is the escape sequence for <
43 | * - > is the escape sequence for >
44 | *
45 | * @param {String} message to escape
46 | * @return escaped message as {String}
47 | */
48 | var escapeSpecialCharacters = function(str) {
49 | if (!str || str.constructor !== String) {
50 | return "";
51 | }
52 | return str.replace(/"/g, "'").replace(/&/g, "&").replace(//g, ">");
53 | };
54 |
55 | if (messages.length > 0) {
56 |
57 | output.push("");
58 | CSSLint.Util.forEach(messages, function (message) {
59 | if (message.rollup) {
60 | output.push("");
61 | } else {
62 | var rule = "";
63 | if (message.rule && message.rule.id) {
64 | rule = "rule=\"" + escapeSpecialCharacters(message.rule.id) + "\" ";
65 | }
66 | output.push("");
68 | }
69 | });
70 | output.push("");
71 | }
72 |
73 | return output.join("");
74 | }
75 | });
76 |
--------------------------------------------------------------------------------
/src/formatters/text.js:
--------------------------------------------------------------------------------
1 | CSSLint.addFormatter({
2 | // format information
3 | id: "text",
4 | name: "Plain Text",
5 |
6 | /**
7 | * Return content to be printed before all file results.
8 | * @return {String} to prepend before all results
9 | */
10 | startFormat: function() {
11 | "use strict";
12 | return "";
13 | },
14 |
15 | /**
16 | * Return content to be printed after all file results.
17 | * @return {String} to append after all results
18 | */
19 | endFormat: function() {
20 | "use strict";
21 | return "";
22 | },
23 |
24 | /**
25 | * Given CSS Lint results for a file, return output for this format.
26 | * @param results {Object} with error and warning messages
27 | * @param filename {String} relative file path
28 | * @param options {Object} (Optional) specifies special handling of output
29 | * @return {String} output for results
30 | */
31 | formatResults: function(results, filename, options) {
32 | "use strict";
33 | var messages = results.messages,
34 | output = "";
35 | options = options || {};
36 |
37 | if (messages.length === 0) {
38 | return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
39 | }
40 |
41 | output = "\n\ncsslint: There ";
42 | if (messages.length === 1) {
43 | output += "is 1 problem";
44 | } else {
45 | output += "are " + messages.length + " problems";
46 | }
47 | output += " in " + filename + ".";
48 |
49 | var pos = filename.lastIndexOf("/"),
50 | shortFilename = filename;
51 |
52 | if (pos === -1) {
53 | pos = filename.lastIndexOf("\\");
54 | }
55 | if (pos > -1) {
56 | shortFilename = filename.substring(pos+1);
57 | }
58 |
59 | CSSLint.Util.forEach(messages, function (message, i) {
60 | output = output + "\n\n" + shortFilename;
61 | if (message.rollup) {
62 | output += "\n" + (i+1) + ": " + message.type;
63 | output += "\n" + message.message;
64 | } else {
65 | output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
66 | output += "\n" + message.message;
67 | output += "\n" + message.evidence;
68 | }
69 | });
70 |
71 | return output;
72 | }
73 | });
74 |
--------------------------------------------------------------------------------
/src/rules/adjoining-classes.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Don't use adjoining classes (.foo.bar).
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "adjoining-classes",
9 | name: "Disallow adjoining classes",
10 | desc: "Don't use adjoining classes.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-adjoining-classes",
12 | browsers: "IE6",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this;
18 | parser.addListener("startrule", function(event) {
19 | var selectors = event.selectors,
20 | selector,
21 | part,
22 | modifier,
23 | classCount,
24 | i, j, k;
25 |
26 | for (i=0; i < selectors.length; i++) {
27 | selector = selectors[i];
28 | for (j=0; j < selector.parts.length; j++) {
29 | part = selector.parts[j];
30 | if (part.type === parser.SELECTOR_PART_TYPE) {
31 | classCount = 0;
32 | for (k=0; k < part.modifiers.length; k++) {
33 | modifier = part.modifiers[k];
34 | if (modifier.type === "class") {
35 | classCount++;
36 | }
37 | if (classCount > 1){
38 | reporter.report("Adjoining classes: "+selectors[i].text, part.line, part.col, rule);
39 | }
40 | }
41 | }
42 | }
43 | }
44 | });
45 | }
46 |
47 | });
48 |
--------------------------------------------------------------------------------
/src/rules/box-model.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Don't use width or height when using padding or border.
3 | */
4 | CSSLint.addRule({
5 |
6 | // rule information
7 | id: "box-model",
8 | name: "Beware of broken box size",
9 | desc: "Don't use width or height when using padding or border.",
10 | url: "https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size",
11 | browsers: "All",
12 |
13 | // initialization
14 | init: function(parser, reporter) {
15 | "use strict";
16 | var rule = this,
17 | widthProperties = {
18 | border: 1,
19 | "border-left": 1,
20 | "border-right": 1,
21 | padding: 1,
22 | "padding-left": 1,
23 | "padding-right": 1
24 | },
25 | heightProperties = {
26 | border: 1,
27 | "border-bottom": 1,
28 | "border-top": 1,
29 | padding: 1,
30 | "padding-bottom": 1,
31 | "padding-top": 1
32 | },
33 | properties,
34 | boxSizing = false;
35 |
36 | function startRule() {
37 | properties = {};
38 | boxSizing = false;
39 | }
40 |
41 | function endRule() {
42 | var prop, value;
43 |
44 | if (!boxSizing) {
45 | if (properties.height) {
46 | for (prop in heightProperties) {
47 | if (heightProperties.hasOwnProperty(prop) && properties[prop]) {
48 | value = properties[prop].value;
49 | // special case for padding
50 | if (!(prop === "padding" && value.parts.length === 2 && value.parts[0].value === 0)) {
51 | reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
52 | }
53 | }
54 | }
55 | }
56 |
57 | if (properties.width) {
58 | for (prop in widthProperties) {
59 | if (widthProperties.hasOwnProperty(prop) && properties[prop]) {
60 | value = properties[prop].value;
61 |
62 | if (!(prop === "padding" && value.parts.length === 2 && value.parts[1].value === 0)) {
63 | reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
64 | }
65 | }
66 | }
67 | }
68 | }
69 | }
70 |
71 | parser.addListener("startrule", startRule);
72 | parser.addListener("startfontface", startRule);
73 | parser.addListener("startpage", startRule);
74 | parser.addListener("startpagemargin", startRule);
75 | parser.addListener("startkeyframerule", startRule);
76 | parser.addListener("startviewport", startRule);
77 |
78 | parser.addListener("property", function(event) {
79 | var name = event.property.text.toLowerCase();
80 |
81 | if (heightProperties[name] || widthProperties[name]) {
82 | if (!/^0\S*$/.test(event.value) && !(name === "border" && event.value.toString() === "none")) {
83 | properties[name] = {
84 | line: event.property.line,
85 | col: event.property.col,
86 | value: event.value
87 | };
88 | }
89 | } else {
90 | if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)) {
91 | properties[name] = 1;
92 | } else if (name === "box-sizing") {
93 | boxSizing = true;
94 | }
95 | }
96 |
97 | });
98 |
99 | parser.addListener("endrule", endRule);
100 | parser.addListener("endfontface", endRule);
101 | parser.addListener("endpage", endRule);
102 | parser.addListener("endpagemargin", endRule);
103 | parser.addListener("endkeyframerule", endRule);
104 | parser.addListener("endviewport", endRule);
105 | }
106 |
107 | });
108 |
--------------------------------------------------------------------------------
/src/rules/box-sizing.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: box-sizing doesn't work in IE6 and IE7.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "box-sizing",
9 | name: "Disallow use of box-sizing",
10 | desc: "The box-sizing properties isn't supported in IE6 and IE7.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-box-sizing",
12 | browsers: "IE6, IE7",
13 | tags: ["Compatibility"],
14 |
15 | // initialization
16 | init: function(parser, reporter) {
17 | "use strict";
18 | var rule = this;
19 |
20 | parser.addListener("property", function(event) {
21 | var name = event.property.text.toLowerCase();
22 |
23 | if (name === "box-sizing") {
24 | reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
25 | }
26 | });
27 | }
28 |
29 | });
30 |
--------------------------------------------------------------------------------
/src/rules/bulletproof-font-face.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Use the bulletproof @font-face syntax to avoid 404's in old IE
3 | * (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
4 | */
5 |
6 | CSSLint.addRule({
7 |
8 | // rule information
9 | id: "bulletproof-font-face",
10 | name: "Use the bulletproof @font-face syntax",
11 | desc: "Use the bulletproof @font-face syntax to avoid 404's in old IE (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax).",
12 | url: "https://github.com/CSSLint/csslint/wiki/Bulletproof-font-face",
13 | browsers: "All",
14 |
15 | // initialization
16 | init: function(parser, reporter) {
17 | "use strict";
18 | var rule = this,
19 | fontFaceRule = false,
20 | firstSrc = true,
21 | ruleFailed = false,
22 | line, col;
23 |
24 | // Mark the start of a @font-face declaration so we only test properties inside it
25 | parser.addListener("startfontface", function() {
26 | fontFaceRule = true;
27 | });
28 |
29 | parser.addListener("property", function(event) {
30 | // If we aren't inside an @font-face declaration then just return
31 | if (!fontFaceRule) {
32 | return;
33 | }
34 |
35 | var propertyName = event.property.toString().toLowerCase(),
36 | value = event.value.toString();
37 |
38 | // Set the line and col numbers for use in the endfontface listener
39 | line = event.line;
40 | col = event.col;
41 |
42 | // This is the property that we care about, we can ignore the rest
43 | if (propertyName === "src") {
44 | var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
45 |
46 | // We need to handle the advanced syntax with two src properties
47 | if (!value.match(regex) && firstSrc) {
48 | ruleFailed = true;
49 | firstSrc = false;
50 | } else if (value.match(regex) && !firstSrc) {
51 | ruleFailed = false;
52 | }
53 | }
54 |
55 |
56 | });
57 |
58 | // Back to normal rules that we don't need to test
59 | parser.addListener("endfontface", function() {
60 | fontFaceRule = false;
61 |
62 | if (ruleFailed) {
63 | reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
64 | }
65 | });
66 | }
67 | });
68 |
--------------------------------------------------------------------------------
/src/rules/duplicate-background-images.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Disallow duplicate background-images (using url).
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "duplicate-background-images",
9 | name: "Disallow duplicate background images",
10 | desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this,
18 | stack = {};
19 |
20 | parser.addListener("property", function(event) {
21 | var name = event.property.text,
22 | value = event.value,
23 | i, len;
24 |
25 | if (name.match(/background/i)) {
26 | for (i=0, len=value.parts.length; i < len; i++) {
27 | if (value.parts[i].type === "uri") {
28 | if (typeof stack[value.parts[i].uri] === "undefined") {
29 | stack[value.parts[i].uri] = event;
30 | } else {
31 | reporter.report("Background image '" + value.parts[i].uri + "' was used multiple times, first declared at line " + stack[value.parts[i].uri].line + ", col " + stack[value.parts[i].uri].col + ".", event.line, event.col, rule);
32 | }
33 | }
34 | }
35 | }
36 | });
37 | }
38 | });
39 |
--------------------------------------------------------------------------------
/src/rules/duplicate-properties.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Duplicate properties must appear one after the other. If an already-defined
3 | * property appears somewhere else in the rule, then it's likely an error.
4 | */
5 |
6 | CSSLint.addRule({
7 |
8 | // rule information
9 | id: "duplicate-properties",
10 | name: "Disallow duplicate properties",
11 | desc: "Duplicate properties must appear one after the other.",
12 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties",
13 | browsers: "All",
14 |
15 | // initialization
16 | init: function(parser, reporter) {
17 | "use strict";
18 | var rule = this,
19 | properties,
20 | lastProperty;
21 |
22 | function startRule() {
23 | properties = {};
24 | }
25 |
26 | parser.addListener("startrule", startRule);
27 | parser.addListener("startfontface", startRule);
28 | parser.addListener("startpage", startRule);
29 | parser.addListener("startpagemargin", startRule);
30 | parser.addListener("startkeyframerule", startRule);
31 | parser.addListener("startviewport", startRule);
32 |
33 | parser.addListener("property", function(event) {
34 | var property = event.property,
35 | name = property.text.toLowerCase();
36 |
37 | if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)) {
38 | reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
39 | }
40 |
41 | properties[name] = event.value.text;
42 | lastProperty = name;
43 |
44 | });
45 |
46 |
47 | }
48 |
49 | });
50 |
--------------------------------------------------------------------------------
/src/rules/empty-rules.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Style rules without any properties defined should be removed.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "empty-rules",
9 | name: "Disallow empty rules",
10 | desc: "Rules without any properties specified should be removed.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this,
18 | count = 0;
19 |
20 | parser.addListener("startrule", function() {
21 | count=0;
22 | });
23 |
24 | parser.addListener("property", function() {
25 | count++;
26 | });
27 |
28 | parser.addListener("endrule", function(event) {
29 | var selectors = event.selectors;
30 |
31 | if (count === 0) {
32 | reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
33 | }
34 | });
35 | }
36 |
37 | });
38 |
--------------------------------------------------------------------------------
/src/rules/errors.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: There should be no syntax errors. (Duh.)
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "errors",
9 | name: "Parsing Errors",
10 | desc: "This rule looks for recoverable syntax errors.",
11 | browsers: "All",
12 |
13 | // initialization
14 | init: function(parser, reporter) {
15 | "use strict";
16 | var rule = this;
17 |
18 | parser.addListener("error", function(event) {
19 | reporter.error(event.message, event.line, event.col, rule);
20 | });
21 |
22 | }
23 |
24 | });
25 |
--------------------------------------------------------------------------------
/src/rules/fallback-colors.js:
--------------------------------------------------------------------------------
1 | CSSLint.addRule({
2 |
3 | // rule information
4 | id: "fallback-colors",
5 | name: "Require fallback colors",
6 | desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
7 | url: "https://github.com/CSSLint/csslint/wiki/Require-fallback-colors",
8 | browsers: "IE6,IE7,IE8",
9 |
10 | // initialization
11 | init: function(parser, reporter) {
12 | "use strict";
13 | var rule = this,
14 | lastProperty,
15 | propertiesToCheck = {
16 | color: 1,
17 | background: 1,
18 | "border-color": 1,
19 | "border-top-color": 1,
20 | "border-right-color": 1,
21 | "border-bottom-color": 1,
22 | "border-left-color": 1,
23 | border: 1,
24 | "border-top": 1,
25 | "border-right": 1,
26 | "border-bottom": 1,
27 | "border-left": 1,
28 | "background-color": 1
29 | };
30 |
31 | function startRule() {
32 | lastProperty = null;
33 | }
34 |
35 | parser.addListener("startrule", startRule);
36 | parser.addListener("startfontface", startRule);
37 | parser.addListener("startpage", startRule);
38 | parser.addListener("startpagemargin", startRule);
39 | parser.addListener("startkeyframerule", startRule);
40 | parser.addListener("startviewport", startRule);
41 |
42 | parser.addListener("property", function(event) {
43 | var property = event.property,
44 | name = property.text.toLowerCase(),
45 | parts = event.value.parts,
46 | i = 0,
47 | colorType = "",
48 | len = parts.length;
49 |
50 | if (propertiesToCheck[name]) {
51 | while (i < len) {
52 | if (parts[i].type === "color") {
53 | if ("alpha" in parts[i] || "hue" in parts[i]) {
54 |
55 | if (/([^\)]+)\(/.test(parts[i])) {
56 | colorType = RegExp.$1.toUpperCase();
57 | }
58 |
59 | if (!lastProperty || (lastProperty.property.text.toLowerCase() !== name || lastProperty.colorType !== "compat")) {
60 | reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
61 | }
62 | } else {
63 | event.colorType = "compat";
64 | }
65 | }
66 |
67 | i++;
68 | }
69 | }
70 |
71 | lastProperty = event;
72 | });
73 |
74 | }
75 |
76 | });
77 |
--------------------------------------------------------------------------------
/src/rules/floats.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: You shouldn't use more than 10 floats. If you do, there's probably
3 | * room for some abstraction.
4 | */
5 |
6 | CSSLint.addRule({
7 |
8 | // rule information
9 | id: "floats",
10 | name: "Disallow too many floats",
11 | desc: "This rule tests if the float property is used too many times",
12 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats",
13 | browsers: "All",
14 |
15 | // initialization
16 | init: function(parser, reporter) {
17 | "use strict";
18 | var rule = this;
19 | var count = 0;
20 |
21 | // count how many times "float" is used
22 | parser.addListener("property", function(event) {
23 | if (!reporter.isIgnored(event.property.line)) {
24 | if (event.property.text.toLowerCase() === "float" &&
25 | event.value.text.toLowerCase() !== "none") {
26 | count++;
27 | }
28 | }
29 | });
30 |
31 | // report the results
32 | parser.addListener("endstylesheet", function() {
33 | reporter.stat("floats", count);
34 | if (count >= 10) {
35 | reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
36 | }
37 | });
38 | }
39 |
40 | });
41 |
--------------------------------------------------------------------------------
/src/rules/font-faces.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Avoid too many @font-face declarations in the same stylesheet.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "font-faces",
9 | name: "Don't use too many web fonts",
10 | desc: "Too many different web fonts in the same stylesheet.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this,
18 | count = 0;
19 |
20 |
21 | parser.addListener("startfontface", function(event) {
22 | if (!reporter.isIgnored(event.line)) {
23 | count++;
24 | }
25 | });
26 |
27 | parser.addListener("endstylesheet", function() {
28 | if (count > 5) {
29 | reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
30 | }
31 | });
32 | }
33 |
34 | });
35 |
--------------------------------------------------------------------------------
/src/rules/font-sizes.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: You shouldn't need more than 9 font-size declarations.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "font-sizes",
9 | name: "Disallow too many font sizes",
10 | desc: "Checks the number of font-size declarations.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-font-size-declarations",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this,
18 | count = 0;
19 |
20 | // check for use of "font-size"
21 | parser.addListener("property", function(event) {
22 | if (!reporter.isIgnored(event.property.line)) {
23 | if (event.property.toString() === "font-size") {
24 | count++;
25 | }
26 | }
27 | });
28 |
29 | // report the results
30 | parser.addListener("endstylesheet", function() {
31 | reporter.stat("font-sizes", count);
32 | if (count >= 10) {
33 | reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
34 | }
35 | });
36 | }
37 |
38 | });
39 |
--------------------------------------------------------------------------------
/src/rules/gradients.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: When using a vendor-prefixed gradient, make sure to use them all.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "gradients",
9 | name: "Require all gradient definitions",
10 | desc: "When using a vendor-prefixed gradient, make sure to use them all.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this,
18 | gradients;
19 |
20 | parser.addListener("startrule", function() {
21 | gradients = {
22 | moz: 0,
23 | webkit: 0,
24 | oldWebkit: 0,
25 | o: 0
26 | };
27 | });
28 |
29 | parser.addListener("property", function(event) {
30 |
31 | if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)) {
32 | gradients[RegExp.$1] = 1;
33 | } else if (/\-webkit\-gradient/i.test(event.value)) {
34 | gradients.oldWebkit = 1;
35 | }
36 |
37 | });
38 |
39 | parser.addListener("endrule", function(event) {
40 | var missing = [];
41 |
42 | if (!gradients.moz) {
43 | missing.push("Firefox 3.6+");
44 | }
45 |
46 | if (!gradients.webkit) {
47 | missing.push("Webkit (Safari 5+, Chrome)");
48 | }
49 |
50 | if (!gradients.oldWebkit) {
51 | missing.push("Old Webkit (Safari 4+, Chrome)");
52 | }
53 |
54 | if (!gradients.o) {
55 | missing.push("Opera 11.1+");
56 | }
57 |
58 | if (missing.length && missing.length < 4) {
59 | reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
60 | }
61 |
62 | });
63 |
64 | }
65 |
66 | });
67 |
--------------------------------------------------------------------------------
/src/rules/ids.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Don't use IDs for selectors.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "ids",
9 | name: "Disallow IDs in selectors",
10 | desc: "Selectors should not contain IDs.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this;
18 | parser.addListener("startrule", function(event) {
19 | var selectors = event.selectors,
20 | selector,
21 | part,
22 | modifier,
23 | idCount,
24 | i, j, k;
25 |
26 | for (i=0; i < selectors.length; i++) {
27 | selector = selectors[i];
28 | idCount = 0;
29 |
30 | for (j=0; j < selector.parts.length; j++) {
31 | part = selector.parts[j];
32 | if (part.type === parser.SELECTOR_PART_TYPE) {
33 | for (k=0; k < part.modifiers.length; k++) {
34 | modifier = part.modifiers[k];
35 | if (modifier.type === "id") {
36 | idCount++;
37 | }
38 | }
39 | }
40 | }
41 |
42 | if (idCount === 1) {
43 | reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
44 | } else if (idCount > 1) {
45 | reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
46 | }
47 | }
48 |
49 | });
50 | }
51 |
52 | });
53 |
--------------------------------------------------------------------------------
/src/rules/import-ie-limit.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: IE6-9 supports up to 31 stylesheet import.
3 | * Reference:
4 | * http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
5 | */
6 |
7 | CSSLint.addRule({
8 |
9 | // rule information
10 | id: "import-ie-limit",
11 | name: "@import limit on IE6-IE9",
12 | desc: "IE6-9 supports up to 31 @import per stylesheet",
13 | browsers: "IE6, IE7, IE8, IE9",
14 |
15 | // initialization
16 | init: function(parser, reporter) {
17 | "use strict";
18 | var rule = this,
19 | MAX_IMPORT_COUNT = 31,
20 | count = 0;
21 |
22 | function startPage() {
23 | count = 0;
24 | }
25 |
26 | parser.addListener("startpage", startPage);
27 |
28 | parser.addListener("import", function() {
29 | count++;
30 | });
31 |
32 | parser.addListener("endstylesheet", function() {
33 | if (count > MAX_IMPORT_COUNT) {
34 | reporter.rollupError(
35 | "Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet.",
36 | rule
37 | );
38 | }
39 | });
40 | }
41 |
42 | });
43 |
--------------------------------------------------------------------------------
/src/rules/import.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Don't use @import, use instead.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "import",
9 | name: "Disallow @import",
10 | desc: "Don't use @import, use instead.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-%40import",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this;
18 |
19 | parser.addListener("import", function(event) {
20 | reporter.report("@import prevents parallel downloads, use instead.", event.line, event.col, rule);
21 | });
22 |
23 | }
24 |
25 | });
26 |
--------------------------------------------------------------------------------
/src/rules/important.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Make sure !important is not overused, this could lead to specificity
3 | * war. Display a warning on !important declarations, an error if it's
4 | * used more at least 10 times.
5 | */
6 |
7 | CSSLint.addRule({
8 |
9 | // rule information
10 | id: "important",
11 | name: "Disallow !important",
12 | desc: "Be careful when using !important declaration",
13 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-%21important",
14 | browsers: "All",
15 |
16 | // initialization
17 | init: function(parser, reporter) {
18 | "use strict";
19 | var rule = this,
20 | count = 0;
21 |
22 | // warn that important is used and increment the declaration counter
23 | parser.addListener("property", function(event) {
24 | if (!reporter.isIgnored(event.line)) {
25 | if (event.important === true) {
26 | count++;
27 | reporter.report("Use of !important", event.line, event.col, rule);
28 | }
29 | }
30 | });
31 |
32 | // if there are more than 10, show an error
33 | parser.addListener("endstylesheet", function() {
34 | reporter.stat("important", count);
35 | if (count >= 10) {
36 | reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
37 | }
38 | });
39 | }
40 |
41 | });
42 |
--------------------------------------------------------------------------------
/src/rules/known-properties.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Properties should be known (listed in CSS3 specification) or
3 | * be a vendor-prefixed property.
4 | */
5 |
6 | CSSLint.addRule({
7 |
8 | // rule information
9 | id: "known-properties",
10 | name: "Require use of known properties",
11 | desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
12 | url: "https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties",
13 | browsers: "All",
14 |
15 | // initialization
16 | init: function(parser, reporter) {
17 | "use strict";
18 | var rule = this;
19 |
20 | parser.addListener("property", function(event) {
21 |
22 | // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
23 | if (event.invalid) {
24 | reporter.report(event.invalid.message, event.line, event.col, rule);
25 | }
26 |
27 | });
28 | }
29 |
30 | });
31 |
--------------------------------------------------------------------------------
/src/rules/order-alphabetical.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: All properties should be in alphabetical order.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "order-alphabetical",
9 | name: "Alphabetical order",
10 | desc: "Assure properties are in alphabetical order",
11 | browsers: "All",
12 |
13 | // initialization
14 | init: function(parser, reporter) {
15 | "use strict";
16 | var rule = this,
17 | properties;
18 |
19 | var startRule = function () {
20 | properties = [];
21 | };
22 |
23 | var endRule = function(event) {
24 | var currentProperties = properties.join(","),
25 | expectedProperties = properties.sort().join(",");
26 |
27 | if (currentProperties !== expectedProperties) {
28 | reporter.report("Rule doesn't have all its properties in alphabetical order.", event.line, event.col, rule);
29 | }
30 | };
31 |
32 | parser.addListener("startrule", startRule);
33 | parser.addListener("startfontface", startRule);
34 | parser.addListener("startpage", startRule);
35 | parser.addListener("startpagemargin", startRule);
36 | parser.addListener("startkeyframerule", startRule);
37 | parser.addListener("startviewport", startRule);
38 |
39 | parser.addListener("property", function(event) {
40 | var name = event.property.text,
41 | lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, "");
42 |
43 | properties.push(lowerCasePrefixLessName);
44 | });
45 |
46 | parser.addListener("endrule", endRule);
47 | parser.addListener("endfontface", endRule);
48 | parser.addListener("endpage", endRule);
49 | parser.addListener("endpagemargin", endRule);
50 | parser.addListener("endkeyframerule", endRule);
51 | parser.addListener("endviewport", endRule);
52 | }
53 |
54 | });
55 |
--------------------------------------------------------------------------------
/src/rules/outline-none.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: outline: none or outline: 0 should only be used in a :focus rule
3 | * and only if there are other properties in the same rule.
4 | */
5 |
6 | CSSLint.addRule({
7 |
8 | // rule information
9 | id: "outline-none",
10 | name: "Disallow outline: none",
11 | desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
12 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-outline%3Anone",
13 | browsers: "All",
14 | tags: ["Accessibility"],
15 |
16 | // initialization
17 | init: function(parser, reporter) {
18 | "use strict";
19 | var rule = this,
20 | lastRule;
21 |
22 | function startRule(event) {
23 | if (event.selectors) {
24 | lastRule = {
25 | line: event.line,
26 | col: event.col,
27 | selectors: event.selectors,
28 | propCount: 0,
29 | outline: false
30 | };
31 | } else {
32 | lastRule = null;
33 | }
34 | }
35 |
36 | function endRule() {
37 | if (lastRule) {
38 | if (lastRule.outline) {
39 | if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1) {
40 | reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
41 | } else if (lastRule.propCount === 1) {
42 | reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
43 | }
44 | }
45 | }
46 | }
47 |
48 | parser.addListener("startrule", startRule);
49 | parser.addListener("startfontface", startRule);
50 | parser.addListener("startpage", startRule);
51 | parser.addListener("startpagemargin", startRule);
52 | parser.addListener("startkeyframerule", startRule);
53 | parser.addListener("startviewport", startRule);
54 |
55 | parser.addListener("property", function(event) {
56 | var name = event.property.text.toLowerCase(),
57 | value = event.value;
58 |
59 | if (lastRule) {
60 | lastRule.propCount++;
61 | if (name === "outline" && (value.toString() === "none" || value.toString() === "0")) {
62 | lastRule.outline = true;
63 | }
64 | }
65 |
66 | });
67 |
68 | parser.addListener("endrule", endRule);
69 | parser.addListener("endfontface", endRule);
70 | parser.addListener("endpage", endRule);
71 | parser.addListener("endpagemargin", endRule);
72 | parser.addListener("endkeyframerule", endRule);
73 | parser.addListener("endviewport", endRule);
74 |
75 | }
76 |
77 | });
78 |
--------------------------------------------------------------------------------
/src/rules/overqualified-elements.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Don't use classes or IDs with elements (a.foo or a#foo).
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "overqualified-elements",
9 | name: "Disallow overqualified elements",
10 | desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this,
18 | classes = {};
19 |
20 | parser.addListener("startrule", function(event) {
21 | var selectors = event.selectors,
22 | selector,
23 | part,
24 | modifier,
25 | i, j, k;
26 |
27 | for (i=0; i < selectors.length; i++) {
28 | selector = selectors[i];
29 |
30 | for (j=0; j < selector.parts.length; j++) {
31 | part = selector.parts[j];
32 | if (part.type === parser.SELECTOR_PART_TYPE) {
33 | for (k=0; k < part.modifiers.length; k++) {
34 | modifier = part.modifiers[k];
35 | if (part.elementName && modifier.type === "id") {
36 | reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
37 | } else if (modifier.type === "class") {
38 |
39 | if (!classes[modifier]) {
40 | classes[modifier] = [];
41 | }
42 | classes[modifier].push({
43 | modifier: modifier,
44 | part: part
45 | });
46 | }
47 | }
48 | }
49 | }
50 | }
51 | });
52 |
53 | parser.addListener("endstylesheet", function() {
54 |
55 | var prop;
56 | for (prop in classes) {
57 | if (classes.hasOwnProperty(prop)) {
58 |
59 | // one use means that this is overqualified
60 | if (classes[prop].length === 1 && classes[prop][0].part.elementName) {
61 | reporter.report("Element (" + classes[prop][0].part + ") is overqualified, just use " + classes[prop][0].modifier + " without element name.", classes[prop][0].part.line, classes[prop][0].part.col, rule);
62 | }
63 | }
64 | }
65 | });
66 | }
67 |
68 | });
69 |
--------------------------------------------------------------------------------
/src/rules/performant-transitions.js:
--------------------------------------------------------------------------------
1 | CSSLint.addRule({
2 | id: "performant-transitions",
3 | name: "Allow only performant transisitons",
4 | desc: "Only allow transitions that trigger compositing for performant, 60fps transformations.",
5 | url: "",
6 | browsers: "All",
7 |
8 | init: function(parser, reporter){
9 | "use strict";
10 | var rule = this;
11 |
12 | var transitionProperties = ["transition-property", "transition", "-webkit-transition", "-o-transition"];
13 | var allowedTransitions = [/-webkit-transform/g, /-ms-transform/g, /transform/g, /opacity/g];
14 |
15 | parser.addListener("property", function(event) {
16 | var propertyName = event.property.toString().toLowerCase(),
17 | propertyValue = event.value.toString(),
18 | line = event.line,
19 | col = event.col;
20 |
21 | var values = propertyValue.split(",");
22 | if (transitionProperties.indexOf(propertyName) !== -1) {
23 | var reportValues = values.filter(function(value) {
24 | var didMatch = [];
25 | for (var i = 0; i < allowedTransitions.length; i++) {
26 | if(value.match(allowedTransitions[i])) {
27 | didMatch.push(i);
28 | }
29 | }
30 | return didMatch.length === 0;
31 | });
32 | if(reportValues.length > 0) {
33 | reporter.report("Unexpected transition property '"+reportValues.join(",").trim()+"'", line, col, rule);
34 | }
35 | }
36 | });
37 | }
38 | });
39 |
--------------------------------------------------------------------------------
/src/rules/qualified-headings.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Headings (h1-h6) should not be qualified (namespaced).
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "qualified-headings",
9 | name: "Disallow qualified headings",
10 | desc: "Headings should not be qualified (namespaced).",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-qualified-headings",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this;
18 |
19 | parser.addListener("startrule", function(event) {
20 | var selectors = event.selectors,
21 | selector,
22 | part,
23 | i, j;
24 |
25 | for (i=0; i < selectors.length; i++) {
26 | selector = selectors[i];
27 |
28 | for (j=0; j < selector.parts.length; j++) {
29 | part = selector.parts[j];
30 | if (part.type === parser.SELECTOR_PART_TYPE) {
31 | if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0) {
32 | reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
33 | }
34 | }
35 | }
36 | }
37 | });
38 | }
39 |
40 | });
41 |
--------------------------------------------------------------------------------
/src/rules/regex-selectors.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Selectors that look like regular expressions are slow and should be avoided.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "regex-selectors",
9 | name: "Disallow selectors that look like regexs",
10 | desc: "Selectors that look like regular expressions are slow and should be avoided.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-selectors-that-look-like-regular-expressions",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this;
18 |
19 | parser.addListener("startrule", function(event) {
20 | var selectors = event.selectors,
21 | selector,
22 | part,
23 | modifier,
24 | i, j, k;
25 |
26 | for (i=0; i < selectors.length; i++) {
27 | selector = selectors[i];
28 | for (j=0; j < selector.parts.length; j++) {
29 | part = selector.parts[j];
30 | if (part.type === parser.SELECTOR_PART_TYPE) {
31 | for (k=0; k < part.modifiers.length; k++) {
32 | modifier = part.modifiers[k];
33 | if (modifier.type === "attribute") {
34 | if (/([~\|\^\$\*]=)/.test(modifier)) {
35 | reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
36 | }
37 | }
38 |
39 | }
40 | }
41 | }
42 | }
43 | });
44 | }
45 |
46 | });
47 |
--------------------------------------------------------------------------------
/src/rules/rules-count.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Total number of rules should not exceed x.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "rules-count",
9 | name: "Rules Count",
10 | desc: "Track how many rules there are.",
11 | browsers: "All",
12 |
13 | // initialization
14 | init: function(parser, reporter) {
15 | "use strict";
16 | var count = 0;
17 |
18 | // count each rule
19 | parser.addListener("startrule", function() {
20 | count++;
21 | });
22 |
23 | parser.addListener("endstylesheet", function() {
24 | reporter.stat("rule-count", count);
25 | });
26 | }
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/src/rules/selector-max-approaching.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Warn people with approaching the IE 4095 limit
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "selector-max-approaching",
9 | name: "Warn when approaching the 4095 selector limit for IE",
10 | desc: "Will warn when selector count is >= 3800 selectors.",
11 | browsers: "IE",
12 |
13 | // initialization
14 | init: function(parser, reporter) {
15 | "use strict";
16 | var rule = this, count = 0;
17 |
18 | parser.addListener("startrule", function(event) {
19 | count += event.selectors.length;
20 | });
21 |
22 | parser.addListener("endstylesheet", function() {
23 | if (count >= 3800) {
24 | reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
25 | }
26 | });
27 | }
28 |
29 | });
30 |
--------------------------------------------------------------------------------
/src/rules/selector-max.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Warn people past the IE 4095 limit
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "selector-max",
9 | name: "Error when past the 4095 selector limit for IE",
10 | desc: "Will error when selector count is > 4095.",
11 | browsers: "IE",
12 |
13 | // initialization
14 | init: function(parser, reporter) {
15 | "use strict";
16 | var rule = this, count = 0;
17 |
18 | parser.addListener("startrule", function(event) {
19 | count += event.selectors.length;
20 | });
21 |
22 | parser.addListener("endstylesheet", function() {
23 | if (count > 4095) {
24 | reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
25 | }
26 | });
27 | }
28 |
29 | });
30 |
--------------------------------------------------------------------------------
/src/rules/selector-newline.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Avoid new-line characters in selectors.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "selector-newline",
9 | name: "Disallow new-line characters in selectors",
10 | desc: "New-line characters in selectors are usually a forgotten comma and not a descendant combinator.",
11 | browsers: "All",
12 |
13 | // initialization
14 | init: function(parser, reporter) {
15 | "use strict";
16 | var rule = this;
17 |
18 | function startRule(event) {
19 | var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine,
20 | selectors = event.selectors;
21 |
22 | for (i = 0, len = selectors.length; i < len; i++) {
23 | selector = selectors[i];
24 | for (p = 0, pLen = selector.parts.length; p < pLen; p++) {
25 | for (n = p + 1; n < pLen; n++) {
26 | part = selector.parts[p];
27 | part2 = selector.parts[n];
28 | type = part.type;
29 | currentLine = part.line;
30 | nextLine = part2.line;
31 |
32 | if (type === "descendant" && nextLine > currentLine) {
33 | reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule);
34 | }
35 | }
36 | }
37 |
38 | }
39 | }
40 |
41 | parser.addListener("startrule", startRule);
42 |
43 | }
44 | });
45 |
--------------------------------------------------------------------------------
/src/rules/shorthand.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Use shorthand properties where possible.
3 | *
4 | */
5 |
6 | CSSLint.addRule({
7 |
8 | // rule information
9 | id: "shorthand",
10 | name: "Require shorthand properties",
11 | desc: "Use shorthand properties where possible.",
12 | url: "https://github.com/CSSLint/csslint/wiki/Require-shorthand-properties",
13 | browsers: "All",
14 |
15 | // initialization
16 | init: function(parser, reporter) {
17 | "use strict";
18 | var rule = this,
19 | prop, i, len,
20 | propertiesToCheck = {},
21 | properties,
22 | mapping = {
23 | "margin": [
24 | "margin-top",
25 | "margin-bottom",
26 | "margin-left",
27 | "margin-right"
28 | ],
29 | "padding": [
30 | "padding-top",
31 | "padding-bottom",
32 | "padding-left",
33 | "padding-right"
34 | ]
35 | };
36 |
37 | // initialize propertiesToCheck
38 | for (prop in mapping) {
39 | if (mapping.hasOwnProperty(prop)) {
40 | for (i=0, len=mapping[prop].length; i < len; i++) {
41 | propertiesToCheck[mapping[prop][i]] = prop;
42 | }
43 | }
44 | }
45 |
46 | function startRule() {
47 | properties = {};
48 | }
49 |
50 | // event handler for end of rules
51 | function endRule(event) {
52 |
53 | var prop, i, len, total;
54 |
55 | // check which properties this rule has
56 | for (prop in mapping) {
57 | if (mapping.hasOwnProperty(prop)) {
58 | total=0;
59 |
60 | for (i=0, len=mapping[prop].length; i < len; i++) {
61 | total += properties[mapping[prop][i]] ? 1 : 0;
62 | }
63 |
64 | if (total === mapping[prop].length) {
65 | reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
66 | }
67 | }
68 | }
69 | }
70 |
71 | parser.addListener("startrule", startRule);
72 | parser.addListener("startfontface", startRule);
73 |
74 | // check for use of "font-size"
75 | parser.addListener("property", function(event) {
76 | var name = event.property.toString().toLowerCase();
77 |
78 | if (propertiesToCheck[name]) {
79 | properties[name] = 1;
80 | }
81 | });
82 |
83 | parser.addListener("endrule", endRule);
84 | parser.addListener("endfontface", endRule);
85 |
86 | }
87 |
88 | });
89 |
--------------------------------------------------------------------------------
/src/rules/star-property-hack.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Don't use properties with a star prefix.
3 | *
4 | */
5 |
6 | CSSLint.addRule({
7 |
8 | // rule information
9 | id: "star-property-hack",
10 | name: "Disallow properties with a star prefix",
11 | desc: "Checks for the star property hack (targets IE6/7)",
12 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-star-hack",
13 | browsers: "All",
14 |
15 | // initialization
16 | init: function(parser, reporter) {
17 | "use strict";
18 | var rule = this;
19 |
20 | // check if property name starts with "*"
21 | parser.addListener("property", function(event) {
22 | var property = event.property;
23 |
24 | if (property.hack === "*") {
25 | reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
26 | }
27 | });
28 | }
29 | });
30 |
--------------------------------------------------------------------------------
/src/rules/text-indent.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Don't use text-indent for image replacement if you need to support rtl.
3 | *
4 | */
5 |
6 | CSSLint.addRule({
7 |
8 | // rule information
9 | id: "text-indent",
10 | name: "Disallow negative text-indent",
11 | desc: "Checks for text indent less than -99px",
12 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-negative-text-indent",
13 | browsers: "All",
14 |
15 | // initialization
16 | init: function(parser, reporter) {
17 | "use strict";
18 | var rule = this,
19 | textIndent,
20 | direction;
21 |
22 |
23 | function startRule() {
24 | textIndent = false;
25 | direction = "inherit";
26 | }
27 |
28 | // event handler for end of rules
29 | function endRule() {
30 | if (textIndent && direction !== "ltr") {
31 | reporter.report("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.", textIndent.line, textIndent.col, rule);
32 | }
33 | }
34 |
35 | parser.addListener("startrule", startRule);
36 | parser.addListener("startfontface", startRule);
37 |
38 | // check for use of "font-size"
39 | parser.addListener("property", function(event) {
40 | var name = event.property.toString().toLowerCase(),
41 | value = event.value;
42 |
43 | if (name === "text-indent" && value.parts[0].value < -99) {
44 | textIndent = event.property;
45 | } else if (name === "direction" && value.toString() === "ltr") {
46 | direction = "ltr";
47 | }
48 | });
49 |
50 | parser.addListener("endrule", endRule);
51 | parser.addListener("endfontface", endRule);
52 |
53 | }
54 |
55 | });
56 |
--------------------------------------------------------------------------------
/src/rules/underscore-property-hack.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Don't use properties with a underscore prefix.
3 | *
4 | */
5 |
6 | CSSLint.addRule({
7 |
8 | // rule information
9 | id: "underscore-property-hack",
10 | name: "Disallow properties with an underscore prefix",
11 | desc: "Checks for the underscore property hack (targets IE6)",
12 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-underscore-hack",
13 | browsers: "All",
14 |
15 | // initialization
16 | init: function(parser, reporter) {
17 | "use strict";
18 | var rule = this;
19 |
20 | // check if property name starts with "_"
21 | parser.addListener("property", function(event) {
22 | var property = event.property;
23 |
24 | if (property.hack === "_") {
25 | reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
26 | }
27 | });
28 | }
29 | });
30 |
--------------------------------------------------------------------------------
/src/rules/unique-headings.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Headings (h1-h6) should be defined only once.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "unique-headings",
9 | name: "Headings should only be defined once",
10 | desc: "Headings should be defined only once.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Headings-should-only-be-defined-once",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this;
18 |
19 | var headings = {
20 | h1: 0,
21 | h2: 0,
22 | h3: 0,
23 | h4: 0,
24 | h5: 0,
25 | h6: 0
26 | };
27 |
28 | parser.addListener("startrule", function(event) {
29 | var selectors = event.selectors,
30 | selector,
31 | part,
32 | pseudo,
33 | i, j;
34 |
35 | for (i=0; i < selectors.length; i++) {
36 | selector = selectors[i];
37 | part = selector.parts[selector.parts.length-1];
38 |
39 | if (reporter.isIgnored(part.line)) {
40 | continue;
41 | }
42 |
43 | if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())) {
44 |
45 | for (j=0; j < part.modifiers.length; j++) {
46 | if (part.modifiers[j].type === "pseudo") {
47 | pseudo = true;
48 | break;
49 | }
50 | }
51 |
52 | if (!pseudo) {
53 | headings[RegExp.$1]++;
54 | if (headings[RegExp.$1] > 1) {
55 | reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
56 | }
57 | }
58 | }
59 | }
60 | });
61 |
62 | parser.addListener("endstylesheet", function() {
63 | var prop,
64 | messages = [];
65 |
66 | for (prop in headings) {
67 | if (headings.hasOwnProperty(prop)) {
68 | if (headings[prop] > 1) {
69 | messages.push(headings[prop] + " " + prop + "s");
70 | }
71 | }
72 | }
73 |
74 | if (messages.length) {
75 | reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
76 | }
77 | });
78 | }
79 |
80 | });
81 |
--------------------------------------------------------------------------------
/src/rules/universal-selector.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Don't use universal selector because it's slow.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "universal-selector",
9 | name: "Disallow universal selector",
10 | desc: "The universal selector (*) is known to be slow.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this;
18 |
19 | parser.addListener("startrule", function(event) {
20 | var selectors = event.selectors,
21 | selector,
22 | part,
23 | i;
24 |
25 | for (i=0; i < selectors.length; i++) {
26 | selector = selectors[i];
27 |
28 | part = selector.parts[selector.parts.length-1];
29 | if (part.elementName === "*") {
30 | reporter.report(rule.desc, part.line, part.col, rule);
31 | }
32 | }
33 | });
34 | }
35 |
36 | });
37 |
--------------------------------------------------------------------------------
/src/rules/unqualified-attributes.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: Don't use unqualified attribute selectors because they're just like universal selectors.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "unqualified-attributes",
9 | name: "Disallow unqualified attribute selectors",
10 | desc: "Unqualified attribute selectors are known to be slow.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 |
18 | var rule = this;
19 |
20 | parser.addListener("startrule", function(event) {
21 |
22 | var selectors = event.selectors,
23 | selectorContainsClassOrId = false,
24 | selector,
25 | part,
26 | modifier,
27 | i, k;
28 |
29 | for (i=0; i < selectors.length; i++) {
30 | selector = selectors[i];
31 |
32 | part = selector.parts[selector.parts.length-1];
33 | if (part.type === parser.SELECTOR_PART_TYPE) {
34 | for (k=0; k < part.modifiers.length; k++) {
35 | modifier = part.modifiers[k];
36 |
37 | if (modifier.type === "class" || modifier.type === "id") {
38 | selectorContainsClassOrId = true;
39 | break;
40 | }
41 | }
42 |
43 | if (!selectorContainsClassOrId) {
44 | for (k=0; k < part.modifiers.length; k++) {
45 | modifier = part.modifiers[k];
46 | if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")) {
47 | reporter.report(rule.desc, part.line, part.col, rule);
48 | }
49 | }
50 | }
51 | }
52 |
53 | }
54 | });
55 | }
56 |
57 | });
58 |
--------------------------------------------------------------------------------
/src/rules/zero-units.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Rule: You don't need to specify units when a value is 0.
3 | */
4 |
5 | CSSLint.addRule({
6 |
7 | // rule information
8 | id: "zero-units",
9 | name: "Disallow units for 0 values",
10 | desc: "You don't need to specify units when a value is 0.",
11 | url: "https://github.com/CSSLint/csslint/wiki/Disallow-units-for-zero-values",
12 | browsers: "All",
13 |
14 | // initialization
15 | init: function(parser, reporter) {
16 | "use strict";
17 | var rule = this;
18 |
19 | // count how many times "float" is used
20 | parser.addListener("property", function(event) {
21 | var parts = event.value.parts,
22 | i = 0,
23 | len = parts.length;
24 |
25 | while (i < len) {
26 | if ((parts[i].units || parts[i].type === "percentage") && parts[i].value === 0 && parts[i].type !== "time") {
27 | reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
28 | }
29 | i++;
30 | }
31 |
32 | });
33 |
34 | }
35 |
36 | });
37 |
--------------------------------------------------------------------------------
/src/worker/Worker.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Web worker for CSSLint
3 | */
4 |
5 | /* global self, JSON */
6 |
7 | // message indicates to start linting
8 | self.onmessage = function(event) {
9 | "use strict";
10 | var data = event.data,
11 | message,
12 | text,
13 | ruleset,
14 | results;
15 |
16 | try {
17 | message = JSON.parse(data);
18 | text = message.text;
19 | ruleset = message.ruleset;
20 | } catch (ex) {
21 | text = data;
22 | }
23 |
24 | results = CSSLint.verify(text, ruleset);
25 |
26 | // Not all browsers support structured clone, so JSON stringify results
27 | self.postMessage(JSON.stringify(results));
28 | };
29 |
--------------------------------------------------------------------------------
/tasks/changelog.js:
--------------------------------------------------------------------------------
1 | /* jshint node:true */
2 | "use strict";
3 |
4 | module.exports = function(grunt) {
5 | grunt.registerMultiTask("changelog", "Write the changelog file", function() {
6 | var done = this.async();
7 | var lastTag;
8 | var files = this.filesSrc;
9 |
10 |
11 | grunt.util.spawn({
12 | cmd: "git",
13 | args: ["tag"]
14 | }, function(error, result) {
15 | // Find the latest git tag
16 | var tags = result.stdout.split("\n"),
17 | semver = tags[0].replace("v", "").split("."),
18 | major = parseInt(semver[0], 10),
19 | minor = parseInt(semver[1], 10),
20 | patch = parseInt(semver[2], 10);
21 |
22 | // A simple array sort can't be used because of the comparison of
23 | // the strings "0.9.9" > "0.9.10"
24 | for (var i = 1, len = tags.length; i < len; i++) {
25 | semver = tags[i].replace("v", "").split(".");
26 |
27 | var currentMajor = parseInt(semver[0], 10);
28 | if (currentMajor < major) {
29 | continue;
30 | } else if (currentMajor > major) {
31 | major = currentMajor;
32 | }
33 |
34 | var currentMinor = parseInt(semver[1], 10);
35 | if (currentMinor < minor) {
36 | continue;
37 | } else if (currentMinor > minor) {
38 | minor = currentMinor;
39 | }
40 |
41 | var currentPatch = parseInt(semver[2], 10);
42 | if (currentPatch < patch) {
43 | continue;
44 | } else if (currentPatch > patch) {
45 | patch = currentPatch;
46 | }
47 | }
48 |
49 | lastTag = "v" + major + "." + minor + "." + patch;
50 |
51 | grunt.verbose.write("Last tag: " + lastTag).writeln();
52 |
53 | grunt.util.spawn({
54 | cmd: "git",
55 | args: ["log", "--pretty=format:'* %s (%an)'", lastTag + "..HEAD"]
56 | }, function(error, result) {
57 | var prettyPrint = result.stdout.split("'\n'")
58 | .join("\n")
59 | .replace(/"$/, "")
60 | .replace(/^"/, "")
61 | .replace(/^'/, "")
62 | .replace(/'$/, "");
63 |
64 | grunt.verbose.writeln().write(prettyPrint).writeln();
65 |
66 | var template = "<%= grunt.template.today('mmmm d, yyyy') %> - v<%= pkg.version %>\n\n" +
67 | prettyPrint + "\n\n" +
68 | grunt.file.read(files[0]);
69 |
70 | grunt.file.write(files[0], grunt.template.process(template));
71 |
72 | done();
73 | });
74 | });
75 |
76 | });
77 | };
78 |
--------------------------------------------------------------------------------
/tasks/test_rhino.js:
--------------------------------------------------------------------------------
1 | /* jshint node:true */
2 | "use strict";
3 |
4 | module.exports = function(grunt) {
5 | // Run test suite through rhino
6 | grunt.registerMultiTask("test_rhino", "Run the test suite through rhino", function() {
7 | var done = this.async();
8 | var files = this.filesSrc;
9 | var progress = files.length;
10 |
11 | files.forEach(function(filepath) {
12 | grunt.util.spawn({
13 | cmd: "java",
14 | args: ["-jar", "lib/js.jar", "lib/yuitest-rhino-cli.js", "dist/csslint.js", filepath],
15 | opts: { stdio: "inherit" }
16 | }, function() {
17 | progress--;
18 | if (progress === 0) {
19 | done();
20 | }
21 | });
22 | });
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/tasks/yuitest.js:
--------------------------------------------------------------------------------
1 | /* jshint evil:true, node:true */
2 | "use strict";
3 |
4 | module.exports = function(grunt) {
5 | grunt.registerMultiTask("yuitest", "Run the YUITests for the project", function() {
6 |
7 | var YUITest = require("yuitest");
8 | var CSSLint = require("../dist/csslint-node").CSSLint; // jshint ignore:line
9 | var files = this.filesSrc;
10 | var TestRunner = YUITest.TestRunner;
11 | var done = this.async();
12 | var failures = [],
13 | stack = [];
14 |
15 | // Eval each file so the tests are brought into this scope where CSSLint and YUITest are loaded already
16 | files.forEach(function(filepath) {
17 | eval(grunt.file.read(filepath));
18 | });
19 |
20 | // From YUITest Node CLI with minor colourization changes
21 | function handleEvent(event) {
22 |
23 | var message = "",
24 | results = event.results,
25 | i, len, gruntFailMessage;
26 |
27 | switch (event.type) {
28 | case TestRunner.BEGIN_EVENT:
29 | grunt.verbose.subhead("YUITest for Node.js");
30 |
31 | if (TestRunner._groups) {
32 | grunt.verbose.writeln("Filtering on groups '" + TestRunner._groups.slice(1, -1) + "'");
33 | }
34 | break;
35 |
36 | case TestRunner.COMPLETE_EVENT:
37 | grunt.log.writeln("Total tests: " + results.total + ", " +
38 | ("Failures: " + results.failed).red + ", " +
39 | ("Skipped: " + results.ignored).yellow +
40 | ", Time: " + results.duration / 1000 + " seconds\n");
41 |
42 | if (failures.length) {
43 | grunt.log.writeln("Tests failed:");
44 |
45 | for (i=0, len=failures.length; i < len; i++) {
46 | gruntFailMessage += failures[i].name + "\n" + failures[i].error;
47 | }
48 | grunt.fail.warn(gruntFailMessage);
49 | }
50 |
51 | // Tell grunt we're done the async operation
52 | done();
53 | break;
54 |
55 | case TestRunner.TEST_FAIL_EVENT:
56 | message = "F".red;
57 | failures.push({
58 | name: stack.concat([event.testName]).join(" > "),
59 | error: event.error
60 | });
61 |
62 | break;
63 |
64 | case TestRunner.ERROR_EVENT:
65 | grunt.fail.fatal(event.error, stack);
66 | break;
67 |
68 | case TestRunner.TEST_IGNORE_EVENT:
69 | message = "S".yellow;
70 | break;
71 |
72 | case TestRunner.TEST_PASS_EVENT:
73 | message = ".".green;
74 | break;
75 |
76 | case TestRunner.TEST_SUITE_BEGIN_EVENT:
77 | stack.push(event.testSuite.name);
78 | break;
79 |
80 | case TestRunner.TEST_CASE_COMPLETE_EVENT:
81 | case TestRunner.TEST_SUITE_COMPLETE_EVENT:
82 | stack.pop();
83 | break;
84 |
85 | case TestRunner.TEST_CASE_BEGIN_EVENT:
86 | stack.push(event.testCase.name);
87 | break;
88 |
89 | // no default
90 | }
91 |
92 | grunt.log.write(message);
93 | }
94 | // Add event listeners
95 | TestRunner.subscribe(TestRunner.BEGIN_EVENT, handleEvent);
96 | TestRunner.subscribe(TestRunner.TEST_FAIL_EVENT, handleEvent);
97 | TestRunner.subscribe(TestRunner.TEST_PASS_EVENT, handleEvent);
98 | TestRunner.subscribe(TestRunner.ERROR_EVENT, handleEvent);
99 | TestRunner.subscribe(TestRunner.TEST_IGNORE_EVENT, handleEvent);
100 | TestRunner.subscribe(TestRunner.TEST_CASE_BEGIN_EVENT, handleEvent);
101 | TestRunner.subscribe(TestRunner.TEST_CASE_COMPLETE_EVENT, handleEvent);
102 | TestRunner.subscribe(TestRunner.TEST_SUITE_BEGIN_EVENT, handleEvent);
103 | TestRunner.subscribe(TestRunner.TEST_SUITE_COMPLETE_EVENT, handleEvent);
104 | TestRunner.subscribe(TestRunner.COMPLETE_EVENT, handleEvent);
105 | TestRunner.run();
106 | });
107 | };
108 |
--------------------------------------------------------------------------------
/tests/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends" : "../.jshintrc",
3 | "globals": {
4 | "YUITest": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/tests/all-rules.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file contains generic tests that are run against every rule. Early on,
3 | * we found some common rule patterns that would cause errors under certain
4 | * conditions. Instead of tracking them down individually, this file runs
5 | * the same tests on every defined rule to track down these patterns.
6 | *
7 | * When run in addition to the other tests, this causes the Rhino CLI test
8 | * to fail due to Java stack overflow. This must be run separate from other tests.
9 | */
10 |
11 | (function() {
12 | "use strict";
13 | var Assert = YUITest.Assert,
14 | suite = new YUITest.TestSuite("General Tests for all Rules"),
15 | rules = CSSLint.getRules(),
16 | len = rules.length,
17 | i;
18 |
19 | function testAll(i, rules) {
20 |
21 | suite.add(new YUITest.TestCase({
22 |
23 | name: "General Tests for " + rules[i].id,
24 |
25 | setUp: function() {
26 | this.options = {};
27 | this.options[rules[i].id] = 1;
28 | },
29 |
30 | "Using @viewport should not result in an error": function() {
31 | var result = CSSLint.verify("@viewport { width: auto; }", this.options);
32 | Assert.areEqual(0, result.messages.length);
33 | },
34 |
35 | "Using @keyframes should not result in an error": function() {
36 | var result = CSSLint.verify("@keyframes resize { 0% {padding: 0;} 50% {padding: 0;} 100% {padding: 0;}}", this.options);
37 | Assert.areEqual(0, result.messages.length);
38 | },
39 |
40 | "Using @page should not result in an error": function() {
41 | var result = CSSLint.verify("@page { width: 100px; }", this.options);
42 | Assert.areEqual(0, result.messages.length);
43 | },
44 |
45 | "Using @page @top-left should not result in an error": function() {
46 | var result = CSSLint.verify("@page { @top-left { content: ''; } }", this.options);
47 | Assert.areEqual(0, result.messages.length);
48 | },
49 |
50 | "Using a regular rule should not result in an error": function() {
51 | var result = CSSLint.verify("body { margin: 0; }", this.options);
52 | Assert.areEqual(0, result.messages.length);
53 | }
54 |
55 | }));
56 |
57 | }
58 |
59 | for (i = 0; i < len; i++) {
60 | testAll(i, rules);
61 | }
62 |
63 | YUITest.TestRunner.add(suite);
64 |
65 | })();
66 |
67 |
--------------------------------------------------------------------------------
/tests/cli/assets/apiStub.js:
--------------------------------------------------------------------------------
1 | /* jshint node:true */
2 |
3 | "use strict";
4 |
5 | var stub = {
6 | logbook: function(log) {
7 | this.logs.push(log);
8 | },
9 | readLogs: function() {
10 | return this.logs.slice();
11 | },
12 |
13 | getFullPath: function(path) {
14 | return path;
15 | },
16 | getFiles: function(dir) {
17 | var filesobj = this.fakedFs[dir],
18 | fileix,
19 | out = [];
20 | for (fileix in filesobj) {
21 | if (filesobj.hasOwnProperty(fileix) && /\.css$/.test(fileix)) {
22 | out.push(dir + "/" + fileix);
23 | }
24 | }
25 | return out;
26 | },
27 | readFile: function(path) {
28 | var spath = path.split("/"),
29 | spathLen = spath.length,
30 | i,
31 | out = this.fakedFs;
32 |
33 | for (i = 0; i < spathLen; i += 1) {
34 | out = out[spath[i]];
35 | }
36 |
37 | return out;
38 | },
39 | isDirectory: function(checkit) {
40 | var result = this.fakedFs[checkit];
41 | return typeof result === "object";
42 | },
43 | print: function(msg) {
44 | this.logbook(msg);
45 | },
46 | quit: function(signal) {
47 | this.logbook(signal);
48 | }
49 | };
50 |
51 | module.exports = function(setup) {
52 | var api,
53 | setix;
54 |
55 | api = Object.create(stub);
56 |
57 | for (setix in setup) {
58 | if (setup.hasOwnProperty(setix)) {
59 | api[setix] = setup[setix];
60 | }
61 | }
62 |
63 | api.logs = [];
64 | return api;
65 | };
66 |
--------------------------------------------------------------------------------
/tests/cli/assets/data.js:
--------------------------------------------------------------------------------
1 | /* jshint node:true */
2 |
3 | module.exports = {
4 | "suites": {
5 | "config csslintrc override": {
6 | "args": [
7 | "--config=.rc1",
8 | "dir"
9 | ],
10 | "expecting": [
11 | "csslint: No errors in dir/a.css.",
12 | "csslint: No errors in dir/b.css.",
13 | 0
14 | ]
15 | },
16 | "straight linting": {
17 | "args": [
18 | "dir"
19 | ],
20 | "expecting": [
21 | "csslint: There is 1 problem in dir/a.css.",
22 | "csslint: There is 1 problem in dir/b.css.",
23 | 0
24 | ]
25 | },
26 | "mix of cli options": {
27 | "args": [
28 | "--config=.rc1",
29 | "--ignore=important",
30 | "dir"
31 | ],
32 | "expecting": [
33 | "csslint: No errors in dir/a.css.",
34 | "csslint: There is 1 problem in dir/b.css.",
35 | 0
36 | ]
37 | },
38 | "more mixes of cli options": {
39 | "args": [
40 | "--config=.rc1",
41 | "--errors=important",
42 | "dir"
43 | ],
44 | "expecting": [
45 | "csslint: There is 1 problem in dir/a.css.",
46 | "csslint: No errors in dir/b.css.",
47 | 1
48 | ]
49 | }
50 | },
51 |
52 | "fakedFs": {
53 | ".rc1": "--ignore=important,ids",
54 | "dir": {
55 | "a.css": ".a {color: red!important;}",
56 | "b.css": "#a {color: red;}"
57 | }
58 | }
59 | };
60 |
--------------------------------------------------------------------------------
/tests/cli/cli-common.js:
--------------------------------------------------------------------------------
1 | /* jshint loopfunc:true, node:true */
2 |
3 | "use strict";
4 | function include(path, sandbox) {
5 | var vm = require("vm"),
6 | fs = require("fs"),
7 | file;
8 |
9 | file = fs.readFileSync(path);
10 | vm.runInNewContext(file, sandbox);
11 | }
12 |
13 |
14 | (function() {
15 |
16 | var Assert = YUITest.Assert,
17 | suite = new YUITest.TestSuite("General Tests for CLI"),
18 | apiStub = require("../tests/cli/assets/apiStub.js"),
19 | data = require("../tests/cli/assets/data.js"),
20 | suites = data.suites,
21 | suiteix,
22 | sandbox = {
23 | CSSLint: CSSLint
24 | };
25 |
26 | include("./src/cli/common.js", sandbox); /* expose sandbox.cli */
27 |
28 | for (suiteix in suites) {
29 | if (suites.hasOwnProperty(suiteix)) {
30 | (function (suiteix) {
31 |
32 | suite.add(new YUITest.TestCase({
33 |
34 | name: "Test " + suiteix,
35 |
36 | "Outcome logs should match expected": function () {
37 | var it = suites[suiteix],
38 | expecting = it.expecting,
39 | expectingLen = expecting.length,
40 | outcome,
41 | api,
42 | exp,
43 | out,
44 | i = 0;
45 |
46 | data.args = it.args.slice();
47 | api = apiStub(data);
48 | sandbox.cli(api);
49 | outcome = api.readLogs();
50 |
51 | for (i; i < expectingLen; i += 1) {
52 | exp = expecting[i];
53 | out = outcome[i];
54 |
55 | if (typeof out === "string") {
56 | out = /^.*/.exec(out.trim())[0];
57 | }
58 | if (exp !== out) {
59 | Assert.fail("Expecting: " + exp + " Got: " + out);
60 | }
61 | }
62 | Assert.pass();
63 |
64 | }
65 | }));
66 | })(suiteix);
67 | }
68 | }
69 |
70 | YUITest.TestRunner.add(suite);
71 | })();
72 |
--------------------------------------------------------------------------------
/tests/core/Reporter.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Reporter Object Tests",
8 |
9 | "Report should cause a warning": function() {
10 | var reporter = new CSSLint._Reporter([], {
11 | "fake-rule": 1
12 | });
13 | reporter.report("Foo", 1, 1, {
14 | id: "fake-rule"
15 | });
16 |
17 | Assert.areEqual(1, reporter.messages.length);
18 | Assert.areEqual("warning", reporter.messages[0].type);
19 | },
20 |
21 | "Report should cause an error": function() {
22 | var reporter = new CSSLint._Reporter([], {
23 | "fake-rule": 2
24 | });
25 | reporter.report("Foo", 1, 1, {
26 | id: "fake-rule"
27 | });
28 |
29 | Assert.areEqual(1, reporter.messages.length);
30 | Assert.areEqual("error", reporter.messages[0].type);
31 | },
32 |
33 | "Calling error() should cause an error": function() {
34 | var reporter = new CSSLint._Reporter([], {
35 | "fake-rule": 1
36 | });
37 | reporter.error("Foo", 1, 1, {
38 | id: "fake-rule"
39 | });
40 |
41 | Assert.areEqual(1, reporter.messages.length);
42 | Assert.areEqual("error", reporter.messages[0].type);
43 | },
44 |
45 | "Allow statement should drop message about specific rule on specific line but not other lines": function() {
46 | var reporter = new CSSLint._Reporter([], {
47 | "fake-rule": 1
48 | }, {
49 | "3": {
50 | "fake-rule": true
51 | }
52 | });
53 | reporter.report("Foo", 2, 1, {
54 | id: "fake-rule"
55 | });
56 | reporter.report("Bar", 3, 1, {
57 | id: "fake-rule"
58 | });
59 |
60 | Assert.areEqual(1, reporter.messages.length);
61 | },
62 |
63 | "Allow statement should drop message about specific rule on specific line but not other rules": function() {
64 | var reporter = new CSSLint._Reporter([], {
65 | "fake-rule": 1,
66 | "fake-rule2": 1
67 | }, {
68 | "3": {
69 | "fake-rule": true
70 | }
71 | });
72 | reporter.report("Foo", 3, 1, {
73 | id: "fake-rule"
74 | });
75 | reporter.report("Bar", 3, 1, {
76 | id: "fake-rule2"
77 | });
78 |
79 | Assert.areEqual(1, reporter.messages.length);
80 | },
81 |
82 | "Allow statement should drop messages about multiple rules on specific line": function() {
83 | var reporter = new CSSLint._Reporter([], {
84 | "fake-rule": 1,
85 | "fake-rule2": 1
86 | }, {
87 | "3": {
88 | "fake-rule": true,
89 | "fake-rule2": true
90 | }
91 | });
92 | reporter.report("Foo", 3, 1, {
93 | id: "fake-rule"
94 | });
95 | reporter.report("Bar", 3, 1, {
96 | id: "fake-rule2"
97 | });
98 |
99 | Assert.areEqual(0, reporter.messages.length);
100 | },
101 |
102 | "Ignores should step over a report in their range": function() {
103 | var reporter = new CSSLint._Reporter([], {
104 | "fake-rule": 1
105 | }, {}, [
106 | [1, 3]
107 | ]);
108 | reporter.report("Foo", 2, 1, {
109 | id: "fake-rule"
110 | });
111 | reporter.report("Bar", 5, 1, {
112 | id: "fake-rule"
113 | });
114 |
115 | Assert.areEqual(1, reporter.messages.length);
116 | }
117 |
118 | }));
119 |
120 | })();
121 |
--------------------------------------------------------------------------------
/tests/css/width-100.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Untitled Document
6 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | width 100% child in a parent with padding.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
filler
25 |
filler
26 |
filler
27 |
filler
28 |
filler
29 |
filler
30 |
31 |
32 |
filler
33 |
filler
34 |
filler
35 |
filler
36 |
filler
37 |
filler
38 |
39 |
40 |
filler
41 |
filler
42 |
filler
43 |
filler
44 |
filler
45 |
filler
46 |
47 |
48 |
filler
49 |
filler
50 |
filler
51 |
filler
52 |
filler
53 |
filler
54 |
55 |
56 |
filler
57 |
filler
58 |
filler
59 |
filler
60 |
filler
61 |
filler
62 |
63 |
64 |
filler
65 |
filler
66 |
filler
67 |
filler
68 |
filler
69 |
filler
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/tests/formatters/checkstyle-xml.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Checkstyle XML formatter test",
8 |
9 | "File with no problems should say so": function() {
10 | var result = {
11 | messages: [],
12 | stats: []
13 | },
14 | expected = "";
15 | Assert.areEqual(expected, CSSLint.format(result, "FILE", "checkstyle-xml"));
16 | },
17 |
18 | "File with problems should list them": function() {
19 | var result = {
20 | messages: [{
21 | type: "warning",
22 | line: 1,
23 | col: 1,
24 | message: "BOGUS",
25 | evidence: "ALSO BOGUS",
26 | rule: {
27 | name: "A Rule"
28 | }
29 | }, {
30 | type: "error",
31 | line: 2,
32 | col: 1,
33 | message: "BOGUS",
34 | evidence: "ALSO BOGUS",
35 | rule: {
36 | name: "Some Other Rule"
37 | }
38 | }],
39 | stats: []
40 | },
41 | file = "",
42 | error1 = "",
43 | error2 = "",
44 | expected = "" + file + error1 + error2 + "",
45 | actual = CSSLint.format(result, "FILE", "checkstyle-xml");
46 | Assert.areEqual(expected, actual);
47 | },
48 |
49 | "Formatter should escape special characters": function() {
50 | var specialCharsSting = "sneaky, 'sneaky', , sneak & sneaky",
51 | result = {
52 | messages: [{
53 | type: "warning",
54 | line: 1,
55 | col: 1,
56 | message: specialCharsSting,
57 | evidence: "ALSO BOGUS",
58 | rule: []
59 | }, {
60 | type: "error",
61 | line: 2,
62 | col: 1,
63 | message: specialCharsSting,
64 | evidence: "ALSO BOGUS",
65 | rule: []
66 | }],
67 | stats: []
68 | },
69 | file = "",
70 | error1 = "",
71 | error2 = "",
72 | expected = "" + file + error1 + error2 + "",
73 | actual = CSSLint.format(result, "FILE", "checkstyle-xml");
74 | Assert.areEqual(expected, actual);
75 | }
76 |
77 | }));
78 | })();
79 |
--------------------------------------------------------------------------------
/tests/formatters/csslint-xml.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 | name: "CSSLint XML formatter test",
7 |
8 | "File with no problems should say so": function() {
9 | var result = {
10 | messages: [],
11 | stats: []
12 | },
13 | expected = "";
14 | Assert.areEqual(expected, CSSLint.format(result, "FILE", "csslint-xml"));
15 | },
16 |
17 | "File with problems should list them": function() {
18 | var result = {
19 | messages: [{
20 | type: "warning",
21 | line: 1,
22 | col: 1,
23 | message: "BOGUS",
24 | evidence: "ALSO BOGUS",
25 | rule: []
26 | }, {
27 | type: "error",
28 | line: 2,
29 | col: 1,
30 | message: "BOGUS",
31 | evidence: "ALSO BOGUS",
32 | rule: []
33 | }],
34 | stats: []
35 | },
36 | file = "",
37 | error1 = "",
38 | error2 = "",
39 | expected = "" + file + error1 + error2 + "",
40 | actual = CSSLint.format(result, "FILE", "csslint-xml");
41 | Assert.areEqual(expected, actual);
42 | },
43 |
44 | "Formatter should escape double quotes": function() {
45 | var doubleQuotedEvidence = "sneaky, \"sneaky\", , sneak & sneaky",
46 | result = {
47 | messages: [{
48 | type: "warning",
49 | line: 1,
50 | col: 1,
51 | message: "BOGUS",
52 | evidence: doubleQuotedEvidence,
53 | rule: []
54 | }, {
55 | type: "error",
56 | line: 2,
57 | col: 1,
58 | message: "BOGUS",
59 | evidence: doubleQuotedEvidence,
60 | rule: []
61 | }],
62 | stats: []
63 | },
64 | file = "",
65 | error1 = "",
66 | error2 = "",
67 | expected = "" + file + error1 + error2 + "",
68 | actual = CSSLint.format(result, "FILE", "csslint-xml");
69 | Assert.areEqual(expected, actual);
70 | }
71 | }));
72 | })();
73 |
--------------------------------------------------------------------------------
/tests/formatters/junit-xml.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "JUNIT XML formatter test",
8 |
9 | "File with no problems should say so": function() {
10 |
11 | var result = {
12 | messages: [],
13 | stats: []
14 | },
15 | expected = "";
16 | Assert.areEqual(expected, CSSLint.format(result, "FILE", "junit-xml"));
17 |
18 | },
19 |
20 | "File with problems should list them": function() {
21 |
22 | var result = {
23 | messages: [{
24 | type: "warning",
25 | line: 1,
26 | col: 1,
27 | message: "BOGUS",
28 | evidence: "ALSO BOGUS",
29 | rule: {
30 | name: "A Rule"
31 | }
32 | }, {
33 | type: "error",
34 | line: 2,
35 | col: 1,
36 | message: "BOGUS",
37 | evidence: "ALSO BOGUS",
38 | rule: {
39 | name: "Some Other Rule"
40 | }
41 | }],
42 | stats: []
43 | },
44 |
45 | file = "",
46 | error1 = "",
47 | error2 = "",
48 | expected = "" + file + error1 + error2 + "",
49 | actual = CSSLint.format(result, "FILE", "junit-xml");
50 |
51 | Assert.areEqual(expected, actual);
52 |
53 | },
54 |
55 | "Formatter should escape special characters": function() {
56 |
57 | var specialCharsSting = "sneaky, 'sneaky', ",
58 | result = {
59 | messages: [{
60 | type: "warning",
61 | line: 1,
62 | col: 1,
63 | message: specialCharsSting,
64 | evidence: "ALSO BOGUS",
65 | rule: []
66 | }, {
67 | type: "error",
68 | line: 2,
69 | col: 1,
70 | message: specialCharsSting,
71 | evidence: "ALSO BOGUS",
72 | rule: []
73 | }],
74 | stats: []
75 | },
76 |
77 | file = "",
78 | error1 = "",
79 | error2 = "",
80 | expected = "" + file + error1 + error2 + "",
81 | actual = CSSLint.format(result, "FILE", "junit-xml");
82 |
83 | Assert.areEqual(expected, actual);
84 |
85 | }
86 |
87 | }));
88 | })();
89 |
--------------------------------------------------------------------------------
/tests/formatters/lint-xml.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Lint XML formatter test",
8 |
9 | "File with no problems should say so": function() {
10 | var result = {
11 | messages: [],
12 | stats: []
13 | },
14 | expected = "";
15 | Assert.areEqual(expected, CSSLint.format(result, "FILE", "lint-xml"));
16 | },
17 |
18 | "File with problems should list them": function() {
19 | var result = {
20 | messages: [{
21 | type: "warning",
22 | line: 1,
23 | col: 1,
24 | message: "BOGUS",
25 | evidence: "ALSO BOGUS",
26 | rule: []
27 | }, {
28 | type: "error",
29 | line: 2,
30 | col: 1,
31 | message: "BOGUS",
32 | evidence: "ALSO BOGUS",
33 | rule: []
34 | }],
35 | stats: []
36 | },
37 | file = "",
38 | error1 = "",
39 | error2 = "",
40 | expected = "" + file + error1 + error2 + "",
41 | actual = CSSLint.format(result, "FILE", "lint-xml");
42 | Assert.areEqual(expected, actual);
43 | },
44 |
45 | "Formatter should escape double quotes": function() {
46 | var doubleQuotedEvidence = "sneaky, \"sneaky\", , sneak & sneaky",
47 | result = {
48 | messages: [{
49 | type: "warning",
50 | line: 1,
51 | col: 1,
52 | message: "BOGUS",
53 | evidence: doubleQuotedEvidence,
54 | rule: []
55 | }, {
56 | type: "error",
57 | line: 2,
58 | col: 1,
59 | message: "BOGUS",
60 | evidence: doubleQuotedEvidence,
61 | rule: []
62 | }],
63 | stats: []
64 | },
65 | file = "",
66 | error1 = "",
67 | error2 = "",
68 | expected = "" + file + error1 + error2 + "",
69 | actual = CSSLint.format(result, "FILE", "lint-xml");
70 | Assert.areEqual(expected, actual);
71 | },
72 |
73 | "Messages should include rule IDs": function() {
74 | var result = {
75 | messages: [{
76 | type: "error",
77 | line: 1,
78 | col: 1,
79 | message: "X",
80 | evidence: "Y",
81 | rule: {
82 | id: "Z"
83 | }
84 | }],
85 | stats: []
86 | };
87 |
88 | var expected =
89 | "" +
90 | "" +
91 | "" +
92 | "" +
93 | "" +
94 | "";
95 |
96 | var actual = CSSLint.format(result, "FILE", "lint-xml");
97 |
98 | Assert.areEqual(expected, actual);
99 | }
100 |
101 | }));
102 | })();
103 |
--------------------------------------------------------------------------------
/tests/formatters/text.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Text formatter",
8 |
9 | "File with no problems should say so": function() {
10 | var result = {
11 | messages: [],
12 | stats: []
13 | },
14 | actual = CSSLint.getFormatter("text").formatResults(result, "path/to/FILE", {
15 | fullPath: "/absolute/path/to/FILE"
16 | });
17 | Assert.areEqual("\n\ncsslint: No errors in path/to/FILE.", actual);
18 | },
19 |
20 | "File with one problem should use proper grammar": function() {
21 | var result = {
22 | messages: [{
23 | type: "warning",
24 | line: 1,
25 | col: 1,
26 | message: "BOGUS",
27 | evidence: "ALSO BOGUS",
28 | rule: []
29 | }],
30 | stats: []
31 | },
32 | error1 = "\n1: warning at line 1, col 1\nBOGUS\nALSO BOGUS",
33 | expected = "\n\ncsslint: There is 1 problem in path/to/FILE.\n\nFILE" + error1,
34 | actual = CSSLint.getFormatter("text").formatResults(result, "path/to/FILE", {
35 | fullPath: "/absolute/path/to/FILE"
36 | });
37 | Assert.areEqual(expected, actual);
38 | },
39 |
40 | "Should have no output when quiet option is specified and no errors": function() {
41 | var result = {
42 | messages: [],
43 | stats: []
44 | },
45 | actual = CSSLint.getFormatter("text").formatResults(result, "path/to/FILE", {
46 | fullPath: "/absolute/path/to/FILE",
47 | quiet: "true"
48 | });
49 | Assert.areEqual("", actual);
50 | },
51 |
52 | "File with problems should list them": function() {
53 | var result = {
54 | messages: [{
55 | type: "warning",
56 | line: 1,
57 | col: 1,
58 | message: "BOGUS",
59 | evidence: "ALSO BOGUS",
60 | rule: []
61 | }, {
62 | type: "error",
63 | line: 2,
64 | col: 1,
65 | message: "BOGUS",
66 | evidence: "ALSO BOGUS",
67 | rule: []
68 | }],
69 | stats: []
70 | },
71 | error1 = "\n1: warning at line 1, col 1\nBOGUS\nALSO BOGUS",
72 | error2 = "\n2: error at line 2, col 1\nBOGUS\nALSO BOGUS",
73 | expected = "\n\ncsslint: There are 2 problems in path/to/FILE.\n\nFILE" + error1 + "\n\nFILE" + error2,
74 | actual = CSSLint.getFormatter("text").formatResults(result, "path/to/FILE", {
75 | fullPath: "/absolute/path/to/FILE"
76 | });
77 | Assert.areEqual(expected, actual);
78 | }
79 |
80 | }));
81 |
82 | })();
83 |
--------------------------------------------------------------------------------
/tests/rules/adjoining-classes.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Adjoining Selector Rule Errors",
8 |
9 | "Adjoining classes should result in a warning": function() {
10 | var result = CSSLint.verify(".foo.bar { }", { "adjoining-classes": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Adjoining classes: .foo.bar", result.messages[0].message);
14 | },
15 |
16 | "Adjoining classes should result in an error": function() {
17 | var result = CSSLint.verify(".foo.bar { }", { "adjoining-classes": 2 });
18 | Assert.areEqual(1, result.messages.length);
19 | Assert.areEqual("error", result.messages[0].type);
20 | Assert.areEqual("Adjoining classes: .foo.bar", result.messages[0].message);
21 | },
22 |
23 | "Descendant selector with classes should not result in a warning": function() {
24 | var result = CSSLint.verify(".foo .bar { }", { "adjoining-classes": 1 });
25 | Assert.areEqual(0, result.messages.length);
26 | }
27 |
28 | }));
29 |
30 | })();
31 |
--------------------------------------------------------------------------------
/tests/rules/box-sizing.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Box Sizing Rule Errors",
8 |
9 | "Using box-sizing should result in a warning": function() {
10 | var result = CSSLint.verify(".foo { box-sizing: border-box; }", { "box-sizing": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("The box-sizing property isn't supported in IE6 and IE7.", result.messages[0].message);
14 | },
15 |
16 | "No box-sizing should not result in a warning": function() {
17 | var result = CSSLint.verify(".foo { width: 100px; padding: 0; }", { "box-sizing": 1 });
18 | Assert.areEqual(0, result.messages.length);
19 | }
20 | }));
21 |
22 | })();
23 |
--------------------------------------------------------------------------------
/tests/rules/compatible-vendor-prefixes.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Compatible Vendor Prefix Warnings",
8 |
9 | "Using -webkit-border-radius should not warn to also include -moz-border-radius.": function() {
10 | var result = CSSLint.verify("h1 { -webkit-border-radius: 5px; }", { "compatible-vendor-prefixes": 1 });
11 | Assert.areEqual(0, result.messages.length);
12 | },
13 |
14 | "Using -webkit-transition and -moz-transition should not warn to also include -o-transition.": function() {
15 | var result = CSSLint.verify("h1 { -webkit-transition: height 20px 1s; -moz-transition: height 20px 1s; }", { "compatible-vendor-prefixes": 1 });
16 | Assert.areEqual(0, result.messages.length);
17 | },
18 |
19 | "Using -webkit-transform should warn to also include -ms-transform.": function() {
20 | var result = CSSLint.verify("div.box { -webkit-transform: translate(50px, 100px); }", { "compatible-vendor-prefixes": 3 });
21 | Assert.areEqual(1, result.messages.length);
22 | Assert.areEqual("warning", result.messages[0].type);
23 | Assert.areEqual("The property -ms-transform is compatible with -webkit-transform and should be included as well.", result.messages[0].message);
24 | },
25 |
26 | "Using -webkit-transform inside of an @-webkit- block shouldn't cause a warning": function() {
27 | var result = CSSLint.verify("@-webkit-keyframes spin {0%{ -webkit-transform: rotateX(-10deg) rotateY(0deg); } 100%{ -webkit-transform: rotateX(-10deg) rotateY(-360deg); } }", { "compatible-vendor-prefixes": 1 });
28 | Assert.areEqual(0, result.messages.length);
29 | },
30 |
31 | "Using all compatible vendor prefixes for animation should be allowed with no warnings.": function() {
32 | var result = CSSLint.verify(".next:focus { -moz-animation: 'diagonal-slide' 5s 10; -webkit-animation: 'diagonal-slide' 5s 10; -ms-animation: 'diagonal-slide' 5s 10; }", { "compatible-vendor-prefixes": 1 });
33 | Assert.areEqual(0, result.messages.length);
34 | },
35 |
36 | "Using box-shadow with no vendor prefixes should be allowed with no warnings.": function() {
37 | var result = CSSLint.verify("h1 { box-shadow: 5px 5px 5px #ccc; }", { "compatible-vendor-prefixes": 1 });
38 | Assert.areEqual(0, result.messages.length);
39 | }
40 |
41 | }));
42 |
43 | })();
44 |
--------------------------------------------------------------------------------
/tests/rules/duplicate-background-images.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Duplicate Background-URL Rule Errors",
8 |
9 | "duplicate background-image should result in a warning": function() {
10 | var result = CSSLint.verify(".foo { background-image: url('mega-sprite.png'); } .foofoo { background-image: url('fancy-sprite.png'); } .bar { background-image: url(\"mega-sprite.png\"); } .foobar { background: white url(mega-sprite.png); }", { "duplicate-background-images": 1 });
11 | Assert.areEqual(2, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Background image 'mega-sprite.png' was used multiple times, first declared at line 1, col 8.", result.messages[0].message);
14 | },
15 |
16 | "duplicate background with url should result in a warning": function() {
17 | var result = CSSLint.verify(".foo { background: url(mega-sprite.png) repeat-x; } .foofoo { background-image: url('fancy-sprite.png'); } .bar { background: white url(\"mega-sprite.png\") no-repeat left top; } .foobar { background: white url('mega-sprite.png'); }", { "duplicate-background-images": 1 });
18 | Assert.areEqual(2, result.messages.length);
19 | Assert.areEqual("warning", result.messages[0].type);
20 | Assert.areEqual("Background image 'mega-sprite.png' was used multiple times, first declared at line 1, col 8.", result.messages[0].message);
21 | }
22 | }));
23 |
24 | })();
25 |
--------------------------------------------------------------------------------
/tests/rules/duplicate-properties.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Duplicate Property Rule Errors",
8 |
9 | "Duplicate properties back-to-back should not result in a warning": function() {
10 | var result = CSSLint.verify(".foo { float: left; float: right }", { "duplicate-properties": 1 });
11 | Assert.areEqual(0, result.messages.length);
12 | },
13 |
14 | "Duplicate properties in @font-face back-to-back should not result in a warning": function() {
15 | var result = CSSLint.verify("@font-face { src: url(foo.svg); src: url(foo1.svg) }", { "duplicate-properties": 1 });
16 | Assert.areEqual(0, result.messages.length);
17 | },
18 |
19 | "Duplicate properties in @page back-to-back should not result in a warning": function() {
20 | var result = CSSLint.verify("@page :left { margin: 5px; margin: 4px; }", { "duplicate-properties": 1 });
21 | Assert.areEqual(0, result.messages.length);
22 | },
23 |
24 | "Duplicate properties not back-to-back should result in a warning": function() {
25 | var result = CSSLint.verify(".foo { float: left; margin: 0; float: right }", { "duplicate-properties": 1 });
26 | Assert.areEqual(1, result.messages.length);
27 | Assert.areEqual("warning", result.messages[0].type);
28 | Assert.areEqual("Duplicate property 'float' found.", result.messages[0].message);
29 | },
30 |
31 | "Duplicate properties not back-to-back with same values should result in a warning": function() {
32 | var result = CSSLint.verify(".foo { float: left; margin: 0; float: left }", { "duplicate-properties": 1 });
33 | Assert.areEqual(1, result.messages.length);
34 | Assert.areEqual("warning", result.messages[0].type);
35 | Assert.areEqual("Duplicate property 'float' found.", result.messages[0].message);
36 | },
37 |
38 | "Duplicate properties back-to-back with same values should result in a warning": function() {
39 | var result = CSSLint.verify(".foo { float: left; float: left }", { "duplicate-properties": 1 });
40 | Assert.areEqual(1, result.messages.length);
41 | Assert.areEqual("warning", result.messages[0].type);
42 | Assert.areEqual("Duplicate property 'float' found.", result.messages[0].message);
43 | },
44 |
45 | "Duplicate properties in @keyframe rules should not result in a warning": function() {
46 | var result = CSSLint.verify("@-webkit-keyframes slide_up { from { bottom:-91px; } to { bottom:0; } }", { "duplicate-properties": 1 });
47 | Assert.areEqual(0, result.messages.length);
48 | }
49 |
50 |
51 | }));
52 |
53 | })();
54 |
--------------------------------------------------------------------------------
/tests/rules/empty-rules.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Empty Rule Errors",
8 |
9 | "Empty rule should result in a warning": function() {
10 | var result = CSSLint.verify("li { }", { "empty-rules": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Rule is empty.", result.messages[0].message);
14 | }
15 | }));
16 |
17 | })();
18 |
--------------------------------------------------------------------------------
/tests/rules/errors.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Parsing Errors",
8 |
9 | "Parsing error should result in one parsing error message": function() {
10 | var result = CSSLint.verify("li { float left;}", { errors: 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("error", result.messages[0].type);
13 | }
14 | }));
15 |
16 | })();
17 |
--------------------------------------------------------------------------------
/tests/rules/floats.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Floats Rule Errors",
8 |
9 | "10 floats should result in a warning": function() {
10 | var result = CSSLint.verify(".foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; }", { "floats": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Too many floats (10), you're probably using them for layout. Consider using a grid system instead.", result.messages[0].message);
14 | },
15 |
16 | "9 floats should not result in a warning": function() {
17 | var result = CSSLint.verify(".foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; }", { "floats": 1 });
18 | Assert.areEqual(0, result.messages.length);
19 | },
20 |
21 | "11 floats should result in a warning": function() {
22 | var result = CSSLint.verify(".foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; }", { "floats": 1 });
23 | Assert.areEqual(1, result.messages.length);
24 | Assert.areEqual("warning", result.messages[0].type);
25 | Assert.areEqual("Too many floats (11), you're probably using them for layout. Consider using a grid system instead.", result.messages[0].message);
26 | },
27 |
28 | "float: none should not count and therefore should not result in a warning": function() {
29 | var result = CSSLint.verify(".foo { float: none; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; } .foo { float: left; }", { "floats": 1 });
30 | Assert.areEqual(0, result.messages.length);
31 | },
32 |
33 | "Ignore should remove rollup warning message for floats": function() {
34 | var report = CSSLint.verify("/* csslint ignore:start */\n.test1 {float:left}\n.test2 {float:left}\n.test3 {float:left}\n.test4 {float:left}\n.test5 {float:left}\n.test6 {float:left}\n.test7 {float:left}\n.test8 {float:left}\n.test9 {float:left}\n.test10 {float:left}\n.test11 {float:left}\n/* csslint ignore:end */h2 {color: #fff}\n");
35 | Assert.areEqual(0, report.messages.length);
36 | }
37 | }));
38 |
39 | })();
40 |
--------------------------------------------------------------------------------
/tests/rules/font-faces.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "font-faces Rule Errors",
8 |
9 | "5 font-faces should result in a warning": function() {
10 | var result = CSSLint.verify("@font-face{ } @font-face{ } @font-face{ } @font-face{ } @font-face{ }", { "font-faces": 1 });
11 | Assert.areEqual(0, result.messages.length);
12 | },
13 |
14 | "4 font-faces should not result in a warning": function() {
15 | var result = CSSLint.verify("@font-face{} @font-face{} @font-face{} @font-face{}", { "font-faces": 1 });
16 | Assert.areEqual(0, result.messages.length);
17 | },
18 |
19 | "6 font-faces should result in a warning": function() {
20 | var result = CSSLint.verify("@font-face{} @font-face{} @font-face{} @font-face{} @font-face{} @font-face{}", { "font-faces": 1 });
21 | Assert.areEqual(1, result.messages.length);
22 | Assert.areEqual("warning", result.messages[0].type);
23 | Assert.areEqual("Too many @font-face declarations (6).", result.messages[0].message);
24 | },
25 |
26 | "Ignore should remove rollup warning message for font-face": function() {
27 | var report = CSSLint.verify("/* csslint ignore:start */\n@font-face{} @font-face{} @font-face{} @font-face{} @font-face{} @font-face{}\n/* csslint ignore:end */@font-face{}\n");
28 | Assert.areEqual(0, report.messages.length);
29 | }
30 | }));
31 |
32 | })();
33 |
--------------------------------------------------------------------------------
/tests/rules/font-sizes.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "font-size Rule Errors",
8 |
9 | "10 font-sizes should result in a warning": function() {
10 | var result = CSSLint.verify(".foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } ", { "font-sizes": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Too many font-size declarations (10), abstraction needed.", result.messages[0].message);
14 | },
15 |
16 | "9 font-sizes should not result in a warning": function() {
17 | var result = CSSLint.verify(" .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } ", { "font-sizes": 1 });
18 | Assert.areEqual(0, result.messages.length);
19 | },
20 |
21 | "11 font-sizes should result in a warning": function() {
22 | var result = CSSLint.verify(".foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } .foo { font-size: 10px; } ", { "font-sizes": 1 });
23 | Assert.areEqual(1, result.messages.length);
24 | Assert.areEqual("warning", result.messages[0].type);
25 | Assert.areEqual("Too many font-size declarations (11), abstraction needed.", result.messages[0].message);
26 | },
27 |
28 | "Ignore should remove rollup warning message for font-sizes": function() {
29 | var report = CSSLint.verify("/* csslint ignore:start */\n.test1 {font-size: 10px;}\n.test2 {font-size: 10px;}\n.test3 {font-size: 10px;}\n.test4 {font-size: 10px;}\n.test5 {font-size: 10px;}\n.test6 {font-size: 10px;}\n.test7 {font-size: 10px;}\n.test8 {font-size: 10px;}\n.test9 {font-size: 10px;}\n.test10 {font-size: 10px;}\n.test11 {font-size: 10px;}\n/* csslint ignore:end */h2 {color: #fff}\n");
30 | Assert.areEqual(0, report.messages.length);
31 | }
32 | }));
33 |
34 | })();
35 |
--------------------------------------------------------------------------------
/tests/rules/gradients.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | /*
8 | background: -moz-linear-gradient(top, #1e5799 , #2989d8 , #207cca , #7db9e8 );
9 | background: -webkit-gradient(linear, left top, left bottom, color-stop(,#1e5799), color-stop(,#2989d8), color-stop(,#207cca), color-stop(10,#7db9e8));
10 | background: -webkit-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 );
11 | background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 );
12 |
13 | */
14 |
15 | name: "Gradients Rule Errors",
16 |
17 | "Only using Mozilla gradients should result in a warning": function() {
18 | var result = CSSLint.verify(".foo { background: -moz-linear-gradient(top, #1e5799 , #2989d8 , #207cca , #7db9e8 ); }", { "gradients": 1 });
19 | Assert.areEqual(1, result.messages.length);
20 | Assert.areEqual("warning", result.messages[0].type);
21 | Assert.areEqual("Missing vendor-prefixed CSS gradients for Webkit (Safari 5+, Chrome), Old Webkit (Safari 4+, Chrome), Opera 11.1+.", result.messages[0].message);
22 | },
23 |
24 | "Only using Opera gradients should result in a warning": function() {
25 | var result = CSSLint.verify(".foo { background: -o-linear-gradient(top, #1e5799 , #2989d8 , #207cca , #7db9e8 ); }", { "gradients": 1 });
26 | Assert.areEqual(1, result.messages.length);
27 | Assert.areEqual("warning", result.messages[0].type);
28 | Assert.areEqual("Missing vendor-prefixed CSS gradients for Firefox 3.6+, Webkit (Safari 5+, Chrome), Old Webkit (Safari 4+, Chrome).", result.messages[0].message);
29 | },
30 |
31 | "Only using WebKit gradients should result in a warning": function() {
32 | var result = CSSLint.verify(".foo { background: -webkit-linear-gradient(top, #1e5799 , #2989d8 , #207cca , #7db9e8 ); }", { "gradients": 1 });
33 | Assert.areEqual(1, result.messages.length);
34 | Assert.areEqual("warning", result.messages[0].type);
35 | Assert.areEqual("Missing vendor-prefixed CSS gradients for Firefox 3.6+, Old Webkit (Safari 4+, Chrome), Opera 11.1+.", result.messages[0].message);
36 | },
37 |
38 | "Only using old WebKit gradients should result in a warning": function() {
39 | var result = CSSLint.verify(".foo { background: -webkit-gradient(linear, left top, left bottom, color-stop(10%,#1e5799), color-stop(20%,#2989d8), color-stop(30%,#207cca), color-stop(100%,#7db9e8)); }", { "gradients": 1 });
40 | Assert.areEqual(1, result.messages.length);
41 | Assert.areEqual("warning", result.messages[0].type);
42 | Assert.areEqual("Missing vendor-prefixed CSS gradients for Firefox 3.6+, Webkit (Safari 5+, Chrome), Opera 11.1+.", result.messages[0].message);
43 | },
44 |
45 | "Using all vendor-prefixed gradients should not result in a warning": function() {
46 | var result = CSSLint.verify("div.box {\n background: -moz-linear-gradient(top, #1e5799 0%, #7db9e8 100%);\n background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#1e5799), color- stop(100%,#7db9e8));\n background: -webkit-linear-gradient(top, #1e5799 0%,#7db9e8 100%);\n background: -o-linear-gradient(top, #1e5799 0%,#7db9e8 100%);\n}", { "gradients": 1 });
47 | Assert.areEqual(0, result.messages.length);
48 | }
49 | }));
50 |
51 | })();
52 |
--------------------------------------------------------------------------------
/tests/rules/ids.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "IDs Rule Errors",
8 |
9 | "Using an ID should result in one warning": function() {
10 | var result = CSSLint.verify("#foo { float: left;}", { ids: 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Don't use IDs in selectors.", result.messages[0].message);
14 | },
15 |
16 | "Using multiple IDs should result in one warning": function() {
17 | var result = CSSLint.verify("#foo #bar { float: left;}", { ids: 1 });
18 | Assert.areEqual(1, result.messages.length);
19 | Assert.areEqual("warning", result.messages[0].type);
20 | Assert.areEqual("2 IDs in the selector, really?", result.messages[0].message);
21 | }
22 | }));
23 |
24 | })();
25 |
--------------------------------------------------------------------------------
/tests/rules/import-ie-limit.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert,
4 | IMPORT_STATEMENT = "@import url('foo.css');",
5 | MAX_IMPORT_LIMIT = 31,
6 | withinLimitCss = "",
7 | exceedLimitCss = "",
8 | greatlyExceedLimitCss = "",
9 | i;
10 |
11 | // Build CSS strings to be used in tests
12 | withinLimitCss = IMPORT_STATEMENT;
13 |
14 | for (i = 0; i < MAX_IMPORT_LIMIT + 1; i++) {
15 | exceedLimitCss += IMPORT_STATEMENT;
16 | }
17 |
18 | for (i = 0; i < MAX_IMPORT_LIMIT + 100; i++) {
19 | greatlyExceedLimitCss += IMPORT_STATEMENT;
20 | }
21 |
22 | YUITest.TestRunner.add(new YUITest.TestCase({
23 |
24 | name: "Import IE Limit Rule Error",
25 |
26 | "Using @import <= 31 times should not result in error": function() {
27 |
28 | var result = CSSLint.verify(withinLimitCss, { "import-ie-limit": 1 });
29 | Assert.areEqual(0, result.messages.length);
30 | },
31 |
32 | "Using @import > 31 times should result in error": function() {
33 | var result = CSSLint.verify(exceedLimitCss, { "import-ie-limit": 1 });
34 | Assert.areEqual(1, result.messages.length);
35 | Assert.areEqual("error", result.messages[0].type);
36 | Assert.areEqual("Too many @import rules (32). IE6-9 supports up to 31 import per stylesheet.", result.messages[0].message);
37 | },
38 |
39 | "Using @import > 31 times repeatedly should result in a single error": function() {
40 | var result = CSSLint.verify(greatlyExceedLimitCss, { "import-ie-limit": 1 });
41 | Assert.areEqual(1, result.messages.length);
42 | Assert.areEqual("error", result.messages[0].type);
43 | Assert.areEqual("Too many @import rules (131). IE6-9 supports up to 31 import per stylesheet.", result.messages[0].message);
44 | }
45 | }));
46 |
47 | })();
48 |
--------------------------------------------------------------------------------
/tests/rules/import.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Import Rule Errors",
8 |
9 | "Using @import should result in a warning": function() {
10 | var result = CSSLint.verify("@import url('foo.css');", { "import": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("@import prevents parallel downloads, use instead.", result.messages[0].message);
14 | }
15 | }));
16 |
17 | })();
18 |
--------------------------------------------------------------------------------
/tests/rules/important.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "!important; Errors",
8 |
9 | "!important declarations should result in a warning": function() {
10 | var result = CSSLint.verify("h1 { color:#fff !important; }", { "important": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Use of !important", result.messages[0].message);
14 | },
15 |
16 | "Using !important at least 10 times should result in an error": function() {
17 | var css = "h1 { color:#fff !important; } h2 { color:#fff !important; } h3 { color:#fff !important; } h4 { color:#fff !important; } h5 { color:#fff !important; } h6 { color:#fff !important; } p { color:#fff !important; } ul { color:#fff !important; } ol { color:#fff !important; } li { color:#fff !important; }";
18 | var result = CSSLint.verify(css, { "important": 1 });
19 | Assert.areEqual(11, result.messages.length);
20 | Assert.areEqual("warning", result.messages[10].type);
21 | Assert.areEqual("Too many !important declarations (10), try to use less than 10 to avoid specificity issues.", result.messages[10].message);
22 | },
23 |
24 | "Ignore should remove rollup warning message for important": function() {
25 | var report = CSSLint.verify("/* csslint ignore:start */\n.test1 {color:#fff !important;}\n.test2 {color:#fff !important;}\n.test3 {color:#fff !important;}\n.test4 {color:#fff !important;}\n.test5 {color:#fff !important;}\n.test6 {color:#fff !important;}\n.test7 {color:#fff !important;}\n.test8 {color:#fff !important;}\n.test9 {color:#fff !important;}\n.test10 {color:#fff !important;}\n.test11 {color:#fff !important;}\n/* csslint ignore:end */h2 {color: #fff}\n");
26 | Assert.areEqual(0, report.messages.length);
27 | }
28 |
29 | }));
30 |
31 | })();
32 |
--------------------------------------------------------------------------------
/tests/rules/known-properties.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Known Properties Errors",
8 |
9 | "Using an unknown property should result in a warning": function() {
10 | var result = CSSLint.verify("h1 { foo: red;}", { "known-properties": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Unknown property 'foo'.", result.messages[0].message);
14 | },
15 |
16 | "Using a known property should not result in a warning": function() {
17 | var result = CSSLint.verify("h1 { color: red;}", { "known-properties": 1 });
18 | Assert.areEqual(0, result.messages.length);
19 | },
20 |
21 | "Using a known property with the star hack should not result in a warning": function() {
22 | var result = CSSLint.verify("h1 { *color: red;}", { "known-properties": 1 });
23 | Assert.areEqual(0, result.messages.length);
24 | },
25 |
26 | "Using a known property with the underscore hack should not result in a warning": function() {
27 | var result = CSSLint.verify("h1 { _color: red;}", { "known-properties": 1 });
28 | Assert.areEqual(0, result.messages.length);
29 | },
30 |
31 | "Using a vendor-prefix property should not result in a warning": function() {
32 | var result = CSSLint.verify("h2 { -moz-border-radius: 5px; }", { "known-properties": 1 });
33 | Assert.areEqual(0, result.messages.length);
34 | },
35 |
36 | "Using src in @font-face should not result in a warning": function() {
37 | var result = CSSLint.verify("@font-face { src: url(foo.otf); }", { "known-properties": 1 });
38 | Assert.areEqual(0, result.messages.length);
39 | }
40 |
41 | }));
42 |
43 | })();
44 |
--------------------------------------------------------------------------------
/tests/rules/order-alphabetical.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Alphabetical order Errors",
8 |
9 | "Rules with properties not in alphabetical order should result in a warning": function() {
10 | var result = CSSLint.verify("li { z-index: 2; color: red; }", { "order-alphabetical": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Rule doesn't have all its properties in alphabetical order.", result.messages[0].message);
14 | },
15 |
16 | "Rules with prefixed properties not in alphabetical order (without the prefix) should result in a warning": function() {
17 | var result = CSSLint.verify("li { -moz-transition: none; -webkit-box-shadow: none; }", { "order-alphabetical": 1 });
18 | Assert.areEqual(1, result.messages.length);
19 | Assert.areEqual("warning", result.messages[0].type);
20 | Assert.areEqual("Rule doesn't have all its properties in alphabetical order.", result.messages[0].message);
21 | },
22 |
23 | "Rules with properties in alphabetical order should not result in a warning": function() {
24 | var result = CSSLint.verify("li { box-shadow: none; color: red; transition: none; }", { "order-alphabetical": 1 });
25 | Assert.areEqual(0, result.messages.length);
26 | },
27 |
28 | "Rules with prefixed properties in alphabetical order should not result in a warning": function() {
29 | var result = CSSLint.verify("li { -webkit-box-shadow: none; color: red; -moz-transition: none; }", { "order-alphabetical": 1 });
30 | Assert.areEqual(0, result.messages.length);
31 | }
32 |
33 | }));
34 |
35 | })();
36 |
--------------------------------------------------------------------------------
/tests/rules/outline-none.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Outline:none Errors",
8 |
9 | "Using outline: none should result in a warning": function() {
10 | var result = CSSLint.verify(".foo { outline: none; }", { "outline-none": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Outlines should only be modified using :focus.", result.messages[0].message);
14 | },
15 |
16 | "Using outline: 0 should result in a warning": function() {
17 | var result = CSSLint.verify(".foo { outline: 0; }", { "outline-none": 1 });
18 | Assert.areEqual(1, result.messages.length);
19 | Assert.areEqual("warning", result.messages[0].type);
20 | Assert.areEqual("Outlines should only be modified using :focus.", result.messages[0].message);
21 | },
22 |
23 | "Using outline: none alone with :focus should result in a warning": function() {
24 | var result = CSSLint.verify(".foo:focus { outline: none; }", { "outline-none": 1 });
25 | Assert.areEqual(1, result.messages.length);
26 | Assert.areEqual("warning", result.messages[0].type);
27 | Assert.areEqual("Outlines shouldn't be hidden unless other visual changes are made.", result.messages[0].message);
28 | },
29 |
30 | "Using outline: 0 alone with :focus should result in a warning": function() {
31 | var result = CSSLint.verify(".foo:focus { outline: 0; }", { "outline-none": 1 });
32 | Assert.areEqual(1, result.messages.length);
33 | Assert.areEqual("warning", result.messages[0].type);
34 | Assert.areEqual("Outlines shouldn't be hidden unless other visual changes are made.", result.messages[0].message);
35 | },
36 |
37 | "Using outline: none with :focus and another property should not result in a warning": function() {
38 | var result = CSSLint.verify(".foo:focus { outline: none; border: 1px solid black; }", { "outline-none": 1 });
39 | Assert.areEqual(0, result.messages.length);
40 | },
41 |
42 | "Using outline: 0 with :focus and another property should not result in a warning": function() {
43 | var result = CSSLint.verify(".foo:focus { outline: 0; border: 1px solid black;}", { "outline-none": 1 });
44 | Assert.areEqual(0, result.messages.length);
45 | }
46 |
47 | }));
48 |
49 | })();
50 |
--------------------------------------------------------------------------------
/tests/rules/overqualified-elements.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Overqualified Elements Errors",
8 |
9 | "Using an ID with an element should result in one warning": function() {
10 | var result = CSSLint.verify("li#foo { float: left;}", { "overqualified-elements": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Element (li#foo) is overqualified, just use #foo without element name.", result.messages[0].message);
14 | },
15 |
16 | "Using a class without an element should not result in a warning": function() {
17 | var result = CSSLint.verify(".foo { float: left;}", { "overqualified-elements": 1 });
18 | Assert.areEqual(0, result.messages.length);
19 | },
20 |
21 | "Using a class with an element should result in one warning": function() {
22 | var result = CSSLint.verify("li.foo { float: left;}", { "overqualified-elements": 1 });
23 | Assert.areEqual(1, result.messages.length);
24 | Assert.areEqual("warning", result.messages[0].type);
25 | Assert.areEqual("Element (li.foo) is overqualified, just use .foo without element name.", result.messages[0].message);
26 | },
27 |
28 | "Using a class with two different elements should not result in a warning": function() {
29 | var result = CSSLint.verify("li.foo { float: left;} p.foo { float: right; }", { "overqualified-elements": 1 });
30 | Assert.areEqual(0, result.messages.length);
31 | },
32 |
33 | "Using a class with an element and without should not result in a warning": function() {
34 | var result = CSSLint.verify("li.foo { float: left;} .foo { float: right; }", { "overqualified-elements": 1 });
35 | Assert.areEqual(0, result.messages.length);
36 | }
37 |
38 | }));
39 |
40 | })();
41 |
--------------------------------------------------------------------------------
/tests/rules/performant-transitions.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | "use strict";
3 |
4 | /*global YUITest, CSSLint*/
5 | var Assert = YUITest.Assert;
6 |
7 | YUITest.TestRunner.add(new YUITest.TestCase({
8 |
9 | name: "Performant Transitions Tests",
10 |
11 | "Using a non-performant transition property (width) should result in one warning": function(){
12 | var result = CSSLint.verify("div { transition: width 0.5s linear; }", { "performant-transitions": 1 });
13 | Assert.areEqual(1, result.messages.length);
14 | Assert.areEqual("warning", result.messages[0].type);
15 | Assert.areEqual("Unexpected transition property 'width 0.5s linear'", result.messages[0].message);
16 | },
17 | "Using a non-performant and performant transition properties (transform, width) should result in one warning": function(){
18 | var result = CSSLint.verify("div { transition: transform 0.5s linear, width 0.2s ease-in-out; }", { "performant-transitions": 1 });
19 | Assert.areEqual(1, result.messages.length);
20 | Assert.areEqual("warning", result.messages[0].type);
21 | Assert.areEqual("Unexpected transition property 'width 0.2s ease-in-out'", result.messages[0].message);
22 | },
23 | "Using a performant transition property (transform) should result in 0 warnings": function(){
24 | var result = CSSLint.verify("div { transition: transform 0.5s linear; }", { "performant-transitions": 1 });
25 | Assert.areEqual(0, result.messages.length);
26 | },
27 | "Using a performant transition property (-webkit-transform) should result in 0 warnings": function(){
28 | var result = CSSLint.verify("div { transition: -webkit-transform 0.5s linea atomr; }", { "performant-transitions": 1 });
29 | Assert.areEqual(0, result.messages.length);
30 | },
31 | "Using a performant transition property (-ms-transform) should result in 0 warnings": function(){
32 | var result = CSSLint.verify("div { transition: -ms-transform 0.5s linear; }", { "performant-transitions": 1 });
33 | Assert.areEqual(0, result.messages.length);
34 | },
35 | "Using a performant transition property (opacity) should result in 0 warnings": function(){
36 | var result = CSSLint.verify("div { transition: opacity 0.5s linear; }", { "performant-transitions": 1 });
37 | Assert.areEqual(0, result.messages.length);
38 | },
39 | "Using multiple performant transition properties (opacity, transform) should result in 0 warnings": function(){
40 | var result = CSSLint.verify("div { transition: opacity 0.5s linear, transform 0.25s ease-in-out; }", { "performant-transitions": 1 });
41 | Assert.areEqual(0, result.messages.length);
42 | }
43 | }));
44 | })();
45 |
--------------------------------------------------------------------------------
/tests/rules/qualified-headings.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Qualified Headings Errors",
8 |
9 | "Using a heading as a descendant should result in one warning": function() {
10 | var result = CSSLint.verify("li h3{ float: left;}", { "qualified-headings": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Heading (h3) should not be qualified.", result.messages[0].message);
14 | }
15 |
16 | }));
17 |
18 | })();
19 |
--------------------------------------------------------------------------------
/tests/rules/regex-selectors.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Regex Selectors Errors",
8 |
9 | "Using |= in an attribute selector should result in one warning": function() {
10 | var result = CSSLint.verify("li[class|=foo]{ color: red; }", { "regex-selectors": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Attribute selectors with |= are slow!", result.messages[0].message);
14 | },
15 |
16 | "Using *= in an attribute selector should result in one warning": function() {
17 | var result = CSSLint.verify("li[class*=foo]{ color: red; }", { "regex-selectors": 1 });
18 | Assert.areEqual(1, result.messages.length);
19 | Assert.areEqual("warning", result.messages[0].type);
20 | Assert.areEqual("Attribute selectors with *= are slow!", result.messages[0].message);
21 | },
22 |
23 | "Using $= in an attribute selector should result in one warning": function() {
24 | var result = CSSLint.verify("li[class$=foo]{ color: red; }", { "regex-selectors": 1 });
25 | Assert.areEqual(1, result.messages.length);
26 | Assert.areEqual("warning", result.messages[0].type);
27 | Assert.areEqual("Attribute selectors with $= are slow!", result.messages[0].message);
28 | },
29 |
30 | "Using ~= in an attribute selector should result in one warning": function() {
31 | var result = CSSLint.verify("li[class~=foo]{ color: red; }", { "regex-selectors": 1 });
32 | Assert.areEqual(1, result.messages.length);
33 | Assert.areEqual("warning", result.messages[0].type);
34 | Assert.areEqual("Attribute selectors with ~= are slow!", result.messages[0].message);
35 | },
36 |
37 | "Using ^= in an attribute selector should result in one warning": function() {
38 | var result = CSSLint.verify("li[class^=foo]{ color: red; }", { "regex-selectors": 1 });
39 | Assert.areEqual(1, result.messages.length);
40 | Assert.areEqual("warning", result.messages[0].type);
41 | Assert.areEqual("Attribute selectors with ^= are slow!", result.messages[0].message);
42 | },
43 |
44 | "Using = in an attribute selector should not result in a warning": function() {
45 | var result = CSSLint.verify("li[class=foo]{ color: red; }", { "regex-selectors": 1 });
46 | Assert.areEqual(0, result.messages.length);
47 | }
48 |
49 | }));
50 |
51 | })();
52 |
--------------------------------------------------------------------------------
/tests/rules/selector-max-approaching.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert, i, j, css1 = "", css2 = "", css3 = "", css4 = "";
4 |
5 | // create css1, which has only 4095 rules and 4095 selectors
6 | for (i = 1; i <= 4095; i++) {
7 | css1 += ".selector" + i + " { background:red; } ";
8 | }
9 |
10 | // create css2, which has 4096 rules and 4096 selectors
11 | for (i = 1; i <= 4096; i++) {
12 | css2 += ".selector" + i + " { background:red; } ";
13 | }
14 |
15 | // create css3, which has 1024 and but only 4095 selectors
16 | for (i = 0; i <= 1022; i++) {
17 | j = i * 4;
18 | css3 += ".selector" + (j+1) + ", .selector" + (j+2) + ", .selector" + (j+3) + ", .selector" + (j+4) + " { background:red; } ";
19 | }
20 | css3 += ".selector4093 { background:red; }.selector4094, .selector4095 { background:red; } ";
21 |
22 | // create css4, which has 1024 rules and 4096 selectors
23 | for (i = 0; i <= 1023; i++) {
24 | j = i * 4;
25 | css4 += ".selector" + (j+1) + ", .selector" + (j+2) + ", .selector" + (j+3) + ", .selector" + (j+4) + " { background:red; } ";
26 | }
27 |
28 | YUITest.TestRunner.add(new YUITest.TestCase({
29 |
30 | name: "Selector Max Errors Approaching",
31 |
32 | "Using 4095 or fewer single-selector rules should not result in a warning": function() {
33 | var result = CSSLint.verify(css1, { "selector-max-approaching": 1 });
34 | Assert.areEqual(1, result.messages.length);
35 | Assert.areEqual("warning", result.messages[0].type);
36 | Assert.areEqual("You have 4095 selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", result.messages[0].message);
37 | },
38 |
39 | "Using 4096 or more single-selector rules should result in a warning": function() {
40 | var result = CSSLint.verify(css2, { "selector-max-approaching": 1 });
41 | Assert.areEqual(1, result.messages.length);
42 | Assert.areEqual("warning", result.messages[0].type);
43 | Assert.areEqual("You have 4096 selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", result.messages[0].message);
44 | },
45 |
46 | "Using 4095 or fewer selectors should not result in a warning": function() {
47 | var result = CSSLint.verify(css3, { "selector-max-approaching": 1 });
48 | Assert.areEqual(1, result.messages.length);
49 | Assert.areEqual("warning", result.messages[0].type);
50 | Assert.areEqual("You have 4095 selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", result.messages[0].message);
51 | },
52 |
53 | "Using 4096 or more selectors should result in a warning": function() {
54 | var result = CSSLint.verify(css4, { "selector-max-approaching": 1 });
55 | Assert.areEqual(1, result.messages.length);
56 | Assert.areEqual("warning", result.messages[0].type);
57 | Assert.areEqual("You have 4096 selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", result.messages[0].message);
58 | },
59 |
60 | "Using fewer than 3800 selectors should not result in a warning": function() {
61 | var result = CSSLint.verify(".selector1 { background: red; }", { "selector-max-approaching": 1 });
62 | Assert.areEqual(0, result.messages.length);
63 | }
64 |
65 | }));
66 |
67 | })();
68 |
--------------------------------------------------------------------------------
/tests/rules/selector-max.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert, i, j, css1 = "", css2 = "", css3 = "", css4 = "";
4 |
5 | // create css1, which has only 4095 rules and 4095 selectors
6 | for (i = 1; i <= 4095; i++) {
7 | css1 += ".selector" + i + " { background:red; } ";
8 | }
9 |
10 | // create css2, which has 4096 rules and 4096 selectors
11 | for (i = 1; i <= 4096; i++) {
12 | css2 += ".selector" + i + " { background:red; } ";
13 | }
14 |
15 | // create css3, which has 1024 and but only 4095 selectors
16 | for (i = 0; i <= 1022; i++) {
17 | j = i * 4;
18 | css3 += ".selector" + (j+1) + ", .selector" + (j+2) + ", .selector" + (j+3) + ", .selector" + (j+4) + " { background:red; } ";
19 | }
20 | css3 += ".selector4093 { background:red; }.selector4094, .selector4095 { background:red; } ";
21 |
22 | // create css4, which has 1024 rules and 4096 selectors
23 | for (i = 0; i <= 1023; i++) {
24 | j = i * 4;
25 | css4 += ".selector" + (j+1) + ", .selector" + (j+2) + ", .selector" + (j+3) + ", .selector" + (j+4) + " { background:red; } ";
26 | }
27 |
28 | YUITest.TestRunner.add(new YUITest.TestCase({
29 |
30 | name: "Selector Max Errors",
31 |
32 | "Using 4095 or fewer single-selector rules should not result in a warning": function() {
33 | var result = CSSLint.verify(css1, { "selector-max": 1 });
34 | Assert.areEqual(0, result.messages.length);
35 | },
36 |
37 | "Using 4096 or more single-selector rules should result in a warning": function() {
38 | var result = CSSLint.verify(css2, { "selector-max": 1 });
39 | Assert.areEqual(1, result.messages.length);
40 | Assert.areEqual("warning", result.messages[0].type);
41 | Assert.areEqual("You have 4096 selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", result.messages[0].message);
42 | },
43 |
44 | "Using 4095 or fewer selectors should not result in a warning": function() {
45 | var result = CSSLint.verify(css3, { "selector-max": 1 });
46 | Assert.areEqual(0, result.messages.length);
47 | },
48 |
49 | "Using 4096 or more selectors should result in a warning": function() {
50 | var result = CSSLint.verify(css4, { "selector-max": 1 });
51 | Assert.areEqual(1, result.messages.length);
52 | Assert.areEqual("warning", result.messages[0].type);
53 | Assert.areEqual("You have 4096 selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", result.messages[0].message);
54 | }
55 |
56 | }));
57 |
58 | })();
59 |
--------------------------------------------------------------------------------
/tests/rules/selector-newline.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | "use strict";
3 | var ruleId = "selector-newline", expectWarning, expectPass;
4 |
5 | expectWarning = function (ruleset, expectedMessage) {
6 | var result, enabledRules = {};
7 | enabledRules[ruleId] = 1;
8 | result = CSSLint.verify(ruleset, enabledRules);
9 | YUITest.Assert.areEqual(1, result.messages.length);
10 | YUITest.Assert.areEqual("warning", result.messages[0].type);
11 | YUITest.Assert.areEqual(expectedMessage, result.messages[0].message);
12 | };
13 |
14 | expectPass = function (ruleset) {
15 | var result, enabledRules = {};
16 | enabledRules[ruleId] = 1;
17 | result = CSSLint.verify(ruleset, enabledRules);
18 | YUITest.Assert.areEqual(0, result.messages.length);
19 | };
20 |
21 | YUITest.TestRunner.add(new YUITest.TestCase({
22 |
23 | name: ruleId + " Rule Errors",
24 |
25 | "a newline in a selector should result in a warning": function () {
26 | expectWarning(".foo\n.bar{}", "newline character found in selector (forgot a comma?)");
27 | },
28 | "a newline between selectors should not result in a warning": function () {
29 | expectPass(".foo,\n.bar{}");
30 | },
31 | "'+' or '>' should not result in a warning": function () {
32 | expectPass(".foo > .bar,\n.foo + .bar,\n.foo >\n.bar{}");
33 | }
34 | }));
35 |
36 | }());
37 |
--------------------------------------------------------------------------------
/tests/rules/shorthand.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Shorthand Rule Errors",
8 |
9 | "All padding properties should result in a warning": function() {
10 | var result = CSSLint.verify(".foo{padding-top: 0px; padding-left: 3px; padding-right: 25px; padding-bottom: 10px;}", { "shorthand": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("The properties padding-top, padding-bottom, padding-left, padding-right can be replaced by padding.", result.messages[0].message);
14 | },
15 |
16 | "All margin properties should result in a warning": function() {
17 | var result = CSSLint.verify(".foo{margin-top: 0px; margin-left: 3px; margin-right: 25px; margin-bottom: 10px;}", { "shorthand": 1 });
18 | Assert.areEqual(1, result.messages.length);
19 | Assert.areEqual("warning", result.messages[0].type);
20 | Assert.areEqual("The properties margin-top, margin-bottom, margin-left, margin-right can be replaced by margin.", result.messages[0].message);
21 | },
22 |
23 | "padding-left should not result in a warning": function() {
24 | var result = CSSLint.verify(".foo{ padding-left: 8px;} ", { "shorthand": 1 });
25 | Assert.areEqual(0, result.messages.length);
26 | },
27 |
28 | "margin-top should not result in a warning": function() {
29 | var result = CSSLint.verify(".foo{ margin-top: 8px;} ", { "shorthand": 1 });
30 | Assert.areEqual(0, result.messages.length);
31 | }
32 |
33 | }));
34 |
35 | })();
36 |
--------------------------------------------------------------------------------
/tests/rules/star-property-hack.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "star-property-hack Rule Errors",
8 |
9 | "a property with a star prefix should result in a warning": function() {
10 | var result = CSSLint.verify(".foo{*width: 100px;}", { "star-property-hack": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Property with star prefix found.", result.messages[0].message);
14 | },
15 |
16 | "a property without a star prefix should not result in a warning": function() {
17 | var result = CSSLint.verify(".foo{width: 100px;}", { "star-property-hack": 1 });
18 | Assert.areEqual(0, result.messages.length);
19 | }
20 |
21 | }));
22 |
23 | })();
24 |
--------------------------------------------------------------------------------
/tests/rules/text-indent.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "text-indent Rule Errors",
8 |
9 | "-100px text-indent should result in a warning": function() {
10 | var result = CSSLint.verify(".foo{text-indent: -100px;}", { "text-indent": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.", result.messages[0].message);
14 | },
15 |
16 | "-99px text-indent should not result in a warning": function() {
17 | var result = CSSLint.verify(".foo{text-indent: -99px;} ", { "text-indent": 1 });
18 | Assert.areEqual(0, result.messages.length);
19 | },
20 |
21 | "-99em text-indent should not result in a warning": function() {
22 | var result = CSSLint.verify(".foo{text-indent: -99em;} ", { "text-indent": 1 });
23 | Assert.areEqual(0, result.messages.length);
24 | },
25 |
26 | "-100px text-indent with LTR should not result in a warning": function() {
27 | var result = CSSLint.verify(".foo{text-indent: -100px; direction: ltr; }", { "text-indent": 1 });
28 | Assert.areEqual(0, result.messages.length);
29 | result = CSSLint.verify(".foo{direction: ltr; text-indent: -100px; }", { "text-indent": 1 });
30 | Assert.areEqual(0, result.messages.length);
31 | },
32 |
33 | "-100em text-indent with RTL should result in a warning": function() {
34 | var result = CSSLint.verify(".foo{text-indent: -100em; direction: rtl; }", { "text-indent": 1 });
35 | Assert.areEqual(1, result.messages.length);
36 | Assert.areEqual("warning", result.messages[0].type);
37 | Assert.areEqual("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.", result.messages[0].message);
38 | },
39 |
40 | "5px text-indent should not result in a warning": function() {
41 | var result = CSSLint.verify(".foo{text-indent: 5px;}", { "text-indent": 1 });
42 | Assert.areEqual(0, result.messages.length);
43 | },
44 |
45 | "This should cause a warning, not an error": function() {
46 | var result = CSSLint.verify(".top h1 a { background: url(../images/background/logo.png) no-repeat; display: block; height: 44px; position: relative; text-indent: -9999px; width: 250px; }", { "text-indent": 1 });
47 | Assert.areEqual(1, result.messages.length);
48 | Assert.areEqual("warning", result.messages[0].type);
49 | Assert.areEqual("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.", result.messages[0].message);
50 | }
51 |
52 | }));
53 |
54 | })();
55 |
--------------------------------------------------------------------------------
/tests/rules/underscore-property-hack.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "underscore-property-hack Rule Errors",
8 |
9 | "a property with an underscore prefix should result in a warning": function() {
10 | var result = CSSLint.verify(".foo{_width: 100px;}", { "underscore-property-hack": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Property with underscore prefix found.", result.messages[0].message);
14 | },
15 |
16 | "a property without an underscore prefix should not result in a warning": function() {
17 | var result = CSSLint.verify(".foo{width: 100px;}", { "underscore-property-hack": 1 });
18 | Assert.areEqual(0, result.messages.length);
19 | }
20 |
21 | }));
22 |
23 | })();
24 |
--------------------------------------------------------------------------------
/tests/rules/unique-headings.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Unique Headings Errors",
8 |
9 | "Defining two rules for h1 should result in two warnings": function() {
10 | var result = CSSLint.verify("h1 { color: red;} h1 {color: blue;}", { "unique-headings": 1 });
11 | Assert.areEqual(2, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Heading (h1) has already been defined.", result.messages[0].message);
14 | Assert.areEqual("warning", result.messages[1].type);
15 | Assert.areEqual("You have 2 h1s defined in this stylesheet.", result.messages[1].message);
16 | },
17 |
18 | "Defining two rules for h1 and h2 should result in one warning": function() {
19 | var result = CSSLint.verify("h1 { color: red;} h1 {color: blue;} h2 { color: red;} h2 {color: blue;}", { "unique-headings": 1 });
20 | Assert.areEqual(3, result.messages.length);
21 | Assert.areEqual("warning", result.messages[0].type);
22 | Assert.areEqual("Heading (h1) has already been defined.", result.messages[0].message);
23 | Assert.areEqual("warning", result.messages[1].type);
24 | Assert.areEqual("Heading (h2) has already been defined.", result.messages[1].message);
25 | Assert.areEqual("warning", result.messages[2].type);
26 | Assert.areEqual("You have 2 h1s, 2 h2s defined in this stylesheet.", result.messages[2].message);
27 | },
28 |
29 | "Defining one rule for h1 should not result in a warning": function() {
30 | var result = CSSLint.verify("h1 { color: red;}", { "unique-headings": 1 });
31 | Assert.areEqual(0, result.messages.length);
32 | },
33 |
34 | "Defining a rule for h1 and h1:hover should not result in a warning": function() {
35 | var result = CSSLint.verify("h1 { color: red;} h1:hover { color: blue; }", { "unique-headings": 1 });
36 | Assert.areEqual(0, result.messages.length);
37 | },
38 |
39 | "Defining multiple rules that contain h1 should not result in a warning": function() {
40 | var result = CSSLint.verify("h2 a, h2 a:active, h2 a:hover, h2 a:visited, h2 a:link { color: red;}", { "unique-headings": 1 });
41 | Assert.areEqual(0, result.messages.length);
42 | },
43 |
44 | "Ignore should remove rollup warning messages for unique headings": function() {
45 | var report = CSSLint.verify("/* csslint ignore:start */\nh1 {color: #f0f}\nh1 {color: #ff0}/* csslint ignore:end */h2 {color: #fff}\n");
46 | Assert.areEqual(0, report.messages.length);
47 | }
48 |
49 | }));
50 |
51 | })();
52 |
--------------------------------------------------------------------------------
/tests/rules/universal-selector.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Universal Selector Errors",
8 |
9 | "Using a universal selector alone should result in a warning": function() {
10 | var result = CSSLint.verify("* { font-size: 10px; }", { "universal-selector": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("The universal selector (*) is known to be slow.", result.messages[0].message);
14 | },
15 |
16 | "Using a universal selector as the right-most part should result in a warning": function() {
17 | var result = CSSLint.verify("p div * { font-size: 10px; }", { "universal-selector": 1 });
18 | Assert.areEqual(1, result.messages.length);
19 | Assert.areEqual("warning", result.messages[0].type);
20 | Assert.areEqual("The universal selector (*) is known to be slow.", result.messages[0].message);
21 | },
22 |
23 | "Using a universal selector in the middle should not result in a warning": function() {
24 | var result = CSSLint.verify("* .foo { font-size: 10px; } ", { "universal-selector": 1 });
25 | Assert.areEqual(0, result.messages.length);
26 | }
27 |
28 | }));
29 |
30 | })();
31 |
--------------------------------------------------------------------------------
/tests/rules/unqualified-attributes.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Unqualified Attributes Errors",
8 |
9 | "Using an unqualified attribute selector alone should result in a warning": function() {
10 | var result = CSSLint.verify("[type=text] { font-size: 10px; }", { "unqualified-attributes": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Unqualified attribute selectors are known to be slow.", result.messages[0].message);
14 | },
15 |
16 | "Using an unqualified attribute selector as the right-most part should result in a warning": function() {
17 | var result = CSSLint.verify("p div [type=text] { font-size: 10px; }", { "unqualified-attributes": 1 });
18 | Assert.areEqual(1, result.messages.length);
19 | Assert.areEqual("warning", result.messages[0].type);
20 | Assert.areEqual("Unqualified attribute selectors are known to be slow.", result.messages[0].message);
21 | },
22 |
23 | "Using an unqualified attribute selector in the middle should not result in a warning": function() {
24 | var result = CSSLint.verify("[type=text] .foo { font-size: 10px; } ", { "unqualified-attributes": 1 });
25 | Assert.areEqual(0, result.messages.length);
26 | },
27 |
28 | "Using a qualified attribute selector should not result in a warning": function() {
29 | var result = CSSLint.verify("input[type=text] { font-size: 10px; } ", { "unqualified-attributes": 1 });
30 | Assert.areEqual(0, result.messages.length);
31 | },
32 |
33 | "Using an attribute selector qualified by a class should not result in a warning": function() {
34 | var result = CSSLint.verify(".fancy[type=text] { font-size: 10px; }", { "unqualified-attributes": 1 });
35 | Assert.areEqual(0, result.messages.length);
36 | },
37 |
38 | "Using an attribute selector qualified by an ID should not result in a warning": function() {
39 | var result = CSSLint.verify("#fancy[type=text] { font-size: 10px; }", { "unqualified-attributes": 1 });
40 | Assert.areEqual(0, result.messages.length);
41 | }
42 |
43 | }));
44 |
45 | })();
46 |
--------------------------------------------------------------------------------
/tests/rules/vendor-prefix.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Vendor Prefix Errors",
8 |
9 | "Using -moz-border-radius without border-radius should result in one warning": function() {
10 | var result = CSSLint.verify("h1 {\n -moz-border-radius: 5px; \n}", { "vendor-prefix": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Missing standard property 'border-radius' to go along with '-moz-border-radius'.", result.messages[0].message);
14 | Assert.areEqual(2, result.messages[0].line);
15 | Assert.areEqual(5, result.messages[0].col);
16 | },
17 |
18 | "Using -webkit-border-radius without border-radius should result in one warning": function() {
19 | var result = CSSLint.verify("h1 { -webkit-border-radius: 5px; }", { "vendor-prefix": 1 });
20 | Assert.areEqual(1, result.messages.length);
21 | Assert.areEqual("warning", result.messages[0].type);
22 | Assert.areEqual("Missing standard property 'border-radius' to go along with '-webkit-border-radius'.", result.messages[0].message);
23 | },
24 |
25 | "Using -o-border-radius without border-radius should result in one warning": function() {
26 | var result = CSSLint.verify("h1 { -o-border-radius: 5px; }", { "vendor-prefix": 1 });
27 | Assert.areEqual(1, result.messages.length);
28 | Assert.areEqual("warning", result.messages[0].type);
29 | Assert.areEqual("Missing standard property 'border-radius' to go along with '-o-border-radius'.", result.messages[0].message);
30 | },
31 |
32 | "Using -moz-border-radius after border-radius should result in one warning": function() {
33 | var result = CSSLint.verify("h1 { \nborder-radius: 5px; \n -moz-border-radius: 5px; }", { "vendor-prefix": 1 });
34 | Assert.areEqual(1, result.messages.length);
35 | Assert.areEqual("warning", result.messages[0].type);
36 | Assert.areEqual("Standard property 'border-radius' should come after vendor-prefixed property '-moz-border-radius'.", result.messages[0].message);
37 | Assert.areEqual(3, result.messages[0].line);
38 | Assert.areEqual(5, result.messages[0].col);
39 |
40 | },
41 |
42 | "Using -webkit-border-bottom-left-radius with border-bottom-left-radius should not result in a warning.": function() {
43 | var result = CSSLint.verify("h1 { -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }", { "vendor-prefix": 1 });
44 | Assert.areEqual(0, result.messages.length);
45 | },
46 |
47 | "Using -moz-border-radius-bottomleft should result in a warning.": function() {
48 | var result = CSSLint.verify("h1 { -moz-border-radius-bottomleft: 5px; }", { "vendor-prefix": 1 });
49 | Assert.areEqual(1, result.messages.length);
50 | Assert.areEqual("warning", result.messages[0].type);
51 | Assert.areEqual("Missing standard property 'border-bottom-left-radius' to go along with '-moz-border-radius-bottomleft'.", result.messages[0].message);
52 | },
53 |
54 | "Using -moz-box-shadow should result in a warning.": function() {
55 | var result = CSSLint.verify("h1 { -moz-box-shadow: 5px; }", { "vendor-prefix": 1 });
56 | Assert.areEqual(1, result.messages.length);
57 | Assert.areEqual("warning", result.messages[0].type);
58 | Assert.areEqual("Missing standard property 'box-shadow' to go along with '-moz-box-shadow'.", result.messages[0].message);
59 | },
60 |
61 | "Using -moz-user-select should not result in a warning.": function() {
62 | var result = CSSLint.verify("h1 { -moz-user-select:none; }", { "vendor-prefix": 1 });
63 | Assert.areEqual(0, result.messages.length);
64 | },
65 |
66 | "Using @font-face should not result in an error (#90)": function() {
67 | var result = CSSLint.verify("@font-face { src:url('../fonts/UniversBold.otf');font-family:Univers;advancedAntiAliasing: true;}", { "vendor-prefix": 1 });
68 | Assert.areEqual(0, result.messages.length);
69 | }
70 |
71 | }));
72 |
73 | })();
74 |
--------------------------------------------------------------------------------
/tests/rules/zero-units.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict";
3 | var Assert = YUITest.Assert;
4 |
5 | YUITest.TestRunner.add(new YUITest.TestCase({
6 |
7 | name: "Zero Units Errors",
8 |
9 | "Using 0px should result in one warning": function() {
10 | var result = CSSLint.verify("h1 { left: 0px; }", { "zero-units": 1 });
11 | Assert.areEqual(1, result.messages.length);
12 | Assert.areEqual("warning", result.messages[0].type);
13 | Assert.areEqual("Values of 0 shouldn't have units specified.", result.messages[0].message);
14 | },
15 |
16 | "Using 0em should result in one warning": function() {
17 | var result = CSSLint.verify("h1 { left: 0em; }", { "zero-units": 1 });
18 | Assert.areEqual(1, result.messages.length);
19 | Assert.areEqual("warning", result.messages[0].type);
20 | Assert.areEqual("Values of 0 shouldn't have units specified.", result.messages[0].message);
21 | },
22 |
23 | "Using 0% should result in one warning": function() {
24 | var result = CSSLint.verify("h1 { left: 0%; }", { "zero-units": 1 });
25 | Assert.areEqual(1, result.messages.length);
26 | Assert.areEqual("warning", result.messages[0].type);
27 | Assert.areEqual("Values of 0 shouldn't have units specified.", result.messages[0].message);
28 | },
29 |
30 | "Using 0 should not result in a warning": function() {
31 | var result = CSSLint.verify("h1 { left: 0; }", { "zero-units": 1 });
32 | Assert.areEqual(0, result.messages.length);
33 | },
34 |
35 | "Using 0s for animation-duration should not result in a warning": function() {
36 | var result = CSSLint.verify("h1 { animation-duration: 0s; }", { "zero-units": 1 });
37 | Assert.areEqual(0, result.messages.length);
38 | }
39 |
40 |
41 | }));
42 |
43 | })();
44 |
--------------------------------------------------------------------------------
/tests/testrunner.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | YUI Test
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |