mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-03-02 20:38:35 -06:00
initial plugin tests
This commit is contained in:
@@ -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
|
||||
|
||||
5
packages/@vue/cli-plugin-eslint/__tests__/.eslintrc
Normal file
5
packages/@vue/cli-plugin-eslint/__tests__/.eslintrc
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"rules": {
|
||||
"vue-libs/no-async-functions": 0
|
||||
}
|
||||
}
|
||||
83
packages/@vue/cli-plugin-eslint/__tests__/generator.spec.js
Normal file
83
packages/@vue/cli-plugin-eslint/__tests__/generator.spec.js
Normal file
@@ -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
packages/@vue/cli-plugin-eslint/__tests__/plugin.spec.js
Normal file
25
packages/@vue/cli-plugin-eslint/__tests__/plugin.spec.js
Normal file
@@ -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
packages/@vue/cli-service-global/.eslintrc
Normal file
5
packages/@vue/cli-service-global/.eslintrc
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"rules": {
|
||||
"vue-libs/no-async-functions": 0
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
// using this requires mocking fs & inquirer
|
||||
|
||||
const Creator = require('@vue/cli/lib/Creator')
|
||||
const { expectPrompts } = require('inquirer') // from mock
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = function createTestProject (plugins) {
|
||||
|
||||
}
|
||||
63
packages/@vue/cli-test-utils/createTestProjectWithOptions.js
Normal file
63
packages/@vue/cli-test-utils/createTestProjectWithOptions.js
Normal file
@@ -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
packages/@vue/cli-test-utils/generateWithPlugin.js
Normal file
11
packages/@vue/cli-test-utils/generateWithPlugin.js
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,6 +20,7 @@ const {
|
||||
|
||||
const {
|
||||
log,
|
||||
error,
|
||||
hasGit,
|
||||
hasYarn,
|
||||
logWithSpinner,
|
||||
@@ -45,6 +46,7 @@ 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
|
||||
@@ -52,6 +54,13 @@ module.exports = class Creator {
|
||||
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()
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
},
|
||||
{
|
||||
@@ -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'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user