Files
2022-02-07 18:57:48 +08:00

181 lines
5.2 KiB
JavaScript

const fs = require('fs')
const { installedBrowsers, info, warn, error, chalk, execa } = require('@vue/cli-shared-utils')
module.exports = (api, options) => {
api.registerCommand('test:e2e', {
description: 'run end-to-end tests with nightwatch',
usage: 'vue-cli-service test:e2e [options]',
options: {
'--url': 'run end-to-end tests against given url instead of auto-starting dev server',
'--config': 'use custom nightwatch config file (overrides internals)',
'--headless': 'use chrome or firefox in headless mode',
'--parallel': 'enable parallel mode via test workers (only available in chromedriver)',
'--use-selenium': 'use Selenium standalone server instead of chromedriver or geckodriver',
'-e, --env': 'specify comma-delimited browser envs to run in (default: chrome)',
'-t, --test': 'specify a test to run by name',
'-f, --filter': 'glob to filter tests by filename'
},
details:
`All Nightwatch CLI options are also supported.\n` +
chalk.yellow(`https://nightwatchjs.org/guide/running-tests/#command-line-options`)
}, (args, rawArgs) => {
if (args.env && args.env.includes('firefox')) {
try {
require('geckodriver')
} catch (e) {
error(`To run e2e tests in Firefox, you need to install ${chalk.yellow.bold('geckodriver')} first.`)
process.exit(1)
}
}
if (installedBrowsers.chrome) {
const userVersion = installedBrowsers.chrome
const driverVersion = require('chromedriver').version
const userMajor = userVersion.match(/^(\d+)\./)[1]
const driverMajor = driverVersion.match(/^(\d+)\./)[1]
if (userMajor !== driverMajor) {
warn(`Local ${chalk.cyan.bold('Chrome')} version is ${chalk.cyan.bold(userMajor)}, but the installed ${chalk.cyan.bold('chromedriver')} is for version ${chalk.cyan.bold(driverMajor)}.`)
warn(`There may be incompatibilities between them.`)
warn(`Please update your ${chalk.cyan.bold('chromedriver')} dependency to match the ${chalk.cyan.bold('Chrome')} version.`)
}
}
// remove args
;['url', 'mode'].forEach(toRemove => removeArg(rawArgs, toRemove))
// remove flags
;['headless', 'use-selenium', 'parallel'].forEach(toRemove => removeArg(rawArgs, toRemove, 0))
return Promise.all([
startDevServer(args, api),
loadNightwatchConfig(rawArgs, api)
]).then((results) => {
const { server, url } = results[0]
let content = args.headless ? 'in headless mode' : ''
if (args.parallel) {
content += ' with concurrency'
}
info(`Running end-to-end tests ${content}...`)
// expose dev server url to tests
process.env.VUE_DEV_SERVER_URL = url
if (rawArgs.indexOf('--env') === -1 && rawArgs.indexOf('-e') === -1) {
rawArgs.push('--env', 'chrome')
}
if (args['use-selenium']) {
process.env.VUE_NIGHTWATCH_USE_SELENIUM = '1'
}
if (args.headless) {
process.env.VUE_NIGHTWATCH_HEADLESS = '1'
}
if (args.parallel) {
process.env.VUE_NIGHTWATCH_CONCURRENT = '1'
}
const nightWatchBinPath = require.resolve('nightwatch/bin/nightwatch')
const runner = execa(nightWatchBinPath, rawArgs, { stdio: 'inherit' })
if (server) {
runner.on('exit', () => server.close())
runner.on('error', () => server.close())
}
if (process.env.VUE_CLI_TEST) {
runner.on('exit', code => {
process.exit(code)
})
}
return runner
})
})
}
module.exports.defaultModes = {
'test:e2e': 'production'
}
function startDevServer (args, api) {
const { url } = args
if (url) {
return Promise.resolve({ url })
}
return api.service.run('serve')
}
async function loadNightwatchConfig (rawArgs, api) {
if (rawArgs.indexOf('--config') === -1) {
// expose user options to config file
let userOptions
const configFiles = [
'nightwatch.config.js',
'nightwatch.conf.js',
'nightwatch.json'
].map((entry) => api.resolve(entry))
const userOptionsPath = await findAsync(configFiles, fileExists)
if (userOptionsPath) {
userOptions = require(userOptionsPath)
}
process.env.VUE_NIGHTWATCH_USER_OPTIONS = JSON.stringify(userOptions || {})
rawArgs.push('--config', require.resolve('./nightwatch.conf.js'))
}
}
async function findAsync (arr, callback) {
while (arr.length) {
const item = arr.shift()
const result = await callback(item)
if (result) {
return item
}
}
return false
}
async function fileExists (path) {
try {
const stats = await checkPath(path)
return stats.isFile()
} catch (err) {
if (err.code === 'ENOENT') {
return false
}
throw err
}
}
function checkPath (source) {
return new Promise(function (resolve, reject) {
fs.stat(source, function (err, stat) {
if (err) {
return reject(err)
}
resolve(stat)
})
})
}
function removeArg (rawArgs, argToRemove, offset = 1) {
const matchRE = new RegExp(`^--${argToRemove}`)
const equalRE = new RegExp(`^--${argToRemove}=`)
const i = rawArgs.findIndex(arg => matchRE.test(arg))
if (i > -1) {
rawArgs.splice(i, offset + (equalRE.test(rawArgs[i]) ? 0 : 1))
}
}