Skip to content

Commit

Permalink
initial plugin tests
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Jan 5, 2018
1 parent e0755f4 commit 0151a81
Show file tree
Hide file tree
Showing 15 changed files with 248 additions and 35 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ cd packages/@vue/cli
yarn link

# create test projects in /packages/test
export VUE_CLI_TEST=true # necessary for manual tests to work
export VUE_CLI_DEBUG=true # necessary for manual tests to work
cd -
cd packages/test
vue create test-app
Expand Down
5 changes: 5 additions & 0 deletions packages/@vue/cli-plugin-eslint/__tests__/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"vue-libs/no-async-functions": 0
}
}
83 changes: 83 additions & 0 deletions packages/@vue/cli-plugin-eslint/__tests__/generator.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const generateWithPlugin = require('@vue/cli-test-utils/generateWithPlugin')

it('base', async () => {
const { pkg } = await generateWithPlugin({
id: 'eslint',
apply: require('../generator'),
options: {}
})

expect(pkg.scripts.lint).toBeTruthy()
expect(pkg.eslintConfig).toEqual({
extends: ['plugin:vue/essential', 'eslint:recommended']
})
expect(pkg.devDependencies).toHaveProperty('eslint-plugin-vue')
})

it('airbnb', async () => {
const { pkg } = await generateWithPlugin({
id: 'eslint',
apply: require('../generator'),
options: {
config: 'airbnb'
}
})

expect(pkg.scripts.lint).toBeTruthy()
expect(pkg.eslintConfig).toEqual({
extends: ['plugin:vue/essential', '@vue/airbnb']
})
expect(pkg.devDependencies).toHaveProperty('eslint-plugin-vue')
expect(pkg.devDependencies).toHaveProperty('@vue/eslint-config-airbnb')
})

it('standard', async () => {
const { pkg } = await generateWithPlugin({
id: 'eslint',
apply: require('../generator'),
options: {
config: 'standard'
}
})

expect(pkg.scripts.lint).toBeTruthy()
expect(pkg.eslintConfig).toEqual({
extends: ['plugin:vue/essential', '@vue/standard']
})
expect(pkg.devDependencies).toHaveProperty('eslint-plugin-vue')
expect(pkg.devDependencies).toHaveProperty('@vue/eslint-config-standard')
})

it('lint on save', async () => {
const { pkg } = await generateWithPlugin({
id: 'eslint',
apply: require('../generator'),
options: {
lintOn: 'save'
}
})

expect(pkg.vue).toEqual({
lintOnSave: true
})
})

it('lint on commit', async () => {
const { pkg } = await generateWithPlugin({
id: 'eslint',
apply: require('../generator'),
options: {
lintOn: 'commit'
}
})
expect(pkg.scripts.precommit).toBe('lint-staged')
expect(pkg.devDependencies).toHaveProperty('lint-staged')
expect(pkg['lint-staged']).toEqual({
'*.js': ['vue-cli-service lint', 'git add'],
'*.vue': ['vue-cli-service lint', 'git add']
})
})

it('prettier', async () => {
// TODO
})
25 changes: 25 additions & 0 deletions packages/@vue/cli-plugin-eslint/__tests__/plugin.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const create = require('@vue/cli-test-utils/createTestProjectWithOptions')

it('should work', async () => {
const { read, write, exec } = await create('eslint', {
plugins: {
'@vue/cli-plugin-babel': {},
'@vue/cli-plugin-eslint': {
config: 'airbnb',
lintOn: 'commit'
}
}
})
// should've applied airbnb autofix
const main = await read('src/main.js')
expect(main).toContain(';')
// remove semicolons
await write('src/main.js', main.replace(/;/g, ''))
// lint
await exec('node_modules/.bin/vue-cli-service lint')
expect(await read('src/main.js')).toContain(';')

// TODO lint-on-commit

// TODO lint-on-save
})
5 changes: 5 additions & 0 deletions packages/@vue/cli-service-global/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"vue-libs/no-async-functions": 0
}
}
2 changes: 2 additions & 0 deletions packages/@vue/cli-test-utils/assertPromptModule.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// using this requires mocking fs & inquirer

