Skip to content

Commit

Permalink
Merge pull request #270 from infinitered/mock-new
Browse files Browse the repository at this point in the history
Improves the testing experience
  • Loading branch information
skellock committed Nov 7, 2017
2 parents e036273 + f8c74ce commit a9ed51f
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 16 deletions.
2 changes: 2 additions & 0 deletions src/cli/cli.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const test = require('ava')
const cli = require('./cli')
const sinon = require('sinon')
sinon.stub(console, 'log')

test('can start the cli', async t => {
const c = await cli()
Expand Down
2 changes: 2 additions & 0 deletions src/cli/commands/generate/generate.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const test = require('ava')
const cli = require('../../cli')
const sinon = require('sinon')
sinon.stub(console, 'log')

test('runs generate', async t => {
const r = await cli(['generate'])
Expand Down
2 changes: 2 additions & 0 deletions src/cli/commands/generate/help.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const test = require('ava')
const cli = require('../../cli')
const sinon = require('sinon')
sinon.stub(console, 'log')

test('runs generate', async t => {
const r = await cli(['generate', 'help'])
Expand Down
2 changes: 2 additions & 0 deletions src/cli/commands/gluegun.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const test = require('ava')
const cli = require('../cli')
const sinon = require('sinon')
sinon.stub(console, 'log')

test('runs generate', async t => {
const r = await cli([''])
Expand Down
2 changes: 2 additions & 0 deletions src/cli/commands/help.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const test = require('ava')
const cli = require('../cli')
const sinon = require('sinon')
sinon.stub(console, 'log')

test('runs generate', async t => {
const r = await cli(['help'])
Expand Down
9 changes: 4 additions & 5 deletions src/cli/commands/new.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ module.exports = {
hidden: false,
run: async (context) => {
const { parameters, template, filesystem, print, strings, system } = context
const { resolve } = filesystem
const { generate } = template
const { kebabCase } = strings

Expand Down Expand Up @@ -36,7 +35,7 @@ module.exports = {
active.push(generate({
template: `cli/bin/cli-executable.ejs`,
target: `./${props.name}/bin/${props.name}`,
props: props,
props: props
}))

const files = [
Expand All @@ -51,7 +50,7 @@ module.exports = {
'.prettierrc.ejs',
'package.json.ejs',
'readme.md.ejs',
'.gitignore.ejs',
'.gitignore.ejs'
]

if (props.typescript) {
Expand All @@ -66,15 +65,15 @@ module.exports = {
? file.replace('.js.ejs', '.ts')
: file.replace('.ejs', ''))

gen = generate({ template, target, props })
const gen = generate({ template, target, props })
return prev.concat([ gen ])
}, active)

// let all generator calls run in parallel
await Promise.all(active)

// make bin executable
filesystem.chmodSync(`${props.name}/bin/${props.name}`, 0755)
filesystem.chmodSync(`${props.name}/bin/${props.name}`, '755')

await system.spawn(`cd ${props.name} && npm i && npm run format`, { shell: true, stdio: 'inherit', stderr: 'inherit' })
print.info(`cd ${props.name} && npm i && npm format`)
Expand Down
195 changes: 185 additions & 10 deletions src/cli/commands/new.test.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,192 @@
const test = require('ava')
const fs = require('fs')
const path = require('path')
const os = require('os')
const sinon = require('sinon')
const command = require('./new')
const strings = require('../../utils/string-utils')
sinon.stub(console, 'log')

const cli = require('../cli')
const createContext = () => ({
strings,
filesystem: {
resolve: sinon.stub(),
dir: sinon.stub(),
chmodSync: sinon.stub()
},
system: {
spawn: sinon.stub()
},
template: { generate: sinon.stub() },
print: {
info: sinon.stub(),
error: sinon.stub()
},
parameters: { first: null, options: {} }
})

test('has the right interface', t => {
t.is(command.name, 'new')
t.is(command.description, 'Creates a new gluegun cli')
t.false(command.hidden)
t.deepEqual(command.alias, ['n', 'create'])
t.is(typeof command.run, 'function')
})

test('name is required', async t => {
const context = createContext()
context.parameters.first = null
await command.run(context)
const { error } = context.print
t.is(error.getCall(0).args[0], 'You must provide a valid CLI name.')
t.is(error.getCall(1).args[0], 'Example: gluegun new foo')
})

test('name cannot be blank', async t => {
const context = createContext()
context.parameters.first = ''
await command.run(context)
const { error } = context.print
t.deepEqual(error.getCall(0).args, ['You must provide a valid CLI name.'])
t.deepEqual(error.getCall(1).args, ['Example: gluegun new foo'])
})

test('name must pass regex', async t => {
const context = createContext()
const name = 'O M G'
context.parameters.first = name
await command.run(context)
const { error } = context.print
t.deepEqual(error.getCall(0).args, [
`${name} is not a valid name. Use lower-case and dashes only.`
])
t.deepEqual(error.getCall(1).args, [
`Suggested: gluegun new ${strings.kebabCase(name)}`
])
})

test('generates properly', async t => {
const name = 'foo'
const typescript = undefined
const context = createContext()
context.parameters.first = name

// here we run the command
const result = await command.run(context)

// setup some conveniences so we don't have giant lines
const { dir, chmodSync } = context.filesystem
const { generate } = context.template
const { spawn } = context.system
const props = { name, typescript }

// assure that the directory was created
t.is(dir.firstCall.args[0], name)

// tracks the number of files generated
let i = 0

// the executable file
t.deepEqual(generate.getCall(i++).args[0], {
template: `cli/bin/cli-executable.ejs`,
target: `./${name}/bin/${name}`,
props
})

const DEFAULT_FILES = [
['docs/commands.md.ejs', 'docs/commands.md'],
['docs/plugins.md.ejs', 'docs/plugins.md'],
['src/commands/help.js.ejs', 'src/commands/help.js'],
['src/commands/generate.js.ejs', 'src/commands/generate.js'],
['src/extensions/cli-extension.js.ejs', 'src/extensions/cli-extension.js'],
['src/templates/model.js.ejs.ejs', 'src/templates/model.js.ejs'],
['src/cli.js.ejs', 'src/cli.js'],
['LICENSE.ejs', 'LICENSE'],
['.prettierrc.ejs', '.prettierrc'],
['package.json.ejs', 'package.json'],
['readme.md.ejs', 'readme.md'],
['.gitignore.ejs', '.gitignore']
]

// test that each our files get generated
DEFAULT_FILES.forEach(file => {
t.deepEqual(generate.getCall(i++).args[0], {
template: `cli/${file[0]}`,
target: `${name}/${file[1]}`,
props
})
})

// test permissions
t.deepEqual(chmodSync.firstCall.args, [`${name}/bin/${name}`, '755'])

// test package installation
t.deepEqual(spawn.firstCall.args, [
`cd ${props.name} && npm i && npm run format`,
{ shell: true, stdio: 'inherit', stderr: 'inherit' }
])

t.is(result, `new ${name}`)
})

test('generates with typescript', async t => {
const name = 'foo'
const typescript = true
const context = createContext()
context.parameters.first = name
context.parameters.options.typescript = true

// here we run the command
const result = await command.run(context)

// setup some conveniences so we don't have giant lines
const { dir, chmodSync } = context.filesystem
const { generate } = context.template
const { spawn } = context.system
const props = { name, typescript }

// assure that the directory was created
t.is(dir.firstCall.args[0], name)

// tracks the number of files generated
let i = 0

// the executable file
t.deepEqual(generate.getCall(i++).args[0], {
template: `cli/bin/cli-executable.ejs`,
target: `./${name}/bin/${name}`,
props
})

const DEFAULT_FILES = [
['docs/commands.md.ejs', 'docs/commands.md'],
['docs/plugins.md.ejs', 'docs/plugins.md'],
['src/commands/help.js.ejs', 'src/commands/help.ts'],
['src/commands/generate.js.ejs', 'src/commands/generate.ts'],
['src/extensions/cli-extension.js.ejs', 'src/extensions/cli-extension.ts'],
['src/templates/model.js.ejs.ejs', 'src/templates/model.ts.ejs'],
['src/cli.js.ejs', 'src/cli.ts'],
['LICENSE.ejs', 'LICENSE'],
['.prettierrc.ejs', '.prettierrc'],
['package.json.ejs', 'package.json'],
['readme.md.ejs', 'readme.md'],
['.gitignore.ejs', '.gitignore']
]

test('runs generate', async t => {
const cwd = process.cwd()
// test that each our files get generated
DEFAULT_FILES.forEach(file => {
t.deepEqual(generate.getCall(i++).args[0], {
template: `cli/${file[0]}`,
target: `${name}/${file[1]}`,
props
})
})

process.chdir(fs.mkdtempSync(path.join(os.tmpdir(), 'gluegun-cli-new-test')))
// test permissions
t.deepEqual(chmodSync.firstCall.args, [`${name}/bin/${name}`, '755'])

const r = await cli(['new', 'foo'])
t.is(r.result, 'new foo')
// test package installation
t.deepEqual(spawn.firstCall.args, [
`cd ${props.name} && npm i && npm run format`,
{ shell: true, stdio: 'inherit', stderr: 'inherit' }
])

process.chdir(cwd)
t.is(result, `new ${name}`)
})
2 changes: 1 addition & 1 deletion src/utils/print.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ test.serial('table', t => {
t.is(spyLog.args[i++][0], ' liam 5 \n matthew 2 ')
})

test.only('spin', t => {
test.serial('spin', t => {
t.is(typeof print.spin, 'function')
const spinner = print.spin()
t.is(typeof spinner.stop, 'function')
Expand Down

0 comments on commit a9ed51f

Please sign in to comment.