diff options
author | Samy Pessé <samypesse@gmail.com> | 2016-02-26 09:41:26 +0100 |
---|---|---|
committer | Samy Pessé <samypesse@gmail.com> | 2016-02-26 09:41:26 +0100 |
commit | d3d64f636c859f7f01a64f7774cf70bd8ccdc562 (patch) | |
tree | 4f7731f37c3a793d187b0ab1cd77680e69534c6c /test | |
parent | 4cb9cbb5ae3aa8f9211ffa3ac5e3d34232c0ca4f (diff) | |
parent | eef072693b17526347c37b66078a5059c71caa31 (diff) | |
download | gitbook-d3d64f636c859f7f01a64f7774cf70bd8ccdc562.zip gitbook-d3d64f636c859f7f01a64f7774cf70bd8ccdc562.tar.gz gitbook-d3d64f636c859f7f01a64f7774cf70bd8ccdc562.tar.bz2 |
Merge pull request #1109 from GitbookIO/3.0.0
Version 3.0.0
Diffstat (limited to 'test')
108 files changed, 1903 insertions, 1573 deletions
diff --git a/test/all.js b/test/all.js new file mode 100644 index 0000000..9c82c91 --- /dev/null +++ b/test/all.js @@ -0,0 +1,27 @@ + +// Utilities +require('./location'); +require('./paths'); + +// Parsing +require('./locate'); +require('./config'); +require('./readme'); +require('./summary'); +require('./glossary'); +require('./langs'); +require('./parse'); + +require('./git'); +require('./plugins'); +require('./template'); +require('./conrefs'); + +// Page and HTML generation +require('./page'); + +// Output +require('./assets-inliner'); +require('./output-json'); +require('./output-website'); +require('./output-ebook'); diff --git a/test/assertions.js b/test/assertions.js index f9c4ba3..56da249 100644 --- a/test/assertions.js +++ b/test/assertions.js @@ -1,25 +1,18 @@ -var _ = require('lodash'); var fs = require('fs'); -var path = require('path'); -var should = require('should'); +var _ = require('lodash'); var cheerio = require('cheerio'); +var should = require('should'); -require('should-promised'); - +// Assertions to test if an Output has generated a file 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 }; - - 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.params = { + actual: this.obj.root(), + operator: 'have file ' + file, + message: description + }; + + this.obj.should.have.property('resolve').which.is.a.Function; + this.assert(fs.existsSync(this.obj.resolve(file))); }); should.Assertion.add('html', function(rules, description) { diff --git a/test/assets-inliner.js b/test/assets-inliner.js new file mode 100644 index 0000000..7ccabc5 --- /dev/null +++ b/test/assets-inliner.js @@ -0,0 +1,86 @@ +var cheerio = require('cheerio'); +var path = require('path'); + +var mock = require('./mock'); +var AssetsInliner = require('../lib/output/assets-inliner')(); + +describe('Assets Inliner Output', function() { + var output; + + before(function() { + var SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" version="1.1"><rect width="200" height="100" stroke="black" stroke-width="6" fill="green"/></svg>'; + + return mock.outputDefaultBook(AssetsInliner, { + 'README.md': '', + + // SVGs + 'svg_file.md': '', + 'svg_inline.md': 'This is a svg: '+SVG, + 'test.svg': '<?xml version="1.0" encoding="UTF-8"?>' + SVG, + + // Relative + 'folder/test.md': '', + + // Remote images + 'remote_png.md': '', + 'remote_svg.md': '', + + 'SUMMARY.md': '* [svg inline](svg_inline.md)\n' + + '* [svg file](svg_file.md)\n' + + '* [remote png file](remote_png.md)\n' + + '* [remote svg file](remote_svg.md)\n' + + '* [relative image](folder/test.md)\n' + + '\n\n' + }) + .then(function(_output) { + output = _output; + }); + }); + + function testImageInPage(filename) { + var page = output.book.getPage(filename); + var $ = cheerio.load(page.content); + + // Is there an image? + var $img = $('img'); + $img.length.should.equal(1); + + // Does the file exists + var src = $img.attr('src'); + + // Resolve the filename + src = page.resolveLocal(src); + + output.should.have.file(src); + path.extname(src).should.equal('.png'); + + return src; + } + + describe('SVG', function() { + it('should correctly convert SVG files to PNG', function() { + testImageInPage('svg_file.md'); + }); + + it('should correctly convert inline SVG to PNG', function() { + testImageInPage('svg_inline.md'); + }); + }); + + describe('Remote Assets', function() { + it('should correctly download a PNG file', function() { + testImageInPage('remote_png.md'); + }); + + it('should correctly download then convert a remote SVG to PNG', function() { + testImageInPage('remote_svg.md'); + }); + }); + + describe('Relative Images', function() { + it('should correctly resolve image', function() { + testImageInPage('folder/test.md'); + }); + }); +}); + diff --git a/test/books/basic/README.md b/test/books/basic/README.md deleted file mode 100644 index 09ade40..0000000 --- a/test/books/basic/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Readme - -Default description for the book. diff --git a/test/books/basic/SUMMARY.md b/test/books/basic/SUMMARY.md deleted file mode 100644 index ac9323c..0000000 --- a/test/books/basic/SUMMARY.md +++ /dev/null @@ -1 +0,0 @@ -# Summary diff --git a/test/books/config-js/README.md b/test/books/config-js/README.md deleted file mode 100644 index f395431..0000000 --- a/test/books/config-js/README.md +++ /dev/null @@ -1 +0,0 @@ -# Readme diff --git a/test/books/config-js/SUMMARY.md b/test/books/config-js/SUMMARY.md deleted file mode 100644 index ac9323c..0000000 --- a/test/books/config-js/SUMMARY.md +++ /dev/null @@ -1 +0,0 @@ -# Summary diff --git a/test/books/config-js/book.js b/test/books/config-js/book.js deleted file mode 100644 index 8731343..0000000 --- a/test/books/config-js/book.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - "title": "js-config" -}; diff --git a/test/books/config-json/README.md b/test/books/config-json/README.md deleted file mode 100644 index f395431..0000000 --- a/test/books/config-json/README.md +++ /dev/null @@ -1 +0,0 @@ -# Readme diff --git a/test/books/config-json/SUMMARY.md b/test/books/config-json/SUMMARY.md deleted file mode 100644 index ac9323c..0000000 --- a/test/books/config-json/SUMMARY.md +++ /dev/null @@ -1 +0,0 @@ -# Summary diff --git a/test/books/config-json/book.json b/test/books/config-json/book.json deleted file mode 100644 index eda10bb..0000000 --- a/test/books/config-json/book.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "title": "json-config" -} diff --git a/test/books/conrefs/README.md b/test/books/conrefs/README.md deleted file mode 100644 index 324ee1f..0000000 --- a/test/books/conrefs/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Readme - -<p id="test-plugin-block-shortcuts-1">$$test_block1$$</p> -<p id="test-plugin-block-shortcuts-2">{% include "./block.md" %}</p> - -### Relative - -<p id="t1">{% include "./hello.md" %}</p> -<p id="t2">{% include "/hello.md" %}</p> - -### Git - -<p id="t3">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}</p> -<p id="t4">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.md" %}</p> -<p id="t5">{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test3.md" %}</p> diff --git a/test/books/conrefs/SUMMARY.md b/test/books/conrefs/SUMMARY.md deleted file mode 100644 index ac9323c..0000000 --- a/test/books/conrefs/SUMMARY.md +++ /dev/null @@ -1 +0,0 @@ -# Summary diff --git a/test/books/conrefs/block.md b/test/books/conrefs/block.md deleted file mode 100644 index 3910cb6..0000000 --- a/test/books/conrefs/block.md +++ /dev/null @@ -1 +0,0 @@ -$$test_block2$$ diff --git a/test/books/conrefs/hello.md b/test/books/conrefs/hello.md deleted file mode 100644 index 557db03..0000000 --- a/test/books/conrefs/hello.md +++ /dev/null @@ -1 +0,0 @@ -Hello World diff --git a/test/books/glossary/GLOSSARY.md b/test/books/glossary/GLOSSARY.md deleted file mode 100644 index 18840d2..0000000 --- a/test/books/glossary/GLOSSARY.md +++ /dev/null @@ -1,13 +0,0 @@ -# Glossary - -## test - -Just a simple and easy to understand test. - -## hakunamatata - -This word is not present in the content. - -## test long - -This is a test with a longer text that the first entry to test order. diff --git a/test/books/glossary/README.md b/test/books/glossary/README.md deleted file mode 100644 index d46da8b..0000000 --- a/test/books/glossary/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Readme - -The word test should be replaced by a glossary link. - -``` -But the word test should not be replaced in code blocks -``` - -The two words test long should be replaced by the correct glossary links. - diff --git a/test/books/glossary/SUMMARY.md b/test/books/glossary/SUMMARY.md deleted file mode 100644 index deaea20..0000000 --- a/test/books/glossary/SUMMARY.md +++ /dev/null @@ -1,3 +0,0 @@ -# Summary - -* [Page](folder/PAGE.md) diff --git a/test/books/glossary/folder/PAGE.md b/test/books/glossary/folder/PAGE.md deleted file mode 100644 index 33b17c2..0000000 --- a/test/books/glossary/folder/PAGE.md +++ /dev/null @@ -1,3 +0,0 @@ -# Page - -Just a page in a sub-directory to test relative link to glossary. diff --git a/test/books/headings/README.md b/test/books/headings/README.md deleted file mode 100644 index b08c485..0000000 --- a/test/books/headings/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Hello World - -## Hello {#hello-custom} diff --git a/test/books/headings/SUMMARY.md b/test/books/headings/SUMMARY.md deleted file mode 100644 index e69de29..0000000 --- a/test/books/headings/SUMMARY.md +++ /dev/null diff --git a/test/books/highlight/README.md b/test/books/highlight/README.md deleted file mode 100644 index f47ac83..0000000 --- a/test/books/highlight/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Readme - -Block without language - -``` -test 1 -``` - -Block with a language - -```lang -test 2 -``` - -Inline code: `test 3` -Inline code with html: `<test>`
\ No newline at end of file diff --git a/test/books/highlight/SUMMARY.md b/test/books/highlight/SUMMARY.md deleted file mode 100644 index ac9323c..0000000 --- a/test/books/highlight/SUMMARY.md +++ /dev/null @@ -1 +0,0 @@ -# Summary diff --git a/test/books/images/README.md b/test/books/images/README.md deleted file mode 100644 index 484f410..0000000 --- a/test/books/images/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Tests for Images - -# SVG relative image - - -### Convert SVG to PNG - - -### Should only convert it once - - -### Download remote images - - -### Remote images with same filename - - - diff --git a/test/books/images/SUMMARY.md b/test/books/images/SUMMARY.md deleted file mode 100644 index ec0c4fc..0000000 --- a/test/books/images/SUMMARY.md +++ /dev/null @@ -1,3 +0,0 @@ -# Summary - -* [Page](./folder/PAGE.md) diff --git a/test/books/images/folder/PAGE.md b/test/books/images/folder/PAGE.md deleted file mode 100644 index 8beb060..0000000 --- a/test/books/images/folder/PAGE.md +++ /dev/null @@ -1,3 +0,0 @@ -### Images from other folder - - diff --git a/test/books/images/test.svg b/test/books/images/test.svg deleted file mode 100644 index 48bba70..0000000 --- a/test/books/images/test.svg +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd"> -<svg version="1.1" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="svg-root" width="100%" height="100%" viewBox="0 0 480 360"> - <title id="test-title">basic SVG tiny doc</title> - <g id="test-body-content"> - <text font-family="Arial" font-size="14" text-anchor="middle" x="225" y="25">hello world</text> - </g> - <text id="revision" x="10" y="340" font-size="40" stroke="none" fill="black">Revision: 1.1</text> - <rect id="test-frame" x="1" y="1" width="478" height="358" fill="none" stroke="#000000"/> -</svg> - diff --git a/test/books/init/.gitignore b/test/books/init/.gitignore deleted file mode 100644 index 8a88b2a..0000000 --- a/test/books/init/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -* -!SUMMARY.md -!.gitignore diff --git a/test/books/init/SUMMARY.md b/test/books/init/SUMMARY.md deleted file mode 100644 index 31c1561..0000000 --- a/test/books/init/SUMMARY.md +++ /dev/null @@ -1,9 +0,0 @@ -# Summary - -* [Hello](hello.md) -* [Hello 2](hello2.md) -* Hello 3 - * [Hello 4](hello3/hello4.md) - * Hello 5 - * [Hello 6](hello3/hello5/hello6.md) - diff --git a/test/books/languages/LANGS.md b/test/books/languages/LANGS.md deleted file mode 100644 index 4267b3c..0000000 --- a/test/books/languages/LANGS.md +++ /dev/null @@ -1,4 +0,0 @@ -# Language - -* [English](en) -* [French](fr) diff --git a/test/books/languages/README.md b/test/books/languages/README.md deleted file mode 100644 index f395431..0000000 --- a/test/books/languages/README.md +++ /dev/null @@ -1 +0,0 @@ -# Readme diff --git a/test/books/languages/en/README.md b/test/books/languages/en/README.md deleted file mode 100644 index e965047..0000000 --- a/test/books/languages/en/README.md +++ /dev/null @@ -1 +0,0 @@ -Hello diff --git a/test/books/languages/en/SUMMARY.md b/test/books/languages/en/SUMMARY.md deleted file mode 100644 index ac9323c..0000000 --- a/test/books/languages/en/SUMMARY.md +++ /dev/null @@ -1 +0,0 @@ -# Summary diff --git a/test/books/languages/fr/README.md b/test/books/languages/fr/README.md deleted file mode 100644 index 632e4fe..0000000 --- a/test/books/languages/fr/README.md +++ /dev/null @@ -1 +0,0 @@ -Bonjour diff --git a/test/books/languages/fr/SUMMARY.md b/test/books/languages/fr/SUMMARY.md deleted file mode 100644 index ac9323c..0000000 --- a/test/books/languages/fr/SUMMARY.md +++ /dev/null @@ -1 +0,0 @@ -# Summary diff --git a/test/books/links/README.md b/test/books/links/README.md deleted file mode 100644 index f395431..0000000 --- a/test/books/links/README.md +++ /dev/null @@ -1 +0,0 @@ -# Readme diff --git a/test/books/links/SUMMARY.md b/test/books/links/SUMMARY.md deleted file mode 100644 index a7debc2..0000000 --- a/test/books/links/SUMMARY.md +++ /dev/null @@ -1,4 +0,0 @@ -# Summary - -* [Folder1](folder1/README.md) -* [Folder2](folder2/README.md)
\ No newline at end of file diff --git a/test/books/links/folder1/README.md b/test/books/links/folder1/README.md deleted file mode 100644 index 99a83f6..0000000 --- a/test/books/links/folder1/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Folder 1 - -[Link to folder2](../folder2/README.md) diff --git a/test/books/links/folder2/README.md b/test/books/links/folder2/README.md deleted file mode 100644 index aa2056a..0000000 --- a/test/books/links/folder2/README.md +++ /dev/null @@ -1 +0,0 @@ -# Folder 2 diff --git a/test/books/structure/README.adoc b/test/books/structure/README.adoc deleted file mode 100644 index 354647f..0000000 --- a/test/books/structure/README.adoc +++ /dev/null @@ -1 +0,0 @@ -== Readme for the bookk diff --git a/test/books/structure/README.md b/test/books/structure/README.md deleted file mode 100644 index 94f18a8..0000000 --- a/test/books/structure/README.md +++ /dev/null @@ -1 +0,0 @@ -# Readme for GitHub diff --git a/test/books/structure/SUMMARY.md b/test/books/structure/SUMMARY.md deleted file mode 100644 index ac9323c..0000000 --- a/test/books/structure/SUMMARY.md +++ /dev/null @@ -1 +0,0 @@ -# Summary diff --git a/test/books/structure/book.json b/test/books/structure/book.json deleted file mode 100644 index 110e0ba..0000000 --- a/test/books/structure/book.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "structure": { - "readme": "README.adoc" - } -}
\ No newline at end of file diff --git a/test/books/structure/glossary.md b/test/books/structure/glossary.md deleted file mode 100644 index 8c6c0fd..0000000 --- a/test/books/structure/glossary.md +++ /dev/null @@ -1,5 +0,0 @@ -# Glossary - -### Hello - -Hello world diff --git a/test/books/style-print/README.md b/test/books/style-print/README.md deleted file mode 100644 index 09ade40..0000000 --- a/test/books/style-print/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Readme - -Default description for the book. diff --git a/test/books/style-print/SUMMARY.md b/test/books/style-print/SUMMARY.md deleted file mode 100644 index ac9323c..0000000 --- a/test/books/style-print/SUMMARY.md +++ /dev/null @@ -1 +0,0 @@ -# Summary diff --git a/test/books/style-print/styles/print.css b/test/books/style-print/styles/print.css deleted file mode 100644 index b05faf8..0000000 --- a/test/books/style-print/styles/print.css +++ /dev/null @@ -1,3 +0,0 @@ -body { - color: red; -} diff --git a/test/books/summary/PAGE1.md b/test/books/summary/PAGE1.md deleted file mode 100644 index 9608001..0000000 --- a/test/books/summary/PAGE1.md +++ /dev/null @@ -1 +0,0 @@ -# Page 1 diff --git a/test/books/summary/README.md b/test/books/summary/README.md deleted file mode 100644 index f395431..0000000 --- a/test/books/summary/README.md +++ /dev/null @@ -1 +0,0 @@ -# Readme diff --git a/test/books/summary/SUMMARY.md b/test/books/summary/SUMMARY.md deleted file mode 100644 index 38d0f6b..0000000 --- a/test/books/summary/SUMMARY.md +++ /dev/null @@ -1,5 +0,0 @@ -# Summary - -* [Page 1](./PAGE1.md) -* [Page 2](./folder/PAGE2.md) -* [Don't exists](./NOTFOUND.md) diff --git a/test/books/summary/folder/PAGE2.md b/test/books/summary/folder/PAGE2.md deleted file mode 100644 index f310be3..0000000 --- a/test/books/summary/folder/PAGE2.md +++ /dev/null @@ -1 +0,0 @@ -# Page 2 diff --git a/test/codehighlighting.js b/test/codehighlighting.js deleted file mode 100644 index f167980..0000000 --- a/test/codehighlighting.js +++ /dev/null @@ -1,65 +0,0 @@ -var path = require('path'); -var fs = require('fs'); - -var Plugin = require('../lib/plugin'); -var PLUGINS_ROOT = path.resolve(__dirname, 'plugins'); - -describe('Code Highlighting', function () { - var book, PAGE; - - before(function() { - return books.generate('highlight', 'website', { - prepare: function(_book) { - book = _book; - - var plugin = new Plugin(book, 'replace_highlight'); - plugin.load('./replace_highlight', PLUGINS_ROOT); - - book.plugins.load(plugin); - } - }) - .then(function() { - PAGE = fs.readFileSync( - path.join(book.options.output, 'index.html'), - { encoding: 'utf-8' } - ); - }); - }); - - it('should correctly replace highlighting', function() { - PAGE.should.be.html({ - 'code': { - index: 0, - text: 'code_test 1\n_code' - } - }); - }); - - it('should correctly replace highlighting with language', function() { - PAGE.should.be.html({ - 'code': { - index: 1, - text: 'lang_test 2\n_lang' - } - }); - }); - - it('should correctly replace highlighting for inline code', function() { - PAGE.should.be.html({ - 'code': { - index: 2, - text: 'code_test 3_code' - } - }); - }); - - it('should correctly replace highlighting for inline code with html tags', function() { - PAGE.should.be.html({ - 'code': { - index: 3, - text: 'code_<test>_code' - } - }); - }); -}); - diff --git a/test/config.js b/test/config.js new file mode 100644 index 0000000..be28fc9 --- /dev/null +++ b/test/config.js @@ -0,0 +1,137 @@ +var should = require('should'); +var mock = require('./mock'); +var validator = require('../lib/config/validator'); + +describe('Configuration', function() { + + describe('Validation', function() { + it('should merge default', function() { + validator.validate({}).should.have.property('gitbook').which.equal('*'); + }); + + it('should throw error for invalid configuration', function() { + should.throws(function() { + validator.validate({ + direction: 'invalid' + }); + }); + }); + + it('should not throw error for non existing configuration', function() { + validator.validate({ + style: { + 'pdf': 'test.css' + } + }); + }); + + it('should validate plugins as an array', function() { + validator.validate({ + plugins: ['hello'] + }); + }); + + it('should validate plugins as a string', function() { + validator.validate({ + plugins: 'hello,world' + }); + }); + + }); + + describe('No configuration', function() { + var book; + + before(function() { + return mock.setupDefaultBook() + .then(function(_book) { + book = _book; + return book.config.load(); + }); + }); + + it('should signal that configuration is not defined', function() { + book.config.exists().should.not.be.ok(); + }); + }); + + describe('JSON file', function() { + var book; + + before(function() { + return mock.setupDefaultBook({ + 'book.json': { title: 'Hello World' } + }) + .then(function(_book) { + book = _book; + return book.config.load(); + }); + }); + + it('should correctly extend configuration', function() { + book.config.get('title', '').should.equal('Hello World'); + }); + }); + + describe('JS file', function() { + var book; + + before(function() { + return mock.setupDefaultBook({ + 'book.js': 'module.exports = { title: "Hello World" };' + }) + .then(function(_book) { + book = _book; + return book.config.load(); + }); + }); + + it('should correctly extend configuration', function() { + book.config.get('title', '').should.equal('Hello World'); + }); + }); + + describe('Multilingual', function() { + var book; + + before(function() { + return mock.setupDefaultBook({ + 'book.json': { + title: 'Hello World', + pluginsConfig: { + 'test': { + 'hello': true + } + } + }, + 'LANGS.md': '# Languages\n\n' + + '* [en](./en)\n' + + '* [fr](./fr)\n\n', + 'en/README.md': '# Hello', + 'fr/README.md': '# Bonjour', + 'en/book.json': { description: 'In english' }, + 'fr/book.json': { description: 'En francais' } + }) + .then(function(_book) { + book = _book; + return book.parse(); + }); + }); + + it('should correctly extend configuration', function() { + book.config.get('title', '').should.equal('Hello World'); + book.config.get('description', '').should.equal(''); + + var en = book.books[0]; + en.config.get('title', '').should.equal('Hello World'); + en.config.get('description', '').should.equal('In english'); + en.config.get('pluginsConfig.test.hello').should.equal(true); + + var fr = book.books[1]; + fr.config.get('title', '').should.equal('Hello World'); + fr.config.get('description', '').should.equal('En francais'); + fr.config.get('pluginsConfig.test.hello').should.equal(true); + }); + }); +}); + diff --git a/test/configuration.js b/test/configuration.js deleted file mode 100644 index d30fd61..0000000 --- a/test/configuration.js +++ /dev/null @@ -1,37 +0,0 @@ -describe('Configuration', function () { - it('should extract default title from README', function() { - return books.parse('basic') - .then(function(book) { - book.options.title.should.be.equal('Readme'); - }); - }); - - it('should extract default description from README', function() { - return books.parse('basic') - .then(function(book) { - book.options.description.should.be.equal('Default description for the book.'); - }); - }); - - it('should correctly load from json (book.json)', function() { - return books.parse('config-json') - .then(function(book) { - book.options.title.should.be.equal('json-config'); - }); - }); - - it('should correctly load from JavaScript (book.js)', function() { - return books.parse('config-js') - .then(function(book) { - book.options.title.should.be.equal('js-config'); - }); - }); - - it('should provide configuration on book.config.get', function() { - return books.parse('basic') - .then(function(book) { - book.config.get('description').should.be.equal('Default description for the book.'); - book.getConfig('description').should.be.equal('Default description for the book.'); - }); - }); -}); diff --git a/test/conrefs.js b/test/conrefs.js index 32e4058..6500709 100644 --- a/test/conrefs.js +++ b/test/conrefs.js @@ -1,68 +1,49 @@ -var fs = require('fs'); -var path = require('path'); +var mock = require('./mock'); +var ConrefsLoader = require('../lib/output/conrefs')(); -describe('ConRefs', function () { - var book, readme; + +describe('Conrefs Loader', function() { + var output; before(function() { - return books.generate('conrefs', 'website') - .then(function(_book) { - book = _book; + return mock.outputDefaultBook(ConrefsLoader, { + 'test.md': 'World' + }) + .then(function(_output) { + output = _output; + }); + }); + - readme = fs.readFileSync( - path.join(book.options.output, 'index.html'), - { encoding: 'utf-8' } - ); - }); + it('should include a local file', function() { + return output.template.renderString('Hello {% include "./test.md" %}') + .should.be.fulfilledWith('Hello World'); }); - it('should handle local references', function() { - readme.should.be.html({ - '.page-inner p#t1': { - count: 1, - text: 'Hello World', - trim: true - } - }); + it('should include a git url', function() { + return output.template.renderString('Hello {% include "./test.md" %}') + .should.be.fulfilledWith('Hello World'); }); - it('should handle local references with absolute paths', function() { - readme.should.be.html({ - '.page-inner p#t2': { - count: 1, - text: 'Hello World', - trim: true - } - }); + it('should reject file out of scope', function() { + return output.template.renderString('Hello {% include "../test.md" %}') + .should.be.rejected(); }); - it('should correctly include file from git reference', function() { - readme.should.be.html({ - '.page-inner p#t3': { - count: 1, - text: 'Hello from git', - trim: true - } + describe('Git Urls', function() { + it('should include a file from a git repo', function() { + return output.template.renderString('{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md" %}') + .should.be.fulfilledWith('Hello from git'); }); - }); - it('should correctly handle deep include in git reference', function() { - readme.should.be.html({ - '.page-inner p#t4': { - count: 1, - text: 'First Hello. Hello from git', - trim: true - } + it('should handle deep inclusion (1)', function() { + return output.template.renderString('{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test2.md" %}') + .should.be.fulfilledWith('First Hello. Hello from git'); }); - }); - it('should correctly handle absolute include in git reference', function() { - readme.should.be.html({ - '.page-inner p#t5': { - count: 1, - text: 'First Hello. Hello from git', - trim: true - } + it('should handle deep inclusion (2)', function() { + return output.template.renderString('{% include "git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test3.md" %}') + .should.be.fulfilledWith('First Hello. Hello from git'); }); }); }); diff --git a/test/ebook.js b/test/ebook.js deleted file mode 100644 index 5caf28b..0000000 --- a/test/ebook.js +++ /dev/null @@ -1,70 +0,0 @@ -var fs = require('fs'); -var path = require('path'); - -describe('eBook generator', function () { - describe('Basic Book', function() { - var book; - - before(function() { - return books.generate('basic', 'ebook') - .then(function(_book) { - book = _book; - }); - }); - - it('should correctly output a SUMMARY.html', function() { - book.should.have.file('SUMMARY.html'); - }); - - it('should correctly copy assets', function() { - book.should.have.file('gitbook'); - book.should.have.file('gitbook/ebook.css'); - }); - - it('should not copy website assets', function() { - book.should.not.have.file('gitbook/style.css'); - }); - }); - - describe('Custom styles', function() { - var book; - - before(function() { - return books.generate('style-print', 'ebook') - .then(function(_book) { - book = _book; - }); - }); - - it('should correctly copy print.css', function() { - book.should.have.file('styles'); - book.should.have.file('styles/print.css'); - }); - - it('should remove default print.css', function() { - var PAGE = fs.readFileSync( - path.join(book.options.output, 'index.html'), - { encoding: 'utf-8' } - ); - - // There are 2 styles (one from plugin-highlight and the new style) - PAGE.should.be.html({ - 'link': { - count: 2 - } - }); - - PAGE.should.be.html({ - 'link[href=\'./styles/print.css\']': { - count: 1 - } - }); - - PAGE.should.be.html({ - 'link[href="gitbook/plugins/gitbook-plugin-highlight/ebook.css"]': { - count: 1 - } - }); - }); - }); -}); diff --git a/test/format.js b/test/format.js deleted file mode 100644 index 2ec1a6f..0000000 --- a/test/format.js +++ /dev/null @@ -1,11 +0,0 @@ -describe('Formatting', function () { - it('should provide formatting with book.formatString', function() { - return books.parse('basic') - .then(function(book) { - return book.formatString('markdown', 'this is a **test**'); - }) - .then(function(content) { - content.should.equal('<p>this is a <strong>test</strong></p>\n'); - }); - }); -}); diff --git a/test/git.js b/test/git.js index 6fd6b41..9fd7490 100644 --- a/test/git.js +++ b/test/git.js @@ -1,31 +1,61 @@ -var should = require("should"); -var git = require("../lib/utils/git"); +var should = require('should'); +var path = require('path'); +var os = require('os'); -describe("GIT parser and getter", function () { - it("should correctly parse an https url", function() { - var parts = git.parseUrl("git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md"); +var Git = require('../lib/utils/git'); - should.exist(parts); - parts.host.should.be.equal("https://gist.github.com/69ea4542e4c8967d2fa7.git"); - parts.ref.should.be.equal("master"); - parts.filepath.should.be.equal("test.md"); - }); +describe('Git', function() { - it("should correctly parse an https url with a reference", function() { - var parts = git.parseUrl("git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md#0.1.2"); + describe('URL parsing', function() { - should.exist(parts); - parts.host.should.be.equal("https://gist.github.com/69ea4542e4c8967d2fa7.git"); - parts.ref.should.be.equal("0.1.2"); - parts.filepath.should.be.equal("test.md"); - }); + it('should correctly validate git urls', function() { + // HTTPS + Git.isUrl('git+https://github.com/Hello/world.git').should.be.ok(); + + // SSH + Git.isUrl('git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1').should.be.ok; + + // Non valid + Git.isUrl('https://github.com/Hello/world.git').should.not.be.ok(); + Git.isUrl('README.md').should.not.be.ok(); + }); + + it('should parse HTTPS urls', function() { + var parts = Git.parseUrl('git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md'); + + should.exist(parts); + parts.host.should.be.equal('https://gist.github.com/69ea4542e4c8967d2fa7.git'); + should(parts.ref).be.equal(null); + parts.filepath.should.be.equal('test.md'); + }); - it("should correctly parse an ssh url", function() { - var parts = git.parseUrl("git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1"); + it('should parse HTTPS urls with a reference', function() { + var parts = Git.parseUrl('git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md#1.0.0'); - should.exist(parts); - parts.host.should.be.equal("git@github.com:GitbookIO/gitbook.git"); - parts.ref.should.be.equal("e1594cde2c32e4ff48f6c4eff3d3d461743d74e1"); - parts.filepath.should.be.equal("directory/README.md"); + should.exist(parts); + parts.host.should.be.equal('https://gist.github.com/69ea4542e4c8967d2fa7.git'); + parts.ref.should.be.equal('1.0.0'); + parts.filepath.should.be.equal('test.md'); + }); + + it('should parse SSH urls', function() { + var parts = Git.parseUrl('git+git@github.com:GitbookIO/gitbook.git/directory/README.md#e1594cde2c32e4ff48f6c4eff3d3d461743d74e1'); + + should.exist(parts); + parts.host.should.be.equal('git@github.com:GitbookIO/gitbook.git'); + parts.ref.should.be.equal('e1594cde2c32e4ff48f6c4eff3d3d461743d74e1'); + parts.filepath.should.be.equal('directory/README.md'); + }); }); + + describe('Cloning and resolving', function() { + it('should clone an HTTPS url', function() { + var git = new Git(path.join(os.tmpdir(), 'test-git-'+Date.now())); + return git.resolve('git+https://gist.github.com/69ea4542e4c8967d2fa7.git/test.md') + .then(function(filename) { + path.extname(filename).should.equal('.md'); + }); + }); + }); + }); diff --git a/test/glossary.js b/test/glossary.js index f0f31a5..d6d1af6 100644 --- a/test/glossary.js +++ b/test/glossary.js @@ -1,87 +1,71 @@ -var fs = require("fs"); -var path = require("path"); +var should = require('should'); +var mock = require('./mock'); -describe("Glossary", function () { - describe("Parsing", function() { - var book; +describe('Glossary', function() { + it('should parse empty glossary', function() { + return mock.setupDefaultBook({ + 'GLOSSARY.md': '' + }) + .then(function(book) { + return book.config.load() - before(function() { - return books.parse("glossary") - .then(function(_book) { - book = _book; - }); - }); - - it("should correctly list items", function() { - book.should.have.property("glossary"); - book.glossary.should.have.lengthOf(3); + .then(function() { + return book.glossary.load(); + }) + .then(function() { + book.glossary.isEmpty().should.be.true(); + }); }); }); - describe("Generation", function() { + describe('Non-empty glossary', function() { var book; before(function() { - return books.generate("glossary", "website") - .then(function(_book) { - book = _book; - }); + return mock.setupDefaultBook({ + 'GLOSSARY.md': '# Glossary\n\n## Hello World\n\nThis is an entry' + }) + .then(function(_book) { + book = _book; + return book.config.load(); + }) + .then(function() { + return book.glossary.load(); + }); }); - it("should correctly generate a GLOSSARY.html", function() { - book.should.have.file("GLOSSARY.html"); + it('should not be empty', function() { + book.glossary.isEmpty().should.be.false(); }); - describe("Page Integration", function() { - var readme, page; - - before(function() { - readme = fs.readFileSync( - path.join(book.options.output, "index.html"), - { encoding: "utf-8" } - ); - page = fs.readFileSync( - path.join(book.options.output, "folder/PAGE.html"), - { encoding: "utf-8" } - ); - }); + describe('glossary.get', function() { + it('should return an existing entry', function() { + var entry = book.glossary.get('hello_world'); + should.exist(entry); - it("should correctly replaced terms by links", function() { - readme.should.be.html({ - ".page-inner a[href=\"GLOSSARY.html#test\"]": { - count: 1, - text: "test", - attributes: { - title: "Just a simple and easy to understand test." - } - } - }); + entry.name.should.equal('Hello World'); + entry.description.should.equal('This is an entry'); + entry.id.should.equal('hello_world'); }); - it("should correctly replaced terms by links (relative)", function() { - page.should.be.html({ - ".page-inner a[href=\"../GLOSSARY.html#test\"]": { - count: 1 - } - }); + it('should undefined return non existing entry', function() { + var entry = book.glossary.get('cool'); + should.not.exist(entry); }); + }); - it("should not replace terms in codeblocks", function() { - readme.should.be.html({ - ".page-inner code a": { - count: 0 - } - }); + describe('glossary.find', function() { + it('should return an existing entry', function() { + var entry = book.glossary.find('HeLLo World'); + should.exist(entry); + entry.id.should.equal('hello_world'); }); - it("should correctly select the longest term", function() { - readme.should.be.html({ - ".page-inner a[href=\"GLOSSARY.html#test_long\"]": { - count: 1, - text: "test long" - } - }); + it('should return undefined for non existing entry', function() { + var entry = book.glossary.find('Hello'); + should.not.exist(entry); }); }); }); }); + diff --git a/test/heading.js b/test/heading.js deleted file mode 100644 index f6d65c3..0000000 --- a/test/heading.js +++ /dev/null @@ -1,37 +0,0 @@ -var path = require('path'); -var fs = require('fs'); - -describe('Headings', function () { - var book, PAGE; - - before(function() { - return books.generate('headings', 'website') - .then(function(_book) { - book = _book; - - PAGE = fs.readFileSync( - path.join(book.options.output, 'index.html'), - { encoding: 'utf-8' } - ); - }); - }); - - describe('IDs', function() { - it('should correctly generate an ID', function() { - PAGE.should.be.html({ - 'h1#hello-world': { - count: 1 - } - }); - }); - - it('should correctly accept custom ID', function() { - PAGE.should.be.html({ - 'h2#hello-custom': { - count: 1 - } - }); - }); - }); -}); - diff --git a/test/helper.js b/test/helper.js deleted file mode 100644 index bbe82de..0000000 --- a/test/helper.js +++ /dev/null @@ -1,78 +0,0 @@ -var os = require('os'); -var path = require('path'); -var Q = require('q'); -var _ = require('lodash'); - -var fsUtil = require('../lib/utils/fs'); -var Book = require('../').Book; -var LOG_LEVELS = require('../').LOG_LEVELS; - -require('./assertions'); - - -var BOOKS = {}; -var TMPDIR = os.tmpdir(); - - -// Generate and return a book -function generateBook(bookId, test, opts) { - opts = _.defaults(opts || {}, { - prepare: function() {} - }); - - return parseBook(bookId, test, opts) - .then(function(book) { - - return Q(opts.prepare(book)) - .then(function() { - return book.generate(test); - }) - .thenResolve(book); - }); -} - -// Generate and return a book -function parseBook(bookId, test, opts) { - opts = _.defaults(opts || {}, { - testId: '' - }); - - test = test || 'website'; - var testId = [test, opts.testId].join('-'); - - BOOKS[bookId] = BOOKS[bookId] || {}; - if (BOOKS[bookId][testId]) return Q(BOOKS[bookId][testId]); - - BOOKS[bookId][testId] = new Book(path.resolve(__dirname, 'books', bookId), { - logLevel: LOG_LEVELS.DISABLED, - config: { - output: path.resolve(TMPDIR, bookId+'-'+testId) - } - }); - - return BOOKS[bookId][testId].parse() - .then(function() { - return BOOKS[bookId][testId]; - }); -} - - -global.books = { - parse: parseBook, - generate: generateBook -}; - -// Cleanup all tests -after(function() { - return _.chain(BOOKS) - .map(function(types) { - return _.values(types); - }) - .flatten() - .reduce(function(prev, book) { - return prev.then(function() { - return fsUtil.remove(book.options.output); - }); - }, Q()) - .value(); -}); diff --git a/test/images.js b/test/images.js deleted file mode 100644 index de45066..0000000 --- a/test/images.js +++ /dev/null @@ -1,58 +0,0 @@ -var fs = require("fs"); -var _ = require("lodash"); -var path = require("path"); -var cheerio = require("cheerio"); - -describe("Images", function () { - var book, readme, $, $img, srcs; - - before(function() { - return books.generate("images", "ebook") - .then(function(_book) { - book = _book; - - readme = fs.readFileSync( - path.join(book.options.output, "index.html"), - { encoding: "utf-8" } - ); - $ = cheerio.load(readme); - $img = $("img"); - srcs = $img.map(function() { - return $(this).attr("src"); - }); - }); - }); - - it("should detect all images", function() { - _.uniq(srcs).should.have.lengthOf(4); - }); - - it("should keep image tags", function() { - srcs.should.have.lengthOf(5); - }); - - it("should not have .svg files", function() { - _.each(srcs, function(src) { - path.extname(src).should.not.equal(".svg"); - }); - }); - - it("should correctly convert svg images to png", function() { - _.each(srcs, function(src) { - book.should.have.file(src); - }); - }); - - it("should handle relative paths", function() { - var PAGE = fs.readFileSync( - path.join(book.options.output, "folder/PAGE.html"), - { encoding: "utf-8" } - ); - - PAGE.should.be.html({ - "img[src=\"../test.png\"]": { - count: 1 - } - }); - }); -}); diff --git a/test/init.js b/test/init.js deleted file mode 100644 index 625d77c..0000000 --- a/test/init.js +++ /dev/null @@ -1,24 +0,0 @@ -var fs = require('fs'); -var path = require('path'); -var should = require('should'); - -var Book = require('../').Book; -var LOG_LEVELS = require('../').LOG_LEVELS; - -describe('Init Books', function () { - var initRoot; - - before(function() { - initRoot = path.resolve(__dirname, 'books/init'); - return Book.init(initRoot, { - logLevel: LOG_LEVELS.DISABLED - }); - }); - - it('should create all chapters', function() { - should(fs.existsSync(path.resolve(initRoot, 'hello.md'))).be.ok(); - should(fs.existsSync(path.resolve(initRoot, 'hello2.md'))).be.ok(); - should(fs.existsSync(path.resolve(initRoot, 'hello3/hello4.md'))).be.ok(); - should(fs.existsSync(path.resolve(initRoot, 'hello3/hello5/hello6.md'))).be.ok(); - }); -}); diff --git a/test/json.js b/test/json.js deleted file mode 100644 index 60baf9a..0000000 --- a/test/json.js +++ /dev/null @@ -1,92 +0,0 @@ -var fs = require('fs'); -var path = require('path'); - -describe('JSON generator', function () { - describe('Basic Book', function() { - var book; - - before(function() { - return books.generate('basic', 'json') - .then(function(_book) { - book = _book; - }); - }); - - it('should correctly output a README.json', function() { - book.should.have.file('README.json'); - }); - - it('should output a valid json', function() { - book.should.have.jsonfile('README.json'); - }); - - describe('Page Format', function() { - var page; - - before(function() { - page = JSON.parse( - fs.readFileSync( - path.join(book.options.output, 'README.json'), - { encoding: 'utf-8' } - ) - ); - }); - - it('should contains valid section', function() { - page.should.have.property('sections').with.lengthOf(1); - page.sections[0].should.have.property('content').which.is.a.String(); - page.sections[0].should.have.property('type', 'normal'); - }); - - it('should contains valid progress', function() { - page.should.have.property('progress'); - page.progress.should.have.property('chapters').with.lengthOf(1); - page.progress.should.have.property('current'); - }); - - it('should contains no languages', function() { - page.should.have.property('langs').with.lengthOf(0); - }); - }); - }); - - describe('Multilingual Book', function() { - var book; - - before(function() { - return books.generate('languages', 'json') - .then(function(_book) { - book = _book; - }); - }); - - it('should correctly output READMEs', function() { - book.should.have.file('README.json'); - book.should.have.file('en/README.json'); - book.should.have.file('fr/README.json'); - }); - - it('should output valid json', function() { - book.should.have.jsonfile('README.json'); - book.should.have.jsonfile('en/README.json'); - book.should.have.jsonfile('fr/README.json'); - }); - - describe('Page Format', function() { - var page; - - before(function() { - page = JSON.parse( - fs.readFileSync( - path.join(book.options.output, 'README.json'), - { encoding: 'utf-8' } - ) - ); - }); - - it('should contains no languages', function() { - page.should.have.property('langs').with.lengthOf(2); - }); - }); - }); -}); diff --git a/test/langs.js b/test/langs.js new file mode 100644 index 0000000..91dbd5a --- /dev/null +++ b/test/langs.js @@ -0,0 +1,46 @@ +var mock = require('./mock'); + +describe('Langs', function() { + it('should parse empty langs', function() { + return mock.setupDefaultBook({ + 'LANGS.md': '' + }) + .then(function(book) { + return book.config.load() + + .then(function() { + return book.langs.load(); + }) + + .then(function() { + book.langs.count().should.equal(0); + }); + }); + }); + + describe('Non-empty languages list', function() { + var book; + + before(function() { + return mock.setupDefaultBook({ + 'LANGS.md': '# Languages\n\n' + + '* [en](./en)\n' + + '* [fr](./fr)\n\n' + }) + .then(function(_book) { + book = _book; + + return book.langs.load(); + }); + }); + + it('should correctly count languages', function() { + book.langs.count().should.equal(2); + }); + + it('should correctly define book as multilingual', function() { + book.isMultilingual().should.equal(true); + }); + }); +}); + diff --git a/test/languages.js b/test/languages.js deleted file mode 100644 index 0bde347..0000000 --- a/test/languages.js +++ /dev/null @@ -1,37 +0,0 @@ -describe("Languages", function () { - describe("Parsing", function() { - var book; - - before(function() { - return books.parse("languages") - .then(function(_book) { - book = _book; - }); - }); - - it("should correctly list languages", function() { - book.should.have.property("books"); - book.books.should.have.lengthOf(2); - - book.books[0].options.language.should.be.equal("en"); - book.books[1].options.language.should.be.equal("fr"); - }); - }); - - describe("Generation", function() { - var book; - - before(function() { - return books.generate("languages", "website") - .then(function(_book) { - book = _book; - }); - }); - - it("should correctly create books", function() { - book.should.have.file("index.html"); - book.should.have.file("en/index.html"); - book.should.have.file("fr/index.html"); - }); - }); -}); diff --git a/test/links.js b/test/links.js deleted file mode 100644 index baca9d1..0000000 --- a/test/links.js +++ /dev/null @@ -1,63 +0,0 @@ -var fs = require("fs"); -var path = require("path"); -var cheerio = require("cheerio"); - -var links = require("../lib/utils/links"); - -describe("Links", function () { - it("should correctly test external links", function() { - links.isExternal("http://google.fr").should.be.exactly(true); - links.isExternal("https://google.fr").should.be.exactly(true); - links.isExternal("test.md").should.be.exactly(false); - links.isExternal("folder/test.md").should.be.exactly(false); - links.isExternal("/folder/test.md").should.be.exactly(false); - }); - - it("should correctly detect anchor links", function() { - links.isAnchor("#test").should.be.exactly(true); - links.isAnchor(" #test").should.be.exactly(true); - links.isAnchor("https://google.fr#test").should.be.exactly(false); - links.isAnchor("test.md#test").should.be.exactly(false); - }); - - describe("toAbsolute", function() { - it("should correctly transform as absolute", function() { - links.toAbsolute("http://google.fr").should.be.equal("http://google.fr"); - links.toAbsolute("test.md", "./", "./").should.be.equal("test.md"); - links.toAbsolute("folder/test.md", "./", "./").should.be.equal("folder/test.md"); - }); - - it("should correctly handle windows path", function() { - links.toAbsolute("folder\\test.md", "./", "./").should.be.equal("folder/test.md"); - }); - - it("should correctly handle absolute path", function() { - links.toAbsolute("/test.md", "./", "./").should.be.equal("test.md"); - links.toAbsolute("/test.md", "test", "test").should.be.equal("../test.md"); - links.toAbsolute("/sub/test.md", "test", "test").should.be.equal("../sub/test.md"); - }); - }); - - describe("page", function() { - var book; - - before(function() { - return books.generate("links", "website") - .then(function(_book) { - book = _book; - }); - }); - - it("should correctly replace relative links", function() { - var readme = fs.readFileSync( - path.join(book.options.output, "folder1/index.html"), - { encoding: "utf-8" } - ); - var $ = cheerio.load(readme); - var $a = $(".page-inner a"); - - $a.attr("href").should.be.exactly("../folder2/index.html"); - }); - }); - -}); diff --git a/test/locate.js b/test/locate.js new file mode 100644 index 0000000..609f2da --- /dev/null +++ b/test/locate.js @@ -0,0 +1,28 @@ +var path = require('path'); + +var Book = require('../').Book; +var mock = require('./mock'); + +describe('Locate', function() { + it('should use root folder if no .gitbook', function() { + return mock.setupFS({ + 'README.md': '# Hello' + }) + .then(function(root) { + return Book.locate(mock.fs, root) + .should.be.fulfilledWith(root); + }); + }); + + it('should use resolve using .gitbook', function() { + return mock.setupFS({ + 'README.md': '# Hello', + '.gitbook': './docs' + }) + .then(function(root) { + return Book.locate(mock.fs, root) + .should.be.fulfilledWith(path.resolve(root, 'docs')); + }); + }); + +}); diff --git a/test/location.js b/test/location.js new file mode 100644 index 0000000..3e2294e --- /dev/null +++ b/test/location.js @@ -0,0 +1,55 @@ +var location = require('../lib/utils/location'); + +describe('Location', function() { + it('should correctly test external location', function() { + location.isExternal('http://google.fr').should.be.exactly(true); + location.isExternal('https://google.fr').should.be.exactly(true); + location.isExternal('test.md').should.be.exactly(false); + location.isExternal('folder/test.md').should.be.exactly(false); + location.isExternal('/folder/test.md').should.be.exactly(false); + }); + + it('should correctly detect anchor location', function() { + location.isAnchor('#test').should.be.exactly(true); + location.isAnchor(' #test').should.be.exactly(true); + location.isAnchor('https://google.fr#test').should.be.exactly(false); + location.isAnchor('test.md#test').should.be.exactly(false); + }); + + describe('.relative', function() { + it('should resolve to a relative path (same folder)', function() { + location.relative('links/', 'links/test.md').should.equal('test.md'); + }); + + it('should resolve to a relative path (parent folder)', function() { + location.relative('links/', 'test.md').should.equal('../test.md'); + }); + + it('should resolve to a relative path (child folder)', function() { + location.relative('links/', 'links/hello/test.md').should.equal('hello/test.md'); + }); + }); + + describe('.toAbsolute', function() { + it('should correctly transform as absolute', function() { + location.toAbsolute('http://google.fr').should.be.equal('http://google.fr'); + location.toAbsolute('test.md', './', './').should.be.equal('test.md'); + location.toAbsolute('folder/test.md', './', './').should.be.equal('folder/test.md'); + }); + + it('should correctly handle windows path', function() { + location.toAbsolute('folder\\test.md', './', './').should.be.equal('folder/test.md'); + }); + + it('should correctly handle absolute path', function() { + location.toAbsolute('/test.md', './', './').should.be.equal('test.md'); + location.toAbsolute('/test.md', 'test', 'test').should.be.equal('../test.md'); + location.toAbsolute('/sub/test.md', 'test', 'test').should.be.equal('../sub/test.md'); + location.toAbsolute('/test.png', 'folder', '').should.be.equal('test.png'); + }); + + it('should correctly handle absolute path (windows)', function() { + location.toAbsolute('\\test.png', 'folder', '').should.be.equal('test.png'); + }); + }); +}); diff --git a/test/mock.js b/test/mock.js new file mode 100644 index 0000000..5ff9f63 --- /dev/null +++ b/test/mock.js @@ -0,0 +1,116 @@ +/* eslint-disable no-console */ + +var Q = require('q'); +var _ = require('lodash'); +var tmp = require('tmp'); +var path = require('path'); + +var Book = require('../').Book; +var NodeFS = require('../lib/fs/node'); +var fs = require('../lib/utils/fs'); + +require('./assertions'); + +// Create filesystem instance for testing +var nodeFS = new NodeFS(); + +function setupFS(files) { + return Q.nfcall(tmp.dir.bind(tmp)).get(0) + .then(function(rootFolder) { + return _.chain(_.pairs(files)) + .sortBy(0) + .reduce(function(prev, pair) { + return prev.then(function() { + var filename = path.resolve(rootFolder, pair[0]); + var buf = pair[1]; + + if (_.isObject(buf)) buf = JSON.stringify(buf); + if (_.isString(buf)) buf = new Buffer(buf, 'utf-8'); + + return fs.mkdirp(path.dirname(filename)) + .then(function() { + return fs.writeFile(filename, buf); + }); + }); + }, Q()) + .value() + .then(function() { + return rootFolder; + }); + }); +} + +// Setup a mock book for testing using a map of files +function setupBook(files, opts) { + opts = opts || {}; + opts.log = function() { }; + + return setupFS(files) + .then(function(folder) { + opts.fs = nodeFS; + opts.root = folder; + + return new Book(opts); + }); +} + +// Setup a book with default README/SUMMARY +function setupDefaultBook(files, summary, opts) { + var summaryContent = '# Summary \n\n' + + _.map(summary, function(article) { + return '* [' + article.title +'](' + article.path + ')'; + }) + .join('\n'); + + return setupBook(_.defaults(files || {}, { + 'README.md': 'Hello', + 'SUMMARY.md': summaryContent + }), opts); +} + +// Output a book with a specific generator +function outputDefaultBook(Output, files, summary, opts) { + return setupDefaultBook(files, summary, opts) + .then(function(book) { + // Parse the book + return book.parse() + + // Start generation + .then(function() { + var output = new Output(book); + return output.generate() + .thenResolve(output); + }); + }); +} + +// Output a book with a specific generator +function outputBook(Output, files, opts) { + return setupBook(files, opts) + .then(function(book) { + // Parse the book + return book.parse() + + // Start generation + .then(function() { + var output = new Output(book); + return output.generate() + .thenResolve(output); + }); + }); +} + +// Log an error +function logError(err) { + console.log(err.stack || err); +} + +module.exports = { + fs: nodeFS, + setupFS: setupFS, + setupBook: setupBook, + outputBook: outputBook, + setupDefaultBook: setupDefaultBook, + outputDefaultBook: outputDefaultBook, + logError: logError +}; diff --git a/test/navigation.js b/test/navigation.js deleted file mode 100644 index 9118b3c..0000000 --- a/test/navigation.js +++ /dev/null @@ -1,61 +0,0 @@ -var should = require("should"); - -describe("Navigation", function () { - var book; - - before(function() { - return books.parse("summary") - .then(function(_book) { - book = _book; - }); - }); - - it("should correctly parse navigation as a map", function() { - book.should.have.property("navigation"); - book.navigation.should.have.property("README.md"); - book.navigation.should.have.property("README.md"); - }); - - it("should correctly include filenames", function() { - book.navigation.should.have.property("README.md"); - book.navigation.should.have.property("PAGE1.md"); - book.navigation.should.have.property("folder/PAGE2.md"); - book.navigation.should.not.have.property("NOTFOUND.md"); - }); - - it("should correctly detect next/prev for README", function() { - var README = book.navigation["README.md"]; - - README.index.should.equal(0); - README.should.have.property("next"); - should(README.prev).not.be.ok(); - - README.next.should.have.property("path"); - README.next.path.should.equal("PAGE1.md"); - }); - - it("should correctly detect next/prev a page", function() { - var PAGE = book.navigation["PAGE1.md"]; - - PAGE.index.should.equal(1); - PAGE.should.have.property("next"); - PAGE.should.have.property("prev"); - - PAGE.prev.should.have.property("path"); - PAGE.prev.path.should.equal("README.md"); - - PAGE.next.should.have.property("path"); - PAGE.next.path.should.equal("folder/PAGE2.md"); - }); - - it("should correctly detect next/prev for last page", function() { - var PAGE = book.navigation["folder/PAGE2.md"]; - - PAGE.index.should.equal(2); - PAGE.should.have.property("prev"); - should(PAGE.next).not.be.ok(); - - PAGE.prev.should.have.property("path"); - PAGE.prev.path.should.equal("PAGE1.md"); - }); -}); diff --git a/test/node_modules/gitbook-plugin-test-blocks/index.js b/test/node_modules/gitbook-plugin-test-blocks/index.js new file mode 100644 index 0000000..7104006 --- /dev/null +++ b/test/node_modules/gitbook-plugin-test-blocks/index.js @@ -0,0 +1,21 @@ +var should = require('should'); + +module.exports = { + blocks: { + // Simple test + hello: function(blk) { + return 'Hello ' + blk.body + '!'; + }, + + // Test using the conetxt "this" + testContext: function(s) { + this.should.have.property('config'); + this.should.have.property('log'); + this.should.have.property('options'); + this.should.have.property('resolve').which.is.a.function; + this.should.have.property('book').which.equal(this); + + return 'Hello ' + s + '!'; + } + } +}; diff --git a/test/node_modules/gitbook-plugin-test-blocks/package.json b/test/node_modules/gitbook-plugin-test-blocks/package.json new file mode 100644 index 0000000..a756fb5 --- /dev/null +++ b/test/node_modules/gitbook-plugin-test-blocks/package.json @@ -0,0 +1,7 @@ +{ + "name": "gitbook-plugin-test-blocks", + "version": "1.0.0", + "engines": { + "gitbook": "*" + } +}
\ No newline at end of file diff --git a/test/plugins/config/index.js b/test/node_modules/gitbook-plugin-test-config/index.js index f053ebf..f053ebf 100644 --- a/test/plugins/config/index.js +++ b/test/node_modules/gitbook-plugin-test-config/index.js diff --git a/test/node_modules/gitbook-plugin-test-config/package.json b/test/node_modules/gitbook-plugin-test-config/package.json new file mode 100644 index 0000000..74bb953 --- /dev/null +++ b/test/node_modules/gitbook-plugin-test-config/package.json @@ -0,0 +1,21 @@ +{ + "name": "gitbook-plugin-test-config", + "version": "1.0.0", + "engines": { + "gitbook": "*" + }, + "gitbook": { + "properties": { + "myProperty": { + "type": "string", + "required": true, + "title": "This is a required property" + }, + "myDefaultProperty": { + "type": "string", + "default": "hello", + "title": "This is a required property" + } + } + } +}
\ No newline at end of file diff --git a/test/node_modules/gitbook-plugin-test-filters/index.js b/test/node_modules/gitbook-plugin-test-filters/index.js new file mode 100644 index 0000000..71f2752 --- /dev/null +++ b/test/node_modules/gitbook-plugin-test-filters/index.js @@ -0,0 +1,21 @@ +var should = require('should'); + +module.exports = { + filters: { + // Simple test + hello: function(s) { + return 'Hello ' + s + '!'; + }, + + // Test using the conetxt "this" + testContext: function(s) { + this.should.have.property('config'); + this.should.have.property('log'); + this.should.have.property('options'); + this.should.have.property('resolve').which.is.a.function; + this.should.have.property('book').which.equal(this); + + return 'Hello ' + s + '!'; + } + } +}; diff --git a/test/node_modules/gitbook-plugin-test-filters/package.json b/test/node_modules/gitbook-plugin-test-filters/package.json new file mode 100644 index 0000000..d555d06 --- /dev/null +++ b/test/node_modules/gitbook-plugin-test-filters/package.json @@ -0,0 +1,7 @@ +{ + "name": "gitbook-plugin-test-filters", + "version": "1.0.0", + "engines": { + "gitbook": "*" + } +}
\ No newline at end of file diff --git a/test/node_modules/gitbook-plugin-test-hooks/index.js b/test/node_modules/gitbook-plugin-test-hooks/index.js new file mode 100644 index 0000000..c0666ca --- /dev/null +++ b/test/node_modules/gitbook-plugin-test-hooks/index.js @@ -0,0 +1,16 @@ +var should = require('should'); + +module.exports = { + hooks: { + 'init': function() { + global._hooks = []; + global._hooks.push('init'); + }, + 'finish': function() { + global._hooks.push('finish'); + }, + 'finish:before': function() { + global._hooks.push('finish:before'); + } + } +}; diff --git a/test/node_modules/gitbook-plugin-test-hooks/package.json b/test/node_modules/gitbook-plugin-test-hooks/package.json new file mode 100644 index 0000000..adb12a8 --- /dev/null +++ b/test/node_modules/gitbook-plugin-test-hooks/package.json @@ -0,0 +1,7 @@ +{ + "name": "gitbook-plugin-test-hooks", + "version": "1.0.0", + "engines": { + "gitbook": "*" + } +}
\ No newline at end of file diff --git a/test/node_modules/gitbook-plugin-test-resources/index.js b/test/node_modules/gitbook-plugin-test-resources/index.js new file mode 100644 index 0000000..e95e411 --- /dev/null +++ b/test/node_modules/gitbook-plugin-test-resources/index.js @@ -0,0 +1,12 @@ +module.exports = { + book: { + assets: './assets', + js: [ + 'myfile.js', + 'https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js' + ], + css: [ + 'myfile.css' + ] + } +}; diff --git a/test/node_modules/gitbook-plugin-test-resources/package.json b/test/node_modules/gitbook-plugin-test-resources/package.json new file mode 100644 index 0000000..606de31 --- /dev/null +++ b/test/node_modules/gitbook-plugin-test-resources/package.json @@ -0,0 +1,7 @@ +{ + "name": "gitbook-plugin-test-resources", + "version": "1.0.0", + "engines": { + "gitbook": "*" + } +}
\ No newline at end of file diff --git a/test/output-ebook.js b/test/output-ebook.js new file mode 100644 index 0000000..c43fc55 --- /dev/null +++ b/test/output-ebook.js @@ -0,0 +1,33 @@ +var mock = require('./mock'); +var EbookOutput = require('../lib/output/ebook'); + +describe('Ebook Output', function() { + describe('Sample Book', function() { + var output; + + before(function() { + return mock.outputDefaultBook(EbookOutput) + .then(function(_output) { + output = _output; + }); + }); + + it('should correctly generate an index.html', function() { + output.should.have.file('index.html'); + }); + + it('should correctly generate a SUMMARY.html', function() { + output.should.have.file('index.html'); + }); + + it('should correctly copy assets', function() { + output.should.have.file('gitbook/ebook.css'); + }); + + it('should correctly copy plugins', function() { + output.should.have.file('gitbook/gitbook-plugin-highlight/ebook.css'); + }); + + }); + +}); diff --git a/test/output-json.js b/test/output-json.js new file mode 100644 index 0000000..1006dab --- /dev/null +++ b/test/output-json.js @@ -0,0 +1,49 @@ +var mock = require('./mock'); +var JSONOutput = require('../lib/output/json'); + +describe('JSON Output', function() { + + describe('Sample book', function() { + var output; + + before(function() { + return mock.outputDefaultBook(JSONOutput) + .then(function(_output) { + output = _output; + }); + }); + + it('should correctly generate a README.json', function() { + output.should.have.file('README.json'); + }); + + }); + + describe('Multilingual Book', function() { + var output; + + before(function() { + return mock.outputBook(JSONOutput, { + 'LANGS.md': '# Languages\n\n' + + '* [en](./en)\n' + + '* [fr](./fr)\n\n', + 'en/README.md': '# Hello', + 'fr/README.md': '# Bonjour' + + }) + .then(function(_output) { + output = _output; + }); + }); + + it('should correctly generate a README.json for each language', function() { + output.should.have.file('en/README.json'); + output.should.have.file('fr/README.json'); + }); + + it('should correctly generate a README.json for the whole book', function() { + output.should.have.file('README.json'); + }); + }); +}); + diff --git a/test/output-website.js b/test/output-website.js new file mode 100644 index 0000000..19459b3 --- /dev/null +++ b/test/output-website.js @@ -0,0 +1,99 @@ +var mock = require('./mock'); +var WebsiteOutput = require('../lib/output/website'); + +describe('Website Output', function() { + + describe('Sample Book', function() { + var output; + + before(function() { + return mock.outputDefaultBook(WebsiteOutput) + .then(function(_output) { + output = _output; + }); + }); + + it('should correctly generate an index.html', function() { + output.should.have.file('index.html'); + }); + + it('should correctly copy assets', function() { + output.should.have.file('gitbook/app.js'); + output.should.have.file('gitbook/images/favicon.ico'); + }); + + it('should correctly copy plugins', function() { + output.should.have.file('gitbook/gitbook-plugin-highlight/website.css'); + }); + }); + + describe('Book with chapters', function() { + var output; + + before(function() { + return mock.outputDefaultBook(WebsiteOutput, { + 'hello/README.md': '# Hello', + 'hello/test.md': '# Test' + }, [ + { + title: 'Hello', + path: 'hello/README.md' + }, + { + title: 'Test', + path: 'hello/test.md' + } + ]) + .then(function(_output) { + output = _output; + }); + }); + + it('should correctly generate an index.html', function() { + output.should.have.file('index.html'); + }); + + it('should correctly generate files in folder', function() { + output.should.have.file('hello/index.html'); + output.should.have.file('hello/test.html'); + }); + }); + + describe('Multilingual Book', function() { + var output; + + before(function() { + return mock.outputBook(WebsiteOutput, { + 'LANGS.md': '# Languages\n\n' + + '* [en](./en)\n' + + '* [fr](./fr)\n\n', + 'en/README.md': '# Hello', + 'fr/README.md': '# Bonjour' + + }) + .then(function(_output) { + output = _output; + }); + }); + + it('should correctly generate an index.html for each language', function() { + output.should.have.file('en/index.html'); + output.should.have.file('fr/index.html'); + }); + + it('should correctly copy assets', function() { + output.should.have.file('gitbook/app.js'); + }); + + it('should not copy assets for each language', function() { + output.should.have.not.file('en/gitbook/app.js'); + output.should.have.not.file('fr/gitbook/app.js'); + }); + + it('should correctly generate an index.html', function() { + output.should.have.file('index.html'); + }); + }); + +}); + diff --git a/test/page.js b/test/page.js new file mode 100644 index 0000000..ae36660 --- /dev/null +++ b/test/page.js @@ -0,0 +1,399 @@ +var mock = require('./mock'); +var Output = require('../lib/output/base'); + +describe('Page', function() { + var book, output; + + before(function() { + return mock.setupDefaultBook({ + 'README.md': ' # Hello World\n\nThis is a description', + 'heading.md': '# Hello\n\n## World', + 'description.md': '# This is a title\n\nThis is the short description.\n\nNot this one.', + 'frontmatter.md': '---\ndescription: Hello World\n---\n\n# This is a title\n\nThis is not the description', + + 'links.md': '[link](hello.md) [link 2](variables/page/next.md) [readme](README.md)', + 'links/relative.md': '[link](../hello.md) [link 2](/variables/page/next.md) [readme](../README.md)', + + 'images.md': '  ', + 'images/relative.md': ' ', + + 'annotations/simple.md': 'A magicien say abracadabra!', + 'annotations/code.md': 'A magicien say `abracadabra`!', + 'annotations/class.md': 'A magicien say <div class="no-glossary"><b>abracadabra</b>, right?</div>!', + + 'codes/simple.md': '```hello world```', + 'codes/lang.md': '```js\nhello world\n```', + 'codes/lang.adoc': '```js\nhello world\n```', + + 'folder/paths.md': '', + + 'variables/file/mtime.md': '{{ file.mtime }}', + 'variables/file/path.md': '{{ file.path }}', + 'variables/page/title.md': '{{ page.title }}', + 'variables/page/previous.md': '{{ page.previous.title }} {{ page.previous.path }}', + 'variables/page/next.md': '{{ page.next.title }} {{ page.next.path }}', + 'variables/page/dir/ltr.md': 'This is english: {{ page.dir }}', + 'variables/page/dir/rtl.md': 'بسيطة {{ page.dir }}', + 'variables/config/title.md': '{{ config.title}}', + + 'GLOSSARY.md': '# Glossary\n\n\n## abracadabra\n\nthis is the description' + }, [ + { + title: 'Test page.next', + path: 'variables/page/next.md' + }, + { + title: 'Test Variables', + path: 'variables/page/title.md' + }, + { + title: 'Test page.previous', + path: 'variables/page/previous.md' + } + ]) + .then(function(_book) { + book = _book; + output = new Output(book); + + return book.parse(); + }); + }); + + describe('.resolveLocal', function() { + it('should correctly resolve path to file', function() { + var page = book.addPage('heading.md'); + + page.resolveLocal('test.png').should.equal('test.png'); + page.resolveLocal('/test.png').should.equal('test.png'); + page.resolveLocal('test/hello.png').should.equal('test/hello.png'); + page.resolveLocal('/test/hello.png').should.equal('test/hello.png'); + }); + + it('should correctly resolve path to file (2)', function() { + var page = book.addPage('folder/paths.md'); + + page.resolveLocal('test.png').should.equal('folder/test.png'); + page.resolveLocal('/test.png').should.equal('test.png'); + page.resolveLocal('test/hello.png').should.equal('folder/test/hello.png'); + page.resolveLocal('/test/hello.png').should.equal('test/hello.png'); + }); + }); + + describe('.relative', function() { + it('should correctly resolve absolute path in the book', function() { + var page = book.addPage('heading.md'); + page.relative('/test.png').should.equal('test.png'); + page.relative('test.png').should.equal('test.png'); + + var page2 = book.addPage('folder/paths.md'); + page2.relative('/test.png').should.equal('../test.png'); + page2.relative('test.png').should.equal('../test.png'); + }); + }); + + describe('.resolve', function() { + var page; + + before(function() { + page = book.addPage('links/relative.md'); + }); + + it('should resolve to a relative path (same folder)', function() { + page.relative('links/test.md').should.equal('test.md'); + }); + + it('should resolve to a relative path (parent folder)', function() { + page.relative('test.md').should.equal('../test.md'); + page.relative('hello/test.md').should.equal('../hello/test.md'); + }); + + it('should resolve to a relative path (child folder)', function() { + page.relative('links/hello/test.md').should.equal('hello/test.md'); + }); + }); + + describe('Headings', function() { + it('should add a default ID to headings', function() { + var page = book.addPage('heading.md'); + + return page.toHTML(output) + .then(function() { + page.content.should.be.html({ + 'h1#hello': { + count: 1 + }, + 'h2#world': { + count: 1 + } + }); + }); + }); + }); + + describe('Description', function() { + it('should extratc page description from content', function() { + var page = book.addPage('description.md'); + + return page.toHTML(output) + .then(function() { + page.description.should.equal('This is the short description.'); + }); + }); + }); + + describe('Font-Matter', function() { + it('should extratc page description from front matter', function() { + var page = book.addPage('frontmatter.md'); + + return page.toHTML(output) + .then(function() { + page.description.should.equal('Hello World'); + }); + }); + }); + + describe('Code Blocks', function() { + var page; + + before(function() { + output.template.addBlock('code', function(blk) { + return (blk.kwargs.language || '') + blk.body + 'test'; + }); + }); + + it('should apply "code" block', function() { + page = book.addPage('codes/simple.md'); + return page.toHTML(output) + .should.be.fulfilledWith('<p><code>hello worldtest</code></p>\n'); + }); + + it('should add language as kwargs', function() { + page = book.addPage('codes/lang.md'); + return page.toHTML(output) + .should.be.fulfilledWith('<pre><code class="lang-js">jshello world\ntest</code></pre>\n'); + }); + + it('should add language as kwargs (asciidoc)', function() { + page = book.addPage('codes/lang.adoc'); + return page.toHTML(output) + .should.be.fulfilledWith('<div class="listingblock">\n<div class="content">\n<pre class="highlight"><code class="language-js" data-lang="js">jshello worldtest</code></pre>\n</div>\n</div>'); + }); + }); + + describe('Links', function() { + describe('From base directory', function() { + var page; + + before(function() { + page = book.addPage('links.md'); + return page.toHTML(output); + }); + + it('should replace links to page to .html', function() { + page.content.should.be.html({ + 'a[href="variables/page/next.html"]': { + count: 1 + } + }); + }); + + it('should use directory urls when file is a README', function() { + page.content.should.be.html({ + 'a[href="./"]': { + count: 1 + } + }); + }); + + it('should not replace links to file not in SUMMARY', function() { + page.content.should.be.html({ + 'a[href="hello.md"]': { + count: 1 + } + }); + }); + }); + + describe('From sub-directory', function() { + var page; + + before(function() { + page = book.addPage('links/relative.md'); + return page.toHTML(output); + }); + + it('should replace links to page to .html', function() { + page.content.should.be.html({ + 'a[href="../variables/page/next.html"]': { + count: 1 + } + }); + }); + + it('should use directory urls when file is a README', function() { + page.content.should.be.html({ + 'a[href="../"]': { + count: 1 + } + }); + }); + + it('should not replace links to file not in SUMMARY', function() { + page.content.should.be.html({ + 'a[href="../hello.md"]': { + count: 1 + } + }); + }); + }); + }); + + describe('Images', function() { + describe('From base directory', function() { + var page; + + before(function() { + page = book.addPage('images.md'); + return page.toHTML(output); + }); + + it('should resolve relative images', function() { + page.content.should.be.html({ + 'img[src="test.png"]': { + count: 1 + } + }); + }); + + it('should resolve absolute images', function() { + page.content.should.be.html({ + 'img[src="test2.png"]': { + count: 1 + } + }); + }); + + it('should keep external images path', function() { + page.content.should.be.html({ + 'img[src="https:/upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"]': { + count: 1 + } + }); + }); + }); + + describe('From sub-directory', function() { + var page; + + before(function() { + page = book.addPage('images/relative.md'); + return page.toHTML(output); + }); + + it('should resolve relative images', function() { + page.content.should.be.html({ + 'img[src="test.png"]': { + count: 1 + } + }); + }); + + it('should resolve absolute images', function() { + page.content.should.be.html({ + 'img[src="../test2.png"]': { + count: 1 + } + }); + }); + }); + }); + + describe('Templating Context', function() { + it('should set file.mtime', function() { + var page = book.addPage('variables/file/mtime.md'); + return page.toHTML(output) + .then(function(content) { + // A date ends with "(CET)" or "(UTC)"" + content.should.endWith(')</p>\n'); + }); + }); + + it('should set file.path', function() { + var page = book.addPage('variables/file/path.md'); + return page.toHTML(output) + .should.be.fulfilledWith('<p>variables/file/path.md</p>\n'); + }); + + it('should set page.title when page is in summary', function() { + var page = book.getPage('variables/page/title.md'); + return page.toHTML(output) + .should.be.fulfilledWith('<p>Test Variables</p>\n'); + }); + + it('should set page.previous when possible', function() { + var page = book.getPage('variables/page/previous.md'); + return page.toHTML(output) + .should.be.fulfilledWith('<p>Test Variables variables/page/title.md</p>\n'); + }); + + it('should set page.next when possible', function() { + var page = book.getPage('variables/page/next.md'); + return page.toHTML(output) + .should.be.fulfilledWith('<p>Test Variables variables/page/title.md</p>\n'); + }); + + it('should set config.title', function() { + var page = book.addPage('variables/config/title.md'); + return page.toHTML(output) + .should.be.fulfilledWith('<p>Hello World</p>\n'); + }); + + describe('page.dir', function() { + it('should detect ltr', function() { + var page = book.addPage('variables/page/dir/ltr.md'); + return page.toHTML(output) + .should.be.fulfilledWith('<p>This is english: ltr</p>\n'); + }); + + it('should detect rtl', function() { + var page = book.addPage('variables/page/dir/rtl.md'); + return page.toHTML(output) + .should.be.fulfilledWith('<p>بسيطة rtl</p>\n'); + }); + }); + }); + + describe('Annotations / Glossary', function() { + it('should replace glossary terms', function() { + return book.addPage('annotations/simple.md').toHTML(output) + .should.finally.be.html({ + '.glossary-term': { + count: 1, + text: 'abracadabra', + attributes: { + title: 'this is the description', + href: '../GLOSSARY.html#abracadabra' + } + } + }); + }); + + it('should not replace terms in code blocks', function() { + return book.addPage('annotations/code.md').toHTML(output) + .should.finally.be.html({ + '.glossary-term': { + count: 0 + } + }); + }); + + it('should not replace terms in ".no-glossary"', function() { + return book.addPage('annotations/class.md').toHTML(output) + .should.finally.be.html({ + '.glossary-term': { + count: 0 + } + }); + }); + }); +}); diff --git a/test/parse.js b/test/parse.js new file mode 100644 index 0000000..63565ee --- /dev/null +++ b/test/parse.js @@ -0,0 +1,59 @@ +var mock = require('./mock'); + +describe('Parsing', function() { + it('should not fail without SUMMARY', function() { + return mock.setupBook({ + 'README.md': '' + }) + .then(function(book) { + return book.parse().should.be.fulfilled(); + }); + }); + + it('should fail without README', function() { + return mock.setupBook({ + 'SUMMARY.md': '' + }) + .then(function(book) { + return book.parse().should.be.rejected; + }); + }); + + it('should add GLOSSARY as a page', function() { + return mock.setupDefaultBook({ + 'GLOSSARY.md': '' + }) + .then(function(book) { + return book.parse() + .then(function() { + book.hasPage('GLOSSARY.md').should.equal(true); + }); + }); + }); + + describe('Multilingual book', function() { + var book; + + before(function() { + return mock.setupBook({ + 'LANGS.md': '# Languages\n\n' + + '* [English](./en)\n' + + '* [French](./fr)\n\n', + 'en/README.md': '# English', + 'en/SUMMARY.md': '# Summary', + 'fr/README.md': '# French', + 'fr/SUMMARY.md': '# Summary' + }) + .then(function(_book) { + book = _book; + return book.parse(); + }); + }); + + it('should list language books', function() { + book.isMultilingual().should.equal(true); + book.books.should.have.lengthOf(2); + }); + }); +}); + diff --git a/test/paths.js b/test/paths.js new file mode 100644 index 0000000..339da51 --- /dev/null +++ b/test/paths.js @@ -0,0 +1,17 @@ +var path = require('path'); +var pathUtils = require('../lib/utils/path'); + +describe('Paths', function() { + + describe('setExtension', function() { + it('should correctly change extension of filename', function() { + pathUtils.setExtension('test.md', '.html').should.be.equal('test.html'); + pathUtils.setExtension('test.md', '.json').should.be.equal('test.json'); + }); + + it('should correctly change extension of path', function() { + pathUtils.setExtension('hello/test.md', '.html').should.be.equal(path.normalize('hello/test.html')); + pathUtils.setExtension('hello/test.md', '.json').should.be.equal(path.normalize('hello/test.json')); + }); + }); +}); diff --git a/test/plugins.js b/test/plugins.js index 1600d0d..4d9cdf1 100644 --- a/test/plugins.js +++ b/test/plugins.js @@ -1,84 +1,103 @@ var _ = require('lodash'); -var fs = require('fs'); -var should = require('should'); var path = require('path'); -var Plugin = require('../lib/plugin'); -var parsers = require('gitbook-parsers'); -var PLUGINS_ROOT = path.resolve(__dirname, 'plugins'); +var mock = require('./mock'); +var registry = require('../lib/plugins/registry'); +var Output = require('../lib/output/base'); +var PluginsManager = require('../lib/plugins'); +var BookPlugin = require('../lib/plugins/plugin'); -describe('Plugins', function () { +var PLUGINS_ROOT = path.resolve(__dirname, 'node_modules'); + +describe('Plugins', function() { var book; before(function() { - return books.parse('basic') + return mock.setupBook({}) .then(function(_book) { book = _book; }); }); - describe('Invalid', function() { - var plugin; - - before(function() { - plugin = new Plugin(book, 'invalid'); - plugin.load('./invalid', PLUGINS_ROOT); - }); - - it('should be detected', function() { - should(plugin.isValid()).be.exactly(false); + describe('Resolve Version', function() { + it('should resolve a plugin version', function() { + return registry.resolve('ga') + .should.be.fulfilled(); }); }); - describe('Empty', function() { - var plugin; - - before(function() { - plugin = new Plugin(book, 'empty'); - plugin.load('./empty', PLUGINS_ROOT); + describe('Installation', function() { + it('should install a plugin from NPM without a specific version', function() { + return registry.install(book, 'ga') + .should.be.fulfilled(); }); - it('should valid a plugin', function() { - should(plugin.isValid()).be.exactly(true); + it('should install a plugin from NPM with a specific version', function() { + return registry.install(book, 'ga', '1.0.0') + .should.be.fulfilled(); }); - it('should return an empty list of resources', function() { - return plugin.getResources() - .then(function(resources) { - _.each(Plugin.RESOURCES, function(resName) { - resources[resName].should.have.lengthOf(0); - }); + it('should correctly install all dependencies (if none)', function() { + return mock.setupBook({}) + .then(function(book) { + var plugins = new PluginsManager(book); + return plugins.install() + .should.be.fulfilledWith(0); }); }); - }); - 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 correctly install all dependencies (if any)', function() { + return mock.setupBook({ + 'book.json': { + plugins: ['ga'] + } + }) + .then(function(book) { + return book.config.load() + .then(function() { + var plugins = new PluginsManager(book); + return plugins.install(); + }); + }) + .should.be.fulfilledWith(1); }); + }); - 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'); + describe('Loading', function() { + it('should load default plugins', function() { + return mock.outputDefaultBook(Output) + .then(function(output) { + output.plugins.count().should.be.greaterThan(0); + }); }); + }); - it('should extend with default values', function() { - return plugin.validateConfig({ - testRequired: 12 + describe('Configuration', function() { + it('should fail loading a plugin with an invalid configuration', function() { + var plugin = new BookPlugin(book, 'test-config'); + return plugin.load(PLUGINS_ROOT) + .should.be.rejectedWith('Error with book\'s configuration: pluginsConfig.test-config.myProperty is required'); + }); + + it('should extend configuration with default properties', function() { + return mock.setupBook({ + 'book.json': { + pluginsConfig: { + 'test-config': { + 'myProperty': 'world' + } + } + } }) - .should.be.fulfilledWith({ - hello: 'world', - testRequired: 12 + .then(function(book2) { + return book2.config.load() + .then(function() { + var plugin = new BookPlugin(book2, 'test-config'); + return plugin.load(PLUGINS_ROOT); + }) + .then(function() { + book2.config.get('pluginsConfig.test-config.myDefaultProperty', '').should.equal('hello'); + }); }); }); }); @@ -87,243 +106,89 @@ describe('Plugins', function () { var plugin; before(function() { - plugin = new Plugin(book, 'resources'); - plugin.load('./resources', PLUGINS_ROOT); - - return book.plugins.load(plugin); - }); - - it('should valid a plugin', function() { - should(plugin.isValid()).be.exactly(true); + plugin = new BookPlugin(book, 'test-resources'); + return plugin.load(PLUGINS_ROOT); }); - describe('Website', function() { - it('should return a valid list of resources', function() { - return plugin.getResources('website') - .then(function(resources) { - resources.js.should.have.lengthOf(1); - }); - }); - it('should extend books plugins', function() { - var resources = book.plugins.resources('website'); - resources.js.should.have.lengthOf(5); - }); - }); - - describe('eBook', function() { - it('should return a valid list of resources', function() { - return plugin.getResources('ebook') - .then(function(resources) { - resources.css.should.have.lengthOf(1); - }); - }); + it('should list all resources for website', function() { + return plugin.getResources('website') + .then(function(resources) { + resources.assets.should.equal('./assets'); - it('should extend books plugins', function() { - var resources = book.plugins.resources('ebook'); + resources.js.should.have.lengthOf(2); + resources.js[0].path.should.equal('gitbook-plugin-test-resources/myfile.js'); + resources.js[1].url.should.equal('https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js'); - // There is resources from highlight plugin and this plugin - resources.css.should.have.lengthOf(2); - should.exist(_.find(resources.css, { - path: 'gitbook-plugin-resources/test' - })); + resources.css.should.have.lengthOf(1); + resources.css[0].path.should.equal('gitbook-plugin-test-resources/myfile.css'); }); }); }); describe('Filters', function() { - var plugin; + var plugin, filters; before(function() { - plugin = new Plugin(book, 'filters'); - plugin.load('./filters', PLUGINS_ROOT); + plugin = new BookPlugin(book, 'test-filters'); + return plugin.load(PLUGINS_ROOT) - return book.plugins.load(plugin); - }); - - it('should valid a plugin', function() { - should(plugin.isValid()).be.exactly(true); + .then(function() { + filters = plugin.getFilters(); + }); }); - it('should return a map of filters', function() { - var filters = plugin.getFilters(); - + it('should list all filters', function() { _.size(filters).should.equal(2); - filters.should.have.property('hello'); - filters.should.have.property('helloCtx'); }); - it('should correctly extend template filters', function() { - return book.template.renderString('{{ \'World\'|hello }}') - .then(function(content) { - content.should.equal('Hello World'); - }); + it('should correctly execute a filter', function() { + filters.hello('World').should.equal('Hello World!'); }); - it('should correctly set book as context', function() { - return book.template.renderString('{{ \'root\'|helloCtx }}') - .then(function(content) { - content.should.equal('root:'+book.root); - }); + it('should correctly set contexts for filter', function() { + filters.testContext('Hello'); }); }); describe('Blocks', function() { - var plugin; + var plugin, blocks; before(function() { - plugin = new Plugin(book, 'blocks'); - plugin.load('./blocks', PLUGINS_ROOT); - - return book.plugins.load(plugin); - }); - - var testTpl = function(str, args, options) { - return book.template.renderString(str, args, options) - .then(book.template.postProcess); - }; - - it('should valid a plugin', function() { - should(plugin.isValid()).be.exactly(true); - }); + plugin = new BookPlugin(book, 'test-blocks'); + return plugin.load(PLUGINS_ROOT) - it('should correctly extend template blocks', function() { - return testTpl('{% test %}hello{% endtest %}') - .then(function(content) { - content.should.equal('testhellotest'); - }); - }); - - describe('Shortcuts', function() { - it('should correctly accept shortcuts', function() { - return testTpl('$$hello$$', {}, { - type: 'markdown' - }) - .then(function(content) { - content.should.equal('testhellotest'); - }); - }); - - it('should correctly apply shortcuts to included file', function() { - return books.generate('conrefs', 'website', { - testId: 'include-plugins', - prepare: function(bookConref) { - plugin = new Plugin(bookConref, 'blocks'); - plugin.load('./blocks', PLUGINS_ROOT); - - return bookConref.plugins.load(plugin); - } - }) - .then(function(bookConref) { - var readme = fs.readFileSync( - path.join(bookConref.options.output, 'index.html'), - { encoding: 'utf-8' } - ); - - readme.should.be.html({ - '.page-inner p#test-plugin-block-shortcuts-1': { - count: 1, - text: 'testtest_block1test', - trim: true - }, - '.page-inner p#test-plugin-block-shortcuts-2': { - count: 1, - text: 'testtest_block2test', - trim: true - } - }); - }); + .then(function() { + blocks = plugin.getBlocks(); }); }); - - it('should correctly extend template blocks with defined end', function() { - return testTpl('{% test2 %}hello{% endtest2end %}') - .then(function(content) { - content.should.equal('test2hellotest2'); - }); + it('should list all blocks', function() { + _.size(blocks).should.equal(2); }); - it('should correctly extend template blocks with sub-blocks', function() { - return testTpl('{% test3join separator=";" %}hello{% also %}world{% endtest3join %}') - .then(function(content) { - content.should.equal('hello;world'); - }); + it('should correctly normalize block', function() { + blocks.hello.process({ body: 'World' }).should.equal('Hello World!'); }); - it('should correctly extend template blocks with different sub-blocks', function() { - return testTpl('{% test4join separator=";" %}hello{% also %}the{% finally %}world{% endtest4join %}') - .then(function(content) { - content.should.equal('hello;the;world'); - }); - }); - - it('should correctly extend template blocks with arguments (1)', function() { - return testTpl('{% test5args "a" %}{% endtest5args %}') - .then(function(content) { - content.should.equal('test5atest5'); - }); - }); - - it('should correctly extend template blocks with arguments (2)', function() { - return testTpl('{% test5args "a", "b" %}{% endtest5args %}') - .then(function(content) { - content.should.equal('test5a,btest5'); - }); - }); - - it('should correctly extend template blocks with arguments (3)', function() { - return testTpl('{% test5args "a", "b", "c" %}{% endtest5args %}') - .then(function(content) { - content.should.equal('test5a,b,ctest5'); - }); - }); - - it('should correctly extend template blocks with args and kwargs', function() { - return testTpl('{% test5kwargs "a", "b", "c", d="test", e="test2" %}{% endtest5kwargs %}') - .then(function(content) { - content.should.equal('test5a,b,c,d:test,e:test2,__keywords:truetest5'); - }); - }); - - it('should correctly extend template blocks with access to context', function() { - return testTpl('{% set name = "john" %}{% test6context %}{% endtest6context %}', {}) - .then(function(content) { - content.should.equal('test6johntest6'); - }); + it('should correctly set contexts for filter', function() { + blocks.testContext.process({ body: 'Hello' }); }); }); - describe('Blocks without parsing', function() { + describe('Hooks', function() { var plugin; before(function() { - plugin = new Plugin(book, 'blocks'); - plugin.load('./blocks', PLUGINS_ROOT); - - return book.plugins.load(plugin); + plugin = new BookPlugin(book, 'test-hooks'); + return plugin.load(PLUGINS_ROOT); }); - var testTpl = function(markup, str, args, options) { - var filetype = parsers.get(markup); - - return book.template.renderString(str, args, options) - .then(filetype.page).get('sections').get(0).get('content') - .then(book.template.postProcess); - }; - - it('should correctly process unparsable for markdown', function() { - return testTpl('.md', '{% test %}**hello**{% endtest %}') - .then(function(content) { - content.should.equal('<p>test**hello**test</p>\n'); - }); - }); - - it('should correctly process unparsable for asciidoc', function() { - return testTpl('.adoc', '{% test %}**hello**{% endtest %}') - .then(function(content) { - content.should.equal('<div class="paragraph">\n<p>test**hello**test</p>\n</div>'); - }); + it('can call a hook', function() { + return plugin.hook('init') + .then(function() { + global._hooks.should.deepEqual(['init']); + }); }); }); }); diff --git a/test/plugins/blocks/index.js b/test/plugins/blocks/index.js deleted file mode 100644 index 9bdbe86..0000000 --- a/test/plugins/blocks/index.js +++ /dev/null @@ -1,61 +0,0 @@ -var assert = require("assert"); - -module.exports = { - blocks: { - "test": { - shortcuts: { - parsers: ["markdown"], - start: "$$", - end: "$$" - }, - process: function(blk) { - return "test"+blk.body+"test"; - } - }, - "test2": { - end: "endtest2end", - process: function(blk) { - return "test2"+blk.body+"test2"; - } - }, - "test3join": { - blocks: [ - "also" - ], - process: function(blk) { - return [blk.body, blk.blocks[0].body].join(blk.kwargs.separator); - } - }, - "test4join": { - blocks: [ - "also", "finally" - ], - process: function(blk) { - assert(blk.blocks.length, 2); - assert(blk.blocks[0].name, "also"); - assert(blk.blocks[1].name, "finally"); - return [blk.body, blk.blocks[0].body, blk.blocks[1].body].join(blk.kwargs.separator); - } - }, - "test5args": { - process: function(blk) { - return "test5"+blk.args.join(",")+"test5"; - } - }, - "test5kwargs": { - process: function(blk) { - var s = blk.args.join(","); - for (var key in blk.kwargs) { - s = s + ","+key+":"+blk.kwargs[key]; - } - - return "test5"+s+"test5"; - } - }, - "test6context": { - process: function() { - return "test6"+(this.ctx.name)+"test6"; - } - }, - } -};
\ No newline at end of file diff --git a/test/plugins/blocks/package.json b/test/plugins/blocks/package.json deleted file mode 100644 index 7c41fd3..0000000 --- a/test/plugins/blocks/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "gitbook-plugin-blocks", - "description": "Test blocks", - "main": "index.js", - "version": "0.0.1", - "engines": { - "gitbook": "*" - } -}
\ No newline at end of file diff --git a/test/plugins/config/package.json b/test/plugins/config/package.json deleted file mode 100644 index 03ef744..0000000 --- a/test/plugins/config/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "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 diff --git a/test/plugins/empty/index.js b/test/plugins/empty/index.js deleted file mode 100644 index a099545..0000000 --- a/test/plugins/empty/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {};
\ No newline at end of file diff --git a/test/plugins/empty/package.json b/test/plugins/empty/package.json deleted file mode 100644 index 78c7e72..0000000 --- a/test/plugins/empty/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "gitbook-plugin-empty", - "description": "Test empty plugin", - "main": "index.js", - "version": "0.0.1", - "engines": { - "gitbook": "*" - } -}
\ No newline at end of file diff --git a/test/plugins/filters/index.js b/test/plugins/filters/index.js deleted file mode 100644 index 2cf53b1..0000000 --- a/test/plugins/filters/index.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - filters: { - hello: function(text) { - return "Hello "+text; - }, - helloCtx: function(text) { - return text+":"+this.book.root; - } - } -};
\ No newline at end of file diff --git a/test/plugins/filters/package.json b/test/plugins/filters/package.json deleted file mode 100644 index f1d4e45..0000000 --- a/test/plugins/filters/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "gitbook-plugin-filters", - "description": "Test filters", - "main": "index.js", - "version": "0.0.1", - "engines": { - "gitbook": "*" - } -}
\ No newline at end of file diff --git a/test/plugins/invalid/index.js b/test/plugins/invalid/index.js deleted file mode 100644 index a099545..0000000 --- a/test/plugins/invalid/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {};
\ No newline at end of file diff --git a/test/plugins/invalid/package.json b/test/plugins/invalid/package.json deleted file mode 100644 index da34090..0000000 --- a/test/plugins/invalid/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "gitbook-plugin-invalid", - "description": "Test invalid plugin", - "main": "index.js", - "version": "0.0.1", - "engines": { - "gitbook": "<2.0.0" - } -}
\ No newline at end of file diff --git a/test/plugins/replace_highlight/index.js b/test/plugins/replace_highlight/index.js deleted file mode 100644 index 8586486..0000000 --- a/test/plugins/replace_highlight/index.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - blocks: { - "code": { - process: function(blk) { - var lang = blk.kwargs.language || "code"; - - return { - body: lang+"_"+blk.body+"_"+lang, - html: false - }; - } - } - } -};
\ No newline at end of file diff --git a/test/plugins/replace_highlight/package.json b/test/plugins/replace_highlight/package.json deleted file mode 100644 index 72d1033..0000000 --- a/test/plugins/replace_highlight/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "gitbook-plugin-replace_highlight", - "description": "Test replacing default code highlighter", - "main": "index.js", - "version": "0.0.1", - "engines": { - "gitbook": "*" - } -}
\ No newline at end of file diff --git a/test/plugins/resources/index.js b/test/plugins/resources/index.js deleted file mode 100644 index bafa54b..0000000 --- a/test/plugins/resources/index.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - website: { - js: [ - "https://cdn.mathjax.org/mathjax/2.4-latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" - ] - }, - ebook: { - css: [ - "test" - ] - } -}; diff --git a/test/plugins/resources/package.json b/test/plugins/resources/package.json deleted file mode 100644 index ab4320d..0000000 --- a/test/plugins/resources/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "gitbook-plugin-resources", - "description": "Test resources plugin", - "main": "index.js", - "version": "0.0.1", - "engines": { - "gitbook": "*" - } -}
\ No newline at end of file diff --git a/test/readme.js b/test/readme.js new file mode 100644 index 0000000..0cf66ff --- /dev/null +++ b/test/readme.js @@ -0,0 +1,43 @@ +var mock = require('./mock'); + +describe('Readme', function() { + it('should parse empty readme', function() { + return mock.setupDefaultBook({ + 'README.md': '' + }) + .then(function(book) { + return book.config.load() + + .then(function() { + return book.readme.load(); + }); + }); + }); + + it('should parse readme', function() { + return mock.setupDefaultBook({ + 'README.md': '# Hello World\nThis is my book' + }) + .then(function(book) { + return book.readme.load() + .then(function() { + book.readme.title.should.equal('Hello World'); + book.readme.description.should.equal('This is my book'); + }); + }); + }); + + it('should parse AsciiDoc readme', function() { + return mock.setupBook({ + 'README.adoc': '# Hello World\n\nThis is my book\n' + }) + .then(function(book) { + return book.readme.load() + .then(function() { + book.readme.title.should.equal('Hello World'); + book.readme.description.should.equal('This is my book'); + }); + }); + }); +}); + diff --git a/test/resolve.js b/test/resolve.js deleted file mode 100644 index e474ed0..0000000 --- a/test/resolve.js +++ /dev/null @@ -1,60 +0,0 @@ -var path = require("path"); - -describe("Resolve Files", function () { - var book; - - before(function() { - return books.parse("basic") - .then(function(_book) { - book = _book; - }); - }); - - describe("book.fileIsInBook", function() { - it("should return true for correct paths", function() { - book.fileIsInBook(path.join(book.root, "README.md")).should.equal(true); - book.fileIsInBook(path.join(book.root, "styles/website.css")).should.equal(true); - }); - - it("should return true for root folder", function() { - book.fileIsInBook(path.join(book.root, "./")).should.equal(true); - book.fileIsInBook(book.root).should.equal(true); - }); - - it("should return false for files out of scope", function() { - book.fileIsInBook(path.join(book.root, "../")).should.equal(false); - book.fileIsInBook("README.md").should.equal(false); - book.fileIsInBook(path.resolve(book.root, "../README.md")).should.equal(false); - }); - - it("should correctly handle windows paths", function() { - book.fileIsInBook(path.join(book.root, "\\styles\\website.css")).should.equal(true); - }); - }); - - describe("book.resolve", function() { - it("should resolve a file to its absolute path", function() { - book.resolve("README.md").should.equal(path.resolve(book.root, "README.md")); - book.resolve("website/README.md").should.equal(path.resolve(book.root, "website/README.md")); - }); - - it("should correctly handle windows paths", function() { - book.resolve("styles\\website.css").should.equal(path.resolve(book.root, "styles\\website.css")); - }); - - it("should correctly resolve all arguments", function() { - book.resolve("test", "hello", "..", "README.md").should.equal(path.resolve(book.root, "test/README.md")); - }); - - it("should correctly resolve to root folder", function() { - book.resolve("test", "/README.md").should.equal(path.resolve(book.root, "README.md")); - book.resolve("test", "\\README.md").should.equal(path.resolve(book.root, "README.md")); - }); - - it("should throw an error for file out of book", function() { - (function() { - return book.resolve("../README.md"); - }).should.throw(); - }); - }); -}); diff --git a/test/structure.js b/test/structure.js deleted file mode 100644 index 90413cb..0000000 --- a/test/structure.js +++ /dev/null @@ -1,20 +0,0 @@ -describe('Structure', function () { - var book; - - before(function() { - return books.parse('structure') - .then(function(_book) { - book = _book; - }); - }); - - - it('should prioritize structure defined in book.json', function() { - book.readmeFile.should.equal('README.adoc'); - }); - - it('should be case incensitive', function() { - book.glossaryFile.should.equal('glossary.md'); - book.glossary.should.have.lengthOf(1); - }); -}); diff --git a/test/summary.js b/test/summary.js index 2d3a248..08b9db8 100644 --- a/test/summary.js +++ b/test/summary.js @@ -1,70 +1,300 @@ -var fs = require("fs"); -var path = require("path"); +var should = require('should'); -describe("Summary", function () { - describe("Parsing", function() { +var mock = require('./mock'); + +function mockSummary(files, summary) { + return mock.setupDefaultBook(files, summary) + .then(function(book) { + return book.readme.load() + .then(function() { + return book.summary.load(); + }) + .thenResolve(book); + }); +} + +describe('Summary / Table of contents', function() { + describe('Empty summary list', function() { var book; before(function() { - return books.parse("summary") - .then(function(_book) { - book = _book; - }); + return mockSummary({}) + .then(function(_book) { + book = _book; + }); }); - it("should correctly list items", function() { - book.should.have.property("summary"); - book.summary.should.have.property("chapters"); - book.summary.chapters.should.have.lengthOf(4); + it('should add README as first entry', function() { + should(book.summary.getArticle('README.md')).be.ok(); }); - it("should correctly mark non-existant entries", function() { - book.summary.chapters[0].exists.should.have.equal(true); - book.summary.chapters[1].exists.should.have.equal(true); - book.summary.chapters[2].exists.should.have.equal(true); - book.summary.chapters[3].exists.should.have.equal(false); + it('should correctly count articles', function() { + book.summary.count().should.equal(1); }); }); - describe("Generation", function() { + describe('Non-existant summary', function() { var book; before(function() { - return books.generate("summary", "website") - .then(function(_book) { - book = _book; + return mock.setupBook({ + 'README.md': 'Hello' + }) + .then(function(_book) { + book = _book; + + return book.readme.load() + .then(function() { + return book.summary.load(); }); + }); + }); + + it('should add README as first entry', function() { + should(book.summary.getArticle('README.md')).be.ok(); + }); + + it('should correctly count articles', function() { + book.summary.count().should.equal(1); + }); + }); + + describe('Non-empty summary list', function() { + var book; + + before(function() { + return mockSummary({ + 'SUMMARY.md': '# Summary\n\n' + + '* [Hello](./hello.md)\n' + + '* [World](./world.md)\n\n' + }) + .then(function(_book) { + book = _book; + }); + }); + + it('should correctly count articles', function() { + book.summary.count().should.equal(3); + }); + }); + + describe('Levels', function() { + var book; + + before(function() { + return mockSummary({ + 'SUMMARY.md': '# Summary\n\n' + + '* [Hello](./hello.md)\n' + + ' * [Hello 2](./hello2.md)\n' + + '* [World](./world.md)\n\n' + + '## Part 2\n\n' + + '* [Hello 3](./hello.md)\n' + + ' * [Hello 4](./hello2.md)\n' + }) + .then(function(_book) { + book = _book; + }); }); - it("should create files according to summary", function() { - book.should.have.file("index.html"); - book.should.have.file("PAGE1.html"); - book.should.have.file("folder/PAGE2.html"); - }); - - it("should correctly output summary", function() { - var PAGE = fs.readFileSync( - path.join(book.options.output, "index.html"), - { encoding: "utf-8" } - ); - - PAGE.should.be.html({ - ".book-summary .chapter[data-level=\"0\"] a": { - attributes: { - href: "./index.html" - } - }, - ".book-summary .chapter[data-level=\"1\"] a": { - attributes: { - href: "./PAGE1.html" - } - }, - ".book-summary .chapter[data-level=\"2\"] a": { - attributes: { - href: "./folder/PAGE2.html" - } + it('should correctly index levels', function() { + book.summary.getArticleByLevel('0').title.should.equal('Introduction'); + book.summary.getArticleByLevel('1.1').title.should.equal('Hello'); + book.summary.getArticleByLevel('1.1.1').title.should.equal('Hello 2'); + book.summary.getArticleByLevel('1.2').title.should.equal('World'); + + book.summary.getArticleByLevel('2.1').title.should.equal('Hello 3'); + book.summary.getArticleByLevel('2.1.1').title.should.equal('Hello 4'); + }); + + it('should correctly calcul depth', function() { + book.summary.getArticleByLevel('0').depth().should.equal(1); + book.summary.getArticleByLevel('1.1').depth().should.equal(2); + book.summary.getArticleByLevel('1.1.1').depth().should.equal(3); + }); + }); + + describe('External', function() { + var book; + + before(function() { + return mockSummary({}, [ + { + title: 'Google', + path: 'https://www.google.fr' } + ]) + .then(function(_book) { + book = _book; }); }); + + it('should correctly count articles', function() { + book.summary.count().should.equal(2); + }); + + it('should correctly signal it as external', function() { + var article = book.summary.getArticleByLevel('1'); + + should(article).be.ok(); + should(article.path).not.be.ok(); + + article.title.should.equal('Google'); + article.ref.should.equal('https://www.google.fr'); + article.isExternal().should.be.ok; + }); + }); + + describe('Next / Previous', function() { + var book; + + before(function() { + return mockSummary({ + 'SUMMARY.md': '# Summary\n\n' + + '* [Hello](hello.md)\n' + + '* [Hello 2](hello2.md)\n' + + ' * [Hello 3](hello3.md)\n' + + ' * [Hello 4](hello4.md)\n' + + ' * [Hello 5](hello5.md)\n' + + '* [Hello 6](hello6.md)\n\n\n' + + '### Part 2\n\n' + + '* [Hello 7](hello7.md)\n' + + ' * [Hello 8](hello8.md)\n\n' + + '### Part 3\n\n' + + '* [Hello 9](hello9.md)\n' + + '* [Hello 10](hello10.md)\n\n' + }) + .then(function(_book) { + book = _book; + }); + }); + + it('should only return a next for the readme', function() { + var article = book.summary.getArticle('README.md'); + + var prev = article.prev(); + var next = article.next(); + + should(prev).not.be.ok(); + should(next).be.ok(); + + next.path.should.equal('hello.md'); + }); + + it('should return next/prev for a first level page', function() { + var article = book.summary.getArticle('hello.md'); + + var prev = article.prev(); + var next = article.next(); + + should(prev).be.ok(); + should(next).be.ok(); + + prev.path.should.equal('README.md'); + next.path.should.equal('hello2.md'); + }); + + it('should return next/prev for a joint -> child', function() { + var article = book.summary.getArticle('hello2.md'); + + var prev = article.prev(); + var next = article.next(); + + should(prev).be.ok(); + should(next).be.ok(); + + prev.path.should.equal('hello.md'); + next.path.should.equal('hello3.md'); + }); + + it('should return next/prev for a joint <- child', function() { + var article = book.summary.getArticle('hello3.md'); + + var prev = article.prev(); + var next = article.next(); + + should(prev).be.ok(); + should(next).be.ok(); + + prev.path.should.equal('hello2.md'); + next.path.should.equal('hello4.md'); + }); + + it('should return next/prev for a children', function() { + var article = book.summary.getArticle('hello4.md'); + + var prev = article.prev(); + var next = article.next(); + + should(prev).be.ok(); + should(next).be.ok(); + + prev.path.should.equal('hello3.md'); + next.path.should.equal('hello5.md'); + }); + + it('should return next/prev for a joint -> parent', function() { + var article = book.summary.getArticle('hello5.md'); + + var prev = article.prev(); + var next = article.next(); + + should(prev).be.ok(); + should(next).be.ok(); + + prev.path.should.equal('hello4.md'); + next.path.should.equal('hello6.md'); + }); + + it('should return next/prev for a joint -> parts', function() { + var article = book.summary.getArticle('hello6.md'); + + var prev = article.prev(); + var next = article.next(); + + should(prev).be.ok(); + should(next).be.ok(); + + prev.path.should.equal('hello5.md'); + next.path.should.equal('hello7.md'); + }); + + it('should return next/prev for a joint <- parts', function() { + var article = book.summary.getArticle('hello7.md'); + + var prev = article.prev(); + var next = article.next(); + + should(prev).be.ok(); + should(next).be.ok(); + + prev.path.should.equal('hello6.md'); + next.path.should.equal('hello8.md'); + }); + + it('should return next and prev', function() { + var article = book.summary.getArticle('hello8.md'); + + var prev = article.prev(); + var next = article.next(); + + should(prev).be.ok(); + should(next).be.ok(); + + prev.path.should.equal('hello7.md'); + next.path.should.equal('hello9.md'); + }); + + it('should return only prev for last', function() { + var article = book.summary.getArticle('hello10.md'); + + var prev = article.prev(); + var next = article.next(); + + should(prev).be.ok(); + should(next).be.not.ok(); + + prev.path.should.equal('hello9.md'); + }); }); }); + diff --git a/test/template.js b/test/template.js new file mode 100644 index 0000000..6eb2278 --- /dev/null +++ b/test/template.js @@ -0,0 +1,54 @@ +var mock = require('./mock'); +var Output = require('../lib/output/base'); + +describe('Template', function() { + var output; + + before(function() { + return mock.outputDefaultBook(Output, {}) + .then(function(_output) { + output = _output; + }); + }); + + describe('.renderString', function() { + it('should render a simple string', function() { + return output.template.renderString('Hello World') + .should.be.fulfilledWith('Hello World'); + }); + + it('should render with variable', function() { + return output.template.renderString('Hello {{ world }}', { world: 'World'}) + .should.be.fulfilledWith('Hello World'); + }); + }); + + describe('Blocks', function() { + it('should correctly add a block', function() { + output.template.addBlock('sayhello', function(blk) { + return 'Hello ' + blk.body + '!'; + }); + + return output.template.renderString('{% sayhello %}World{% endsayhello %}') + .should.be.fulfilledWith('Hello World!'); + }); + + it('should correctly add a block with args', function() { + output.template.addBlock('sayhello_args', function(blk) { + return 'Hello ' + blk.args[0] + '!'; + }); + + return output.template.renderString('{% sayhello_args "World" %}{% endsayhello_args %}') + .should.be.fulfilledWith('Hello World!'); + }); + + it('should correctly add a block with kwargs', function() { + output.template.addBlock('sayhello_kwargs', function(blk) { + return 'Hello ' + blk.kwargs.name + '!'; + }); + + return output.template.renderString('{% sayhello_kwargs name="World" %}{% endsayhello_kwargs %}') + .should.be.fulfilledWith('Hello World!'); + }); + }); +}); diff --git a/test/templating.js b/test/templating.js deleted file mode 100644 index f92154b..0000000 --- a/test/templating.js +++ /dev/null @@ -1,33 +0,0 @@ -var pkg = require("../package.json"); - -describe("Templating", function () { - var book; - - before(function() { - return books.parse("basic") - .then(function(_book) { - book = _book; - }); - }); - - var testTpl = function(str, args, options) { - return book.template.renderString(str, args, options) - .then(book.template.postProcess); - }; - - describe("Context", function() { - it("should correctly have access to generator", function() { - return testTpl("{{ gitbook.generator }}") - .then(function(content) { - content.should.equal("website"); - }); - }); - - it("should correctly have access to gitbook version", function() { - return testTpl("{{ gitbook.version }}") - .then(function(content) { - content.should.equal(pkg.version.toString()); - }); - }); - }); -}); diff --git a/test/website.js b/test/website.js deleted file mode 100644 index 6a0fd1c..0000000 --- a/test/website.js +++ /dev/null @@ -1,26 +0,0 @@ -describe('Website generator', function () { - describe('Basic Book', function() { - var book; - - before(function() { - return books.generate('basic', 'website') - .then(function(_book) { - book = _book; - }); - }); - - it('should correctly output an index.html', function() { - book.should.have.file('index.html'); - }); - - it('should correctly copy assets', function() { - book.should.have.file('gitbook'); - book.should.have.file('gitbook/app.js'); - book.should.have.file('gitbook/style.css'); - }); - - it('should not copy ebook assets', function() { - book.should.not.have.file('gitbook/ebook.css'); - }); - }); -}); |