refactor(cli): simplify options parsing + include options in integration test

pull/178/head
Romain 5 years ago
parent 1502e8be0d
commit 3671c1211c

@ -2,6 +2,7 @@ const messages = require('./messages')
const path = require('path')
const yargs = require('yargs')
const os = require('os')
const _ = require('lodash')
const OPTIONS = {
@ -12,12 +13,14 @@ const OPTIONS = {
'input': {
group: 'Required:',
description: 'Path to the folder with all photos/videos',
type: 'string',
normalize: true,
demand: true
},
'output': {
group: 'Required:',
description: 'Output path for the static website',
type: 'string',
normalize: true,
demand: true
},
@ -149,7 +152,8 @@ const OPTIONS = {
'watermark': {
group: 'Output options:',
description: 'Path to a transparent PNG to be overlaid on all images',
type: 'string'
type: 'string',
normalize: true
},
'watermark-position': {
group: 'Output options:',
@ -214,11 +218,13 @@ const OPTIONS = {
'index': {
group: 'Website options:',
description: 'Filename of the home page',
type: 'string',
'default': 'index.html'
},
'albums-output-folder': {
group: 'Website options:',
description: 'Output subfolder for HTML albums (default: website root)',
type: 'string',
'default': '.'
},
'theme': {
@ -230,26 +236,31 @@ const OPTIONS = {
'theme-path': {
group: 'Website options:',
description: 'Path to a custom theme',
type: 'string',
normalize: true
},
'theme-style': {
group: 'Website options:',
description: 'Path to a custom LESS/CSS file for additional styles',
type: 'string',
normalize: true
},
'theme-settings': {
group: 'Website options:',
description: 'Path to a JSON file with theme settings',
type: 'string',
normalize: true
},
'title': {
group: 'Website options:',
description: 'Website title',
type: 'string',
'default': 'Photo album'
},
'footer': {
group: 'Website options:',
description: 'Text or HTML footer',
type: 'string',
'default': null
},
'google-analytics': {
@ -339,7 +350,7 @@ const OPTIONS = {
// explicitly pass <process.argv> so we can unit test this logic
// otherwise it pre-loads all process arguments on require()
exports.get = (args) => {
const opts = yargs(args)
const parsedOptions = yargs(args)
.usage(messages.USAGE())
.wrap(null)
.help('help')
@ -350,92 +361,48 @@ exports.get = (args) => {
// Warn users when they use deprecated options
const deprecated = Object.keys(OPTIONS).filter(name => OPTIONS[name].group === 'Deprecated:')
const specified = deprecated.filter(name => typeof opts[name] !== 'undefined')
const specified = deprecated.filter(name => typeof parsedOptions[name] !== 'undefined')
if (specified.length > 0) {
const warnings = specified.map(name => `Warning: --${name} is deprecated`)
console.error(warnings.join('\n') + '\n')
}
// Delete all options containing dashes, because yargs already aliases them as camelCase
// This means we can process the camelCase version only after that
const opts = _.omitBy(parsedOptions, (value, key) => key.indexOf('-') >= 0)
// Make input/output folder absolute paths
opts['input'] = path.resolve(opts['input'])
opts['output'] = path.resolve(opts['output'])
opts.input = path.resolve(opts.input)
opts.output = path.resolve(opts.output)
// By default, use relative links to the input folder
if (opts['download-link-prefix']) opts['link-prefix'] = opts['download-link-prefix']
if (!opts['link-prefix']) {
opts['link-prefix'] = path.relative(opts['output'], opts['input'])
if (opts.downloadLinkPrefix) opts.linkPrefix = opts.downloadLinkPrefix
if (!opts.linkPrefix) {
opts.linkPrefix = path.relative(opts.output, opts.input)
}
// Convert deprecated --download
if (opts['original-photos']) opts['download-photos'] = 'copy'
if (opts['original-videos']) opts['download-videos'] = 'copy'
if (opts['download-photos']) opts['photo-download'] = opts['download-photos']
if (opts['download-videos']) opts['video-download'] = opts['download-videos']
if (opts['photo-download'] === 'large') opts['photo-download'] = 'resize'
if (opts['video-download'] === 'large') opts['video-download'] = 'resize'
if (opts.originalPhotos) opts.downloadPhotos = 'copy'
if (opts.originalVideos) opts.downloadVideos = 'copy'
if (opts.downloadPhotos) opts.photoDownload = opts.downloadPhotos
if (opts.downloadVideos) opts.videoDownload = opts.downloadVideos
if (opts.photoDownload === 'large') opts.photoDownload = 'resize'
if (opts.videoDownload === 'large') opts.videoDownload = 'resize'
// Convert deprecated --albums-from
replaceInArray(opts['albums-from'], 'folders', '%path')
replaceInArray(opts['albums-from'], 'date', `{${opts['albums-date-format']}}`)
replaceInArray(opts.albumsFrom, 'folders', '%path')
replaceInArray(opts.albumsFrom, 'date', `{${opts.albumsDateFormat}}`)
// Convert deprecated --css
if (opts['css']) opts['theme-style'] = opts['css']
if (opts.css) opts.themeStyle = opts.css
// Add a dash prefix to any --gm-args value
// We can't specify the prefix on the CLI otherwise the parser thinks it's a thumbsup arg
if (opts['gm-args']) {
opts['gm-args'] = opts['gm-args'].map(val => `-${val}`)
if (opts.gmArgs) {
opts.gmArgs = opts.gmArgs.map(val => `-${val}`)
}
// All options as an object
return {
input: opts['input'],
output: opts['output'],
includePhotos: opts['include-photos'],
includeVideos: opts['include-videos'],
includeRawPhotos: opts['include-raw-photos'],
include: opts['include'],
exclude: opts['exclude'],
cleanup: opts['cleanup'],
title: opts['title'],
thumbSize: opts['thumb-size'],
largeSize: opts['large-size'],
photoQuality: opts['photo-quality'],
videoQuality: opts['video-quality'],
videoBitrate: opts['video-bitrate'],
videoFormat: opts['video-format'],
photoPreview: opts['photo-preview'],
videoPreview: opts['video-preview'],
photoDownload: opts['photo-download'],
videoDownload: opts['video-download'],
linkPrefix: opts['link-prefix'],
albumsFrom: opts['albums-from'],
albumsDateFormat: opts['albums-date-format'],
sortAlbumsBy: opts['sort-albums-by'],
sortAlbumsDirection: opts['sort-albums-direction'],
sortMediaBy: opts['sort-media-by'],
sortMediaDirection: opts['sort-media-direction'],
homeAlbumName: opts['home-album-name'],
albumZipFiles: opts['album-zip-files'],
theme: opts['theme'],
themePath: opts['theme-path'],
themeStyle: opts['theme-style'],
themeSettings: opts['theme-settings'],
css: opts['css'],
googleAnalytics: opts['google-analytics'],
index: opts['index'],
footer: opts['footer'],
albumsOutputFolder: opts['albums-output-folder'],
usageStats: opts['usage-stats'],
log: opts['log'],
dryRun: opts['dry-run'],
concurrency: opts['concurrency'],
outputStructure: opts['output-structure'],
gmArgs: opts['gm-args'],
watermark: opts['watermark'],
watermarkPosition: opts['watermark-position'],
embedExif: opts['embed-exif']
}
return opts
}
function replaceInArray (list, match, replacement) {

@ -5,13 +5,30 @@ const options = require('../../bin/options.js')
const BASE_ARGS = ['--input', 'photos', '--output', 'website']
describe('options', function () {
it('--input is converted to an absolute path', () => {
const opts = options.get(BASE_ARGS)
should(opts.input).eql(path.join(process.cwd(), 'photos'))
describe('parsing', () => {
it('parses a single basic option', () => {
const opts = options.get(BASE_ARGS)
should(opts.input.endsWith('photos')).eql(true)
})
it('options with dashes are converted to camel case', () => {
const opts = options.get(BASE_ARGS.concat(['--theme-path', 'foobar']))
should(opts['theme-path']).eql(undefined)
should(opts.themePath).eql('foobar')
})
it('can use --no to reverse a boolean', () => {
const opts = options.get(BASE_ARGS.concat(['--no-usage-stats']))
should(opts.usageStats).eql(false)
})
})
it('--output is converted to an absolute path', () => {
const opts = options.get(BASE_ARGS)
should(opts.output).eql(path.join(process.cwd(), 'website'))
describe('paths', () => {
it('--input is converted to an absolute path', () => {
const opts = options.get(BASE_ARGS)
should(opts.input).eql(path.join(process.cwd(), 'photos'))
})
it('--output is converted to an absolute path', () => {
const opts = options.get(BASE_ARGS)
should(opts.output).eql(path.join(process.cwd(), 'website'))
})
})
describe('--albums-from', () => {
it('can be specified once', () => {

@ -3,6 +3,7 @@ const glob = require('glob')
const path = require('path')
const should = require('should/as-function')
const fixtures = require('../fixtures')
const options = require('../../bin/options')
const index = require('../../src/index')
describe('Full integration', function () {
@ -10,7 +11,7 @@ describe('Full integration', function () {
this.timeout(5000)
var tmpdir = null
var options = null
var opts = null
before(() => {
const image = fixtures.fromDisk('photo.jpg')
@ -20,14 +21,14 @@ describe('Full integration', function () {
'input/newyork/day 1/IMG_0003.jpg': image,
'input/newyork/day 2/IMG_0004.jpg': image
})
options = {
input: path.join(tmpdir, 'input'),
output: path.join(tmpdir, 'output'),
title: 'Photo album',
homeAlbumName: 'Home',
theme: 'classic',
log: 'info'
}
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
@ -44,14 +45,15 @@ describe('Full integration', function () {
})
it('builds the gallery from scratch', function (testDone) {
index.build(options, err => {
index.build(opts, err => {
// Reset the logger ASAP to print the test status
console.log = console.logOld
// Check for any errors
console.log(err)
should(err).eql(null)
debug.assertNotContains('thumbsup:error')
// Check the contents of the output folder
const actualFiles = actualStructure(options.output)
const actualFiles = actualStructure(opts.output)
// Database
assertExist(actualFiles, [
'thumbsup.db'
@ -82,7 +84,7 @@ describe('Full integration', function () {
})
it('builds the gallery a second time (nothing to do)', function (testDone) {
index.build(options, err => {
index.build(opts, err => {
// Reset the logger ASAP to print the test status
console.log = console.logOld
should(err).eql(null)

Loading…
Cancel
Save