diff --git a/package-lock.json b/package-lock.json index f683580..e39ed74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6357,6 +6357,12 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index c5182f1..6258495 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,8 @@ "sinon": "^9.2.2", "standard": "^12.0.1", "stream-mock": "^2.0.5", - "tmp": "^0.2.1" + "tmp": "^0.2.1", + "yaml": "^1.10.0" }, "standard": { "ignore": [ diff --git a/test-fixtures/theme/album.hbs b/test-fixtures/theme/album.hbs new file mode 100644 index 0000000..6fd504d --- /dev/null +++ b/test-fixtures/theme/album.hbs @@ -0,0 +1,21 @@ +{{!-- + This theme renders useful information as a YAML file + For reliable integration tests +--}} + +title: {{ album.title }} + +albums: +{{#each album.albums}} + - title: {{title}} + url: {{relative url}} +{{/each}} + +files: +{{#each album.files}} + - name: {{filename}} + caption: {{meta.caption}} + thumbnail: {{relative urls.thumbnail}} + preview: {{relative urls.large}} + download: {{relative urls.download}} +{{/each}} diff --git a/test-fixtures/theme/package.json b/test-fixtures/theme/package.json new file mode 100644 index 0000000..dc3253c --- /dev/null +++ b/test-fixtures/theme/package.json @@ -0,0 +1,6 @@ +{ + "name": "@thumbsup/test-theme", + "thumbsup": { + "themeRoot": "." + } +} diff --git a/test-fixtures/theme/theme.less b/test-fixtures/theme/theme.less new file mode 100644 index 0000000..f569c8e --- /dev/null +++ b/test-fixtures/theme/theme.less @@ -0,0 +1,5 @@ +@color: #333; + +h1 { + border: @color; +} diff --git a/test/integration/integration-structure.spec.js b/test/integration/integration-structure.spec.js new file mode 100644 index 0000000..8dfc24d --- /dev/null +++ b/test/integration/integration-structure.spec.js @@ -0,0 +1,57 @@ +const IntegrationTest = require('./integration-test') +const fixtures = require('../fixtures') + +describe('Integration: media files', function () { + this.slow(5000) + this.timeout(5000) + + beforeEach(IntegrationTest.before) + afterEach(IntegrationTest.after) + + const image = fixtures.fromDisk('photo.jpg') + const integration = new IntegrationTest({ + 'input/london/IMG_0001.jpg': image, + 'input/london/IMG_0002.jpg': image, + 'input/newyork/day 1/IMG_0003.jpg': image, + 'input/newyork/day 2/IMG_0004.jpg': image + }) + + it('builds the gallery from scratch', function (done) { + const customOpts = [] + integration.run(customOpts, () => { + // Database + integration.assertExist([ + 'thumbsup.db' + ]) + // Albums + integration.assertExist([ + 'index.html', + 'london.html', + 'newyork-day-1.html', + 'newyork-day-2.html' + ]) + // Thumbnails + integration.assertExist([ + 'media/thumbs/london/IMG_0001.jpg', + 'media/thumbs/london/IMG_0002.jpg', + 'media/thumbs/newyork/day 1/IMG_0003.jpg', + 'media/thumbs/newyork/day 2/IMG_0004.jpg' + ]) + // Large versions + integration.assertExist([ + 'media/large/london/IMG_0001.jpg', + 'media/large/london/IMG_0002.jpg', + 'media/large/newyork/day 1/IMG_0003.jpg', + 'media/large/newyork/day 2/IMG_0004.jpg' + ]) + done() + }) + }) + + it('builds the gallery a second time', function (done) { + const customOpts = [] + integration.run(customOpts, () => { + done() + }) + }) +}) diff --git a/test/integration/integration-test.js b/test/integration/integration-test.js new file mode 100644 index 0000000..a92e111 --- /dev/null +++ b/test/integration/integration-test.js @@ -0,0 +1,75 @@ +const fs = require('fs') +const debug = require('debug') +const glob = require('glob') +const path = require('path') +const YAML = require('yaml') +const should = require('should/as-function') +const fixtures = require('../fixtures') +const options = require('../../src/cli/options') +const index = require('../../src/index') + +class IntegrationTest { + constructor (structure) { + this.tmpdir = fixtures.createTempStructure(structure) + this.input = path.join(this.tmpdir, 'input') + this.output = path.join(this.tmpdir, 'output') + this.actualFiles = [] + } + + run (customOptions, done) { + const defaultOptions = [ + '--input', this.input, + '--output', this.output, + '--theme-path', 'test-fixtures/theme', + '--log', 'info' + ] + const allOptions = defaultOptions.concat(customOptions) + const opts = options.get(allOptions) + index.build(opts, err => { + // Reset the logger ASAP to print the test status + console.log = console.logOld + should(err).eql(null) + debug.assertNotContains('thumbsup:error') + this.actualFiles = glob.sync('**/*', { + cwd: this.output, + nodir: true, + nonull: false + }) + setImmediate(done) + }) + } + + assertExist (expected) { + const missing = expected.filter(f => this.actualFiles.indexOf(f) === -1) + should(missing).eql([]) + } + + parse (filepath) { + const fullpath = path.join(this.output, filepath) + return fs.readFileSync(fullpath, { encoding: 'utf8' }) + } + + parseYaml (filepath) { + const contents = this.parse(filepath) + return YAML.parse(contents) + } + + getPath (structurePath) { + return path.join(this.tmpdir, structurePath) + } +} + +IntegrationTest.before = function () { + // Listr uses control.log() to print progress + // But so does Mocha to print test results + // So we override it for the duration of the integration test + console.logOld = console.log + console.log = debug('thumbsup:info') + debug.reset() +} + +IntegrationTest.after = function () { + console.log = console.logOld +} + +module.exports = IntegrationTest diff --git a/test/integration/integration-themes.spec.js b/test/integration/integration-themes.spec.js new file mode 100644 index 0000000..cf22065 --- /dev/null +++ b/test/integration/integration-themes.spec.js @@ -0,0 +1,38 @@ +const should = require('should/as-function') +const IntegrationTest = require('./integration-test') +const fixtures = require('../fixtures') + +describe('Integration: themes', function () { + this.slow(5000) + this.timeout(5000) + + beforeEach(IntegrationTest.before) + afterEach(IntegrationTest.after) + + const integration = new IntegrationTest({ + 'input/IMG_0001.jpg': fixtures.fromDisk('photo.jpg'), + 'custom.less': '@color: #444;' + }) + + it('processes LESS variables', function (done) { + const customOpts = [] + integration.run(customOpts, () => { + integration.assertExist(['public/theme.css']) + const res = integration.parse('public/theme.css') + should(res.includes('border: #333')).eql(true) + done() + }) + }) + + it('can customise LESS variables', function (done) { + const customOpts = [ + '--theme-style', integration.getPath('custom.less') + ] + integration.run(customOpts, () => { + integration.assertExist(['public/theme.css']) + const res = integration.parse('public/theme.css') + should(res.includes('border: #444')).eql(true) + done() + }) + }) +}) diff --git a/test/integration/integration-urls.spec.js b/test/integration/integration-urls.spec.js new file mode 100644 index 0000000..500ef65 --- /dev/null +++ b/test/integration/integration-urls.spec.js @@ -0,0 +1,38 @@ +const should = require('should/as-function') +const IntegrationTest = require('./integration-test') +const fixtures = require('../fixtures') + +describe('Integration: urls', function () { + this.slow(5000) + this.timeout(5000) + + beforeEach(IntegrationTest.before) + afterEach(IntegrationTest.after) + + const integration = new IntegrationTest({ + 'input/IMG_0001.jpg': fixtures.fromDisk('photo.jpg') + }) + + it('uses relative URLs by default', function (done) { + const customOpts = [] + integration.run(customOpts, () => { + integration.assertExist(['index.html']) + const res = integration.parseYaml('index.html') + should(res.files[0].thumbnail).eql('media/thumbs/IMG_0001.jpg') + done() + }) + }) + + it('can use an external link prefix', function (done) { + const customOpts = [ + '--photo-preview', 'link', + '--link-prefix', 'http://example.com' + ] + integration.run(customOpts, () => { + integration.assertExist(['index.html']) + const res = integration.parseYaml('index.html') + should(res.files[0].preview).eql('http://example.com/IMG_0001.jpg') + done() + }) + }) +}) diff --git a/test/integration/integration.spec.js b/test/integration/integration.spec.js deleted file mode 100644 index 67ad573..0000000 --- a/test/integration/integration.spec.js +++ /dev/null @@ -1,107 +0,0 @@ -const debug = require('debug') -const glob = require('glob') -const path = require('path') -const should = require('should/as-function') -const fixtures = require('../fixtures') -const options = require('../../src/cli/options') -const index = require('../../src/index') - -describe('Full integration', function () { - this.slow(5000) - this.timeout(5000) - - var tmpdir = null - var opts = null - - before(() => { - const image = fixtures.fromDisk('photo.jpg') - tmpdir = fixtures.createTempStructure({ - 'input/london/IMG_0001.jpg': image, - 'input/london/IMG_0002.jpg': image, - 'input/newyork/day 1/IMG_0003.jpg': image, - 'input/newyork/day 2/IMG_0004.jpg': image - }) - opts = options.get([ - '--input', path.join(tmpdir, 'input'), - '--output', path.join(tmpdir, 'output'), - '--title', 'Photo album', - '--homeAlbumName', 'Home', - '--theme', 'classic', - '--log', 'info' - ]) - }) - - // Listr uses control.log() to print progress - // But so does Mocha to print test results - // So we override it for the duration of the integration test - beforeEach(() => { - console.logOld = console.log - console.log = debug('thumbsup:info') - debug.reset() - }) - - afterEach(() => { - console.log = console.logOld - }) - - it('builds the gallery from scratch', function (testDone) { - index.build(opts, err => { - // Reset the logger ASAP to print the test status - console.log = console.logOld - // Check for any errors - should(err).eql(null) - debug.assertNotContains('thumbsup:error') - // Check the contents of the output folder - const actualFiles = actualStructure(opts.output) - // Database - assertExist(actualFiles, [ - 'thumbsup.db' - ]) - // Albums - assertExist(actualFiles, [ - 'index.html', - 'london.html', - 'newyork-day-1.html', - 'newyork-day-2.html' - ]) - // Thumbnails - assertExist(actualFiles, [ - 'media/thumbs/london/IMG_0001.jpg', - 'media/thumbs/london/IMG_0002.jpg', - 'media/thumbs/newyork/day 1/IMG_0003.jpg', - 'media/thumbs/newyork/day 2/IMG_0004.jpg' - ]) - // Large versions - assertExist(actualFiles, [ - 'media/large/london/IMG_0001.jpg', - 'media/large/london/IMG_0002.jpg', - 'media/large/newyork/day 1/IMG_0003.jpg', - 'media/large/newyork/day 2/IMG_0004.jpg' - ]) - testDone() - }) - }) - - it('builds the gallery a second time (nothing to do)', function (testDone) { - index.build(opts, err => { - // Reset the logger ASAP to print the test status - console.log = console.logOld - should(err).eql(null) - testDone() - }) - }) -}) - -function actualStructure (dir) { - return glob.sync('**/*', { - cwd: dir, - ignore: 'public', - nodir: true, - nonull: false - }) -} - -function assertExist (actual, expected) { - const missing = expected.filter(f => actual.indexOf(f) === -1) - should([]).eql(missing) -}