diff options
author | Samy Pessé <samypesse@gmail.com> | 2015-10-13 15:14:26 +0200 |
---|---|---|
committer | Samy Pessé <samypesse@gmail.com> | 2015-10-13 15:14:26 +0200 |
commit | 8ba18cd587de8f073c9818acbdeaeaafe9a478c1 (patch) | |
tree | e83f6a9b6e4a37d2058d11653db73123a799aef0 | |
parent | 4ae5b2e05001df9bdf8c5b64f8a82725e14d6414 (diff) | |
parent | 54c12fea89198ad6582529cf7bfd6c7ff6f92ed2 (diff) | |
download | gitbook-8ba18cd587de8f073c9818acbdeaeaafe9a478c1.zip gitbook-8ba18cd587de8f073c9818acbdeaeaafe9a478c1.tar.gz gitbook-8ba18cd587de8f073c9818acbdeaeaafe9a478c1.tar.bz2 |
Merge pull request #979 from GitbookIO/feature/plugins_config
Improvements for plugin configurations
-rw-r--r-- | lib/book.js | 17 | ||||
-rw-r--r-- | lib/config_default.js | 108 | ||||
-rw-r--r-- | lib/configuration.js | 130 | ||||
-rw-r--r-- | lib/generator.js | 26 | ||||
-rw-r--r-- | lib/plugin.js | 107 | ||||
-rw-r--r-- | lib/pluginslist.js | 56 | ||||
-rw-r--r-- | package.json | 12 | ||||
-rw-r--r-- | test/assertions.js | 34 | ||||
-rw-r--r-- | test/plugins.js | 33 | ||||
-rw-r--r-- | test/plugins/config/index.js | 1 | ||||
-rw-r--r-- | test/plugins/config/package.json | 21 |
11 files changed, 353 insertions, 192 deletions
diff --git a/lib/book.js b/lib/book.js index 72f0ec2..39d4719 100644 --- a/lib/book.js +++ b/lib/book.js @@ -178,6 +178,14 @@ Book.prototype.generate = function(generator) { return generator.prepare(); }) + // Transform configuration + .then(function() { + return that.callHook('config', that.config.dump()) + .then(function(newConfig) { + that.config.replace(newConfig); + }); + }) + // Generate content .then(function() { if (that.isMultilingual()) { @@ -238,13 +246,13 @@ Book.prototype.generate = function(generator) { // Finish generation .then(function() { - return generator.callHook('finish:before'); + return that.callHook('finish:before'); }) .then(function() { return generator.finish(); }) .then(function() { - return generator.callHook('finish'); + return that.callHook('finish'); }) .then(function() { that.log.info.ln('generation is finished'); @@ -793,4 +801,9 @@ Book.prototype.normError = function(err, opts, defs) { return err; }; +// Call a hook in plugins +Book.prototype.callHook = function(name, data) { + return this.plugins.hook(name, data); +}; + module.exports= Book; diff --git a/lib/config_default.js b/lib/config_default.js new file mode 100644 index 0000000..328531c --- /dev/null +++ b/lib/config_default.js @@ -0,0 +1,108 @@ +var path = require('path'); + +module.exports = { + // Options that can't be extend + 'configFile': 'book', + 'generator': 'website', + 'extension': null, + + // Book metadats (somes are extracted from the README by default) + 'title': null, + 'description': null, + 'isbn': null, + 'language': 'en', + 'direction': null, + 'author': null, + + // version of gitbook to use + 'gitbook': '*', + + // Structure + 'structure': { + 'langs': 'LANGS.md', + 'readme': 'README.md', + 'glossary': 'GLOSSARY.md', + 'summary': 'SUMMARY.md' + }, + + // CSS Styles + 'styles': { + 'website': 'styles/website.css', + 'print': 'styles/print.css', + 'ebook': 'styles/ebook.css', + 'pdf': 'styles/pdf.css', + 'mobi': 'styles/mobi.css', + 'epub': 'styles/epub.css' + }, + + // Plugins list, can contain '-name' for removing default plugins + 'plugins': [], + + // Global configuration for plugins + 'pluginsConfig': {}, + + // Variables for templating + 'variables': {}, + + // Set another theme with your own layout + // It's recommended to use plugins or add more options for default theme, though + // See https://github.com/GitbookIO/gitbook/issues/209 + 'theme': path.resolve(__dirname, '../theme'), + + // Links in template (null: default, false: remove, string: new value) + 'links': { + // Custom links at top of sidebar + 'sidebar': { + // 'Custom link name': 'https://customlink.com' + }, + + // Sharing links + 'sharing': { + 'google': null, + 'facebook': null, + 'twitter': null, + 'weibo': null, + 'all': null + } + }, + + + // Options for PDF generation + 'pdf': { + // Add toc at the end of the file + 'toc': true, + + // Add page numbers to the bottom of every page + 'pageNumbers': false, + + // Font size for the file content + 'fontSize': 12, + + // Paper size for the pdf + // Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’] + 'paperSize': 'a4', + + // How to mark detected chapters. + // Choices are “pagebreak”, “rule”, 'both' or “none”. + 'chapterMark' : 'pagebreak', + + // An XPath expression. Page breaks are inserted before the specified elements. + // To disable use the expression: '/' + 'pageBreaksBefore': '/', + + // Margin (in pts) + // Note: 72 pts equals 1 inch + 'margin': { + 'right': 62, + 'left': 62, + 'top': 56, + 'bottom': 56 + }, + + // Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_. + 'headerTemplate': null, + + // Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_. + 'footerTemplate': null + } +}; diff --git a/lib/configuration.js b/lib/configuration.js index 7488fae..d514720 100644 --- a/lib/configuration.js +++ b/lib/configuration.js @@ -1,12 +1,13 @@ var _ = require('lodash'); var Q = require('q'); -var fs = require('fs'); var path = require('path'); var semver = require('semver'); var pkg = require('../package.json'); var i18n = require('./utils/i18n'); +var DEFAULT_CONFIG = require('./config_default'); + // Default plugins added to each books var DEFAULT_PLUGINS = ['highlight', 'search', 'sharing', 'fontsettings']; @@ -85,9 +86,7 @@ var Configuration = function(book, options) { var that = this; this.book = book; - - this.options = _.cloneDeep(Configuration.DEFAULT); - this.options = _.merge(this.options, options || {}); + this.replace(options); // options.input == book.root Object.defineProperty(this.options, 'input', { @@ -169,6 +168,17 @@ Configuration.prototype.extend = function(options) { _.extend(this.options, options); }; +// Replace the whole configuration +Configuration.prototype.replace = function(options) { + this.options = _.cloneDeep(DEFAULT_CONFIG); + this.options = _.merge(this.options, options || {}); +}; + +// Dump configuration as json object +Configuration.prototype.dump = function() { + return _.cloneDeep(this.options); +}; + // Get structure file Configuration.prototype.getStructure = function(name) { return this.options.structure[name].split('.').slice(0, -1).join('.'); @@ -184,112 +194,12 @@ Configuration.prototype.get = function(key, def) { return _.get(this.options, key, def); }; -// Default configuration -Configuration.DEFAULT = { - // Options that can't be extend - 'configFile': 'book', - 'generator': 'website', - 'extension': null, - - // Book metadats (somes are extracted from the README by default) - 'title': null, - 'description': null, - 'isbn': null, - 'language': 'en', - 'direction': null, - 'author': null, - - // version of gitbook to use - 'gitbook': '*', - - // Structure - 'structure': { - 'langs': 'LANGS.md', - 'readme': 'README.md', - 'glossary': 'GLOSSARY.md', - 'summary': 'SUMMARY.md' - }, - - // CSS Styles - 'styles': { - 'website': 'styles/website.css', - 'print': 'styles/print.css', - 'ebook': 'styles/ebook.css', - 'pdf': 'styles/pdf.css', - 'mobi': 'styles/mobi.css', - 'epub': 'styles/epub.css' - }, - - // Plugins list, can contain '-name' for removing default plugins - 'plugins': [], - - // Global configuration for plugins - 'pluginsConfig': {}, - - // Variables for templating - 'variables': {}, - - // Set another theme with your own layout - // It's recommended to use plugins or add more options for default theme, though - // See https://github.com/GitbookIO/gitbook/issues/209 - 'theme': path.resolve(__dirname, '../theme'), - - // Links in template (null: default, false: remove, string: new value) - 'links': { - // Custom links at top of sidebar - 'sidebar': { - // 'Custom link name': 'https://customlink.com' - }, - - // Sharing links - 'sharing': { - 'google': null, - 'facebook': null, - 'twitter': null, - 'weibo': null, - 'all': null - } - }, - - - // Options for PDF generation - 'pdf': { - // Add toc at the end of the file - 'toc': true, - - // Add page numbers to the bottom of every page - 'pageNumbers': false, - - // Font size for the file content - 'fontSize': 12, - - // Paper size for the pdf - // Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’] - 'paperSize': 'a4', - - // How to mark detected chapters. - // Choices are “pagebreak”, “rule”, 'both' or “none”. - 'chapterMark' : 'pagebreak', - - // An XPath expression. Page breaks are inserted before the specified elements. - // To disable use the expression: '/' - 'pageBreaksBefore': '/', - - // Margin (in pts) - // Note: 72 pts equals 1 inch - 'margin': { - 'right': 62, - 'left': 62, - 'top': 56, - 'bottom': 56 - }, - - // Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_. - 'headerTemplate': null, - - // Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_. - 'footerTemplate': null - } +// Update a configuration +Configuration.prototype.set = function(key, value) { + return _.set(this.options, key, value); }; +// Default configuration +Configuration.DEFAULT = DEFAULT_CONFIG; + module.exports= Configuration; diff --git a/lib/generator.js b/lib/generator.js index fca5b3c..4e280d8 100644 --- a/lib/generator.js +++ b/lib/generator.js @@ -1,12 +1,12 @@ -var _ = require("lodash"); -var path = require("path"); -var Q = require("q"); -var fs = require("./utils/fs"); +var _ = require('lodash'); +var path = require('path'); +var Q = require('q'); +var fs = require('./utils/fs'); var BaseGenerator = function(book) { this.book = book; - Object.defineProperty(this, "options", { + Object.defineProperty(this, 'options', { get: function () { return this.book.options; } @@ -16,19 +16,19 @@ var BaseGenerator = function(book) { }; BaseGenerator.prototype.callHook = function(name, data) { - return this.book.plugins.hook(name, data); + return this.book.callHook(name, data); }; // Prepare the genertor BaseGenerator.prototype.prepare = function() { var that = this; - return that.callHook("init"); + return that.callHook('init'); }; // Write a parsed file to the output BaseGenerator.prototype.convertFile = function(input) { - return Q.reject(new Error("Could not convert "+input)); + return Q.reject(new Error('Could not convert '+input)); }; // Copy file to the output (non parsable) @@ -51,16 +51,16 @@ BaseGenerator.prototype.copyCover = function() { var that = this; return Q.all([ - fs.copy(that.book.resolve("cover.jpg"), path.join(that.options.output, "cover.jpg")), - fs.copy(that.book.resolve("cover_small.jpg"), path.join(that.options.output, "cover_small.jpg")) + fs.copy(that.book.resolve('cover.jpg'), path.join(that.options.output, 'cover.jpg')), + fs.copy(that.book.resolve('cover_small.jpg'), path.join(that.options.output, 'cover_small.jpg')) ]) .fail(function() { // If orignaly from multi-lang, try copy from parent if (!that.book.isSubBook()) return; return Q.all([ - fs.copy(path.join(that.book.parentRoot(), "cover.jpg"), path.join(that.options.output, "cover.jpg")), - fs.copy(path.join(that.book.parentRoot(), "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg")) + fs.copy(path.join(that.book.parentRoot(), 'cover.jpg'), path.join(that.options.output, 'cover.jpg')), + fs.copy(path.join(that.book.parentRoot(), 'cover_small.jpg'), path.join(that.options.output, 'cover_small.jpg')) ]); }) .fail(function() { @@ -70,7 +70,7 @@ BaseGenerator.prototype.copyCover = function() { // At teh end of the generation BaseGenerator.prototype.finish = function() { - return Q.reject(new Error("Could not finish generation")); + return Q.reject(new Error('Could not finish generation')); }; module.exports = BaseGenerator; diff --git a/lib/plugin.js b/lib/plugin.js index 5e1c427..b4d4aa1 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -1,16 +1,28 @@ -var _ = require("lodash"); -var Q = require("q"); -var semver = require("semver"); -var path = require("path"); -var url = require("url"); -var fs = require("./utils/fs"); -var resolve = require("resolve"); +var _ = require('lodash'); +var Q = require('q'); +var semver = require('semver'); +var path = require('path'); +var url = require('url'); +var fs = require('./utils/fs'); +var resolve = require('resolve'); +var mergeDefaults = require('merge-defaults'); +var jsonschema = require('jsonschema'); +var jsonSchemaDefaults = require('json-schema-defaults'); + +var pkg = require('../package.json'); + +var PLUGIN_PREFIX = 'gitbook-plugin-'; + +// Return an absolute name for the plugin (the one on NPM) +function absoluteName(name) { + if (name.indexOf(PLUGIN_PREFIX) === 0) return name; + return [PLUGIN_PREFIX, name].join(''); +} -var pkg = require("../package.json"); var Plugin = function(book, name) { this.book = book; - this.name = name; + this.name = absoluteName(name); this.packageInfos = {}; this.infos = {}; @@ -18,9 +30,8 @@ var Plugin = function(book, name) { _.bindAll(this); _.each([ - "gitbook-plugin-"+name, - "gitbook-"+name, - name, + absoluteName(name), + name ], function(_name) { // Load from the book if (this.load(_name, book.root)) return false; @@ -31,20 +42,27 @@ var Plugin = function(book, name) { }; // Type of plugins resources -Plugin.RESOURCES = ["js", "css"]; +Plugin.RESOURCES = ['js', 'css']; Plugin.HOOKS = [ - "init", "finish", "finish:before", "page", "page:before" + 'init', 'finish', 'finish:before', 'config', 'page', 'page:before' ]; +// Return the reduce name for the plugin +// "gitbook-plugin-test" -> "test" +// Return a relative name for the plugin (the one on GitBook) +Plugin.prototype.reducedName = function() { + return this.name.replace(PLUGIN_PREFIX, ''); +}; + // Load from a name Plugin.prototype.load = function(name, baseDir) { try { - var res = resolve.sync(name+"/package.json", { basedir: baseDir }); + var res = resolve.sync(name+'/package.json', { basedir: baseDir }); this.baseDir = path.dirname(res); this.packageInfos = require(res); this.infos = require(resolve.sync(name, { basedir: baseDir })); - this.name = name; + this.name = this.packageInfos.name; return true; } catch (e) { @@ -62,13 +80,13 @@ Plugin.prototype.normalizeResource = function(resource) { // so we will simply link to using it's URL if (parsed.protocol) { return { - "url": resource + 'url': resource }; } // This will be copied over from disk // and shipped with the book's build - return { "path": this.name+"/"+resource }; + return { 'path': this.name+'/'+resource }; }; // Return resources @@ -77,7 +95,7 @@ Plugin.prototype._getResources = function(base) { var book = this.infos[base]; // Compatibility with version 1.x.x - if (base == "website") book = book || this.infos.book; + if (base == 'website') book = book || this.infos.book; // Nothing specified, fallback to default if (!book) { @@ -85,7 +103,7 @@ Plugin.prototype._getResources = function(base) { } // Dynamic function - if(typeof book === "function") { + if(typeof book === 'function') { // Call giving it the context of our book return Q().then(book.bind(this.book)); } @@ -133,12 +151,43 @@ Plugin.prototype.isValid = function() { // Valid hooks _.each(this.infos.hooks, function(hook, hookName) { if (_.contains(Plugin.HOOKS, hookName)) return; - that.book.log.warn.ln("Hook '"+hookName+" 'used by plugin '"+that.packageInfos.name+"' has been removed or is deprecated"); + that.book.log.warn.ln('Hook "'+hookName+'"" used by plugin "'+that.packageInfos.name+'" has been removed or is deprecated'); }); return isValid; }; +// Normalize, validate configuration for this plugin using its schema +// Throw an error when shcema is not respected +Plugin.prototype.validateConfig = function(config) { + var that = this; + + return Q() + .then(function() { + var schema = that.packageInfos.gitbook || {}; + if (!schema) return config; + + // Normalize schema + schema.id = '/pluginsConfig.'+that.reducedName(); + schema.type = 'object'; + + // Validate and throw if invalid + var v = new jsonschema.Validator(); + var result = v.validate(config, schema, { + propertyName: 'pluginsConfig.'+that.reducedName() + }); + + // Throw error + if (result.errors.length > 0) { + throw new Error('Configuration Error: '+result.errors[0].stack); + } + + // Insert default values + var defaults = jsonSchemaDefaults(schema); + return mergeDefaults(config, defaults); + }); +}; + // Resolve file path Plugin.prototype.resolveFile = function(filename) { return path.resolve(this.baseDir, filename); @@ -154,8 +203,8 @@ Plugin.prototype.callHook = function(name, data) { if (!hookFunc) return Q(data); - this.book.log.debug.ln("call hook", name); - if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln("hook '"+name+"' used by plugin '"+this.name+"' is deprecated, and will be removed in the coming versions"); + this.book.log.debug.ln('call hook', name); + if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln('hook "'+name+'" used by plugin "'+this.name+'" is deprecated, and will be removed in the coming versions'); return Q() .then(function() { @@ -168,7 +217,7 @@ Plugin.prototype.copyAssets = function(out, base) { var that = this; return this.getResources(base) - .get("assets") + .get('assets') .then(function(assets) { // Assets are undefined if(!assets) return false; @@ -180,4 +229,14 @@ Plugin.prototype.copyAssets = function(out, base) { }, _.constant(false)); }; +// Get config from book +Plugin.prototype.getConfig = function() { + return this.book.config.get('pluginsConfig.'+this.reducedName(), {}); +}; + +// Set configuration for this plugin +Plugin.prototype.setConfig = function(values) { + return this.book.config.set('pluginsConfig.'+this.reducedName(), values); +}; + module.exports = Plugin; diff --git a/lib/pluginslist.js b/lib/pluginslist.js index e4594d6..8830950 100644 --- a/lib/pluginslist.js +++ b/lib/pluginslist.js @@ -78,34 +78,46 @@ PluginsList.prototype.load = function(plugin) { that.list.push(plugin); } - // Extract filters - that.book.template.addFilters(plugin.getFilters()); + return Q() - // Extract blocks - that.book.template.addBlocks(plugin.getBlocks()); + // Validate and normalize configuration + .then(function() { + var config = plugin.getConfig(); + return plugin.validateConfig(config); + }) + .then(function(config) { + // Update configuration + plugin.setConfig(config); - return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) { - return prev.then(function() { - return plugin.getResources(namespaceName) - .then(function(plResources) { - var namespace = that.namespaces[namespaceName]; - - // Extract js and css - _.each(Plugin.RESOURCES, function(resourceType) { - namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []); - }); + // Extract filters + that.book.template.addFilters(plugin.getFilters()); - // Map of html resources by name added by each plugin - _.each(plResources.html || {}, function(value, tag) { - // Turn into function if not one already - if (!_.isFunction(value)) value = _.constant(value); + // Extract blocks + that.book.template.addBlocks(plugin.getBlocks()); - namespace.html[tag] = namespace.html[tag] || []; - namespace.html[tag].push(value); + return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) { + return prev.then(function() { + return plugin.getResources(namespaceName) + .then(function(plResources) { + var namespace = that.namespaces[namespaceName]; + + // Extract js and css + _.each(Plugin.RESOURCES, function(resourceType) { + namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []); + }); + + // Map of html resources by name added by each plugin + _.each(plResources.html || {}, function(value, tag) { + // Turn into function if not one already + if (!_.isFunction(value)) value = _.constant(value); + + namespace.html[tag] = namespace.html[tag] || []; + namespace.html[tag].push(value); + }); }); }); - }); - }, Q()); + }, Q()); + }); }; // Call a hook diff --git a/package.json b/package.json index 3eeec2e..a1e1667 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,9 @@ "fstream-ignore": "1.0.2", "gitbook-parsers": "0.8.4", "gitbook-plugin-highlight": "1.0.3", - "gitbook-plugin-sharing": "1.0.0", - "gitbook-plugin-search": "1.0.0", - "gitbook-plugin-fontsettings": "1.0.0", + "gitbook-plugin-sharing": "1.0.1", + "gitbook-plugin-search": "1.0.2", + "gitbook-plugin-fontsettings": "1.0.2", "nunjucks": "2.1.0", "nunjucks-autoescape": "1.0.0", "nunjucks-filter": "1.0.0", @@ -36,12 +36,16 @@ "dom-serializer": "0.1.0", "spawn-cmd": "0.0.2", "escape-string-regexp": "1.0.3", - "juice": "1.5.0" + "juice": "1.5.0", + "jsonschema": "1.0.2", + "json-schema-defaults": "0.1.1", + "merge-defaults": "0.2.1" }, "devDependencies": { "eslint": "1.5.0", "mocha": "2.3.2", "should": "7.1.0", + "should-promised": "0.3.1", "gulp": "^3.8.11", "gulp-rename": "^1.2.2", "gulp-uglify": "1.1.0", diff --git a/test/assertions.js b/test/assertions.js index 7e14ecb..f9c4ba3 100644 --- a/test/assertions.js +++ b/test/assertions.js @@ -1,27 +1,29 @@ -var _ = require("lodash"); -var fs = require("fs"); -var path = require("path"); -var should = require("should"); -var cheerio = require("cheerio"); +var _ = require('lodash'); +var fs = require('fs'); +var path = require('path'); +var should = require('should'); +var cheerio = require('cheerio'); -should.Assertion.add("file", function(file, description) { - this.params = { actual: this.obj.toString(), operator: "have file " + file, message: description }; +require('should-promised'); - this.obj.should.have.property("options").which.is.an.Object(); - this.obj.options.should.have.property("output").which.is.a.String(); +should.Assertion.add('file', function(file, description) { + this.params = { actual: this.obj.toString(), operator: 'have file ' + file, message: description }; + + this.obj.should.have.property('options').which.is.an.Object(); + this.obj.options.should.have.property('output').which.is.a.String(); this.assert(fs.existsSync(path.resolve(this.obj.options.output, file))); }); -should.Assertion.add("jsonfile", function(file, description) { - this.params = { actual: this.obj.toString(), operator: "have valid jsonfile " + file, message: description }; +should.Assertion.add('jsonfile', function(file, description) { + this.params = { actual: this.obj.toString(), operator: 'have valid jsonfile ' + file, message: description }; - this.obj.should.have.property("options").which.is.an.Object(); - this.obj.options.should.have.property("output").which.is.a.String(); - this.assert(JSON.parse(fs.readFileSync(path.resolve(this.obj.options.output, file), { encoding: "utf-8" }))); + this.obj.should.have.property('options').which.is.an.Object(); + this.obj.options.should.have.property('output').which.is.a.String(); + this.assert(JSON.parse(fs.readFileSync(path.resolve(this.obj.options.output, file), { encoding: 'utf-8' }))); }); -should.Assertion.add("html", function(rules, description) { - this.params = { actual: "HTML string", operator: "valid html", message: description }; +should.Assertion.add('html', function(rules, description) { + this.params = { actual: 'HTML string', operator: 'valid html', message: description }; var $ = cheerio.load(this.obj); _.each(rules, function(validations, query) { diff --git a/test/plugins.js b/test/plugins.js index c0f2373..db2d225 100644 --- a/test/plugins.js +++ b/test/plugins.js @@ -51,6 +51,37 @@ describe('Plugins', function () { }); }); + describe('Configuration', function() { + var plugin; + + before(function() { + plugin = new Plugin(book, 'testconfig'); + plugin.load('./config', PLUGINS_ROOT); + }); + + it('should throw error for invalid configuration', function() { + return plugin.validateConfig({}) + .should.be.rejectedWith('Configuration Error: pluginsConfig.testconfig.testRequired is required'); + }); + + it('should throw error for invalid types', function() { + return plugin.validateConfig({ + testRequired: 'hello' + }) + .should.be.rejectedWith('Configuration Error: pluginsConfig.testconfig.testRequired is not of a type(s) number'); + }); + + it('should extend with default values', function() { + return plugin.validateConfig({ + testRequired: 12 + }) + .should.be.fulfilledWith({ + hello: 'world', + testRequired: 12 + }); + }); + }); + describe('Resources', function() { var plugin; @@ -93,7 +124,7 @@ describe('Plugins', function () { // There is resources from highlight plugin and this plugin resources.css.should.have.lengthOf(2); should.exist(_.find(resources.css, { - path: './resources/test' + path: 'gitbook-plugin-resources/test' })); }); }); diff --git a/test/plugins/config/index.js b/test/plugins/config/index.js new file mode 100644 index 0000000..f053ebf --- /dev/null +++ b/test/plugins/config/index.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/test/plugins/config/package.json b/test/plugins/config/package.json new file mode 100644 index 0000000..03ef744 --- /dev/null +++ b/test/plugins/config/package.json @@ -0,0 +1,21 @@ +{ + "name": "gitbook-plugin-testconfig", + "description": "Test plugin configuration", + "main": "index.js", + "version": "0.0.1", + "engines": { + "gitbook": "*" + }, + "gitbook": { + "properties": { + "hello": { + "type": "string", + "default": "world" + }, + "testRequired": { + "type": "number", + "required": true + } + } + } +}
\ No newline at end of file |