diff --git a/CHANGELOG.md b/CHANGELOG.md index ee6c74a5..755aa333 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Sass Lint Changelog +## v1.9.0 + +**August 18, 2016** + +**Fixes** +* Fixed an issue with teh indentation rule when it encountered at-rules with no block immediately preceeding a map [#779](https://github.com/sasstools/sass-lint/issues/779) [#783](https://github.com/sasstools/sass-lint/issues/783) +* Fixed an issue in `single-lint-per-selector` where inline comments were seen as selectors [#789](https://github.com/sasstools/sass-lint/issues/789) +* Fixed an issue with interpolation in placeholders within the `bem-depth` rule [#782](https://github.com/sasstools/sass-lint/issues/782) +* Removed duplicated code from `no-mergeable-selectors` to helper methods + +**Documentation** +* Fixed typos in no-vendor-prefixes rule documentation [#787](https://github.com/sasstools/sass-lint/issues/787) +* Added link to Visual Studio extension [#815](https://github.com/sasstools/sass-lint/pull/815) + +**New Rules** +* Added the `no-color-hex` rule to disallow all hexadecimal colour definitions [#754](https://github.com/sasstools/sass-lint/issues/754) + +**Updates** +* Gonzales-pe updated to version 3.4.4 which fixes a lot of longstanding issues see the [Changelog](https://github.com/tonyganch/gonzales-pe/blob/dev/CHANGELOG.md) + ## v1.8.2 **June 23, 2016** diff --git a/README.md b/README.md index 555a4a75..8052517b 100644 --- a/README.md +++ b/README.md @@ -194,3 +194,4 @@ Our AST is [Gonzales-PE](https://github.com/tonyganch/gonzales-pe/tree/dev). Eac * [Sublime Text](https://github.com/skovhus/SublimeLinter-contrib-sass-lint) * [Brackets](https://github.com/petetnt/brackets-sass-lint) * [IntelliJ IDEA, RubyMine, WebStorm, PhpStorm, PyCharm](https://github.com/idok/sass-lint-plugin) +* [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=glen-84.sass-lint) diff --git a/docs/rules/no-color-hex.md b/docs/rules/no-color-hex.md new file mode 100644 index 00000000..9d55d95e --- /dev/null +++ b/docs/rules/no-color-hex.md @@ -0,0 +1,33 @@ +# No Color Hex + +Rule `no-color-hex` will disallow the use of hexadecimal colors + +## Examples + +When enabled the following are disallowed. + +```scss +$foo-color: #456; + +.bar { + background: linear-gradient(top, #3ff, #ddd); +} + +.baz { + color: #fff; +} +``` + +When enabled the following are allowed: + +```scss +$foo-color: red; + +.bar { + background: linear-gradient(top, blue, green); +} + +.baz { + color: white; +} +``` diff --git a/docs/rules/no-vendor-prefixes.md b/docs/rules/no-vendor-prefixes.md index 22bd1567..957f9cf3 100644 --- a/docs/rules/no-vendor-prefixes.md +++ b/docs/rules/no-vendor-prefixes.md @@ -41,14 +41,14 @@ When enabled, the following are disallowed: ### Additional Identifiers -When `additional-identifiers` contains a custom prefix value of `test` as show below +When `additional-identifiers` contains a custom prefix value of `khtml` as show below ```yaml -no-vendor-prefix: +no-vendor-prefixes: - 1 - - 'additional-identifiers': - - 'khtml' + additional-identifiers: + - khtml ``` The following would now also be disallowed @@ -64,12 +64,12 @@ The following would now also be disallowed When `excluded-identifiers` contains currently disallowed prefix values such as `webkit` and `moz` as show below ```yaml -no-vendor-prefix: +no-vendor-prefixes: - 1 - - 'excluded-identifiers': - - 'webkit' - - 'moz' + excluded-identifiers: + - webkit + - moz ``` The following would now be allowed diff --git a/lib/config/sass-lint.yml b/lib/config/sass-lint.yml index dd3e5113..1b3ed511 100644 --- a/lib/config/sass-lint.yml +++ b/lib/config/sass-lint.yml @@ -18,6 +18,7 @@ rules: # Disallows no-attribute-selectors: 0 + no-color-hex: 0 no-color-keywords: 1 no-color-literals: 1 no-combinators: 0 diff --git a/lib/rules/bem-depth.js b/lib/rules/bem-depth.js index 9910b765..356efa25 100644 --- a/lib/rules/bem-depth.js +++ b/lib/rules/bem-depth.js @@ -1,6 +1,7 @@ 'use strict'; var helpers = require('../helpers'); +var selectorHelpers = require('../selector-helpers'); /** * Get number of BEM elements in @@ -32,18 +33,19 @@ module.exports = { maxDepth = parser.options['max-depth']; if (node.is('placeholder')) { - name = node.first('ident') && node.first('ident').content; - depth = bemDepth(name); - - if (name && depth > maxDepth) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': node.start.line, - 'column': node.start.column, - 'message': ['Placeholder \'%', name, '\' should have ', maxDepth, ' or fewer BEM elements, but ', - depth, ' were found.'].join(''), - 'severity': parser.severity - }); + name = selectorHelpers.constructSelector(node); + if (name) { + depth = bemDepth(name); + if (depth > maxDepth) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': node.start.line, + 'column': node.start.column, + 'message': ['Placeholder \'%', name, '\' should have ', maxDepth, ' or fewer BEM elements, but ', + depth, ' were found.'].join(''), + 'severity': parser.severity + }); + } } } else { diff --git a/lib/rules/indentation.js b/lib/rules/indentation.js index 7ef79b16..83b688b1 100644 --- a/lib/rules/indentation.js +++ b/lib/rules/indentation.js @@ -166,7 +166,7 @@ module.exports = { mixedWarning = false; } // if we're in an atrule make we need to possibly handle multiline arguments - if (n.is('atrule')) { + if (n.is('atrule') && n.contains('block')) { inAtRule = true; inBlock = false; } diff --git a/lib/rules/no-color-hex.js b/lib/rules/no-color-hex.js new file mode 100644 index 00000000..d5190056 --- /dev/null +++ b/lib/rules/no-color-hex.js @@ -0,0 +1,22 @@ +'use strict'; + +var helpers = require('../helpers'); + +module.exports = { + 'name': 'no-color-hex', + 'defaults': {}, + 'detect': function (ast, parser) { + var result = []; + + ast.traverseByType('color', function (value) { + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': value.start.line, + 'column': value.start.column, + 'message': 'Hexadecimal colors should not be used', + 'severity': parser.severity + }); + }); + return result; + } +}; diff --git a/lib/rules/no-mergeable-selectors.js b/lib/rules/no-mergeable-selectors.js index 9bb70d48..d5752e06 100644 --- a/lib/rules/no-mergeable-selectors.js +++ b/lib/rules/no-mergeable-selectors.js @@ -1,10 +1,10 @@ 'use strict'; -var helpers = require('../helpers'); +var helpers = require('../helpers'), + selectorHelpers = require('../selector-helpers'); var mergeableNodes = ['atrule', 'include', 'ruleset'], validAtRules = ['media'], - simpleIdents = ['ident', 'number', 'operator', 'combinator', 'string', 'parentSelector', 'delimiter', 'typeSelector', 'attributeMatch'], curLevel = 0, curSelector = [], parentSelector = [], @@ -12,109 +12,6 @@ var mergeableNodes = ['atrule', 'include', 'ruleset'], syntax = ''; -/** - * Adds grammar around our content blocks to construct selectors with - * more readable formats. - * - * @param {object} val - The current value node - * @param {string} prefix - The grammar to prefix the value with - * @param {string} suffix - The grammar to add after the value - * @returns {string} The correct readable format - */ -var addGrammar = function (val, prefix, suffix) { - return prefix + val.content + suffix; -}; - -/** - * Adds grammar around our content blocks to construct selectors with - * more readable formats and loops the content as they're within sub blocks. - * - * @param {object} val - The current value node - * @param {string} prefix - The grammar to prefix the value with - * @param {string} suffix - The grammar to add after the value - * @param {function} constructSelector - The callback we wish to use which means constructSelector in this instance - * @returns {string} The correct readable format - */ -var constructSubSelector = function (val, prefix, suffix, constructSelector) { - var content = prefix; - val.forEach(function (subItem) { - content += constructSelector(subItem); - }); - - return content + suffix; -}; - -/** - * Constructs a syntax complete selector for our selector matching and warning output - * - * @param {object} val - The current node / part of our selector - * @returns {string} - Content: The current node with correct syntax e.g. class my-class = '.my-class' - */ -var constructSelector = function (val) { - var content = null; - - if (val.is('id')) { - content = addGrammar(val, '#', ''); - } - - else if (val.is('class')) { - content = addGrammar(val, '.', ''); - } - - else if (simpleIdents.indexOf(val.type) !== -1) { - content = val.content; - } - - else if (val.is('attributeSelector')) { - content = constructSubSelector(val, '[', ']', constructSelector); - } - - else if (val.is('atkeyword')) { - content = constructSubSelector(val, '@', '', constructSelector); - } - - else if (val.is('placeholder')) { - content = constructSubSelector(val, '%', '', constructSelector); - } - - else if (val.is('variable')) { - content = constructSubSelector(val, '$', '', constructSelector); - } - - else if (val.is('pseudoClass')) { - content = addGrammar(val, ':', ''); - } - - else if (val.is('pseudoElement')) { - content = addGrammar(val, '::', ''); - } - - else if (val.is('nth')) { - content = addGrammar(val, '(', ')'); - } - - else if (val.is('nthSelector')) { - content = constructSubSelector(val, ':', '', constructSelector); - } - - else if (val.is('parentheses')) { - content = constructSubSelector(val, '(', ')', constructSelector); - } - - else if (val.is('space')) { - content = ' '; - } - - else if (val.is('parentSelectorExtension') || val.is('attributeName') || val.is('attributeValue') || val.is('dimension')) { - content = constructSubSelector(val, '', '', constructSelector); - } - - else if (val.is('interpolation')) { - content = constructSubSelector(val, '#{', '}', constructSelector); - } - return content; -}; - /** * Traverses a block and calls our callback function for each block encountered * @@ -161,11 +58,11 @@ var checkRuleset = function (ruleNode) { if (!ruleNodeItem.is('block')) { if (ruleNodeItem.is('selector')) { ruleNodeItem.forEach(function (selectorContent) { - ruleSet += constructSelector(selectorContent); + ruleSet += selectorHelpers.constructSelector(selectorContent); }); } else if (ruleNodeItem.is('delimiter') || ruleNodeItem.is('space')) { - ruleSet += constructSelector(ruleNodeItem); + ruleSet += selectorHelpers.constructSelector(ruleNodeItem); } } }); @@ -184,7 +81,7 @@ var checkAtRule = function (atRule) { var test = ''; atRule.forEach(function (atRuleItem) { if (!atRuleItem.is('block')) { - test += constructSelector(atRuleItem); + test += selectorHelpers.constructSelector(atRuleItem); } }); updateList(test, true, atRule.start.line, atRule.start.column); diff --git a/lib/rules/single-line-per-selector.js b/lib/rules/single-line-per-selector.js index 9ba9c7ca..53cdf902 100644 --- a/lib/rules/single-line-per-selector.js +++ b/lib/rules/single-line-per-selector.js @@ -2,6 +2,31 @@ var helpers = require('../helpers'); +/** + * Checks a ruleset for selectors or EOL characters. If a selector is found before an EOL + * then it returns the selector node for reporting or returns false + * + * @param {Object} ruleset - The ruleset node + * @param {number} index - The current index of the delimiter + * @returns {Object|boolean} Either the selector node or false + */ +var checkLineForSelector = function (ruleset, index) { + var curIndex = index += 1; + if (ruleset.content[curIndex]) { + for (; curIndex < ruleset.content.length; curIndex++) { + var curType = ruleset.content[curIndex].type; + if (curType === 'space' && helpers.hasEOL(ruleset.content[curIndex])) { + return false; + } + if (curType === 'selector' || curType === 'typeSelector') { + return ruleset.content[curIndex]; + } + } + } + + return false; +}; + module.exports = { 'name': 'single-line-per-selector', 'defaults': {}, @@ -10,22 +35,16 @@ module.exports = { ast.traverseByType('ruleset', function (ruleset) { ruleset.forEach('delimiter', function (delimiter, j) { - var next = ruleset.content[j + 1] || false; + var next = checkLineForSelector(ruleset, j); if (next) { - if (next.is('selector')) { - next = next.content[0]; - } - - if (!(next.is('space') && helpers.hasEOL(next.content))) { - result = helpers.addUnique(result, { - 'ruleId': parser.rule.name, - 'line': next.start.line, - 'column': next.start.column, - 'message': 'Selectors must be placed on new lines', - 'severity': parser.severity - }); - } + result = helpers.addUnique(result, { + 'ruleId': parser.rule.name, + 'line': next.start.line, + 'column': next.start.column, + 'message': 'Selectors must be placed on new lines', + 'severity': parser.severity + }); } }); }); diff --git a/lib/selector-helpers.js b/lib/selector-helpers.js new file mode 100644 index 00000000..f522c964 --- /dev/null +++ b/lib/selector-helpers.js @@ -0,0 +1,132 @@ +'use strict'; + +// ============================================================================== +// Helpers +// ============================================================================== + +var simpleIdents = [ + 'ident', + 'number', + 'operator', + 'combinator', + 'string', + 'parentSelector', + 'delimiter', + 'typeSelector', + 'attributeMatch' +]; + +/** + * Adds grammar around our content blocks to construct selectors with + * more readable formats. + * + * @param {object} val - The current value node + * @param {string} prefix - The grammar to prefix the value with + * @param {string} suffix - The grammar to add after the value + * @returns {string} The correct readable format + */ +var addGrammar = function (val, prefix, suffix) { + return prefix + val.content + suffix; +}; + +/** + * Adds grammar around our content blocks to construct selectors with + * more readable formats and loops the content as they're within sub blocks. + * + * @param {object} val - The current value node + * @param {string} prefix - The grammar to prefix the value with + * @param {string} suffix - The grammar to add after the value + * @param {function} constructSelector - The callback we wish to use which means constructSelector in this instance + * @returns {string} The correct readable format + */ +var constructSubSelector = function (val, prefix, suffix, constructSelector) { + var content = prefix; + val.forEach(function (subItem) { + content += constructSelector(subItem); + }); + + return content + suffix; +}; + +// ============================================================================== +// Public Methods +// ============================================================================== + +/** + * Constructs a syntax complete selector for our selector matching and warning output + * + * @param {object} val - The current node / part of our selector + * @returns {string} - Content: The current node with correct syntax e.g. class my-class = '.my-class' + */ +var constructSelector = function (val) { + var content = null; + + if (val.is('id')) { + content = addGrammar(val, '#', ''); + } + + else if (val.is('class')) { + content = addGrammar(val, '.', ''); + } + + else if (simpleIdents.indexOf(val.type) !== -1) { + content = val.content; + } + + else if (val.is('arguments')) { + content = constructSubSelector(val, '(', ')', constructSelector); + } + + else if (val.is('attributeSelector')) { + content = constructSubSelector(val, '[', ']', constructSelector); + } + + else if (val.is('atkeyword')) { + content = constructSubSelector(val, '@', '', constructSelector); + } + + else if (val.is('placeholder')) { + content = constructSubSelector(val, '%', '', constructSelector); + } + + else if (val.is('variable')) { + content = constructSubSelector(val, '$', '', constructSelector); + } + + else if (val.is('pseudoClass')) { + content = constructSubSelector(val, ':', '', constructSelector); + } + + else if (val.is('pseudoElement')) { + content = addGrammar(val, '::', ''); + } + + else if (val.is('nth')) { + content = addGrammar(val, '(', ')'); + } + + else if (val.is('nthSelector')) { + content = constructSubSelector(val, ':', '', constructSelector); + } + + else if (val.is('parentheses')) { + content = constructSubSelector(val, '(', ')', constructSelector); + } + + else if (val.is('space')) { + content = ' '; + } + + else if (val.is('parentSelectorExtension') || val.is('attributeName') || val.is('attributeValue') || val.is('dimension')) { + content = constructSubSelector(val, '', '', constructSelector); + } + + else if (val.is('interpolation')) { + content = constructSubSelector(val, '#{', '}', constructSelector); + } + return content; +}; + +module.exports = { + constructSelector: constructSelector +}; diff --git a/package.json b/package.json index 306f01f6..4184e54c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sass-lint", - "version": "1.8.2", + "version": "1.9.0", "description": "All Node Sass linter!", "main": "index.js", "scripts": { @@ -33,7 +33,7 @@ "fs-extra": "^0.30.0", "glob": "^7.0.0", "globule": "^1.0.0", - "gonzales-pe": "3.3.4", + "gonzales-pe": "3.4.4", "js-yaml": "^3.5.4", "lodash.capitalize": "^4.1.0", "lodash.kebabcase": "^4.0.0", @@ -45,7 +45,7 @@ "coveralls": "^2.11.4", "deep-equal": "^1.0.1", "istanbul": "^0.4.0", - "mocha": "^2.2.5", + "mocha": "^3.0.1", "sinon": "^1.17.4" } } diff --git a/tests/rules/indentation.js b/tests/rules/indentation.js index 7ccf15f3..43968f51 100644 --- a/tests/rules/indentation.js +++ b/tests/rules/indentation.js @@ -13,7 +13,7 @@ describe('indentation - scss', function () { lint.test(spaceFile, { 'indentation': 1 }, function (data) { - lint.assert.equal(12, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); @@ -27,7 +27,7 @@ describe('indentation - scss', function () { } ] }, function (data) { - lint.assert.equal(12, data.warningCount); + lint.assert.equal(14, data.warningCount); done(); }); }); diff --git a/tests/rules/no-color-hex.js b/tests/rules/no-color-hex.js new file mode 100644 index 00000000..2cfe53e5 --- /dev/null +++ b/tests/rules/no-color-hex.js @@ -0,0 +1,35 @@ +'use strict'; + +var lint = require('./_lint'); + +////////////////////////////// +// SCSS syntax tests +////////////////////////////// +describe('no color hex - scss', function () { + var file = lint.file('no-color-hex.scss'); + + it('enforce', function (done) { + lint.test(file, { + 'no-color-hex': 1 + }, function (data) { + lint.assert.equal(9, data.warningCount); + done(); + }); + }); +}); + +////////////////////////////// +// Sass syntax tests +////////////////////////////// +describe('no color hex - sass', function () { + var file = lint.file('no-color-hex.sass'); + + it('enforce', function (done) { + lint.test(file, { + 'no-color-hex': 1 + }, function (data) { + lint.assert.equal(9, data.warningCount); + done(); + }); + }); +}); diff --git a/tests/sass/indentation/indentation-spaces.scss b/tests/sass/indentation/indentation-spaces.scss index 52d83606..15d8ea6b 100644 --- a/tests/sass/indentation/indentation-spaces.scss +++ b/tests/sass/indentation/indentation-spaces.scss @@ -143,3 +143,33 @@ $colors: ( margin: 16px; } } + +// Issue #783 and issue #779 - at-rule throws off indentation of maps etc + +@import 'echo-base/defaults/breakpoints'; + +$textsizes: ( + xs: ( + s: 10px, + m: 10px + ), + s: ( + s: 12px, + m: 13px + ), +); + +@function em($pixels, $context: $font-size-base) { + @return #{($pixels / $context)}rem; +} + +$textsizes: ( + xs: ( + s: 10px, + m: 10px + ), + s: ( + s: 12px, + m: 13px + ), +); diff --git a/tests/sass/indentation/indentation-tabs.scss b/tests/sass/indentation/indentation-tabs.scss index d0bf98fa..c34f5d5a 100644 --- a/tests/sass/indentation/indentation-tabs.scss +++ b/tests/sass/indentation/indentation-tabs.scss @@ -143,3 +143,33 @@ $colors: ( margin: 16px; } } + +// Issue #783 and issue #779 - at-rule throws off indentation of maps etc + +@import 'echo-base/defaults/breakpoints'; + +$textsizes: ( + xs: ( + s: 10px, + m: 10px + ), + s: ( + s: 12px, + m: 13px + ), +); + +@function em($pixels, $context: $font-size-base) { + @return #{($pixels / $context)}rem; +} + +$textsizes: ( + xs: ( + s: 10px, + m: 10px + ), + s: ( + s: 12px, + m: 13px + ), +); diff --git a/tests/sass/no-color-hex.sass b/tests/sass/no-color-hex.sass new file mode 100644 index 00000000..69c26e65 --- /dev/null +++ b/tests/sass/no-color-hex.sass @@ -0,0 +1,25 @@ +$foo-color: #123 + +.foo + background: linear-gradient(top, #cc2, #44d) + color: #fff + + +$bar-color: #112233 + +.bar + background: linear-gradient(top, #cccc22, #4444dd) + color: #ffffff + +.baz + border-color: #123456 + + +// color literals, rgb and hsl values currently don't get returned +// by the AST's color type + +$qux-color: red +$rgb-color: rgb(255, 255, 255) +$rgba-color: rgba(0, 0, 0, .1) +$hsl-color: hsl(40, 50%, 50%) +$hsla-color: hsla(40, 50%, 50%, .3) diff --git a/tests/sass/no-color-hex.scss b/tests/sass/no-color-hex.scss new file mode 100644 index 00000000..58c383d4 --- /dev/null +++ b/tests/sass/no-color-hex.scss @@ -0,0 +1,26 @@ +$foo-color: #123; + +.foo { + background: linear-gradient(top, #cc2, #44d); + color: #fff; +} + +$bar-color: #112233; + +.bar { + background: linear-gradient(top, #cccc22, #4444dd); + color: #ffffff; +} + +.baz { + border-color: #123456; +} + +// color literals, rgb and hsl values currently don't get returned +// by the AST's color type + +$qux-color: red; +$rgb-color: rgb(255, 255, 255); +$rgba-color: rgba(0, 0, 0, .1); +$hsl-color: hsl(40, 50%, 50%); +$hsla-color: hsla(40, 50%, 50%, .3); diff --git a/tests/sass/selector-helpers/selector-helpers.scss b/tests/sass/selector-helpers/selector-helpers.scss new file mode 100644 index 00000000..7a449784 --- /dev/null +++ b/tests/sass/selector-helpers/selector-helpers.scss @@ -0,0 +1,61 @@ +.test{ + color: red; +} + +#test{ + color: red; +} + +%test { + color: red; +} + +.#{test} { + color: red +} + +.test, #test { + color: red; +} + +input[type="text"] { + color: red; +} + +.test > li { + color: red; +} + +span[lang~=en-us] { + color: red; +} + +.block__element-one { + color: red; +} + +@media (max-width: 200px) { + color: red; +} + +##{$id} { + color: red; +} + +.right-element::-ms-backdrop { + content: "right-prefixed-element"; +} + +.wrong-element:selection { + content: "wrong-element"; +} + +p:nth-of-type(2) { + margin: 0; +} + +.test { + &__test { + color: red; + } +} diff --git a/tests/sass/single-line-per-selector.sass b/tests/sass/single-line-per-selector.sass index 72210d79..f2b94b0f 100644 --- a/tests/sass/single-line-per-selector.sass +++ b/tests/sass/single-line-per-selector.sass @@ -48,3 +48,12 @@ .foo .bar &, .baz & content: 'foo' + +// Issue #789 - Issue with comments being warned as selector content on single lines +button, +html, +html input[type='button'], // 6 +input[type='reset'], +input[type='submit'] + -webkit-appearance: button; // 7 + cursor: pointer; // 8 diff --git a/tests/sass/single-line-per-selector.scss b/tests/sass/single-line-per-selector.scss index 65ed18ef..18fcb5df 100644 --- a/tests/sass/single-line-per-selector.scss +++ b/tests/sass/single-line-per-selector.scss @@ -54,3 +54,13 @@ content: 'foo'; } } + +// Issue #789 - Issue with comments being warned as selector content on single lines +button, +html, +html input[type='button'], // 6 +input[type='reset'], +input[type='submit'] { + -webkit-appearance: button; // 7 + cursor: pointer; // 8 +} diff --git a/tests/selector-helpers/selectorHelpers.js b/tests/selector-helpers/selectorHelpers.js new file mode 100644 index 00000000..9a502ef9 --- /dev/null +++ b/tests/selector-helpers/selectorHelpers.js @@ -0,0 +1,114 @@ +'use strict'; + +var assert = require('assert'), + selectorHelpers = require('../../lib/selector-helpers'), + groot = require('../../lib/groot'), + path = require('path'), + fs = require('fs'), + equal = require('deep-equal'); + +describe('selectorHelpers - constructSelector', function () { + + var expectedSelectors = [ + '.test', + '#test', + '%test', + '.#{test}', + '.test', + '#test', + 'input[type="text"]', + '.test > li', + 'span[lang~=en-us]', + '.block__element-one', + '##{$id}', + '.right-element::-ms-backdrop', + '.wrong-element:selection', + 'p:nth-of-type(2)', + '.test', + '&__test' + ], + selectorList = []; + + before(function () { + var file = '../sass/selector-helpers/selector-helpers.scss', + ast = groot(fs.readFileSync(path.join(__dirname, file)), path.extname(file).replace('.', ''), file); + + ast.traverseByType('selector', function (value) { + var ruleSet = ''; + value.forEach(function (selectorContent) { + ruleSet += selectorHelpers.constructSelector(selectorContent); + }); + selectorList.push(ruleSet); + }); + }); + + ////////////////////////////// + // contructSelector + ////////////////////////////// + + it('should return the correct class name', function (done) { + assert(equal(selectorList[0], expectedSelectors[0])); + done(); + }); + + it('should return the correct ID name', function (done) { + assert(equal(selectorList[1], expectedSelectors[1])); + done(); + }); + + it('should return the correct placeholder name', function (done) { + assert(equal(selectorList[2], expectedSelectors[2])); + done(); + }); + + it('should return the correct interpolated selector name', function (done) { + assert(equal(selectorList[3], expectedSelectors[3])); + done(); + }); + + it('should return the correct type selector name', function (done) { + assert(equal(selectorList[6], expectedSelectors[6])); + done(); + }); + + it('should return the correct combinator selector name', function (done) { + assert(equal(selectorList[7], expectedSelectors[7])); + done(); + }); + + it('should return the correct attribute selector name', function (done) { + assert(equal(selectorList[8], expectedSelectors[8])); + done(); + }); + + it('should return the correct BEM selector name', function (done) { + assert(equal(selectorList[9], expectedSelectors[9])); + done(); + }); + + it('should return the correct interpolated ID selector name', function (done) { + assert(equal(selectorList[10], expectedSelectors[10])); + done(); + }); + + it('should return the correct pseudo element selector name', function (done) { + assert(equal(selectorList[11], expectedSelectors[11])); + done(); + }); + + it('should return the correct pseudo selector name', function (done) { + assert(equal(selectorList[12], expectedSelectors[12])); + done(); + }); + + it('should return the correct nth selector name', function (done) { + assert(equal(selectorList[13], expectedSelectors[13])); + done(); + }); + + it('should return the correct parent selector name', function (done) { + assert(equal(selectorList[16], expectedSelectors[16])); + done(); + }); + +});