const Creator = require('@vue/cli/lib/Creator')
const { expectPrompts } = require('inquirer') // from mock

Expand Down
3 changes: 0 additions & 3 deletions packages/@vue/cli-test-utils/createTestProject.js

This file was deleted.

63 changes: 63 additions & 0 deletions packages/@vue/cli-test-utils/createTestProjectWithOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const fs = require('fs')
const path = require('path')
const { promisify } = require('util')
const childProcess = require('child_process')
const cliBinPath = require.resolve('@vue/cli/bin/vue')
const { spawn } = childProcess

const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)
const _exec = promisify(childProcess.exec)
const mkdirp = promisify(require('mkdirp'))

module.exports = function createTestProjectWithOptions (name, config, cwd) {
cwd = cwd || path.resolve(__dirname, '../../test')

config = Object.assign({
packageManager: 'yarn',
useTaobaoRegistry: false,
plugins: {}
}, config)

const read = file => {
return readFile(path.resolve(cwd, name, file), 'utf-8')
}

const write = (file, content) => {
const targetPath = path.resolve(cwd, name, file)
const dir = path.dirname(targetPath)
return mkdirp(dir).then(() => writeFile(targetPath, content))
}

const exec = command => {
return _exec(command, { cwd: path.resolve(cwd, name) })
}

return new Promise((resolve, reject) => {
const child = spawn(cliBinPath, [
'create',
name,
'--force',
'--config',
JSON.stringify(config)
], {
cwd,
env: process.env,
stdio: 'inherit'
})

child.on('exit', code => {
if (code !== 0) {
reject(`cli creation failed with code ${code}`)
} else {
resolve({
read,
write,
exec
})
}
})

child.on('error', reject)
})
}
11 changes: 11 additions & 0 deletions packages/@vue/cli-test-utils/generateWithPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const Generator = require('@vue/cli/lib/Generator')

module.exports = async function generateWithPlugin (plugin, pkg) {
process.env.VUE_CLI_SKIP_WRITE = true
const generator = new Generator('/', pkg || {}, [].concat(plugin))
await generator.generate()
return {
pkg: generator.pkg,
files: generator.files
}
}
1 change: 0 additions & 1 deletion packages/@vue/cli-test-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"name": "@vue/cli-test-utils",
"version": "0.1.0",
"description": "test utilities for vue-cli packages",
"main": "index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/vue-cli.git"
Expand Down
8 changes: 5 additions & 3 deletions packages/@vue/cli/bin/vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ program
program
.command('create <app-name>')
.description('create a new project powered by vue-cli-service')
.option('-s, --saved', 'Skip prompts and use saved options')
.option('-d, --default', 'Skip prompts and use default options')
.option('-s, --saved', 'Skip prompts and use saved config')
.option('-d, --default', 'Skip prompts and use default config')
.option('-c, --config <json>', 'Skip prompts and use inline JSON string as config')
.option('-r, --registry <url>', 'Use specified NPM registry when installing dependencies')
.option('-p, --package-manager <command>', 'Use specified NPM client when installing dependencies')
.option('-m, --package-manager <command>', 'Use specified NPM client when installing dependencies')
.option('-f, --force', 'Overwrite target directory if it exists')
.action(require('../lib/create'))

