Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ Refactoring inquirer code #69

Merged
merged 3 commits into from
Jun 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 25 additions & 41 deletions src/cli.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,41 @@
const inquirer = require('inquirer')
const { isNil } = require('lodash')

const readme = require('./readme')
const projectInfos = require('./project-infos')
const infos = require('./project-infos')
const questionsBuilders = require('./questions')
const utils = require('./utils')

/**
* Ask user questions and return context to generate a README
* Get questions
*
* @param {Object} projectInfos
*/
const askQuestions = async (projectInfos, skipQuestions) => {
let answersContext = {
isGithubRepos: projectInfos.isGithubRepos,
repositoryUrl: projectInfos.repositoryUrl,
projectPrerequisites: undefined
}

for (const questionBuilder of Object.values(questionsBuilders)) {
const question = questionBuilder(projectInfos, answersContext)

if (!isNil(question)) {
const currentAnswerContext = skipQuestions
? { [question.name]: getDefaultAnswer(question) }
: await inquirer.prompt([question])

answersContext = {
...answersContext,
...currentAnswerContext
}
}
}

return answersContext
}
const getQuestions = projectInfos =>
Object.values(questionsBuilders).reduce(
(questions, questionBuilder) => [
...questions,
questionBuilder(projectInfos)
],
[]
)

