UNPKG

codeceptjs

Version:

Supercharged End 2 End Testing Framework for NodeJS

230 lines (203 loc) 6.07 kB
const { existsSync, readFileSync } = require('fs') const { globSync } = require('glob') const shuffle = require('lodash.shuffle') const fsPath = require('path') const { resolve } = require('path') const container = require('./container') const Config = require('./config') const event = require('./event') const runHook = require('./hooks') const output = require('./output') const { emptyFolder } = require('./utils') /** * CodeceptJS runner */ class Codecept { /** * Create CodeceptJS runner. * Config and options should be passed * * @param {*} config * @param {*} opts */ constructor(config, opts) { this.config = Config.create(config) this.opts = opts this.testFiles = new Array(0) this.requireModules(config.require) } /** * Require modules before codeceptjs running * * @param {string[]} requiringModules */ requireModules(requiringModules) { if (requiringModules) { requiringModules.forEach(requiredModule => { const isLocalFile = existsSync(requiredModule) || existsSync(`${requiredModule}.js`) if (isLocalFile) { requiredModule = resolve(requiredModule) } require(requiredModule) }) } } /** * Initialize CodeceptJS at specific directory. * If async initialization is required, pass callback as second parameter. * * @param {string} dir */ init(dir) { this.initGlobals(dir) // initializing listeners container.create(this.config, this.opts) this.runHooks() } /** * Creates global variables * * @param {string} dir */ initGlobals(dir) { global.codecept_dir = dir global.output_dir = fsPath.resolve(dir, this.config.output) if (this.config.emptyOutputFolder) emptyFolder(global.output_dir) if (!this.config.noGlobals) { global.Helper = global.codecept_helper = require('@codeceptjs/helper') global.actor = global.codecept_actor = require('./actor') global.pause = require('./pause') global.within = require('./within') global.session = require('./session') global.DataTable = require('./data/table') global.locate = locator => require('./locator').build(locator) global.inject = container.support global.share = container.share global.secret = require('./secret').secret global.codecept_debug = output.debug global.codeceptjs = require('./index') // load all objects // BDD const stepDefinitions = require('./mocha/bdd') global.Given = stepDefinitions.Given global.When = stepDefinitions.When global.Then = stepDefinitions.Then global.DefineParameterType = stepDefinitions.defineParameterType // debug mode global.debugMode = false // mask sensitive data global.maskSensitiveData = this.config.maskSensitiveData || false } } /** * Executes hooks. */ runHooks() { // default hooks runHook(require('./listener/store')) runHook(require('./listener/steps')) runHook(require('./listener/config')) runHook(require('./listener/result')) runHook(require('./listener/helpers')) runHook(require('./listener/globalTimeout')) runHook(require('./listener/globalRetry')) runHook(require('./listener/exit')) runHook(require('./listener/emptyRun')) // custom hooks (previous iteration of plugins) this.config.hooks.forEach(hook => runHook(hook)) } /** * Executes bootstrap. * */ async bootstrap() { return runHook(this.config.bootstrap, 'bootstrap') } /** * Executes teardown. */ async teardown() { return runHook(this.config.teardown, 'teardown') } /** * Loads tests by pattern or by config.tests * * @param {string} [pattern] */ loadTests(pattern) { const options = { cwd: global.codecept_dir, } let patterns = [pattern] if (!pattern) { patterns = [] // If the user wants to test a specific set of test files as an array or string. if (this.config.tests && !this.opts.features) { if (Array.isArray(this.config.tests)) { patterns.push(...this.config.tests) } else { patterns.push(this.config.tests) } } if (this.config.gherkin.features && !this.opts.tests) { if (Array.isArray(this.config.gherkin.features)) { this.config.gherkin.features.forEach(feature => { patterns.push(feature) }) } else { patterns.push(this.config.gherkin.features) } } } for (pattern of patterns) { if (pattern) { globSync(pattern, options).forEach(file => { if (file.includes('node_modules')) return if (!fsPath.isAbsolute(file)) { file = fsPath.join(global.codecept_dir, file) } if (!this.testFiles.includes(fsPath.resolve(file))) { this.testFiles.push(fsPath.resolve(file)) } }) } } if (this.opts.shuffle) { this.testFiles = shuffle(this.testFiles) } } /** * Run a specific test or all loaded tests. * * @param {string} [test] * @returns {Promise<void>} */ async run(test) { await container.started() return new Promise((resolve, reject) => { const mocha = container.mocha() mocha.files = this.testFiles if (test) { if (!fsPath.isAbsolute(test)) { test = fsPath.join(global.codecept_dir, test) } mocha.files = mocha.files.filter(t => fsPath.basename(t, '.js') === test || t === test) } const done = () => { event.emit(event.all.result, container.result()) event.emit(event.all.after, this) resolve() } try { event.emit(event.all.before, this) mocha.run(() => done()) } catch (e) { output.error(e.stack) reject(e) } }) } static version() { return JSON.parse(readFileSync(`${__dirname}/../package.json`, 'utf8')).version } } module.exports = Codecept