test(all): add unit tests for theme + cleanup logic

pull/118/head
Romain 6 years ago
parent 72a23807f1
commit 3fa8867757

90
package-lock.json generated

@ -12,6 +12,15 @@
"any-observable": "0.3.0"
}
},
"@sinonjs/formatio": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz",
"integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==",
"dev": true,
"requires": {
"samsam": "1.3.0"
}
},
"JSONStream": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.3.tgz",
@ -2561,6 +2570,12 @@
"array-includes": "3.0.3"
}
},
"just-extend": {
"version": "1.1.27",
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz",
"integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==",
"dev": true
},
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@ -2906,6 +2921,12 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
},
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
"dev": true
},
"log-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
@ -2989,6 +3010,12 @@
}
}
},
"lolex": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.0.tgz",
"integrity": "sha512-uJkH2e0BVfU5KOJUevbTOtpDduooSarH5PopO+LfM/vZf8Z9sJzODqKev804JYM2i++ktJfUmC1le4LwFQ1VMg==",
"dev": true
},
"longest": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
@ -3295,6 +3322,19 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
"nise": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/nise/-/nise-1.4.1.tgz",
"integrity": "sha512-9JX3YwoIt3kS237scmSSOpEv7vCukVzLfwK0I0XhocDSHUANid8ZHnLEULbbSkfeMn98B2y5kphIWzZUylESRQ==",
"dev": true,
"requires": {
"@sinonjs/formatio": "2.0.0",
"just-extend": "1.1.27",
"lolex": "2.7.0",
"path-to-regexp": "1.7.0",
"text-encoding": "0.6.4"
}
},
"node-fetch": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
@ -3606,6 +3646,23 @@
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
"dev": true
},
"path-to-regexp": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
"integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
"dev": true,
"requires": {
"isarray": "0.0.1"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
"dev": true
}
}
},
"path-type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
@ -4172,6 +4229,12 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"samsam": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz",
"integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==",
"dev": true
},
"semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
@ -4290,6 +4353,21 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"sinon": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-6.0.0.tgz",
"integrity": "sha512-MatciKXyM5pXMSoqd593MqTsItJNCkSSl53HJYeKR5wfsDdp2yljjUQJLfVwAWLoBNfx1HThteqygGQ0ZEpXpQ==",
"dev": true,
"requires": {
"@sinonjs/formatio": "2.0.0",
"diff": "3.5.0",
"lodash.get": "4.4.2",
"lolex": "2.7.0",
"nise": "1.4.1",
"supports-color": "5.4.0",
"type-detect": "4.0.8"
}
},
"slice-ansi": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
@ -4662,6 +4740,12 @@
"execa": "0.7.0"
}
},
"text-encoding": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
"integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
"dev": true
},
"text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@ -4783,6 +4867,12 @@
"prelude-ls": "1.1.2"
}
},
"type-detect": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
"dev": true
},
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",

@ -64,6 +64,7 @@
"require-all": "^2.2.0",
"require-lint": "^1.3.0",
"should": "^13.2.1",
"sinon": "^6.0.0",
"standard": "^11.0.1",
"tmp": "0.0.33"
},