/**
* Get the default answer depending on the question type
* Ask user questions and return context to generate a README
*
* @param {Object} question
* @param {Object} projectInfos
*/
const getDefaultAnswer = question => {
switch (question.type) {
case 'input':
return question.default || ''
case 'checkbox':
return question.choices
.filter(choice => choice.checked)
.map(choice => choice.value)
default:
return undefined
const askQuestions = async (projectInfos, skipQuestions) => {
const questions = getQuestions(projectInfos)

const answersContext = skipQuestions
? utils.getDefaultAnswers(questions)
: await inquirer.prompt(questions)

return {
isGithubRepos: projectInfos.isGithubRepos,
repositoryUrl: projectInfos.repositoryUrl,
projectPrerequisites: undefined,
...answersContext
}
}

Expand All @@ -64,7 +49,7 @@ const getDefaultAnswer = question => {
* @param {Object} args
*/
const mainProcess = async ({ template, yes }) => {
const projectInformations = await projectInfos.getProjectInfos()
const projectInformations = await infos.getProjectInfos()
const answersContext = await cli.askQuestions(projectInformations, yes)
const readmeContent = await readme.buildReadmeContent(
answersContext,
Expand All @@ -78,7 +63,6 @@ const mainProcess = async ({ template, yes }) => {

const cli = {
mainProcess,
getDefaultAnswer,
askQuestions
}

Expand Down
42 changes: 7 additions & 35 deletions src/cli.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ const questions = require('./questions')

const realAskQuestions = cli.askQuestions

inquirer.prompt = jest.fn(([question]) =>
Promise.resolve({ [question.name]: 'value' })
inquirer.prompt = jest.fn(questions =>
Promise.resolve(
questions.reduce((result, question) => {
result[question.name] = 'value'
return result
}, {})
)
)

jest.mock('./questions', () => ({
Expand Down Expand Up @@ -95,39 +100,6 @@ describe('cli', () => {
})
})

describe('getDefaultAnswer', () => {
it('should handle input prompts correctly', () => {
const question = { type: 'input', default: 'default' }
const result = cli.getDefaultAnswer(question)
expect(result).toEqual(question.default)
})

it('should handle choices prompts correctly', () => {
const value = { name: 'name', value: 'value' }
const question = {
type: 'checkbox',
choices: [{ value, checked: true }, { checked: false }]
}
const result = cli.getDefaultAnswer(question)

expect(result).toEqual([value])
})

it('should return empty string for non-defaulted fields', () => {
const question = { type: 'input' }
const result = cli.getDefaultAnswer(question)

expect(result).toEqual('')
})

it('should return undefined for invalid types', () => {
const question = { type: 'invalid' }
const result = cli.getDefaultAnswer(question)

expect(result).toEqual(undefined)
})
})

describe('askQuestions', () => {
it('should call all builder functions exported by questions', async () => {
const projectInfos = { name: 'readme-md-generator' }
Expand Down
16 changes: 7 additions & 9 deletions src/questions/license-url.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
const isEmpty = require('lodash/isEmpty')

module.exports = (projectInfos, answersContext) =>
isEmpty(answersContext.licenseName)
? undefined
: {
type: 'input',
message: '📝 License url (use empty value to skip)',
name: 'licenseUrl',
default: projectInfos.licenseUrl
}
module.exports = projectInfos => ({
type: 'input',
message: '📝 License url (use empty value to skip)',
name: 'licenseUrl',
default: projectInfos.licenseUrl,
when: answersContext => !isEmpty(answersContext.licenseName)
})
44 changes: 30 additions & 14 deletions src/questions/license-url.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,42 @@ describe('askLicenseUrl', () => {
const licenseUrl =
'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE'
const projectInfos = { licenseUrl }

const result = askLicenseUrl(projectInfos)

expect(result).toEqual(
expect.objectContaining({
type: 'input',
message: '📝 License url (use empty value to skip)',
name: 'licenseUrl',
default: licenseUrl
})
)
})

it('should show this question if licenseName is defined', () => {
const projectInfos = {
licenseUrl:
'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE'
}
const answersContext = { licenseName: 'MIT' }

const result = askLicenseUrl(projectInfos, answersContext)
const question = askLicenseUrl(projectInfos)
const result = question.when(answersContext)

expect(result).toEqual({
type: 'input',
message: '📝 License url (use empty value to skip)',
name: 'licenseUrl',
default: licenseUrl
})
expect(result).toBe(true)
})

it('should return undefined', () => {
const licenseUrl =
'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE'
const projectInfos = { licenseUrl }
const answersContext = { licenseName: '' }
it('should not show this question if licenseName is not defined', () => {
const projectInfos = {
licenseUrl:
'https://github.com/kefranabg/readme-md-generator/blob/master/LICENSE'
}
const answersContext = {}

const result = askLicenseUrl(projectInfos, answersContext)
const question = askLicenseUrl(projectInfos)
const result = question.when(answersContext)

expect(result).toBe(undefined)
expect(result).toBe(false)
})
})
34 changes: 17 additions & 17 deletions src/questions/project-prerequisites.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ const isNil = require('lodash/isNil')
* @param {Object} engines
*/
const buildFormattedChoices = engines =>
Object.keys(engines).map(key => ({
name: `${key} ${engines[key]}`,
value: {
name: key,
value: engines[key]
},
checked: true
}))
isNil(engines)
? null
: Object.keys(engines).map(key => ({
name: `${key} ${engines[key]}`,
value: {
name: key,
value: engines[key]
},
checked: true
}))

/**
* Check if projectInfos has engines properties
Expand All @@ -24,12 +26,10 @@ const buildFormattedChoices = engines =>
const hasProjectInfosEngines = projectInfos =>
!isNil(projectInfos.engines) && !isEmpty(projectInfos.engines)

module.exports = projectInfos =>
hasProjectInfosEngines(projectInfos)
? {
type: 'checkbox',
message: '⚠️ Project prerequisites',
name: 'projectPrerequisites',
choices: buildFormattedChoices(projectInfos.engines)
}
: undefined
module.exports = projectInfos => ({
type: 'checkbox',
message: '⚠️ Project prerequisites',
name: 'projectPrerequisites',
choices: buildFormattedChoices(projectInfos.engines),
when: () => hasProjectInfosEngines(projectInfos)
})
66 changes: 41 additions & 25 deletions src/questions/project-prerequisites.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,55 @@ describe('askProjectPrerequisites', () => {

const result = askProjectPrerequisites(projectInfos)

expect(result).toEqual({
type: 'checkbox',
message: '⚠️ Project prerequisites',
name: 'projectPrerequisites',
choices: [
{
checked: true,
name: 'npm >=5.5.0',
value: { name: 'npm', value: '>=5.5.0' }
},
{
checked: true,
name: 'node >= 9.3.0',
value: { name: 'node', value: '>= 9.3.0' }
}
]
})
expect(result).toEqual(
expect.objectContaining({
type: 'checkbox',
message: '⚠️ Project prerequisites',
name: 'projectPrerequisites',
choices: [
{
checked: true,
name: 'npm >=5.5.0',
value: { name: 'npm', value: '>=5.5.0' }
},
{
checked: true,
name: 'node >= 9.3.0',
value: { name: 'node', value: '>= 9.3.0' }
}
]
})
)
})

it('should return undefined when engines property is empty object', () => {
const engines = {}
const projectInfos = { engines }
it('should not show the question when engines property is empty object', () => {
const projectInfos = { engines: {} }

const result = askProjectPrerequisites(projectInfos)
const question = askProjectPrerequisites(projectInfos)
const result = question.when()

expect(result).toEqual(undefined)
expect(result).toEqual(false)
})

it('should return undefined when engines property is not defined', () => {
it('should not show the question when engines property is not defined', () => {
const projectInfos = {}

const result = askProjectPrerequisites(projectInfos)
const question = askProjectPrerequisites(projectInfos)
const result = question.when()

expect(result).toEqual(false)
})

it('should show the question when engines property is defined and not empty', () => {
const projectInfos = {
engines: {
node: '>=10'
}
}

const question = askProjectPrerequisites(projectInfos)
const result = question.when()

expect(result).toEqual(undefined)
expect(result).toEqual(true)
})
})
Loading