From 4798ad76db8b11f1ed78e3bab08aea638d83c94c Mon Sep 17 00:00:00 2001 From: Hannu Pelkonen Date: Thu, 11 Dec 2014 14:42:17 +0200 Subject: [PATCH] Find variable declarations from every file. Use styleVariables to filter selected files --- README.md | 6 +- lib/app/js/services/Styleguide.js | 2 + lib/app/js/services/Variables.js | 74 +++---- lib/app/sass/_styleguide_variables.scss | 2 +- lib/modules/io.js | 81 +++++--- lib/modules/kss-parser.js | 54 +++-- lib/modules/variable-parser.js | 53 ++++- lib/styleguide.js | 62 +++--- package.json | 4 +- test/angular/unit/services/Variables.test.js | 187 +++++++++--------- test/integration/structure.test.js | 49 +++-- .../less-project/source/styles/style.less | 2 + .../scss-project/source/styles/style.scss | 2 + test/unit/modules/io.test.js | 186 ++++++----------- test/unit/modules/kss-parser.test.js | 186 ++++++++++------- test/unit/modules/variable-parser.test.js | 27 +-- 16 files changed, 532 insertions(+), 445 deletions(-) diff --git a/README.md b/README.md index 084df89e..94947d07 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Port in which the server will run Automatically generate style guide on file change. `--watch` does not run server. Combile with `--server` if you want to run server -Config JSON file could contain following settings +Config JSON file could look like following: { title: "My Style guide", @@ -101,7 +101,6 @@ For more specific documentation. See [Build options](#build-options) section. title: "My Styleguide", server: true, rootPath: outputPath, - styleVariables: '', overviewPath: "", sass: { // Options passed to gulp-sass @@ -142,7 +141,6 @@ Then you are able to use the same gulp task inside you `Gruntfile`: title: "My Styleguide", server: true, rootPath: outputPath, - styleVariables: '', overviewPath: "", sass: { // Options passed to gulp-sass @@ -231,7 +229,7 @@ This allows Angular to deal with the routing. However, the static files should b **styleVariables** (string, optional) -Path to the file containing SASS variables that can be used as modifiers in the KSS notation. +By default variable definitions are searched from every file passed in gulp.src. styleVariables parameter could be used to filter from which files variables are loaded. **filesConfig** (array, optional) **(Experimental feature)** diff --git a/lib/app/js/services/Styleguide.js b/lib/app/js/services/Styleguide.js index 8866add0..9138c50a 100644 --- a/lib/app/js/services/Styleguide.js +++ b/lib/app/js/services/Styleguide.js @@ -13,6 +13,7 @@ angular.module('sgApp') this.sections = {}; this.config = {}; + this.variables = {}; this.status = { hasError: false, error: {} @@ -24,6 +25,7 @@ angular.module('sgApp') url: 'styleguide.json' }).success(function(response) { _this.config.data = response.config; + _this.variables.data = response.variables; _this.sections.data = response.sections; }); }; diff --git a/lib/app/js/services/Variables.js b/lib/app/js/services/Variables.js index 1e968c2d..55291414 100644 --- a/lib/app/js/services/Variables.js +++ b/lib/app/js/services/Variables.js @@ -16,25 +16,29 @@ _this.refreshDirtyStates(); }, true); - this.getLocalVarByName = function(name) { + this.variableMatches = function(var1, var2) { + return var1.name === var2.name && var1.file === var2.file; + }; + + this.getLocalVar = function(variable) { for (var i = this.variables.length - 1; i >= 0; i--) { - if (this.variables[i].name === name) { + if (this.variableMatches(this.variables[i], variable)) { return this.variables[i]; } } }; - this.getLocalIndexByName = function(name) { + this.getLocalIndex = function(variable) { for (var i = this.variables.length - 1; i >= 0; i--) { - if (this.variables[i].name === name) { + if (this.variableMatches(this.variables[i], variable)) { return i; } } }; - this.getServerVarByName = function(name) { + this.getServerVar = function(variable) { for (var i = serverData.length - 1; i >= 0; i--) { - if (serverData[i].name === name) { + if (this.variableMatches(serverData[i], variable)) { return serverData[i]; } } @@ -44,7 +48,7 @@ var _this = this; // Mark variables that differ from the server version as dirty angular.forEach(_this.variables, function(variable) { - var serverVar = _this.getServerVarByName(variable.name); + var serverVar = _this.getServerVar(variable); if (serverVar && serverVar.value !== variable.value && !variable.dirty) { variable.dirty = true; } else if (serverVar && serverVar.value === variable.value && variable.dirty) { @@ -59,28 +63,30 @@ } else { for (var i = 0; i < serverData.length; i++) { var oldIndex; - if (this.variables[i] && this.variables[i].name !== serverData[i].name) { - if (!this.getServerVarByName(this.variables[i].name)) { + if (this.variables[i] && !this.variableMatches(this.variables[i], serverData[i])) { + if (!this.getServerVar(this.variables[i])) { // This variable does not exists anymore on the server. Remove it this.variables.splice(i, 1); - } else if (this.getLocalVarByName(serverData[i].name) && !this.getLocalVarByName(serverData[i].name).dirty) { + } else if (this.getLocalVar(serverData[i]) && !this.getLocalVar(serverData[i]).dirty) { // The variable already exists but in another position // It is not changed so we can just remove it - oldIndex = this.getLocalIndexByName(serverData[i].name); + oldIndex = this.getLocalIndex(serverData[i]); this.variables.splice(oldIndex, 1); - this.variables.splice(i, 0, {name: serverData[i].name, value: serverData[i].value}); - } else if (this.getLocalVarByName(serverData[i].name)) { + this.variables.splice(i, 0, angular.copy(serverData[i])); + } else if (this.getLocalVar(serverData[i])) { // The variable already exists but in another position // It is changed so we need to keep the old values - oldIndex = this.getLocalIndexByName(serverData[i].name); + oldIndex = this.getLocalIndex(serverData[i]); var oldValue = this.variables[oldIndex].value; this.variables.splice(oldIndex, 1); - this.variables.splice(i, 0, {name: serverData[i].name, value: oldValue}); + var newObject = angular.copy(serverData[i]); + newObject.value = oldValue; + this.variables.splice(i, 0, newObject); } else { // The variable does not exists anywhere else. Just add it - this.variables.splice(i, 0, {name: serverData[i].name, value: serverData[i].value}); + this.variables.splice(i, 0, angular.copy(serverData[i])); } - } else if (this.variables[i] && this.variables[i].name === serverData[i].name) { + } else if (this.variables[i] && this.variableMatches(this.variables[i], serverData[i])) { // Variable exists already locally // Update value if variable does not have any local changes if (!this.variables[i].dirty) { @@ -88,7 +94,7 @@ } } else if (!this.variables[i]) { // Add new local variable - this.variables.push({name: serverData[i].name, value: serverData[i].value}); + this.variables.push(angular.copy(serverData[i])); } } } @@ -98,7 +104,7 @@ var _this = this; // Reset every key to corresponding server value angular.forEach(this.variables, function(variable) { - var serverVar = _this.getServerVarByName(variable.name); + var serverVar = _this.getServerVar(variable); if (serverVar) { variable.value = serverVar.value; } @@ -117,29 +123,9 @@ return this; }; - this.sync = function(method) { - var validMethods = ['load', 'save']; - - // Parameter validation - if (validMethods.indexOf(method) === -1) { - throw 'No valid method provided. Available methods: ' + validMethods.join(', '); - } - - // Load variables from server or localStorage - if (method === 'load') { - this.socket.emit('request variables from server'); - } - - // Save variables to server - if (method === 'save') { - this.socket.emit('variables to server', this.variables); - } - }; - this.saveVariables = function() { - var _this = this; if (this.socket) { - _this.sync('save'); + this.socket.emit('variables to server', this.variables); } else { throw 'Socket not available.'; } @@ -152,14 +138,10 @@ // Update new server data when it is available $rootScope.$watch(function() { - return Styleguide.config.data; + return Styleguide.variables.data; }, function(newValue) { if (newValue) { - if (newValue.settings) { - serverData = newValue.settings; - } else { - serverData = []; - } + serverData = newValue; _this.refreshValues(); _this.refreshDirtyStates(); } diff --git a/lib/app/sass/_styleguide_variables.scss b/lib/app/sass/_styleguide_variables.scss index 65b1d153..f54e8fdf 100644 --- a/lib/app/sass/_styleguide_variables.scss +++ b/lib/app/sass/_styleguide_variables.scss @@ -4,7 +4,7 @@ $secondary-color: #E4E4E4; $tertiary-color: #F7FCF1; // Button and link colors -$primary-action-color: #EB7F00; +$primary-action-color: #70e7e0; $default-action-color: #1695A3; $action-color-change: 5%; diff --git a/lib/modules/io.js b/lib/modules/io.js index 112c412a..63f0d8d0 100644 --- a/lib/modules/io.js +++ b/lib/modules/io.js @@ -1,12 +1,11 @@ var fs = require('fs'), path = require('path'), + Q = require('q'), parser = require('./variable-parser'); module.exports = function(ioServer, options) { - var loadVariables, - saveVariables, - io = ioServer, + var io = ioServer, compileError = false; if (options.styleVariables && !fs.existsSync(options.styleVariables)) { @@ -14,35 +13,49 @@ module.exports = function(ioServer, options) { return; } - loadVariables = function(socket) { - return function() { - var values, syntax = path.extname(options.styleVariables).substring(1); - fs.readFile(options.styleVariables, {encoding: 'utf8'}, function(err, data) { - if (err) { - return console.error(err); - } - values = parser.parseVariables(data, syntax); - socket.emit('variables from server', values); - console.log('EVENT: variables from server'); + function groupVariablesByFilename(variables) { + return variables.reduce(function(prev, curr) { + var filePath = curr.file; + if (!prev[filePath]) { + prev[filePath] = []; + } + prev[filePath].push(curr); + return prev; + }, {}); + } + + function saveVariables(variables) { + return Q.Promise(function(resolve) { + + // First group variables by file name + var groupedVariables = groupVariablesByFilename(variables), + filePromises = []; + + // Go trough every file and update variables defined in that file + Object.keys(groupedVariables).forEach(function(filePath) { + filePromises.push(Q.promise(function(resolveFile) { + var fileVariables = groupedVariables[filePath], + syntax = path.extname(filePath).substring(1); + + // Read original file contents to be updated + fs.readFile(filePath, {encoding: 'utf8'}, function(err, originalData) { + // Update variables and store results back to the original file + var data = parser.setVariables(originalData, syntax, fileVariables); + fs.writeFile(filePath, data, function(err) { + if (err) { + console.error(err); + } + resolveFile(); + }); + }); + })); }); - }; - }; - saveVariables = function(socket) { - return function(variables) { - var syntax = path.extname(options.styleVariables).substring(1); - fs.readFile(options.styleVariables, {encoding: 'utf8'}, function(err, originalData) { - var data = parser.setVariables(originalData, syntax, variables); - fs.writeFile(options.styleVariables, data, function(err, data) { - if (err) { - return console.error(err); - } - socket.emit('variables saved to server', data); - console.log('EVENT: variables saved to server'); - }); + Q.all(filePromises).then(function() { + resolve(); }); - }; - }; + }); + } function emitProgressStart() { io.sockets.emit('styleguide progress start'); @@ -66,8 +79,12 @@ module.exports = function(ioServer, options) { io.on('connection', function(socket) { console.log('Socket connection established (id:', socket.conn.id + ')'); - socket.on('request variables from server', loadVariables(socket)); - socket.on('variables to server', saveVariables(socket)); + socket.on('variables to server', function(variables) { + saveVariables(variables).then(function() { + socket.emit('variables saved to server'); + console.log('EVENT: variables saved to server'); + }); + }); if (compileError) { emitCompileError(); } else { @@ -76,6 +93,8 @@ module.exports = function(ioServer, options) { }); return { + saveVariables: saveVariables, + groupVariablesByFilename: groupVariablesByFilename, emitProgressStart: emitProgressStart, emitCompileError: emitCompileError, emitCompileSuccess: emitCompileSuccess diff --git a/lib/modules/kss-parser.js b/lib/modules/kss-parser.js index eaf8af1a..34bfc396 100644 --- a/lib/modules/kss-parser.js +++ b/lib/modules/kss-parser.js @@ -47,7 +47,7 @@ function sanitize(string) { return sanitizeHtml(string, {allowedTags: [], allowedAttributes: []}); } -function processBlock(block, options, json) { +function processBlock(block, options) { return Q.Promise(function(resolve, reject) { kss.parse(block.kss, options, function(err, styleguide) { var section, @@ -69,15 +69,14 @@ function processBlock(block, options, json) { if (blockStyles && blockStyles !== '') { section[0].css = blockStyles; } - json.sections = json.sections.concat(section); } - resolve(); + resolve(section); } }); }); } -function processFile(contents, syntax, options, json) { +function processFile(contents, syntax, options) { return Q.Promise(function(resolve, reject) { var blockPromises = [], blocks; @@ -86,12 +85,25 @@ function processFile(contents, syntax, options, json) { // Process every block in the current file blocks.forEach(function(block) { - blockPromises.push(processBlock(block, options, json)); + blockPromises.push(processBlock(block, options)); }); } catch (err) { reject(err); } - Q.all(blockPromises).then(resolve); + Q.all(blockPromises).then(function(results) { + resolve(results.reduce(function(memo, result) { + var blockResult = result.valueOf(); + if (blockResult && blockResult.length > 0) { + // Map syntax to every block. This is later used when parsing used variables + // Finally add sections to array + return memo.concat(blockResult.map(function(currentBlock) { + currentBlock.syntax = syntax; + return currentBlock; + })); + } + return memo; + }, [])); + }); }); } @@ -124,25 +136,31 @@ function bySectionReference(x, y) { module.exports = { // Parse node-kss object ( {'file.path': 'file.contents.toString('utf8'}' ) - parseKSS: function(files, options) { + parseKssSections: function(files, options) { return Q.Promise(function(resolve, reject) { - var json = { - sections: [] - }, - filePromises = [], - fileKeys = Object.keys(files); + var filePromises = [], + sections = []; - fileKeys.forEach(function(filePath) { + // Process every file + Object.keys(files).forEach(function(filePath) { var contents = files[filePath], syntax = path.extname(filePath).substring(1); - filePromises.push(processFile(contents, syntax, options, json)); + filePromises.push(processFile(contents, syntax, options)); }); + // All files are processed + Q.all(filePromises).then(function(results) { + // Combine sections from every file to a single array + results.map(function(result) { + var fileSections = result.valueOf(); + if (fileSections && fileSections.length > 0) { + sections = sections.concat(fileSections); + } + }); - Q.all(filePromises).then(function() { - // All files are processed. Sort results and call main promise + // Sort sections by reference number and call main promise try { - json.sections.sort(bySectionReference); - resolve(json); + sections.sort(bySectionReference); + resolve(sections); } catch (err) { reject(err); } diff --git a/lib/modules/variable-parser.js b/lib/modules/variable-parser.js index fb763778..05023e57 100644 --- a/lib/modules/variable-parser.js +++ b/lib/modules/variable-parser.js @@ -1,5 +1,7 @@ var gonzales = require('gonzales-pe'), gonzo = require('gonzales-ast'), + path = require('path'), + Q = require('q'), _ = require('lodash'); function astToSrc(ast, syntax) { @@ -10,7 +12,7 @@ function astToSrc(ast, syntax) { } // Parse Style variables to object -module.exports.parseVariables = function(string, syntax) { +function parseVariableDeclarations(string, syntax) { syntax = syntax || 'scss'; var out = [], @@ -61,10 +63,10 @@ module.exports.parseVariables = function(string, syntax) { }]); return out; -}; +} // Parse Style variables to object -module.exports.findVariables = function(string, syntax) { +function findVariables(string, syntax) { syntax = syntax || 'scss'; var out = [], @@ -98,10 +100,39 @@ module.exports.findVariables = function(string, syntax) { } }]); return out; -}; +} + +function parseVariableDeclarationsFromFiles(files) { + return Q.Promise(function(resolve) { + var variables = [], + filePromises = []; + + Object.keys(files).forEach(function(filePath) { + var contents = files[filePath], + syntax = path.extname(filePath).substring(1); + filePromises.push(Q.promise(function(resolve) { + var fileVariables = parseVariableDeclarations(contents, syntax); + + // Map correct file name to every variable + fileVariables.map(function(variable) { + variable.file = filePath; + return variable; + }); + + variables = variables.concat(fileVariables); + resolve(); + })); + }); + + Q.all(filePromises).then(function() { + // All files are processed. Call main promise + resolve(variables); + }); + }); +} // Modifies string so that variables passed in object are updated -module.exports.setVariables = function(string, syntax, variables) { +function setVariables(string, syntax, variables) { var ast, result, changeVariable; changeVariable = function(ast, key, value) { @@ -165,9 +196,9 @@ module.exports.setVariables = function(string, syntax, variables) { syntax: syntax }); return result; -}; +} -module.exports.findModifierVariables = function(modifiers) { +function findModifierVariables(modifiers) { var out = []; if (modifiers) { modifiers.forEach(function(modifier) { @@ -177,4 +208,12 @@ module.exports.findModifierVariables = function(modifiers) { }); } return out; +} + +module.exports = { + parseVariableDeclarations: parseVariableDeclarations, + parseVariableDeclarationsFromFiles: parseVariableDeclarationsFromFiles, + setVariables: setVariables, + findVariables: findVariables, + findModifierVariables: findModifierVariables }; diff --git a/lib/styleguide.js b/lib/styleguide.js index ef655588..f3402756 100644 --- a/lib/styleguide.js +++ b/lib/styleguide.js @@ -10,18 +10,17 @@ 'use strict'; var through = require('through2'), - fs = require('fs'), gulp = require('gulp'), rename = require('gulp-rename'), markdown = require(__dirname + '/modules/markdown'), atRules = require(__dirname + '/modules/at-rules'), mustache = require('gulp-mustache'), variableParser = require(__dirname + '/modules/variable-parser'), - parseKSS = require(__dirname + '/modules/kss-parser').parseKSS, + kssParser = require(__dirname + '/modules/kss-parser'), pseudoSelectors = require(__dirname + '/modules/pseudo-selectors'), preprocess = require(__dirname + '/modules/preprocess'), wrapperMarkup = require(__dirname + '/modules/wrapper-markup'), - path = require('path'), + minimatch = require('minimatch'), sgServer = require(__dirname + '/server'), File = require('vinyl'), Q = require('q'), @@ -39,7 +38,7 @@ function sanitizeOptions(opt) { extraHead: (typeof opt.extraHead === 'object') ? opt.extraHead.join('\n') : opt.extraHead, appRoot: opt.appRoot || '', commonClass: opt.commonClass || '', - styleVariables: opt.styleVariables || '', + styleVariables: opt.styleVariables || false, server: opt.server || false, port: opt.port, rootPath: opt.rootPath, @@ -84,24 +83,29 @@ function removeConfigOutputPath(json) { } function appendUsedVariablesToEachBlock(opt, styleguide) { - if (opt.styleVariables) { - var syntax = path.extname(opt.styleVariables).substring(1); - // Parse variables from the defined file - styleguide.config.settings = variableParser.parseVariables(fs.readFileSync(opt.styleVariables, 'utf-8'), syntax); - // Go trough every styleguide style block and find used variables - styleguide.sections.forEach(function(section) { - if (section.css) { - section.variables = variableParser.findVariables(section.css, syntax); + // Go trough every styleguide style block and find used variables + styleguide.sections.forEach(function(section) { + if (section.css) { + section.variables = variableParser.findVariables(section.css, section.syntax); + } + variableParser.findModifierVariables(section.modifiers).forEach(function(varName) { + if (!section.variables || section.variables.indexOf(varName) === -1) { + section.variables = section.variables || []; + section.variables.push(varName); } - variableParser.findModifierVariables(section.modifiers).forEach(function(varName) { - if (!section.variables || section.variables.indexOf(varName) === -1) { - section.variables = section.variables || []; - section.variables.push(varName); - } - }); - return section; }); - } + return section; + }); +} + +function filterFiles(files, filter) { + var filtered = {}; + Object.keys(files).forEach(function(filePath) { + if (minimatch(filePath, filter)) { + filtered[filePath] = files[filePath]; + } + }); + return filtered; } module.exports = function(options) { @@ -113,7 +117,6 @@ module.exports = function(options) { }; function bufferFileContents(file, enc, done) { - if (file.isNull()) { return; } @@ -134,9 +137,22 @@ module.exports = function(options) { // A stream through which each file will pass return through(throughOpts, bufferFileContents, function(callback) { - var _this = this; + var _this = this, + // Styleguide object to be built + styleguide = {}, + // Parse KSS sections + parseKSSPromise = kssParser.parseKssSections(filesBuffer, opt.kssOpt), + // Filter variable files + // File paths are full absolute paths so we need to add wildcard prefix + // Also empty wildcard should return all files + variableFiles = opt.styleVariables ? filterFiles(filesBuffer, '**/' + opt.styleVariables) : filesBuffer, + // Parse variable decarations from files + parseVariablesPromise = variableParser.parseVariableDeclarationsFromFiles(variableFiles); + + Q.all([parseKSSPromise, parseVariablesPromise]).spread(function(sections, variables) { + styleguide.sections = sections; + styleguide.variables = variables; - parseKSS(filesBuffer, opt.kssOpt).then(function(styleguide) { function pushAllFiles() { return through.obj(function(file, enc, cb) { _this.push(file); diff --git a/package.json b/package.json index 8549be89..20de7fab 100644 --- a/package.json +++ b/package.json @@ -50,10 +50,11 @@ "kss": "^1.2.0", "less": "^2.1.1", "lodash": "^2.4.1", + "minimatch": "^2.0.1", "morgan": "^1.5.0", "node-bourbon": "^1.2.3", - "node.extend": "^1.1.3", "node-neat": "^1.3.0", + "node.extend": "^1.1.3", "q": "^1.1.2", "run-sequence": "^1.0.2", "sanitize-html": "^1.4.3", @@ -91,7 +92,6 @@ "karma-phantomjs-launcher": "^0.1.4", "karma-sinon-chai": "^0.2.0", "main-bower-files": "^2.4.1", - "minimatch": "^2.0.1", "multiline": "^1.0.1", "proxyquire": "^1.1.0", "requirefrom": "^0.2.0", diff --git a/test/angular/unit/services/Variables.test.js b/test/angular/unit/services/Variables.test.js index a996b038..a7479ba8 100644 --- a/test/angular/unit/services/Variables.test.js +++ b/test/angular/unit/services/Variables.test.js @@ -19,13 +19,11 @@ describe('Service: Variables', function() { }; styleguideMock = { - config: { - data: { - settings: [ - {name: 'setting1', value: 'value1'}, - {name: 'setting2', value: 'value2'} - ] - } + variables: { + data: [ + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting2', value: 'value2'} + ] } }; $provide.value('Styleguide', styleguideMock); @@ -43,39 +41,45 @@ describe('Service: Variables', function() { it('should get default values from Styleguide service', function() { rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting1', value: 'value1'}, - {name: 'setting2', value: 'value2'} + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting2', value: 'value2'} ]); }); - it('should set empty array as server data if variables do not exists', function() { + it('should set empty array as server data no vairables are found', function() { rootScope.$digest(); - styleguideMock.config.data = {}; + styleguideMock.variables.data = []; rootScope.$digest(); expect(Variables.variables).to.eql([]); }); - it('should set empty array as server data no vairables are found', function() { + it('should not mark server side changes as dirty', function() { rootScope.$digest(); - styleguideMock.config.data = { - settings: [] - }; + styleguideMock.variables.data = [ + {file: 'file', name: 'setting1', value: 'new value1'}, + {file: 'file', name: 'setting2', value: 'new value2'} + ]; rootScope.$digest(); - expect(Variables.variables).to.eql([]); + expect(Variables.variables).to.eql([ + {file: 'file', name: 'setting1', value: 'new value1'}, + {file: 'file', name: 'setting2', value: 'new value2'} + ]); }); - it('should not mark server side changes as dirty', function() { + it('should allow same named variables from different files', function() { rootScope.$digest(); - styleguideMock.config.data = { - settings: [ - {name: 'setting1', value: 'new value1'}, - {name: 'setting2', value: 'new value2'} - ] - }; + styleguideMock.variables.data = [ + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting2', value: 'value2'}, + {file: 'file2', name: 'setting1', value: 'value1'}, + {file: 'file2', name: 'setting2', value: 'value2'} + ]; rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting1', value: 'new value1'}, - {name: 'setting2', value: 'new value2'} + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting2', value: 'value2'}, + {file: 'file2', name: 'setting1', value: 'value1'}, + {file: 'file2', name: 'setting2', value: 'value2'} ]); }); @@ -84,8 +88,25 @@ describe('Service: Variables', function() { Variables.variables[0].value = 'changed'; rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting1', value: 'changed', dirty: true}, - {name: 'setting2', value: 'value2'} + {file: 'file', name: 'setting1', value: 'changed', dirty: true}, + {file: 'file', name: 'setting2', value: 'value2'} + ]); + }); + + it('should not mark variable as dirty if it is from different file', function() { + rootScope.$digest(); + styleguideMock.variables.data = [ + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting2', value: 'value2'}, + {file: 'file2', name: 'setting1', value: 'value1'} + ]; + rootScope.$digest(); + Variables.variables[2].value = 'changed'; + rootScope.$digest(); + expect(Variables.variables).to.eql([ + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting2', value: 'value2'}, + {file: 'file2', name: 'setting1', value: 'changed', dirty: true} ]); }); @@ -96,8 +117,8 @@ describe('Service: Variables', function() { Variables.variables[0].value = 'value1'; rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting1', value: 'value1'}, - {name: 'setting2', value: 'value2'} + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting2', value: 'value2'} ]); }); @@ -108,8 +129,8 @@ describe('Service: Variables', function() { Variables.variables[0].value = 'value1'; rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting1', value: 'value1'}, - {name: 'setting2', value: 'value2'} + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting2', value: 'value2'} ]); }); @@ -117,29 +138,25 @@ describe('Service: Variables', function() { rootScope.$digest(); Variables.variables[1].value = 'changed2'; rootScope.$digest(); - styleguideMock.config.data = { - settings: [ - {name: 'setting1', value: 'new value1'}, - {name: 'setting2', value: 'new value2'} - ] - }; + styleguideMock.variables.data = [ + {file: 'file', name: 'setting1', value: 'new value1'}, + {file: 'file', name: 'setting2', value: 'new value2'} + ]; rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting1', value: 'new value1'}, - {name: 'setting2', value: 'changed2', dirty: true} + {file: 'file', name: 'setting1', value: 'new value1'}, + {file: 'file', name: 'setting2', value: 'changed2', dirty: true} ]); }); it('should remove local values that does not exist on server side', function() { rootScope.$digest(); - styleguideMock.config.data = { - settings: [ - {name: 'setting2', value: 'value2'} - ] - }; + styleguideMock.variables.data = [ + {file: 'file', name: 'setting2', value: 'value2'} + ]; rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting2', value: 'value2'} + {file: 'file', name: 'setting2', value: 'value2'} ]); }); @@ -150,61 +167,55 @@ describe('Service: Variables', function() { Variables.resetLocal(); rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting1', value: 'value1'}, - {name: 'setting2', value: 'value2'} + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting2', value: 'value2'} ]); }); it('should allow new server side variables at the end of the data', function() { rootScope.$digest(); - styleguideMock.config.data = { - settings: [ - {name: 'setting1', value: 'value1'}, - {name: 'setting2', value: 'value2'}, - {name: 'setting3', value: 'value3'} - ] - }; + styleguideMock.variables.data = [ + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting2', value: 'value2'}, + {file: 'file', name: 'setting3', value: 'value3'} + ]; rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting1', value: 'value1'}, - {name: 'setting2', value: 'value2'}, - {name: 'setting3', value: 'value3'} + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting2', value: 'value2'}, + {file: 'file', name: 'setting3', value: 'value3'} ]); }); it('should allow new server side variables between existing variables', function() { rootScope.$digest(); - styleguideMock.config.data = { - settings: [ - {name: 'setting1', value: 'value1'}, - {name: 'setting3', value: 'value3'}, - {name: 'setting2', value: 'value2'} - ] - }; + styleguideMock.variables.data = [ + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting3', value: 'value3'}, + {file: 'file', name: 'setting2', value: 'value2'} + ]; rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting1', value: 'value1'}, - {name: 'setting3', value: 'value3'}, - {name: 'setting2', value: 'value2'} + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting3', value: 'value3'}, + {file: 'file', name: 'setting2', value: 'value2'} ]); }); it('should handle properly mixed index changes and new variables', function() { rootScope.$digest(); - styleguideMock.config.data = { - settings: [ - {name: 'setting3', value: 'value3'}, - {name: 'setting4', value: 'value4'}, - {name: 'setting2', value: 'value2'}, - {name: 'setting1', value: 'value1'} - ] - }; + styleguideMock.variables.data = [ + {file: 'file', name: 'setting3', value: 'value3'}, + {file: 'file', name: 'setting4', value: 'value4'}, + {file: 'file', name: 'setting2', value: 'value2'}, + {file: 'file', name: 'setting1', value: 'value1'} + ]; rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting3', value: 'value3'}, - {name: 'setting4', value: 'value4'}, - {name: 'setting2', value: 'value2'}, - {name: 'setting1', value: 'value1'} + {file: 'file', name: 'setting3', value: 'value3'}, + {file: 'file', name: 'setting4', value: 'value4'}, + {file: 'file', name: 'setting2', value: 'value2'}, + {file: 'file', name: 'setting1', value: 'value1'} ]); }); @@ -213,18 +224,16 @@ describe('Service: Variables', function() { Variables.variables[0].value = 'new value1'; Variables.variables[1].value = 'new value2'; rootScope.$digest(); - styleguideMock.config.data = { - settings: [ - {name: 'setting2', value: 'value2'}, - {name: 'setting1', value: 'value1'}, - {name: 'setting3', value: 'value3'} - ] - }; + styleguideMock.variables.data = [ + {file: 'file', name: 'setting2', value: 'value2'}, + {file: 'file', name: 'setting1', value: 'value1'}, + {file: 'file', name: 'setting3', value: 'value3'} + ]; rootScope.$digest(); expect(Variables.variables).to.eql([ - {name: 'setting2', value: 'new value2', dirty: true}, - {name: 'setting1', value: 'new value1', dirty: true}, - {name: 'setting3', value: 'value3'} + {file: 'file', name: 'setting2', value: 'new value2', dirty: true}, + {file: 'file', name: 'setting1', value: 'new value1', dirty: true}, + {file: 'file', name: 'setting3', value: 'value3'} ]); }); diff --git a/test/integration/structure.test.js b/test/integration/structure.test.js index 9033bb33..f8f6b209 100644 --- a/test/integration/structure.test.js +++ b/test/integration/structure.test.js @@ -3,17 +3,17 @@ var gulp = require('gulp'), expect = chai.expect, through = require('through2'), styleguide = require('requirefrom')('lib')('styleguide'), - defaultSource = './test/projects/scss-project/source/**/*.scss', + defaultSource = 'test/projects/scss-project/source/**/*.scss', defaultConfig = { title: 'Test Styleguide', - overviewPath: './test/projects/scss-project/source/test_overview.md', + overviewPath: 'test/projects/scss-project/source/test_overview.md', appRoot: '/my-styleguide-book', extraHead: [ '', '' ], commonClass: ['custom-class-1', 'custom-class-2'], - styleVariables: './test/projects/scss-project/source/styles/_styleguide_variables.scss', + styleVariables: 'test/projects/scss-project/source/styles/_styleguide_variables.scss', sass: { // Options passed to gulp-sass in preprocess.js }, @@ -216,13 +216,16 @@ function sharedStyleguideJSON() { expect(this.jsonData.config.commonClass).to.eql(['custom-class-1', 'custom-class-2']); }); - it('should contain all style variables from defined file', function() { - var sassData = [ - {name: 'color-red', value: '#ff0000'}, - {name: 'color-green', value: '#00ff00'}, - {name: 'color-blue', value: '#0000ff'} - ]; - expect(this.jsonData.config.settings).to.eql(sassData); + it('should contain all style variable names from defined file', function() { + expect(this.jsonData.variables[0].name).to.eql('color-red'); + expect(this.jsonData.variables[1].name).to.eql('color-green'); + expect(this.jsonData.variables[2].name).to.eql('color-blue'); + }); + + it('should contain all style variable values from defined file', function() { + expect(this.jsonData.variables[0].value).to.eql('#ff0000'); + expect(this.jsonData.variables[1].value).to.eql('#00ff00'); + expect(this.jsonData.variables[2].value).to.eql('#0000ff'); }); it('should not reveal outputPath', function() { @@ -278,7 +281,7 @@ describe('styleguide.css for SCSS project', function() { config; config = defaultConfig; - config.styleVariables = './test/projects/scss-project/source/styles/_styleguide_variables.scss'; + config.styleVariables = 'test/projects/scss-project/source/styles/_styleguide_variables.scss'; source = './test/projects/scss-project/source/**/*.scss'; styleguideStream(source, config).pipe( through.obj({objectMode: true}, collector(files), function(callback) { @@ -300,7 +303,7 @@ describe('styleguide.css for LESS project', function() { config; config = defaultConfig; - config.styleVariables = './test/projects/less-project/source/styles/_styleguide_variables.less'; + config.styleVariables = 'test/projects/less-project/source/styles/_styleguide_variables.less'; source = './test/projects/less-project/source/**/*.less'; styleguideStream(source, config).pipe( through.obj({objectMode: true}, collector(files), function(callback) { @@ -322,8 +325,8 @@ describe('styleguide.json for SCSS project', function() { config; config = defaultConfig; - config.styleVariables = './test/projects/scss-project/source/styles/_styleguide_variables.scss'; - source = './test/projects/scss-project/source/**/*.scss'; + config.styleVariables = 'test/projects/scss-project/source/styles/_styleguide_variables.scss'; + source = 'test/projects/scss-project/source/**/*.scss'; styleguideStream(source, config).pipe( through.obj({objectMode: true}, collector(files), function(callback) { _this.jsonData = JSON.parse(findFile(files, 'styleguide.json').contents); @@ -333,6 +336,13 @@ describe('styleguide.json for SCSS project', function() { ); }); + it('should contain filenames where variables are defined', function() { + var path = 'test/projects/scss-project/source/styles/_styleguide_variables.scss'; + expect(this.jsonData.variables[0].file).to.contain(path); + expect(this.jsonData.variables[1].file).to.contain(path); + expect(this.jsonData.variables[2].file).to.contain(path); + }); + sharedStyleguideJSON(); }); @@ -344,8 +354,8 @@ describe('styleguide.json for LESS project', function() { config; config = defaultConfig; - config.styleVariables = './test/projects/less-project/source/styles/_styleguide_variables.less'; - source = './test/projects/less-project/source/**/*.less'; + config.styleVariables = 'test/projects/less-project/source/styles/_styleguide_variables.less'; + source = 'test/projects/less-project/source/**/*.less'; styleguideStream(source, config).pipe( through.obj({objectMode: true}, collector(files), function(callback) { _this.jsonData = JSON.parse(findFile(files, 'styleguide.json').contents); @@ -355,5 +365,12 @@ describe('styleguide.json for LESS project', function() { ); }); + it('should contain filenames where variables are defined', function() { + var path = 'test/projects/less-project/source/styles/_styleguide_variables.less'; + expect(this.jsonData.variables[0].file).to.contain(path); + expect(this.jsonData.variables[1].file).to.contain(path); + expect(this.jsonData.variables[2].file).to.contain(path); + }); + sharedStyleguideJSON(); }); diff --git a/test/projects/less-project/source/styles/style.less b/test/projects/less-project/source/styles/style.less index 77d117a3..0a3d95c3 100644 --- a/test/projects/less-project/source/styles/style.less +++ b/test/projects/less-project/source/styles/style.less @@ -1,5 +1,7 @@ @import "_styleguide_variables"; +@test-variable: 10px; + // First section // // Markup: diff --git a/test/projects/scss-project/source/styles/style.scss b/test/projects/scss-project/source/styles/style.scss index 96993691..2c2f9c9b 100644 --- a/test/projects/scss-project/source/styles/style.scss +++ b/test/projects/scss-project/source/styles/style.scss @@ -1,5 +1,7 @@ @import "styleguide_variables"; +$test-variable: 10px; + // First section // // Markup: diff --git a/test/unit/modules/io.test.js b/test/unit/modules/io.test.js index 02e1e396..b11b2638 100644 --- a/test/unit/modules/io.test.js +++ b/test/unit/modules/io.test.js @@ -79,7 +79,6 @@ describe('module io', function() { }); describe('socket connection listener', function() { - var listener; beforeEach(function() { @@ -91,11 +90,6 @@ describe('module io', function() { expect(listener).to.be.a('function'); }); - it('registers listener on "request variables from server" event', function() { - listener.call(undefined, socket); - expect(socket.on).to.have.been.calledWith('request variables from server'); - }); - it('registers listener on "variables to server" event', function() { listener.call(undefined, socket); expect(socket.on).to.have.been.calledWith('variables to server'); @@ -114,122 +108,77 @@ describe('module io', function() { listener.call(undefined, socket); expect(socket.emit).to.have.been.calledWith('styleguide compile error').and.calledWith('styleguide progress end'); }); - }); - describe('loading variables', function() { - - var listener, - readCallback; - - beforeEach(function() { - sinon.spy(console, 'error'); - opt.styleVariables = 'test/vars.scss'; - listener = getSocketListener('request variables from server'); - fs.readFile = sinon.spy(); - - listener.call(); - readCallback = fs.readFile.lastCall.args[2]; - }); - - afterEach(function() { - console.error.restore(); - }); - - it('reads file defined in options.styleVariables', function() { - expect(fs.readFile).to.have.been.calledWith(opt.styleVariables, { encoding: 'utf8' }); - }); - - it('passes file contents to variable parser.parseVariables', function() { - var fileData = '$foo: 10px;'; - readCallback.call(undefined, undefined, fileData); - expect(parser.parseVariables).to.have.been.calledWith(fileData, 'scss'); - }); - - it('emits variable data with socket event "variables from server"', function() { - var data = [{ name: 'foo', value: '10px' }]; - parser.parseVariables = sinon.stub().returns(data); - readCallback.call(); - expect(socket.emit).to.have.been.calledWith('variables from server', data); - }); - - it('only logs error to console if reading variables file fails', function() { - socket.emit.reset(); - readCallback.call(undefined, 'read fail'); - expect(console.error).to.have.been.calledWith('read fail'); - expect(parser.parseVariables).not.to.have.been.called; - expect(socket.emit).not.to.have.been.called; - }); - - }); - - describe('saving variables', function() { - - var listener, - readCallback, - writeCallback, - fileData = '$foo: 10px;', - newVariables = [{ name: 'foo', value: '16px' }], - newData = '$foo: 16px;'; + describe('save variables', function() { + var newVariables = [ + { + file: 'path/first_file.less', + name: 'myvar1', + value: 'myvalue1' + }, + { + file: 'path/second_file.scss', + name: 'myvar3', + value: 'myvalue3' + }, + { + file: 'path/first_file.less', + name: 'myvar2', + value: 'myvalue2' + } + ]; - beforeEach(function() { - sinon.spy(console, 'error'); + beforeEach(function(done) { opt.styleVariables = 'test/vars.scss'; - listener = getSocketListener('variables to server'); - fs.readFile = sinon.spy(); - fs.writeFile = sinon.spy(); - - listener.call(undefined, newVariables); - readCallback = fs.readFile.lastCall.args[2]; - }); - afterEach(function() { - console.error.restore(); + // Stub the file system module + sinon.stub(fs, 'readFile'); + sinon.stub(fs, 'writeFile'); + + fs.readFile + .withArgs('path/first_file.less', sinon.match.any, sinon.match.func) + .callsArgWith(2, null, 'First file content'); + + fs.readFile + .withArgs('path/second_file.scss', sinon.match.any, sinon.match.func) + .callsArgWith(2, null, 'Second file content'); + + fs.writeFile + .withArgs('path/first_file.less', sinon.match.any, sinon.match.func) + .callsArgWith(2, null, 'Changed first file content'); + + fs.writeFile + .withArgs('path/second_file.scss', sinon.match.any, sinon.match.func) + .callsArgWith(2, null, 'Changed cecond file content'); + + io.saveVariables(newVariables).then(function() { + done(); + }); + }); + + it('should call set variables with the original file contents and variables from that file', function() { + var firstFileVars = [{ + file: 'path/first_file.less', + name: 'myvar1', + value: 'myvalue1' + }, + { + file: 'path/first_file.less', + name: 'myvar2', + value: 'myvalue2' + }], + secondFileVars = [{ + file: 'path/second_file.scss', + name: 'myvar3', + value: 'myvalue3' + }]; + expect(parser.setVariables).to.have.been.calledWith('First file content', 'less', firstFileVars); + expect(parser.setVariables).to.have.been.calledWith('Second file content', 'scss', secondFileVars); }); - - it('reads file defined in options.styleVariables', function() { - expect(fs.readFile).to.have.been.calledWith(opt.styleVariables, { encoding: 'utf8' }); - }); - - it('passes original file contents to variable parser.setVariables', function() { - - readCallback.call(undefined, undefined, fileData); - expect(parser.setVariables).to.have.been.calledWith(fileData, 'scss'); - }); - - it('passes new variables to variable parser.setVariables', function() { - readCallback.call(undefined, undefined, fileData); - expect(parser.setVariables).to.have.been.calledWith(fileData, 'scss', newVariables); - }); - - it('writes data returned from parser.setVariables() to options.styleVariables file', function() { - parser.setVariables = sinon.stub().returns(newData); - readCallback(); - expect(fs.writeFile).to.have.been.calledWith(opt.styleVariables, newData); - }); - - it('emits new variable data with socket event "variables saved to server"', function() { - readCallback(); - writeCallback = fs.writeFile.lastCall.args[2]; - - writeCallback.call(undefined, undefined, newData); - expect(socket.emit).to.have.been.calledWith('variables saved to server', newData); - }); - - it('only logs error to console if writing new variables data fails', function() { - socket.emit.reset(); - readCallback.call(); - writeCallback = fs.writeFile.lastCall.args[2]; - - writeCallback.call(undefined, 'write fail'); - expect(console.error).to.have.been.calledWith('write fail'); - expect(socket.emit).not.to.have.been.called; - }); - }); function setUp() { - socket = { conn: { id: '123' }, emit: sinon.spy(), @@ -255,15 +204,4 @@ describe('module io', function() { io = ioModule(server, opt); } - - function getSocketListener(event) { - var connectionListener = server.on.lastCall.args[1]; - connectionListener.call(undefined, socket); - for (var i = 0; i < socket.on.callCount; i += 1) { - if (socket.on.getCall(i).args[0] === event) { - return socket.on.getCall(i).args[1]; - } - } - } - }); diff --git a/test/unit/modules/kss-parser.test.js b/test/unit/modules/kss-parser.test.js index 0ff9f346..c97fc17b 100644 --- a/test/unit/modules/kss-parser.test.js +++ b/test/unit/modules/kss-parser.test.js @@ -6,27 +6,48 @@ var requireModule = require('requirefrom')('lib/modules'), describe('KSS parser', function() { - function parse() { - var files = {}; - Array.prototype.slice.call(arguments).forEach(function(file, idx) { - files['file' + idx] = file; - }); - return parser.parseKSS(files, {}); + function parse(files) { + return parser.parseKssSections(files, {}); } function expectOrder(expected) { - return function(styleguide) { - var order = styleguide.sections.map(function(section) { + return function(sections) { + var order = sections.map(function(section) { return section.reference; }); expect(order).to.eql(expected); }; } - var file1, file2, file3, file4; + it('parses sections from multiple files', function(done) { + var files = { + 'file1.less': multiline(function() { + /* + // Styleguide 1.0 + */ + }), + 'file2.scss': multiline(function() { + /* + // Styleguide 2.0 + + // Styleguide 2.1 + + // Styleguide 2.2 + */ + }), + 'file3.scss': multiline(function() { + /* + // Styleguide 3.0 + */ + }) + }; + parse(files).then(function(sections) { + expect(sections.length).to.eql(5); + }).then(done).catch(done); + }); - beforeEach(function() { - file1 = multiline(function() { + it('sorts sections numerically according to first level', function(done) { + var file = {'file1.less': multiline(function() { /* // Styleguide 10 @@ -34,8 +55,13 @@ describe('KSS parser', function() { // Styleguide 1 */ - }); - file2 = multiline(function() { + })}, + order = ['1', '2', '10']; + parse(file).then(expectOrder(order)).then(done).catch(done); + }); + + it('sorts sub-sections numerically according to second level', function(done) { + var file = {'file2.less': multiline(function() { /* // Styleguide 2.10 @@ -45,86 +71,102 @@ describe('KSS parser', function() { // Styleguide 2.1 */ - }); - file3 = multiline(function() { + })}, + order = ['2.1', '2.2', '2.10', '3']; + parse(file).then(expectOrder(order)).then(done).catch(done); + }); + + it('sorts sub-sub-sections numerically according to third level', function(done) { + var file = {'file3.less': multiline(function() { /* - // Styleguide 3.1.10 + // Styleguide 3.1.10 - // Styleguide 3.2 + // Styleguide 3.2 - // Styleguide 4 + // Styleguide 4 - // Styleguide 3.1.2 + // Styleguide 3.1.2 - // Styleguide 3.1.1 + // Styleguide 3.1.1 */ - }); - file4 = multiline(function() { - /* - // Styleguide 1.2.3.4.10 - - // Styleguide 1.2.3.4.5.6.7 - - // Styleguide 1.2.4.19 - - // Styleguide 1.2.4.2 - */ - }); - }); - - it('parses sections from multiple files', function(done) { - parse(file1, file2, file3).then(function(styleguide) { - expect(styleguide.sections.length).to.eql(12); - }).then(done).catch(done); + })}, + order = ['3.1.1', '3.1.2', '3.1.10', '3.2', '4']; + parse(file).then(expectOrder(order)).then(done).catch(done); }); - it('sorts sections numerically according to first level', function(done) { - var order = ['1', '2', '10']; - parse(file1).then(expectOrder(order)).then(done).catch(done); - }); + it('sorts arbitrarily deep sub-sections correctly', function(done) { + var file = {'file4.less': multiline(function() { + /* + // Styleguide 1.2.3.4.10 - it('sorts sub-sections numerically according to second level', function(done) { - var order = ['2.1', '2.2', '2.10', '3']; - parse(file2).then(expectOrder(order)).then(done).catch(done); - }); + // Styleguide 1.2.3.4.5.6.7 - it('sorts sub-sub-sections numerically according to third level', function(done) { - var order = ['3.1.1', '3.1.2', '3.1.10', '3.2', '4']; - parse(file3).then(expectOrder(order)).then(done).catch(done); - }); + // Styleguide 1.2.4.19 - it('sorts arbitrarily deep sub-sections correctly', function(done) { - var order = ['1.2.3.4.5.6.7', '1.2.3.4.10', '1.2.4.2', '1.2.4.19']; - parse(file4).then(expectOrder(order)).then(done).catch(done); + // Styleguide 1.2.4.2 + */ + })}, + order = ['1.2.3.4.5.6.7', '1.2.3.4.10', '1.2.4.2', '1.2.4.19']; + parse(file).then(expectOrder(order)).then(done).catch(done); }); it('parses section reference 1.0 as 1', function(done) { - var file = multiline(function() { - /* - // Styleguide 2.0 - - // Styleguide 1.0 - */ - }); + var file = { + 'file1.less': multiline(function() { + /* + // Styleguide 2.0 + + // Styleguide 1.0 + */ + }) + }; parse(file).then(expectOrder(['1', '2'])).then(done).catch(done); }); + it('should store correct syntax', function(done) { + var files = { + 'file1.less': multiline(function() { + /* + // Styleguide 1.0 + */ + }), + 'file2.scss': multiline(function() { + /* + // Styleguide 2.0 + */ + }) + }; + parse(files).then(function(sections) { + expect(sections[0].syntax).to.eql('less'); + expect(sections[1].syntax).to.eql('scss'); + }).then(done).catch(done); + }); + it('ignores trailing dot when parsing section reference', function(done) { - parse('//Styleguide 1.2.').then(expectOrder(['1.2'])).then(done).catch(done); + var file = { + 'file1.less': multiline(function() { + /* + // Styleguide 1.2. + */ + }) + }; + parse(file).then(expectOrder(['1.2'])).then(done).catch(done); }); it('rejects with error if two sections are defined with same reference number', function(done) { - var file = multiline(function() { - /* - // Foo - // - // Styleguide 1.1 - - // Bar - // - // Styleguide 1.1 - */ - }); + var file = { + 'file1.less': multiline(function() { + /* + // Foo + // + // Styleguide 1.1 + + // Bar + // + // Styleguide 1.1 + */ + }) + }; parse(file).done(function() { done(Error('expected promise to reject')); }, function(err) { diff --git a/test/unit/modules/variable-parser.test.js b/test/unit/modules/variable-parser.test.js index 5d95999b..3cfd94ef 100644 --- a/test/unit/modules/variable-parser.test.js +++ b/test/unit/modules/variable-parser.test.js @@ -146,7 +146,7 @@ describe('Parser', function() { {name: 'mypadding', value: '3px'}, {name: 'myfont', value: '"Helvetica Neue", Helvetica, Arial, sans-serif'} ]; - expect(parser.parseVariables(str)).eql(result); + expect(parser.parseVariableDeclarations(str)).eql(result); }); it('should parse variables from file with containing comments and intended lines', function() { @@ -163,7 +163,7 @@ describe('Parser', function() { {name: 'mypadding', value: '3px'}, {name: 'myfont', value: '"Helvetica Neue", Helvetica, Arial, sans-serif'} ]; - expect(parser.parseVariables(str)).eql(result); + expect(parser.parseVariableDeclarations(str)).eql(result); }); it('should parse variables correct when there are multiple variables in a single line', function() { @@ -173,7 +173,7 @@ describe('Parser', function() { {name: 'color2', value: '#00ff00'}, {name: 'color3', value: '#0000ff'} ]; - expect(parser.parseVariables(str)).eql(result); + expect(parser.parseVariableDeclarations(str)).eql(result); }); it('should not take commented variables', function() { @@ -189,7 +189,7 @@ describe('Parser', function() { {name: 'color1', value: '#ff0000'}, {name: 'color3', value: '#0000ff'} ]; - expect(parser.parseVariables(str)).eql(result); + expect(parser.parseVariableDeclarations(str)).eql(result); }); it('should not detect @import as variable', function() { @@ -199,7 +199,7 @@ describe('Parser', function() { */ }), result = []; - expect(parser.parseVariables(str)).eql(result); + expect(parser.parseVariableDeclarations(str)).eql(result); }); }); @@ -217,7 +217,7 @@ describe('Parser', function() { {name: 'mypadding', value: '3px'}, {name: 'myfont', value: '"Helvetica Neue", Helvetica, Arial, sans-serif'} ]; - expect(parser.parseVariables(str, 'less')).eql(result); + expect(parser.parseVariableDeclarations(str, 'less')).eql(result); }); it('should parse variables from file with containing comments and intended lines', function() { @@ -234,7 +234,7 @@ describe('Parser', function() { {name: 'mypadding', value: '3px'}, {name: 'myfont', value: '"Helvetica Neue", Helvetica, Arial, sans-serif'} ]; - expect(parser.parseVariables(str, 'less')).eql(result); + expect(parser.parseVariableDeclarations(str, 'less')).eql(result); }); it('should parse variables correct when there are multiple variables in a single line', function() { @@ -244,7 +244,7 @@ describe('Parser', function() { {name: 'color2', value: '#00ff00'}, {name: 'color3', value: '#0000ff'} ]; - expect(parser.parseVariables(str, 'less')).eql(result); + expect(parser.parseVariableDeclarations(str, 'less')).eql(result); }); it('should not take commented variables', function() { @@ -260,17 +260,17 @@ describe('Parser', function() { {name: 'color1', value: '#ff0000'}, {name: 'color3', value: '#0000ff'} ]; - expect(parser.parseVariables(str, 'less')).eql(result); + expect(parser.parseVariableDeclarations(str, 'less')).eql(result); }); - it('should not detect @import as variable', function() { + it('should not detect @import as a variable', function() { var str = multiline(function() { /* @import 'file'; */ }), result = []; - expect(parser.parseVariables(str, 'less')).eql(result); + expect(parser.parseVariableDeclarations(str, 'less')).eql(result); }); it('should accept variables named @import', function() { @@ -282,7 +282,7 @@ describe('Parser', function() { result = [ {name: 'import', value: '3px'} ]; - expect(parser.parseVariables(str, 'less')).eql(result); + expect(parser.parseVariableDeclarations(str, 'less')).eql(result); }); }); }); @@ -333,6 +333,7 @@ describe('Parser', function() { changed = parser.setVariables(str, 'scss', variables); expect(changed).eql(result); }); + it('should preserve indents', function() { var str = multiline(function() { /* @@ -354,6 +355,7 @@ describe('Parser', function() { changed = parser.setVariables(str, 'scss', variables); expect(changed).eql(result); }); + it('should preserve comments', function() { var str = '' + '$mycolor: #00ff00;\n' + @@ -370,6 +372,7 @@ describe('Parser', function() { expect(changed).eql(result); }); }); + describe('LESS syntax', function() { it('should change single value variable', function() { var str = multiline(function() {