refactor(cli): cleanups and improvements (#4222)

This commit is contained in:
Jonas Galvez 2018-10-29 19:16:16 -03:00 committed by Pooya Parsa
parent 2e2b32b547
commit 4503d42d54
21 changed files with 391 additions and 396 deletions

View File

@ -1,19 +1,26 @@
import parseArgs from 'minimist' import parseArgs from 'minimist'
import wrapAnsi from 'wrap-ansi'
import { name, version } from '../package.json' import { name, version } from '../package.json'
import { loadNuxtConfig, indent, indentLines, foldLines } from './utils' import { loadNuxtConfig, indent, foldLines } from './utils'
import { options as Options, defaultOptions as DefaultOptions } from './options'
import * as imports from './imports' import * as imports from './imports'
const startSpaces = 6 const startSpaces = 2
const optionSpaces = 2 const optionSpaces = 2
const maxCharsPerLine = 80 const maxCharsPerLine = 80
export default class NuxtCommand { export default class NuxtCommand {
constructor({ description, usage, options } = {}) { constructor({ name, description, usage, options, run } = {}) {
this.name = name || ''
this.description = description || '' this.description = description || ''
this.usage = usage || '' this.usage = usage || ''
this.options = Array.from(new Set((options || []).concat(DefaultOptions))) this.options = Object.assign({}, options)
this._run = run
}
static from(options) {
if (options instanceof NuxtCommand) {
return options
}
return new NuxtCommand(options)
} }
_getMinimistOptions() { _getMinimistOptions() {
@ -24,8 +31,8 @@ export default class NuxtCommand {
default: {} default: {}
} }
for (const name of this.options) { for (const name of Object.keys(this.options)) {
const option = Options[name] const option = this.options[name]
if (option.alias) { if (option.alias) {
minimistOptions.alias[option.alias] = name minimistOptions.alias[option.alias] = name
@ -54,13 +61,17 @@ export default class NuxtCommand {
return argv return argv
} }
run() {
return this._run(this)
}
async getNuxtConfig(argv, extraOptions) { async getNuxtConfig(argv, extraOptions) {
const config = await loadNuxtConfig(argv) const config = await loadNuxtConfig(argv)
const options = Object.assign(config, extraOptions || {}) const options = Object.assign(config, extraOptions || {})
for (const name of this.options) { for (const name of Object.keys(this.options)) {
if (Options[name].handle) { if (this.options[name].prepare) {
Options[name].handle(options, argv) this.options[name].prepare(this, options, argv)
} }
} }
@ -86,12 +97,11 @@ export default class NuxtCommand {
_getHelp() { _getHelp() {
const options = [] const options = []
let maxOptionLength = 0 let maxOptionLength = 0
// For consistency Options determines order
for (const name in Options) { for (const name in this.options) {
const option = Options[name] const option = this.options[name]
if (this.options.includes(name)) {
let optionHelp = '--' let optionHelp = '--'
optionHelp += option.type === 'boolean' && option.default ? 'no-' : '' optionHelp += option.type === 'boolean' && option.default ? 'no-' : ''
optionHelp += name optionHelp += name
@ -102,22 +112,22 @@ export default class NuxtCommand {
maxOptionLength = Math.max(maxOptionLength, optionHelp.length) maxOptionLength = Math.max(maxOptionLength, optionHelp.length)
options.push([ optionHelp, option.description ]) options.push([ optionHelp, option.description ])
} }
}
const optionStr = options.map(([option, description]) => { const _opts = options.map(([option, description]) => {
const line = option + const i = indent(maxOptionLength + optionSpaces - option.length)
indent(maxOptionLength + optionSpaces - option.length) + return foldLines(
wrapAnsi(description, maxCharsPerLine - startSpaces - maxOptionLength - optionSpaces) option + i + description,
return indentLines(line, startSpaces + maxOptionLength + optionSpaces, startSpaces) maxCharsPerLine,
startSpaces + maxOptionLength + optionSpaces * 2,
startSpaces + optionSpaces
)
}).join('\n') }).join('\n')
const usage = foldLines(`Usage: nuxt ${this.usage} [options]`, maxCharsPerLine, startSpaces)
const description = foldLines(this.description, maxCharsPerLine, startSpaces) const description = foldLines(this.description, maxCharsPerLine, startSpaces)
const opts = foldLines(`Options:`, maxCharsPerLine, startSpaces) + '\n\n' + _opts
return ` return `${usage}\n\n${description}\n\n${opts}\n\n`
Description\n${description}
Usage
$ nuxt ${this.usage}
Options\n${optionStr}\n\n`
} }
showVersion() { showVersion() {

View File

@ -1,13 +1,43 @@
import consola from 'consola' import consola from 'consola'
import NuxtCommand from '../command' import { common } from '../options'
export default async function build() { export default {
const nuxtCmd = new NuxtCommand({ name: 'build',
description: 'Compiles the application for production deployment', description: 'Compiles the application for production deployment',
usage: 'build <dir>', usage: 'build <dir>',
options: [ 'analyze', 'quiet' ] options: {
}) ...common,
analyze: {
alias: 'a',
type: 'boolean',
description: 'Launch webpack-bundle-analyzer to optimize your bundles',
prepare(cmd, options, argv) {
// Analyze option
options.build = options.build || {}
if (argv.analyze && typeof options.build.analyze !== 'object') {
options.build.analyze = true
}
}
},
generate: {
type: 'boolean',
default: true,
description: 'Don\'t generate static version for SPA mode (useful for nuxt start)'
},
quiet: {
alias: 'q',
type: 'boolean',
description: 'Disable output except for errors',
handle(options, argv) {
// Silence output when using --quiet
options.build = options.build || {}
if (argv.quiet) {
options.build.quiet = !!argv.quiet
}
}
}
},
async run(nuxtCmd) {
const argv = nuxtCmd.getArgv() const argv = nuxtCmd.getArgv()
// Create production build when calling `nuxt build` (dev: false) // Create production build when calling `nuxt build` (dev: false)
@ -44,3 +74,4 @@ export default async function build() {
}) })
.catch(err => consola.fatal(err)) .catch(err => consola.fatal(err))
} }
}

View File

@ -1,14 +1,16 @@
import consola from 'consola' import consola from 'consola'
import NuxtCommand from '../command' import { common, server } from '../options'
export default async function dev() { export default {
const nuxtCmd = new NuxtCommand({ name: 'dev',
description: 'Start the application in development mode (e.g. hot-code reloading, error reporting)', description: 'Start the application in development mode (e.g. hot-code reloading, error reporting)',
usage: 'dev <dir> -p <port number> -H <hostname>', usage: 'dev <dir>',
options: [ 'hostname', 'port' ] options: {
}) ...common,
...server
const argv = nuxtCmd.getArgv() },
async run(cmd) {
const argv = cmd.getArgv()
const errorHandler = (err, instance) => { const errorHandler = (err, instance) => {
instance && instance.builder.watchServer() instance && instance.builder.watchServer()
@ -20,10 +22,10 @@ export default async function dev() {
let nuxt, builder let nuxt, builder
try { try {
nuxt = await nuxtCmd.getNuxt( nuxt = await cmd.getNuxt(
await nuxtCmd.getNuxtConfig(argv, { dev: true }) await cmd.getNuxtConfig(argv, { dev: true })
) )
builder = await nuxtCmd.getBuilder(nuxt) builder = await cmd.getBuilder(nuxt)
nuxt.hook('watch:fileChanged', async (builder, fname) => { nuxt.hook('watch:fileChanged', async (builder, fname) => {
consola.debug(`[${fname}] changed, Rebuilding the app...`) consola.debug(`[${fname}] changed, Rebuilding the app...`)
await startDev({ nuxt: builder.nuxt, builder }) await startDev({ nuxt: builder.nuxt, builder })
@ -57,3 +59,4 @@ export default async function dev() {
await startDev() await startDev()
} }
}

View File

@ -1,18 +1,24 @@
import consola from 'consola' import consola from 'consola'
import NuxtCommand from '../command' import { common } from '../options'
export default async function generate() { export default {
const nuxtCmd = new NuxtCommand({ name: 'generate',
description: 'Generate a static web application (server-rendered)', description: 'Generate a static web application (server-rendered)',
usage: 'generate <dir>', usage: 'generate <dir>',
options: [ 'build' ] options: {
}) ...common,
build: {
type: 'boolean',
default: true,
description: 'Only generate pages for dynamic routes. Nuxt has to be built once before using this option'
}
},
async run(cmd) {
const argv = cmd.getArgv()
const argv = nuxtCmd.getArgv() const generator = await cmd.getGenerator(
await cmd.getNuxt(
const generator = await nuxtCmd.getGenerator( await cmd.getNuxtConfig(argv, { dev: false })
await nuxtCmd.getNuxt(
await nuxtCmd.getNuxtConfig(argv, { dev: false })
) )
) )
@ -23,3 +29,4 @@ export default async function generate() {
process.exit(0) process.exit(0)
}).catch(err => consola.fatal(err)) }).catch(err => consola.fatal(err))
} }
}

View File

@ -1,20 +1,22 @@
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import consola from 'consola' import consola from 'consola'
import NuxtCommand from '../command' import { common, server } from '../options'
export default async function start() { export default {
const nuxtCmd = new NuxtCommand({ name: 'start',
description: 'Start the application in production mode (the application should be compiled with `nuxt build` first)', description: 'Start the application in production mode (the application should be compiled with `nuxt build` first)',
usage: 'start <dir> -p <port number> -H <hostname>', usage: 'start <dir>',
options: [ 'hostname', 'port', 'unix-socket' ] options: {
}) ...common,
...server
const argv = nuxtCmd.getArgv() },
async run(cmd) {
const argv = cmd.getArgv()
// Create production build when calling `nuxt build` // Create production build when calling `nuxt build`
const nuxt = await nuxtCmd.getNuxt( const nuxt = await cmd.getNuxt(
await nuxtCmd.getNuxtConfig(argv, { dev: false }) await cmd.getNuxtConfig(argv, { dev: false })
) )
// Setup hooks // Setup hooks
@ -47,3 +49,4 @@ export default async function start() {
nuxt.showReady(false) nuxt.showReady(false)
}) })
} }
}

View File

@ -4,7 +4,7 @@ import * as _imports from './imports'
export const commands = _commands export const commands = _commands
export const imports = _imports export const imports = _imports
export { default as NuxtCommand } from './command'
export { default as setup } from './setup' export { default as setup } from './setup'
export { default as run } from './run' export { default as run } from './run'
export { loadNuxtConfig } from './utils' export { loadNuxtConfig } from './utils'

View File

@ -1,101 +0,0 @@
import consola from 'consola'
export const defaultOptions = [
'spa',
'universal',
'config-file',
'version',
'help'
]
export const options = {
port: {
alias: 'p',
type: 'string',
description: 'Port number on which to start the application',
handle(options, argv) {
if (argv.port) {
options.server.port = +argv.port
}
}
},
hostname: {
alias: 'H',
type: 'string',
description: 'Hostname on which to start the application',
handle(options, argv) {
if (argv.hostname === '') {
consola.fatal('Provided hostname argument has no value')
}
}
},
'unix-socket': {
alias: 'n',
type: 'string',
description: 'Path to a UNIX socket'
},
analyze: {
alias: 'a',
type: 'boolean',
description: 'Launch webpack-bundle-analyzer to optimize your bundles',
handle(options, argv) {
// Analyze option
options.build = options.build || {}
if (argv.analyze && typeof options.build.analyze !== 'object') {
options.build.analyze = true
}
}
},
build: {
type: 'boolean',
default: true,
description: 'Only generate pages for dynamic routes. Nuxt has to be built once before using this option'
},
generate: {
type: 'boolean',
default: true,
description: 'Don\'t generate static version for SPA mode (useful for nuxt start)'
},
spa: {
alias: 's',
type: 'boolean',
description: 'Launch in SPA mode'
},
universal: {
alias: 'u',
type: 'boolean',
description: 'Launch in Universal mode (default)'
},
'config-file': {
alias: 'c',
type: 'string',
default: 'nuxt.config.js',
description: 'Path to Nuxt.js config file (default: nuxt.config.js)'
},
quiet: {
alias: 'q',
type: 'boolean',
description: 'Disable output except for errors',
handle(options, argv) {
// Silence output when using --quiet
options.build = options.build || {}
if (argv.quiet) {
options.build.quiet = !!argv.quiet
}
}
},
verbose: {
alias: 'v',
type: 'boolean',
description: 'Show debug information'
},
version: {
type: 'boolean',
description: 'Display the Nuxt version'
},
help: {
alias: 'h',
type: 'boolean',
description: 'Display this message'
}
}

View File

@ -0,0 +1,27 @@
export default {
spa: {
alias: 's',
type: 'boolean',
description: 'Launch in SPA mode'
},
universal: {
alias: 'u',
type: 'boolean',
description: 'Launch in Universal mode (default)'
},
'config-file': {
alias: 'c',
type: 'string',
default: 'nuxt.config.js',
description: 'Path to Nuxt.js config file (default: nuxt.config.js)'
},
version: {
type: 'boolean',
description: 'Display the Nuxt version'
},
help: {
alias: 'h',
type: 'boolean',
description: 'Display this message'
}
}

View File

@ -0,0 +1,2 @@
export { default as common } from './common'
export { default as server } from './server'

View File

@ -0,0 +1,29 @@
import consola from 'consola'
export default {
port: {
alias: 'p',
type: 'string',
description: 'Port number on which to start the application',
prepare(cmd, options, argv) {
if (argv.port) {
options.server.port = +argv.port
}
}
},
hostname: {
alias: 'H',
type: 'string',
description: 'Hostname on which to start the application',
prepare(cmd, options, argv) {
if (argv.hostname === '') {
consola.fatal('Provided hostname argument has no value')
}
}
},
'unix-socket': {
alias: 'n',
type: 'string',
description: 'Path to a UNIX socket'
}
}

View File

@ -1,4 +1,5 @@
import consola from 'consola' import consola from 'consola'
import NuxtCommand from './command'
import * as commands from './commands' import * as commands from './commands'
import setup from './setup' import setup from './setup'
@ -26,7 +27,9 @@ export default function run() {
}) })
return commands[cmd]() // eslint-disable-line import/namespace return commands[cmd]() // eslint-disable-line import/namespace
.then(m => m.default()) .then(m => m.default)
.then(options => NuxtCommand.from(options))
.then(command => command.run())
.catch((error) => { .catch((error) => {
consola.fatal(error) consola.fatal(error)
}) })

View File

@ -76,11 +76,11 @@ export function indentLines(string, spaces, firstLineSpaces) {
} }
if (lines.length) { if (lines.length) {
const i = indent(spaces) const i = indent(spaces)
s += '\n' + lines.map(l => i + l.trim()).join('\n') s += '\n' + lines.map(l => i + l).join('\n')
} }
return s return s
} }
export function foldLines(string, maxCharsPerLine, spaces, firstLineSpaces) { export function foldLines(string, maxCharsPerLine, spaces, firstLineSpaces) {
return indentLines(wrapAnsi(string, maxCharsPerLine), spaces, firstLineSpaces) return indentLines(wrapAnsi(string, maxCharsPerLine, { trim: false }), spaces, firstLineSpaces)
} }

View File

@ -0,0 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`cli/command builds help text 1`] = `
" Usage: nuxt this is how you do it [options]
a very long description that is longer than 80 chars and should wrap to the next
line while keeping indentation
Options:
--spa, -s Launch in SPA mode
--universal, -u Launch in Universal mode (default)
--config-file, -c Path to Nuxt.js config file (default: nuxt.config.js)
--version Display the Nuxt version
--help, -h Display this message
--port, -p Port number on which to start the application
--hostname, -H Hostname on which to start the application
--unix-socket, -n Path to a UNIX socket
--foo very long option that is longer than 80 chars and should wrap
to the next line while keeping indentation
"
`;

View File

@ -1,25 +1,18 @@
import { consola, mockGetNuxt, mockGetBuilder, mockGetGenerator } from '../utils' import { consola, mockGetNuxt, mockGetBuilder, mockGetGenerator, NuxtCommand } from '../utils'
describe('build', () => { describe('build', () => {
let build let build
beforeAll(async () => { beforeAll(async () => {
build = await import('../../src/commands/build') build = await import('../../src/commands/build').then(m => m.default)
build = build.default
jest.spyOn(process, 'exit').mockImplementation(code => code) jest.spyOn(process, 'exit').mockImplementation(code => code)
}) })
afterAll(() => { afterAll(() => process.exit.mockRestore())
process.exit.mockRestore() afterEach(() => jest.resetAllMocks())
})
afterEach(() => { test('has run function', () => {
jest.resetAllMocks() expect(typeof build.run).toBe('function')
})
test('is function', () => {
expect(typeof build).toBe('function')
}) })
test('builds on universal mode', async () => { test('builds on universal mode', async () => {
@ -31,7 +24,7 @@ describe('build', () => {
}) })
const builder = mockGetBuilder(Promise.resolve()) const builder = mockGetBuilder(Promise.resolve())
await build() await NuxtCommand.from(build).run()
expect(builder).toHaveBeenCalled() expect(builder).toHaveBeenCalled()
}) })
@ -45,7 +38,7 @@ describe('build', () => {
}) })
const generate = mockGetGenerator(Promise.resolve()) const generate = mockGetGenerator(Promise.resolve())
await build() await NuxtCommand.from(build).run()
expect(generate).toHaveBeenCalled() expect(generate).toHaveBeenCalled()
expect(process.exit).toHaveBeenCalled() expect(process.exit).toHaveBeenCalled()
@ -55,7 +48,7 @@ describe('build', () => {
mockGetNuxt({ mode: 'universal' }) mockGetNuxt({ mode: 'universal' })
mockGetBuilder(Promise.reject(new Error('Builder Error'))) mockGetBuilder(Promise.reject(new Error('Builder Error')))
await build() await NuxtCommand.from(build).run()
expect(consola.fatal).toHaveBeenCalledWith(new Error('Builder Error')) expect(consola.fatal).toHaveBeenCalledWith(new Error('Builder Error'))
}) })

View File

@ -10,9 +10,7 @@ const readDir = promisify(readdir)
jest.mock('../../src/commands') jest.mock('../../src/commands')
describe('cli', () => { describe('cli', () => {
afterEach(() => { afterEach(() => jest.resetAllMocks())
jest.resetAllMocks()
})
test('exports for all commands defined', async () => { test('exports for all commands defined', async () => {
const cmds = await readDir(resolve(__dirname, '..', '..', 'src', 'commands')) const cmds = await readDir(resolve(__dirname, '..', '..', 'src', 'commands'))
@ -32,12 +30,14 @@ describe('cli', () => {
test('calls expected method', async () => { test('calls expected method', async () => {
const argv = process.argv const argv = process.argv
process.argv = ['', '', 'dev'] process.argv = ['', '', 'dev']
const defaultExport = jest.fn().mockImplementation(() => Promise.resolve()) const defaultExport = {
run: jest.fn().mockImplementation(() => Promise.resolve())
}
commands.dev.mockImplementationOnce(() => Promise.resolve({ default: defaultExport })) commands.dev.mockImplementationOnce(() => Promise.resolve({ default: defaultExport }))
await run() await run()
expect(defaultExport).toHaveBeenCalled() expect(defaultExport.run).toHaveBeenCalled()
process.argv = argv process.argv = argv
}) })

View File

@ -1,39 +1,31 @@
import Command from '../../src/command' import Command from '../../src/command'
import { options as Options } from '../../src/options' import { common, server } from '../../src/options'
import { consola } from '../utils' import { consola } from '../utils'
jest.mock('@nuxt/core') jest.mock('@nuxt/core')
jest.mock('@nuxt/builder') jest.mock('@nuxt/builder')
jest.mock('@nuxt/generator') jest.mock('@nuxt/generator')
const allOptions = {
...common,
...server
}
describe('cli/command', () => { describe('cli/command', () => {
beforeEach(() => { beforeEach(() => jest.restoreAllMocks())
jest.restoreAllMocks()
})
test('adds default options', () => {
const cmd = new Command()
expect(cmd.options.length).not.toBe(0)
})
test('builds minimist options', () => { test('builds minimist options', () => {
const cmd = new Command({ const cmd = new Command({ options: allOptions })
options: Object.keys(Options)
})
const minimistOptions = cmd._getMinimistOptions() const minimistOptions = cmd._getMinimistOptions()
expect(minimistOptions.string.length).toBe(4) expect(minimistOptions.string.length).toBe(4)
expect(minimistOptions.boolean.length).toBe(9) expect(minimistOptions.boolean.length).toBe(4)
expect(minimistOptions.alias.c).toBe('config-file') expect(minimistOptions.alias.c).toBe('config-file')
expect(minimistOptions.default.c).toBe(Options['config-file'].default) expect(minimistOptions.default.c).toBe(common['config-file'].default)
}) })
test('parses args', () => { test('parses args', () => {
const cmd = new Command({ const cmd = new Command({ options: { ...common, ...server } })
options: Object.keys(Options)
})
let args = ['-c', 'test-file', '-s', '-p', '3001'] let args = ['-c', 'test-file', '-s', '-p', '3001']
let argv = cmd.getArgv(args) let argv = cmd.getArgv(args)
@ -41,7 +33,6 @@ describe('cli/command', () => {
expect(argv['config-file']).toBe(args[1]) expect(argv['config-file']).toBe(args[1])
expect(argv.spa).toBe(true) expect(argv.spa).toBe(true)
expect(argv.universal).toBe(false) expect(argv.universal).toBe(false)
expect(argv.build).toBe(true)
expect(argv.port).toBe('3001') expect(argv.port).toBe('3001')
args = ['--no-build'] args = ['--no-build']
@ -61,7 +52,7 @@ describe('cli/command', () => {
}) })
test('prints help automatically', () => { test('prints help automatically', () => {
const cmd = new Command() const cmd = new Command({ options: allOptions })
cmd.showHelp = jest.fn() cmd.showHelp = jest.fn()
const args = ['-h'] const args = ['-h']
@ -71,9 +62,7 @@ describe('cli/command', () => {
}) })
test('returns nuxt config', async () => { test('returns nuxt config', async () => {
const cmd = new Command({ const cmd = new Command({ options: allOptions })
options: Object.keys(Options)
})
const args = ['-c', 'test-file', '-a', '-p', '3001', '-q', '-H'] const args = ['-c', 'test-file', '-a', '-p', '3001', '-q', '-H']
const argv = cmd.getArgv(args) const argv = cmd.getArgv(args)
@ -83,8 +72,6 @@ describe('cli/command', () => {
expect(options.testOption).toBe(true) expect(options.testOption).toBe(true)
expect(options.server.port).toBe(3001) expect(options.server.port).toBe(3001)
expect(options.build.quiet).toBe(true)
expect(options.build.analyze).toBe(true)
expect(consola.fatal).toHaveBeenCalledWith('Provided hostname argument has no value') // hostname check expect(consola.fatal).toHaveBeenCalledWith('Provided hostname argument has no value') // hostname check
}) })
@ -117,26 +104,17 @@ describe('cli/command', () => {
description: 'a very long description that is longer than 80 chars and ' + description: 'a very long description that is longer than 80 chars and ' +
'should wrap to the next line while keeping indentation', 'should wrap to the next line while keeping indentation',
usage: 'this is how you do it', usage: 'this is how you do it',
options: ['build'] options: {
...allOptions,
foo: {
type: 'boolean',
description: 'very long option that is longer than 80 chars and ' +
'should wrap to the next line while keeping indentation'
}
}
}) })
const expectedText = ` expect(cmd._getHelp()).toMatchSnapshot()
Description
a very long description that is longer than 80 chars and should wrap to the next
line while keeping indentation
Usage
$ nuxt this is how you do it
Options
--no-build Only generate pages for dynamic routes. Nuxt has to be
built once before using this option
--spa, -s Launch in SPA mode
--universal, -u Launch in Universal mode (default)
--config-file, -c Path to Nuxt.js config file (default: nuxt.config.js)
--version Display the Nuxt version
--help, -h Display this message
`
expect(cmd._getHelp()).toBe(expectedText)
}) })
test('show version prints to stdout and exits', () => { test('show version prints to stdout and exits', () => {

View File

@ -1,26 +1,23 @@
import { consola, mockNuxt, mockBuilder, mockGetNuxtConfig } from '../utils' import { consola, mockNuxt, mockBuilder, mockGetNuxtConfig, NuxtCommand } from '../utils'
describe('dev', () => { describe('dev', () => {
let dev let dev
beforeAll(async () => { beforeAll(async () => {
dev = await import('../../src/commands/dev') dev = await import('../../src/commands/dev').then(m => m.default)
dev = dev.default
}) })
afterEach(() => { afterEach(() => jest.clearAllMocks())
jest.clearAllMocks()
})
test('is function', () => { test('has run function', () => {
expect(typeof dev).toBe('function') expect(typeof dev.run).toBe('function')
}) })
test('reloads on fileChanged hook', async () => { test('reloads on fileChanged hook', async () => {
const Nuxt = mockNuxt() const Nuxt = mockNuxt()
const Builder = mockBuilder() const Builder = mockBuilder()
await dev() await NuxtCommand.from(dev).run()
expect(consola.error).not.toHaveBeenCalled() expect(consola.error).not.toHaveBeenCalled()
@ -51,7 +48,7 @@ describe('dev', () => {
const Nuxt = mockNuxt() const Nuxt = mockNuxt()
const Builder = mockBuilder() const Builder = mockBuilder()
await dev() await NuxtCommand.from(dev).run()
jest.clearAllMocks() jest.clearAllMocks()
// Test error on second build so we cover oldInstance stuff // Test error on second build so we cover oldInstance stuff
@ -68,7 +65,7 @@ describe('dev', () => {
const Nuxt = mockNuxt() const Nuxt = mockNuxt()
const Builder = mockBuilder() const Builder = mockBuilder()
await dev() await NuxtCommand.from(dev).run()
jest.clearAllMocks() jest.clearAllMocks()
const builder = new Builder() const builder = new Builder()
@ -84,7 +81,7 @@ describe('dev', () => {
const Nuxt = mockNuxt() const Nuxt = mockNuxt()
const Builder = mockBuilder() const Builder = mockBuilder()
await dev() await NuxtCommand.from(dev).run()
jest.clearAllMocks() jest.clearAllMocks()
mockGetNuxtConfig().mockImplementationOnce(() => { mockGetNuxtConfig().mockImplementationOnce(() => {
@ -106,7 +103,7 @@ describe('dev', () => {
}) })
mockBuilder() mockBuilder()
await dev() await NuxtCommand.from(dev).run()
expect(consola.error).toHaveBeenCalledWith(new Error('Listen Error')) expect(consola.error).toHaveBeenCalledWith(new Error('Listen Error'))
}) })

View File

@ -1,33 +1,26 @@
import { consola, mockGetNuxt, mockGetGenerator } from '../utils' import { consola, mockGetNuxt, mockGetGenerator, NuxtCommand } from '../utils'
import Command from '../../src/command' import Command from '../../src/command'
describe('generate', () => { describe('generate', () => {
let generate let generate
beforeAll(async () => { beforeAll(async () => {
generate = await import('../../src/commands/generate') generate = await import('../../src/commands/generate').then(m => m.default)
generate = generate.default
jest.spyOn(process, 'exit').mockImplementation(code => code) jest.spyOn(process, 'exit').mockImplementation(code => code)
}) })
afterAll(() => { afterAll(() => process.exit.mockRestore())
process.exit.mockRestore() afterEach(() => jest.resetAllMocks())
})
afterEach(() => { test('has run function', () => {
jest.resetAllMocks() expect(typeof generate.run).toBe('function')
})
test('is function', () => {
expect(typeof generate).toBe('function')
}) })
test('builds by default', async () => { test('builds by default', async () => {
mockGetNuxt() mockGetNuxt()
const generator = mockGetGenerator(Promise.resolve()) const generator = mockGetGenerator(Promise.resolve())
await generate() await NuxtCommand.from(generate).run()
expect(generator).toHaveBeenCalled() expect(generator).toHaveBeenCalled()
expect(generator.mock.calls[0][0].build).toBe(true) expect(generator.mock.calls[0][0].build).toBe(true)
@ -46,7 +39,7 @@ describe('generate', () => {
}) })
const generator = mockGetGenerator(Promise.resolve()) const generator = mockGetGenerator(Promise.resolve())
await generate() await NuxtCommand.from(generate).run()
expect(generator).toHaveBeenCalled() expect(generator).toHaveBeenCalled()
expect(generator.mock.calls[0][0].build).toBe(false) expect(generator.mock.calls[0][0].build).toBe(false)
@ -57,7 +50,7 @@ describe('generate', () => {
mockGetNuxt() mockGetNuxt()
mockGetGenerator(Promise.reject(new Error('Generator Error'))) mockGetGenerator(Promise.reject(new Error('Generator Error')))
await generate() await NuxtCommand.from(generate).run()
expect(consola.fatal).toHaveBeenCalledWith(new Error('Generator Error')) expect(consola.fatal).toHaveBeenCalledWith(new Error('Generator Error'))
}) })

View File

@ -1,29 +1,27 @@
import fs from 'fs' import fs from 'fs'
import { consola, mockGetNuxtStart, mockGetNuxtConfig } from '../utils' import { consola, mockGetNuxtStart, mockGetNuxtConfig, NuxtCommand } from '../utils'
describe('start', () => { describe('start', () => {
let start let start
beforeAll(async () => { beforeAll(async () => {
start = await import('../../src/commands/start') start = await import('../../src/commands/start').then(m => m.default)
start = start.default
}) })
afterEach(() => { afterEach(() => {
if (fs.existsSync.mockRestore) { if (fs.existsSync.mockRestore) {
fs.existsSync.mockRestore() fs.existsSync.mockRestore()
} }
jest.resetAllMocks() jest.resetAllMocks()
}) })
test('is function', () => { test('has run function', () => {
expect(typeof start).toBe('function') expect(typeof start.run).toBe('function')
}) })
test('starts listening and calls showReady', async () => { test('starts listening and calls showReady', async () => {
const { listen, showReady } = mockGetNuxtStart() const { listen, showReady } = mockGetNuxtStart()
await start() await NuxtCommand.from(start).run()
expect(listen).toHaveBeenCalled() expect(listen).toHaveBeenCalled()
expect(showReady).toHaveBeenCalled() expect(showReady).toHaveBeenCalled()
@ -34,7 +32,7 @@ describe('start', () => {
mockGetNuxtConfig() mockGetNuxtConfig()
jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => true) jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => true)
await start() await NuxtCommand.from(start).run()
expect(consola.fatal).not.toHaveBeenCalled() expect(consola.fatal).not.toHaveBeenCalled()
}) })
@ -43,7 +41,7 @@ describe('start', () => {
mockGetNuxtStart() mockGetNuxtStart()
jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => false) jest.spyOn(fs, 'existsSync').mockImplementationOnce(() => false)
await start() await NuxtCommand.from(start).run()
expect(consola.fatal).toHaveBeenCalledWith('No build files found, please run `nuxt build` before launching `nuxt start`') expect(consola.fatal).toHaveBeenCalledWith('No build files found, please run `nuxt build` before launching `nuxt start`')
}) })
@ -53,7 +51,7 @@ describe('start', () => {
mockGetNuxtConfig() mockGetNuxtConfig()
jest.spyOn(fs, 'existsSync').mockImplementation(() => true) jest.spyOn(fs, 'existsSync').mockImplementation(() => true)
await start() await NuxtCommand.from(start).run()
expect(consola.fatal).not.toHaveBeenCalled() expect(consola.fatal).not.toHaveBeenCalled()
}) })
@ -62,7 +60,7 @@ describe('start', () => {
mockGetNuxtStart(true) mockGetNuxtStart(true)
jest.spyOn(fs, 'existsSync').mockImplementation(() => false) jest.spyOn(fs, 'existsSync').mockImplementation(() => false)
await start() await NuxtCommand.from(start).run()
expect(consola.fatal).toHaveBeenCalledWith('No SSR build! Please start with `nuxt start --spa` or build using `nuxt build --universal`') expect(consola.fatal).toHaveBeenCalledWith('No SSR build! Please start with `nuxt start --spa` or build using `nuxt build --universal`')
}) })

View File

@ -3,9 +3,7 @@ import { consola } from '../utils'
import * as utils from '../../src/utils' import * as utils from '../../src/utils'
describe('cli/utils', () => { describe('cli/utils', () => {
afterEach(() => { afterEach(() => jest.resetAllMocks())
jest.resetAllMocks()
})
test('loadNuxtConfig: defaults', async () => { test('loadNuxtConfig: defaults', async () => {
const argv = { const argv = {

View File

@ -1,5 +1,6 @@
import consola from 'consola' import consola from 'consola'
export * from './mocking' export * from './mocking'
export { NuxtCommand } from '../../src'
jest.mock('consola') jest.mock('consola')