boip
Version:
a command line application that creates a new project from a boilerplate stored on GitHub
374 lines (367 loc) • 11.8 kB
JavaScript
/* eslint-disable jsdoc/require-example */
const del = require('del')
const fs = require('fs')
const globby = require('globby')
const prompt = require('../prompt')
const Con = require('../../src/helpers/con')
const cli = require('../cli')
const createAction = require('../createAction')
const configAction = require('../configAction')
// const opts = {}
jest.mock('../prompt')
jest.mock('ora')
const projectName = 'testProjectName'
const repoName = 'mick-whats/bo#test'
// const repoName = 'mick-whats/bo#test_v1.0.0'
// eslint-disable-next-line jsdoc/require-example
// eslint-disable-next-line jsdoc/require-param
/**
* cac.cli.parseを利用した実行test
* 内部的にactionを呼び出して実行しているのでcli.js-_action()は使っていない
*
*/
async function exec (args) {
const opts = cli.parse(args, { run: false })
const fn = (() => {
switch (args[2]) {
case 'create':
return createAction
case 'injections':
opts.args.unshift('injections')
return configAction
default:
throw new Error('Unknown command')
}
})()
const res = await fn(opts.options, ...opts.args)
return res
}
/**
* 未使用
* cli.parse(args)の終了を取得できないのでsetTimeoutで処理。その為に時間がかかる
*
* @param {*} args - cliに渡すoptions
* @param {number} [timeOut=500] - 終了まで待機するミリ秒数
* @returns {Promise} 処理結果
*/
function _exec (args, timeOut = 500) {
return new Promise((resolve, reject) => {
try {
cli.parse(args)
setTimeout(() => {
resolve(0)
}, timeOut)
} catch (error) {
return reject(error)
}
})
}
/**
* 呼び出された回数を返す
*
* @returns {Object} mockが呼ばれた回数
*/
function getCalls () {
return {
projectName: prompt.inputProjectName.mock.calls.length,
repoName: prompt.inputRepositoryName.mock.calls.length,
injectKey: prompt.inputInjectKey.mock.calls.length,
selectRepo: prompt.selectRepository.mock.calls.length,
confirm: prompt.confirm.mock.calls.length
}
}
/**
* 変換前の値を確認
*
* @param {*} _path - 確認するファイルパス
*/
function assertBeforePlace (_path) {
const sample = fs.readFileSync(_path, 'utf-8')
expect(sample).toMatch('pascal: {{ text1 : pascal }}')
expect(sample).toMatch('dot: {{text1 : dot}}')
expect(sample).toMatch('path: {{text1:path}}')
expect(sample).toMatch('header: {{ text1: header }}')
}
/**
* 変換後の値を確認
*
* @param {*} _path - 確認するファイルパス
*/
function assertAfterPlace (_path) {
const sample = fs.readFileSync(_path, 'utf-8')
expect(sample).toMatch('pascal: HelloWorld')
expect(sample).toMatch('dot: hello.world')
expect(sample).toMatch('path: hello/world')
expect(sample).toMatch('header: Hello-World')
}
beforeEach(async () => {
const con = Con('injections')
con('owner', 'mick-whats')
con('text1', 'helloWorld')
con('test1', 'delete')
con('test2', 'delete')
jest.clearAllMocks()
prompt.inputProjectName.mockResolvedValue(projectName)
prompt.inputRepositoryName.mockResolvedValue(repoName)
prompt.selectRepository.mockResolvedValue(repoName)
prompt.inputInjectKey.mockImplementation(key => {
const obj = { owner: 'mick-whats', text1: 'helloWorld' }
return Promise.resolve(obj[key])
})
prompt.confirm.mockResolvedValue(true)
await del(['testProjectName/**'], { dot: true, onlyFiles: false })
})
test('injections', () => {
const args = cli.parse(['node', 'boip', 'injections', 'key1', 'val1', '-d'], {
run: false
})
expect(args).toEqual({
args: ['key1', 'val1'],
options: { '--': [], d: true }
})
})
describe('real execute', () => {
test('create projectName', async () => {
const args = ['node', 'boip', 'create', projectName, '-r', repoName]
const opts = cli.parse(args, { run: false })
expect(opts).toEqual({
args: ['testProjectName'],
options: {
'--': [],
r: 'mick-whats/bo#test',
repository: 'mick-whats/bo#test'
}
})
const res = await exec(args)
expect(res).toBe('created new Project: testProjectName')
const files = await globby(['testProjectName/**/*'])
expect(files).toEqual(
expect.arrayContaining([
'testProjectName/LICENSE',
'testProjectName/README.md',
'testProjectName/nojectSample.md',
'testProjectName/package.json',
'testProjectName/sample.md',
'testProjectName/lib/index.js',
'testProjectName/test/index.test.js'
])
)
assertAfterPlace('testProjectName/sample.md')
assertBeforePlace('testProjectName/nojectSample.md')
await expect(createAction(opts)).rejects.toThrow(
'The project "testProjectName" already exists'
)
})
test('noject', async () => {
const args = [
'node',
'boip',
'create',
projectName,
'-r',
repoName,
'--noject',
'sample.md',
'-o'
]
const opts = cli.parse(args, { run: false })
expect(opts).toEqual({
args: ['testProjectName'],
options: {
'--': [],
n: 'sample.md',
noject: 'sample.md',
o: true,
offline: true,
r: 'mick-whats/bo#test',
repository: 'mick-whats/bo#test'
}
})
const res = await exec(args)
// console.log('res: ', res)
expect(res).toBe('created new Project: testProjectName')
const files = await globby(['testProjectName/**/*'])
expect(files).toEqual(
expect.arrayContaining([
'testProjectName/LICENSE',
'testProjectName/README.md',
'testProjectName/nojectSample.md',
'testProjectName/package.json',
'testProjectName/sample.md',
'testProjectName/lib/index.js',
'testProjectName/test/index.test.js'
])
)
const sample = fs.readFileSync('testProjectName/sample.md', 'utf-8')
expect(sample).toMatch('pascal: {{ text1 : pascal }}')
expect(sample).toMatch('dot: {{text1 : dot}}')
expect(sample).toMatch('path: {{text1:path}}')
expect(sample).toMatch('header: {{ text1: header }}')
})
})
describe('dry test', () => {
test('create projectName', () => {
const args = ['node', 'boip', 'create', projectName, '-r', repoName, '-od']
const opts = cli.parse(args, { run: false })
expect(opts).toEqual({
args: ['testProjectName'],
options: {
'--': [],
r: 'mick-whats/bo#test',
o: true,
d: true,
repository: 'mick-whats/bo#test',
offline: true,
dry: true
}
})
})
test('create nothing projectName', () => {
const args = ['node', 'boip', 'create', '-r', repoName, '-od']
const opts = cli.parse(args, { run: false })
expect(opts).toEqual({
args: [],
options: {
'--': [],
r: 'mick-whats/bo#test',
o: true,
d: true,
repository: 'mick-whats/bo#test',
offline: true,
dry: true
}
})
})
test('create nothing projectName', async () => {
const args = ['node', 'boip', 'create', '-r', repoName, '-od']
const res = await exec(args)
expect(res).toBe('try remove option "-d" or "--dry"')
expect(getCalls()).toEqual({
projectName: 1,
repoName: 0,
injectKey: 2,
selectRepo: 0,
confirm: 1
})
})
test('create nothing repoName(prompt-> repoName)', async () => {
const args = ['node', 'boip', 'create', projectName, '-od']
prompt.selectRepository.mockResolvedValue(repoName)
const res = await exec(args)
expect(res).toBe('try remove option "-d" or "--dry"')
// promptではまずselectRepoが呼ばれる
expect(getCalls()).toEqual({
projectName: 0,
repoName: 0,
injectKey: 2,
selectRepo: 1,
confirm: 1
})
})
test('create nothing repoName(prompt-> false)', async () => {
const args = ['node', 'boip', 'create', projectName, '-od']
prompt.selectRepository.mockResolvedValue(false)
const res = await exec(args)
expect(res).toBe('try remove option "-d" or "--dry"')
// promptではまずselectRepoが呼ばれて戻り値がfalsyならinputRepoが呼ばれる
expect(getCalls()).toEqual({
projectName: 0,
repoName: 1,
injectKey: 2,
selectRepo: 1,
confirm: 1
})
})
test('confirm === false', async () => {
const args = ['node', 'boip', 'create', projectName, '-od']
prompt.confirm.mockResolvedValue(false)
const res = await exec(args)
expect(res).toBe('Come again')
})
})
describe('create throws', () => {
it('create nothing projectName', async () => {
prompt.inputProjectName.mockResolvedValue(null)
const args = ['node', 'boip', 'create', '-od']
await expect(exec(args)).rejects.toThrow('required param "opts.name"')
})
it('invalidRepoName', async () => {
const args = ['node', 'boip', 'create', '-r', 'invalidRepoName', '-od']
await expect(exec(args)).rejects.toThrow(
'Expected format of repository name is "owner/repo”'
)
})
it('invalidRepoName', async () => {
prompt.selectRepository.mockResolvedValue(null)
prompt.inputRepositoryName.mockResolvedValue(null)
const args = ['node', 'boip', 'create', '-od']
await expect(exec(args)).rejects.toThrow('required param "opts.repository"')
})
})
describe('command injections', () => {
const conf = Con('injections')
it('set get delete', async () => {
let state = conf()
expect(state.test1).toBeUndefined()
// set
let res = await exec(['node', 'boip', 'injections', 'test1', 'value1'])
expect(res).toBeUndefined()
res = await exec(['node', 'boip', 'injections', 'test2', 'value2'])
expect(res).toBeUndefined()
state = conf()
expect(state.test1).toBe('value1')
expect(state.test2).toBe('value2')
// get
res = await exec(['node', 'boip', 'injections', 'test1'])
expect(res).toBe('value1')
res = await exec(['node', 'boip', 'injections', 'test2'])
expect(res).toBe('value2')
// get all
res = await exec(['node', 'boip', 'injections'])
expect(res).toMatch('test1: value1')
expect(res).toMatch('test2: value2')
// delete
res = await exec(['node', 'boip', 'injections', 'test1', 'delete'])
res = await exec(['node', 'boip', 'injections', 'test2', 'delete'])
state = conf()
expect((() => state.test1)()).toBeUndefined()
expect((() => state.test2)()).toBeUndefined()
})
it('throw', async () => {
await expect(
exec(['node', 'boip', 'injections', 'name', 'myName'])
).rejects.toThrow('"Name" will be "project name" Can not be set')
})
})
describe('cli command execute', () => {
it('injections', async () => {
const spy = jest.spyOn(console, 'info')
let cmd = ['node', 'boip', 'injections', 'test1']
await _exec(cmd, 500)
expect(spy).not.toHaveBeenCalled()
cmd = ['node', 'boip', 'injections', 'test1', 'value1']
await _exec(cmd, 500)
cmd = ['node', 'boip', 'injections', 'test1']
await _exec(cmd, 500)
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenCalledWith(expect.stringMatching(/value1/))
// delete
spy.mockClear()
cmd = ['node', 'boip', 'injections', 'test1', 'delete']
await _exec(cmd, 500)
cmd = ['node', 'boip', 'injections', 'test1']
await _exec(cmd, 500)
expect(spy).not.toHaveBeenCalled()
})
it('path', async () => {
const spy = jest.spyOn(console, 'info')
const cmd = ['node', 'boip', 'path']
await _exec(cmd, 500)
// console.log('spy: ', spy.mock)
expect(spy).toHaveBeenCalledWith('config path: ', expect.any(String))
expect(spy).toHaveBeenCalledWith(
'data(repository cache) path: ',
expect.any(String)
)
})
})