program
Expand Down
17 changes: 13 additions & 4 deletions packages/@vue/cli/lib/Creator.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const {

const {
log,
error,
hasGit,
hasYarn,
logWithSpinner,
Expand All @@ -45,13 +46,21 @@ module.exports = class Creator {
}

async create (cliOptions = {}) {
const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG
const { name, context, createCompleteCbs } = this

let options
if (cliOptions.saved) {
options = loadOptions()
} else if (cliOptions.default) {
options = defaults
} else if (cliOptions.config) {
try {
options = JSON.parse(cliOptions.config)
} catch (e) {
error(`CLI inline config is not valid JSON: ${cliOptions.config}`)
process.exit(1)
}
} else {
options = await this.promptAndResolveOptions()
}
Expand Down Expand Up @@ -87,7 +96,7 @@ module.exports = class Creator {
// install plugins
logWithSpinner('⚙', `Installing CLI plugins. This might take a while...`)
const deps = Object.keys(options.plugins)
if (process.env.VUE_CLI_TEST) {
if (isTestOrDebug) {
// in development, avoid installation process
setupDevProject(context, deps)
} else {
Expand All @@ -108,7 +117,7 @@ module.exports = class Creator {

// install additional deps (injected by generators)
logWithSpinner('📦', `Installing additional dependencies...`)
if (!process.env.VUE_CLI_TEST) {
if (!isTestOrDebug) {
await installDeps(context, packageManager, null, cliOptions.registry)
}

Expand Down Expand Up @@ -186,7 +195,7 @@ module.exports = class Creator {
message: `Please pick a project creation mode:`,
choices: [
{
name: `Zero-configuration with defaults (${defualtFeatures})`,
name: `Zero-config with defaults (${defualtFeatures})`,
value: 'default'
},
{
Expand All @@ -199,7 +208,7 @@ module.exports = class Creator {
if (savedOptions.plugins) {
const savedFeatures = formatFeatures(savedOptions.plugins)
modePrompt.choices.unshift({
name: `Use previously saved preferences (${savedFeatures})`,
name: `Use previously saved config (${savedFeatures})`,
value: 'saved'
})
}
Expand Down
38 changes: 21 additions & 17 deletions packages/@vue/cli/lib/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,33 @@ const { error, stopSpinner } = require('@vue/cli-shared-utils')
async function create (projectName, options) {
const targetDir = path.resolve(process.cwd(), projectName)
if (fs.existsSync(targetDir)) {
clearConsole()
const { action } = await inquirer.prompt([
{
name: 'action',
type: 'list',
message: `Target directory ${chalk.cyan(targetDir)} already exists. Pick an action:`,
choices: [
{ name: 'Overwrite', value: 'overwrite' },
{ name: 'Merge', value: 'merge' },
{ name: 'Cancel', value: false }
]
}
])
if (!action) {
return
} else if (action === 'overwrite') {
if (options.force) {
rimraf.sync(targetDir)
} else {
clearConsole()
const { action } = await inquirer.prompt([
{
name: 'action',
type: 'list',
message: `Target directory ${chalk.cyan(targetDir)} already exists. Pick an action:`,
choices: [
{ name: 'Overwrite', value: 'overwrite' },
{ name: 'Merge', value: 'merge' },
{ name: 'Cancel', value: false }
]
}
])
if (!action) {
return
} else if (action === 'overwrite') {
rimraf.sync(targetDir)
}
}
}

const promptModules = fs
.readdirSync(path.resolve(__dirname, './promptModules'))
.filter(file => file.charAt(0) !== '.')
.filter(file => file.charAt(0) !== '.' && file.charAt(0) !== '_')
.map(file => require(`./promptModules/${file}`))

const creator = new Creator(projectName, targetDir, promptModules)
Expand Down
5 changes: 4 additions & 1 deletion packages/@vue/cli/lib/util/clearConsole.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ const { clearConsole } = require('@vue/cli-shared-utils')

let title = chalk.bold.green(`Vue CLI v${version}`)
if (process.env.VUE_CLI_TEST) {
title += ' ' + chalk.magenta.bold('TEST MODE')
title += ' ' + chalk.blue.bold('TEST')
}
if (process.env.VUE_CLI_DEBUG) {
title += ' ' + chalk.magenta.bold('DEBUG')
}

module.exports = () => clearConsole(title)
15 changes: 10 additions & 5 deletions packages/@vue/cli/lib/util/writeFileTree.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
const fs = require('fs')
const path = require('path')
const mkdirp = require('mkdirp')
const { promisify } = require('util')
const mkdirp = promisify(require('mkdirp'))
const write = promisify(fs.writeFile)

module.exports = function writeFileTree (dir, files) {
for (const name in files) {
const filePath = path.join(dir, name)
mkdirp.sync(path.dirname(filePath))
fs.writeFileSync(filePath, files[name])
if (process.env.VUE_CLI_SKIP_WRITE) {
return
}
return Promise.all(Object.keys(files).map(async (name) => {
const filePath = path.join(dir, name)
await mkdirp(path.dirname(filePath))
await write(filePath, files[name])
}))
}

0 comments on commit 0151a81

Please sign in to comment.