@ -17,7 +17,6 @@ exports.run = function (fileCollection, outputRoot) {
})
const useless = _.difference(diskFiles, requiredFiles)
if (useless.length) {
// const bar = progress.create('Cleaning up', useless.length)
useless.forEach(f => {
observer.next(path.relative(outputRoot, f))
fs.unlinkSync(f)

@ -0,0 +1,61 @@
const fs = require('fs')
const sinon = require('sinon')
const should = require('should/as-function')
const cleanup = require('../../src/steps/step-cleanup')
const fixtures = require('../fixtures')
describe('Steps: cleanup', () => {
// we require "mock-fs" inside the tests, otherwise it also affects other tests
var mock = null
beforeEach(() => {
mock = require('mock-fs')
sinon.stub(fs, 'unlinkSync')
})
afterEach(() => {
mock.restore()
fs.unlinkSync.restore()
})
it('does nothing if there are no extra files', testEnd => {
const input = [
fixtures.file({path: 'paris/IMG_0001.jpg'}),
fixtures.file({path: 'london/IMG_0002.jpg'})
]
mock({
'output/media/thumbs/paris/IMG_0001.jpg': '',
'output/media/thumbs/london/IMG_0002.jpg': '',
'output/media/large/paris/IMG_0001.jpg': '',
'output/media/large/london/IMG_0002.jpg': ''
})
const obs = cleanup.run(input, 'output')
obs.reduce(toArray, []).subscribe(deletedFiles => {
should(deletedFiles).deepEqual([])
should(fs.unlinkSync.args).deepEqual([])
testEnd()
})
})
it('deletes output files that are not linked to the input', testEnd => {
const input = [
fixtures.file({path: 'paris/IMG_0001.jpg'}),
fixtures.file({path: 'london/IMG_0002.jpg'})
]
mock({
'output/media/large/paris/IMG_0001.jpg': '',
'output/media/large/london/IMG_0002.jpg': '',
'output/media/large/newyork/IMG_0003.jpg': ''
})
const obs = cleanup.run(input, 'output')
obs.reduce(toArray, []).subscribe(deletedFiles => {
should(deletedFiles).deepEqual(['media/large/newyork/IMG_0003.jpg'])
should(fs.unlinkSync.args).deepEqual([['output/media/large/newyork/IMG_0003.jpg']])
testEnd()
})
})
})
function toArray (list, item) {
return list.concat([item])
}

@ -1,5 +1,7 @@
const fs = require('fs')
const _ = require('lodash')
const fs = require('fs-extra')
const should = require('should/as-function')
const tmp = require('tmp')
const Theme = require('../../src/website/theme')
describe('Theme', () => {
@ -13,48 +15,60 @@ describe('Theme', () => {
mock.restore()
})
it('renders the main HTML file', testEnd => {
it('throws an error if it doesnt have the mandatory HBS template', () => {
mock({
'dest': mock.directory(),
'theme/theme.less': '',
'theme/album.hbs': 'Welcome to {{album.title}}'
'theme/theme.less': ''
})
const album = { path: 'albums/holidays.html', title: 'my album' }
const theme = new Theme('theme', 'dest', {})
renderTheme(theme, album, () => {
const html = fs.readFileSync('dest/albums/holidays.html', 'utf-8')
should(html).equal('Welcome to my album')
testEnd()
should(() => theme.validateStructure()).throw(/Invalid theme structure/)
})
it('throws an error if it doesnt have the mandatory LESS stylesheet', () => {
mock({
'theme/album.hbs': ''
})
const theme = new Theme('theme', 'dest', {})
should(() => theme.validateStructure()).throw(/Invalid theme structure/)
})
it('renders the main CSS file', testEnd => {
mock({
'dest': mock.directory(),
'theme/theme.less': '@color: #abc; body { color: @color; }',
'theme/album.hbs': ''
})
const album = { path: 'index.html' }
const theme = new Theme('theme', 'dest', {})
renderTheme(theme, album, () => {
theme.prepare(err => {
should(err).be.null()
const css = fs.readFileSync('dest/public/theme.css', 'utf-8')
should(css).equal('body {\n color: #abc;\n}\n')
testEnd()
})
})
it('returns an error if the main LESS file is invalid', testEnd => {
mock({
'theme/theme.less': 'body color = red',
'theme/album.hbs': ''
})
const theme = new Theme('theme', 'dest', {})
theme.validateStructure()
theme.prepare(err => {
should(err).property('message').eql('Unrecognised input')
testEnd()
})
})
it('can include an extra CSS files (unprocessed)', testEnd => {
mock({
'dest': mock.directory(),
'theme/theme.less': 'body { color: red; }',
'theme/other.css': 'h1 { color: blue; }',
'theme/album.hbs': ''
})
const album = { path: 'index.html' }
const theme = new Theme('theme', 'dest', {
customStylesPath: 'other.css'
})
renderTheme(theme, album, () => {
theme.prepare(err => {
should(err).be.null()
// Note the CSS file was not re-indented
// It's important it's not processed since not all CSS is valid LESS
const css = fs.readFileSync('dest/public/theme.css', 'utf-8')
@ -65,24 +79,112 @@ describe('Theme', () => {
it('can include an extra LESS file', testEnd => {
mock({
'dest': mock.directory(),
'theme/theme.less': 'body { color: red; }',
'theme/other.less': 'h1 { color: blue; }',
'theme/album.hbs': ''
})
const album = { path: 'index.html' }
const theme = new Theme('theme', 'dest', {
customStylesPath: 'other.less'
})
renderTheme(theme, album, () => {
theme.prepare(err => {
should(err).be.null()
const css = fs.readFileSync('dest/public/theme.css', 'utf-8')
should(css).equal('body {\n color: red;\n}\nh1 {\n color: blue;\n}\n')
testEnd()
})
})
it('renders the main HTML file', testEnd => {
mock({
'theme/theme.less': '',
'theme/album.hbs': 'Welcome to {{album.title}}'
})
const album = { path: 'albums/holidays.html', title: 'my album' }
const theme = new Theme('theme', 'dest', {})
renderTheme(theme, album, () => {
const html = fs.readFileSync('dest/albums/holidays.html', 'utf-8')
should(html).equal('Welcome to my album')
testEnd()
})
})
it('loads all partials', testEnd => {
mock({
'theme/theme.less': '',
'theme/album.hbs': 'Partial says {{>hello}}',
'theme/partials/hello.hbs': 'hello world'
})
const album = { path: 'index.html', title: 'my album' }
const theme = new Theme('theme', 'dest', {})
renderTheme(theme, album, () => {
const html = fs.readFileSync('dest/index.html', 'utf-8')
should(html).equal('Partial says hello world')
testEnd()
})
})
it('loads all helpers', testEnd => {
// because helpers use require(...) we cannot use a mock filesystem
const tmpdir = createTempStructure({
'theme/theme.less': '',
'theme/album.hbs': 'Partial says {{hello "world"}}',
'theme/helpers/hello.js': 'module.exports = args => "hello " + args'
})
const album = { path: 'index.html', title: 'my album' }
const theme = new Theme(`${tmpdir}/theme`, `${tmpdir}/dest`, {})
renderTheme(theme, album, () => {
const html = fs.readFileSync(`${tmpdir}/dest/index.html`, 'utf-8')
should(html).equal('Partial says hello world')
testEnd()
})
})
it('loads require() statements relative to the theme folder', testEnd => {
// because helpers use require(...) we cannot use a mock filesystem
const tmpdir = createTempStructure({
'theme/theme.less': '',
'theme/album.hbs': 'Partial says {{hello "world"}}',
'theme/node_modules/foo/package.json': '{"name": "foo", "main": "index.js"}',
'theme/node_modules/foo/index.js': 'module.exports = () => "bar"',
'theme/helpers/hello.js': `
const foo = require('foo')
module.exports = args => "hello " + foo()
`
})
const album = { path: 'index.html', title: 'my album' }
const theme = new Theme(`${tmpdir}/theme`, `${tmpdir}/dest`, {})
renderTheme(theme, album, () => {
const html = fs.readFileSync(`${tmpdir}/dest/index.html`, 'utf-8')
should(html).equal('Partial says hello bar')
testEnd()
})
})
it('copies public files', testEnd => {
// fs.copy() doesn't seem compatible with mock-fs either
const tmpdir = createTempStructure({
'theme/theme.less': '',
'theme/album.hbs': '',
'theme/public/logo.jpg': 'LOGO'
})
const album = { path: 'index.html', title: 'my album' }
const theme = new Theme(`${tmpdir}/theme`, `${tmpdir}/dest`, {})
renderTheme(theme, album, () => {
const html = fs.readFileSync(`${tmpdir}/dest/public/logo.jpg`, 'utf-8')
should(html).equal('LOGO')
testEnd()
})
})
})
process.on('unhandledRejection', err => console.log('err', err))
function createTempStructure (files) {
const tmpdir = tmp.dirSync({unsafeCleanup: true}).name
_.each(files, (content, filepath) => {
fs.ensureFileSync(`${tmpdir}/${filepath}`)
fs.writeFileSync(`${tmpdir}/${filepath}`, content)
})
return tmpdir
}
function renderTheme (theme, album, next) {
theme.validateStructure()

Loading…
Cancel
Save