diff --git a/lib/utils/cssSelector.js b/lib/utils/cssSelector.js index 82c62c8..1260894 100644 --- a/lib/utils/cssSelector.js +++ b/lib/utils/cssSelector.js @@ -10,6 +10,20 @@ const inList = (list = [], selector = '') => { }); }; +const sanitizeForEmmet = (selectors = '') => { + if (!isString(selectors)) { + return false; + } + + return selectors.trim() + .replace(/\ > \ /g, ' ') + .replace(/>/g, ' ') + .replace(/\ \ \ \ /g, ' ') + .replace(/\ \ \ /g, ' ') + .replace(/\ \ /g, ' ') + .replace(/\ /g, ' > '); +}; + const split = (selector = '') => { if (!isString(selector)) { return false; @@ -20,5 +34,6 @@ const split = (selector = '') => { module.exports = { inList, + sanitizeForEmmet, split, }; diff --git a/lib/utils/isString.js b/lib/utils/isString.js index baa5d5a..9189d22 100644 --- a/lib/utils/isString.js +++ b/lib/utils/isString.js @@ -1,5 +1,9 @@ const isString = function(string) { - return (string && typeof string === 'string' && string !== ''); + if (string && typeof string === 'string' && string !== '') { + return true; + } else { + return false; + } }; module.exports = isString; diff --git a/lib/utils/renderer.js b/lib/utils/renderer.js index cb620d8..c7ac81a 100644 --- a/lib/utils/renderer.js +++ b/lib/utils/renderer.js @@ -1,22 +1,14 @@ const emmet = require('../vendor/emmet'); const isString = require('./isString'); +const sanitizeForEmmet = require('./cssSelector').sanitizeForEmmet; +const hasProp = require('./object').hasProp; -const sanitize = (selectors = '') => { - return selectors.trim() - .replace(/\ > \ /g, ' ') - .replace(/>/g, ' ') - .replace(/\ \ \ \ /g, ' ') - .replace(/\ \ \ /g, ' ') - .replace(/\ \ /g, ' ') - .replace(/\ /g, ' > '); -}; - -const renderer = (document, selectors) => { - if (!document || !isString(selectors)) { - return; +const renderer = (document = false, selectors = '') => { + if (!document || !hasProp(document, 'location') || !isString(selectors)) { + return false; } - return emmet(document, sanitize(selectors)); + return emmet(document, sanitizeForEmmet(selectors)); }; module.exports = renderer; diff --git a/lib/utils/sass.js b/lib/utils/sass.js index 675ec8d..1dcd8cf 100644 --- a/lib/utils/sass.js +++ b/lib/utils/sass.js @@ -34,7 +34,7 @@ const getOptions = (o = defaultOptions) => { return opts; }; -const render = (o = {}) => { +const render = (o = defaultOptions) => { const opts = getOptions(o); return opts ? nodeSass.renderSync(opts).css.toString() : false; diff --git a/test/setup.mocha.js b/test/setup.mocha.js index 92e2885..c25a26d 100644 --- a/test/setup.mocha.js +++ b/test/setup.mocha.js @@ -26,6 +26,9 @@ global.utils = { cssFile: require('../lib/utils/cssFile'), cssSelector: require('../lib/utils/cssSelector'), cssom: require('../lib/utils/cssom'), + isString: require('../lib/utils/isString'), isValid: require('../lib/utils/isValid'), + renderer: require('../lib/utils/renderer'), resolve: require('../lib/utils/resolve'), + sass: require('../lib/utils/sass'), }; diff --git a/test/unit/utils/cssSelector.js b/test/unit/utils/cssSelector.js index d5b1694..a534021 100644 --- a/test/unit/utils/cssSelector.js +++ b/test/unit/utils/cssSelector.js @@ -22,6 +22,27 @@ describe('utils', () => { }); }); + describe('.sanitizeForEmmet()', () => { + const fn = cssSelector.sanitizeForEmmet; + + it('should return false if arguments are invalid', () => { + expect(fn()).to.be.false; + expect(fn('')).to.be.false; + }); + + it('should trim whitespace before/after string', () => { + expect(fn(' div ')).to.equal('div'); + }); + + it('should add > symbol for space', () => { + expect(fn('div li')).to.equal('div > li'); + }); + + it('should resolve excess spacing', () => { + expect(fn('div li')).to.equal('div > li'); + }); + }); + describe('.split()', () => { const fn = cssSelector.split; diff --git a/test/unit/utils/cssom.js b/test/unit/utils/cssom.js index ff4468e..6b1cf65 100644 --- a/test/unit/utils/cssom.js +++ b/test/unit/utils/cssom.js @@ -14,6 +14,117 @@ describe('utils', () => { }); }); + describe('.isRule()', () => { + const fn = cssom.isRule; + + it('should return true if valid (based on PostCSS AST)', () => { + expect(fn()).to.be.false; + expect(fn({})).to.be.false; + expect(fn({ type: 'rule' })).to.be.true; + }); + }); + + describe('.isSelector()', () => { + const fn = cssom.isSelector; + + it('should return true if valid (based on PostCSS AST)', () => { + expect(fn()).to.be.false; + expect(fn({})).to.be.false; + expect(fn({ nodes: true })).to.be.true; + }); + }); + + describe('.findSelectorsFromNodes()', () => { + const fn = cssom.findSelectorsFromNodes; + + it('should return false if invalid args', () => { + expect(fn()).to.be.false; + expect(fn(true, '')).to.be.false; + expect(fn({}, '.body')).to.be.false; + }); + + it('should return empty array if selectors cannot be found from nodes', () => { + expect(fn([{ selector: '.html' }], '.body')).to.be.an('array').and.to.have.lengthOf(0); + }); + + it('should return selector it exists in node', () => { + expect(fn([{ selector: '.body' }], '.body')).to.be.an('array').and.to.have.lengthOf(1); + }); + }); + + describe('.findSelectorFromParamsByString()', () => { + const fn = cssom.findSelectorFromParamsByString; + + it('should return false if invalid args', () => { + expect(fn()).to.be.false; + expect(fn(true, '')).to.be.false; + expect(fn({}, '.body')).to.be.false; + }); + }); + + describe('.findSelectorFromParamsByArray()', () => { + const fn = cssom.findSelectorFromParamsByArray; + + it('should return false if invalid args', () => { + expect(fn()).to.be.undefined; + expect(fn(true, '')).to.be.false; + expect(fn({}, '.body')).to.be.false; + expect(fn([], '.body')).to.be.false; + expect(fn([], [])).to.be.undefined; + }); + }); + + describe('.getAtRules()', () => { + const fn = cssom.getAtRules; + + it('should return empty array if invalid args', () => { + expect(fn()).to.be.an('array').and.have.lengthOf(0); + expect(fn([], '')).to.be.an('array').and.have.lengthOf(0); + }); + }); + + describe('.getAtRuleFromSelector()', () => { + const fn = cssom.getAtRuleFromSelector; + + it('should return false if selector isn\'t a nested rule', () => { + expect(fn()).to.be.false; + expect(fn('.selector')).to.be.false; + expect(fn({ selector: '.selector'})).to.be.false; + }); + + it('should return rule if selector is a nested rule', () => { + expect(fn({ parent: { type: 'atrule' } })).to.not.be.false; + }); + }); + + describe('.getParamNodes', () => { + const fn = cssom.getParamNodes; + + it('should return empty array if invalid args', () => { + expect(fn()).to.be.an('array').and.to.have.lengthOf(0); + expect(fn('true')).to.be.an('array').and.to.have.lengthOf(0); + expect(fn(true)).to.be.an('array').and.to.have.lengthOf(0); + }); + }); + + describe('.getPropsFromSelector()', () => { + const fn = cssom.getPropsFromSelector; + + it('should return false if invalid args', () => { + expect(fn()).to.be.false; + expect(fn({})).to.be.false; + }); + }); + + describe('.getPropDataFromSelector()', () => { + const fn = cssom.getPropDataFromSelector; + + it('should return false if invalid args', () => { + expect(fn()).to.be.false; + expect(fn({}, '')).to.be.false; + }); + }); + describe('.getRuleParams()', () => { const fn = cssom.getRuleParams; @@ -54,5 +165,24 @@ describe('utils', () => { expect(cssom.type).to.equal('root'); }); }); + + describe('.sanitizeParens()', () => { + const fn = cssom.sanitizeParens; + + it('should return arg if arg (string) is invalid', () => { + expect(fn('')).to.equal(''); + expect(fn(true)).to.equal(true); + expect(fn(123)).to.equal(123); + }); + + it('should strip parenthesis', () => { + expect(fn('((content))(rule)((()))')).to.not.contain('('); + expect(fn('((content))(rule)((()))')).to.not.contain(')'); + }); + + it('should return rule in lowercase', () => { + expect(fn('(CoNtEnT)')).to.equal('content'); + }); + }); }); }); diff --git a/test/unit/utils/isString.js b/test/unit/utils/isString.js new file mode 100644 index 0000000..f56e473 --- /dev/null +++ b/test/unit/utils/isString.js @@ -0,0 +1,27 @@ +/* globals barista: true, expect: true, describe: true, it: true, sinon: true, utils: true */ + +const isString = global.utils.isString; + +describe('utils', () => { + describe('isString', () => { + it('should return false for non-strings', () => { + expect(isString(0)).to.be.false; + expect(isString(1)).to.be.false; + expect(isString([])).to.be.false; + expect(isString(true)).to.be.false; + expect(isString(false)).to.be.false; + expect(isString({})).to.be.false; + expect(isString()).to.be.false; + }); + + it('should return false for empty strings', () => { + expect(isString('')).to.be.false; + }); + + it('should return true for non-empty strings', () => { + expect(isString('y')).to.be.true; + expect(isString('yiss')).to.be.true; + expect(isString('aw yiss')).to.be.true; + }); + }); +}); diff --git a/test/unit/utils/renderer.js b/test/unit/utils/renderer.js new file mode 100644 index 0000000..81de853 --- /dev/null +++ b/test/unit/utils/renderer.js @@ -0,0 +1,13 @@ +/* globals barista: true, expect: true, describe: true, it: true, sinon: true, utils: true */ + +const renderer = global.utils.renderer; + +describe('utils', () => { + describe('renderer', () => { + it('should return false for invalid args', () => { + expect(renderer(true, '')).to.be.false; + expect(renderer(true, '.selector')).to.be.false; + expect(renderer({}, '.selector')).to.be.false; + }); + }); +}); diff --git a/test/unit/utils/sass.js b/test/unit/utils/sass.js new file mode 100644 index 0000000..ba90713 --- /dev/null +++ b/test/unit/utils/sass.js @@ -0,0 +1,23 @@ +/* globals barista: true, expect: true, describe: true, it: true, sinon: true, utils: true */ + +const sass = global.utils.sass; + +describe('utils', () => { + describe('sass', () => { + describe('render', () => { + it('should render CSS with valid option.content', () => { + const output = sass.render({ content: 'h1 { color: black; }' }); + + expect(output).to.be.a('string'); + expect(output).to.contain('h1'); + }); + + it('should render SCSS with valid option.content', () => { + const output = sass.render({ content: 'h1 { color: black; a { display: block; } }' }); + + expect(output).to.be.a('string'); + expect(output).to.contain('h1 a'); + }); + }); + }); +});