From c48768abe918a91d8fa66cfca4582c5cb2dce731 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Fri, 14 Sep 2018 19:08:58 +0200 Subject: [PATCH] Add checkNode in ParserCSS and start tests --- src/parser/model/BrowserParserCss.js | 53 +++++++++++------ src/parser/model/ParserCss.js | 27 ++++++++- test/specs/parser/model/ParserCss.js | 86 +++++++++++++++++++++++++++- 3 files changed, 146 insertions(+), 20 deletions(-) diff --git a/src/parser/model/BrowserParserCss.js b/src/parser/model/BrowserParserCss.js index fb400323c..4acd59b29 100644 --- a/src/parser/model/BrowserParserCss.js +++ b/src/parser/model/BrowserParserCss.js @@ -15,6 +15,7 @@ const atRules = { }; const atRuleKeys = keys(atRules); const singleAtRules = ['5', '6', '11', '15']; +const singleAtRulesNames = ['font-face', 'page', 'counter-style', 'viewport']; /** * Parse selector string to array. @@ -94,6 +95,38 @@ export const parseCondition = node => { return condition.trim(); }; +/** + * Create node for the editor + * @param {Array} selectors Array containing strings of classes + * @param {Object} style Key-value object of style declarations + * @return {Object} + */ +export const createNode = (selectors, style, opts = {}) => { + const node = {}; + const selLen = selectors.length; + const lastClass = selectors[selLen - 1]; + const stateArr = lastClass ? lastClass.split(/:(.+)/) : []; + const state = stateArr[1]; + const { atRule, selectorsAdd, mediaText } = opts; + const singleAtRule = singleAtRulesNames.indexOf(atRule) >= 0; + singleAtRule && (node.singleAtRule = 1); + atRule && (node.atRuleType = atRule); + selectorsAdd && (node.selectorsAdd = selectorsAdd); + mediaText && (node.mediaText = mediaText); + + // Isolate the state from selectors + if (state) { + selectors[selLen - 1] = stateArr[0]; + node.state = state; + stateArr.splice(stateArr.length - 1, 1); + } + + node.selectors = selectors; + node.style = style; + + return node; +}; + /** * Fetch data from node * @param {StyleSheet|CSSRule} el @@ -139,23 +172,11 @@ export const parseNode = el => { let lastRule; // For each group of selectors for (var k = 0, len3 = sels.length; k < len3; k++) { - var selArr = sels[k]; - var model = {}; - singleAtRule && (model.singleAtRule = singleAtRule); - atRuleType && (model.atRuleType = atRuleType); - - //Isolate state from selector - var stateArr = selArr[selArr.length - 1].split(/:(.+)/); - if (stateArr[1]) { - selArr[selArr.length - 1] = stateArr[0]; - model.state = stateArr[1]; - stateArr.splice(stateArr.length - 1, 1); - } - - model.selectors = selArr; - model.style = style; - lastRule = model; + const model = createNode(sels[k], style, { + atRule: atRules[type] + }); result.push(model); + lastRule = model; } // Need to push somewhere not class-based selectors, if some rule was diff --git a/src/parser/model/ParserCss.js b/src/parser/model/ParserCss.js index 289603d86..7dcdb0273 100644 --- a/src/parser/model/ParserCss.js +++ b/src/parser/model/ParserCss.js @@ -1,5 +1,8 @@ import { isString } from 'underscore'; -import BrowserCssParser from './BrowserParserCss'; +import BrowserCssParser, { + parseSelector, + createNode +} from './BrowserParserCss'; module.exports = (config = {}) => ({ /** @@ -22,7 +25,27 @@ module.exports = (config = {}) => ({ * @return {[type]} */ checkNode(node) { - if (isString(node.selectors)) { + const { selectors, style } = node; + + if (isString(selectors)) { + const nodes = []; + const selsParsed = parseSelector(selectors); + const classSets = selsParsed.result; + const opts = { + atRule: node.atRule, + selectorsAdd: selsParsed.add.join(', '), + mediaText: node.params + }; + + if (classSets.length) { + classSets.forEach(classSet => { + nodes.push(createNode(classSet, style, opts)); + }); + } else { + nodes.push(createNode([], style, opts)); + } + + node = nodes; } return node; diff --git a/test/specs/parser/model/ParserCss.js b/test/specs/parser/model/ParserCss.js index b49d7b332..4db32157f 100644 --- a/test/specs/parser/model/ParserCss.js +++ b/test/specs/parser/model/ParserCss.js @@ -4,7 +4,7 @@ const Selector = require('selector_manager/model/Selector'); module.exports = { run() { - describe('ParserCss', () => { + describe.only('ParserCss', () => { let obj; let config; let customParser; @@ -359,7 +359,7 @@ module.exports = { expect(obj.parse(str)).toEqual([result]); }); - test('Parse CSS with custom async parser', async () => { + test.skip('Parse CSS with custom async parser', async () => { var str = '.test1 { color:red }'; var result = { selectors: ['test1'], @@ -371,6 +371,88 @@ module.exports = { const cssResult = await obj.parse(str); expect(cssResult).toEqual([result]); }); + + test('Check node with font-face rule', () => { + const style = { + 'font-family': '"Glyphicons Halflings"', + src: + 'url("https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/fonts/glyphicons-halflings-regular.eot")' + }; + expect( + obj.checkNode({ + atRule: 'font-face', + selectors: '', + style: style + }) + ).toEqual([ + { + style: style, + atRuleType: 'font-face', + singleAtRule: 1, + selectors: [] + } + ]); + }); + + test('Check node with keyframes rule', () => { + const style = { opacity: 0 }; + expect( + obj.checkNode({ + atRule: 'keyframes', + params: 'name', + selectors: 'from', + style: style + }) + ).toEqual([ + { + selectors: [], + atRuleType: 'keyframes', + selectorsAdd: 'from', + style: style, + mediaText: 'name' + } + ]); + }); + + test('Check node with media rule', () => { + const style = { color: 'blue' }; + expect( + obj.checkNode({ + atRule: 'media', + params: 'screen and (min-width: 480px)', + selectors: '.class-test.class2:hover, div > span ', + style + }) + ).toEqual([ + { + atRuleType: 'media', + selectors: ['class-test', 'class2'], + selectorsAdd: 'div > span', + style: style, + state: 'hover', + mediaText: 'screen and (min-width: 480px)' + } + ]); + }); + + test('Check node with a rule containing id', () => { + const style = { color: 'blue' }; + expect( + obj.checkNode({ + selectors: '#main', + style: { border: '1px solid black' } + }) + ).toEqual([ + { + atRuleType: 'media', + selectors: ['class-test', 'class2'], + selectorsAdd: 'div > span', + style: style, + state: 'hover', + mediaText: 'screen and (min-width: 480px)' + } + ]); + }); }); } };