UNPKG

webdriverio-automation

Version:

WebdriverIO-Automation android ios project

266 lines (265 loc) 11.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.adapterFactory = exports.MochaAdapter = void 0; const path_1 = __importDefault(require("path")); const mocha_1 = __importDefault(require("mocha")); const util_1 = require("util"); const logger_1 = __importDefault(require("@wdio/logger")); const utils_1 = require("@wdio/utils"); const utils_2 = require("./utils"); const constants_1 = require("./constants"); const log = logger_1.default('@wdio/mocha-framework'); const MOCHA_UI_TYPE_EXTRACTOR = /^(?:.*-)?([^-.]+)(?:.js)?$/; const DEFAULT_INTERFACE_TYPE = 'bdd'; class MochaAdapter { constructor(_cid, _config, _specs, _capabilities, _reporter) { this._cid = _cid; this._config = _config; this._specs = _specs; this._capabilities = _capabilities; this._reporter = _reporter; this._level = 0; this._hasTests = true; this._suiteIds = ['0']; this._suiteCnt = new Map(); this._hookCnt = new Map(); this._testCnt = new Map(); this._config = Object.assign({ mochaOpts: {} }, _config); } async init() { const { mochaOpts } = this._config; const mocha = this._mocha = new mocha_1.default(mochaOpts); await mocha.loadFilesAsync(); mocha.reporter(constants_1.NOOP); mocha.fullTrace(); this._specs.forEach((spec) => mocha.addFile(spec)); mocha.suite.on('pre-require', this.preRequire.bind(this)); await this._loadFiles(mochaOpts); const { setOptions } = require('expect-webdriverio'); setOptions({ wait: this._config.waitforTimeout, interval: this._config.waitforInterval, }); return this; } async _loadFiles(mochaOpts) { try { await this._mocha.loadFilesAsync(); const mochaRunner = new mocha_1.default.Runner(this._mocha.suite, false); if (mochaOpts.grep) { mochaRunner.grep(this._mocha.options.grep, mochaOpts.invert); } this._hasTests = mochaRunner.total > 0; } catch (err) { const error = '' + 'Unable to load spec files quite likely because they rely on `browser` object that is not fully initialised.\n' + '`browser` object has only `capabilities` and some flags like `isMobile`.\n' + 'Helper files that use other `browser` commands have to be moved to `before` hook.\n' + `Spec file(s): ${this._specs.join(',')}\n` + `Error: ${err.stack}`; this._specLoadError = new Error(error); log.warn(error); } } hasTests() { return this._hasTests; } async run() { const mocha = this._mocha; let runtimeError; const result = await new Promise((resolve) => { try { this._runner = mocha.run(resolve); } catch (e) { runtimeError = e; return resolve(1); } Object.keys(constants_1.EVENTS).forEach((e) => this._runner.on(e, this.emit.bind(this, constants_1.EVENTS[e]))); this._runner.suite.beforeAll(this.wrapHook('beforeSuite')); this._runner.suite.afterAll(this.wrapHook('afterSuite')); }); await utils_1.executeHooksWithArgs('after', this._config.after, [runtimeError || result, this._capabilities, this._specs]); if (runtimeError || this._specLoadError) { throw runtimeError || this._specLoadError; } return result; } options(options, context) { let { require = [], compilers = [] } = options; if (typeof require === 'string') { require = [require]; } this.requireExternalModules([...compilers, ...require], context); } preRequire(context, file, mocha) { const options = this._config.mochaOpts; const match = MOCHA_UI_TYPE_EXTRACTOR.exec(options.ui); const type = (match && constants_1.INTERFACES[match[1]] && match[1]) || DEFAULT_INTERFACE_TYPE; const hookArgsFn = (context) => { var _a, _b; return [{ ...context.test, parent: (_b = (_a = context.test) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.title }, context]; }; constants_1.INTERFACES[type].forEach((fnName) => { let testCommand = constants_1.INTERFACES[type][0]; const isTest = [testCommand, testCommand + '.only'].includes(fnName); utils_1.runTestInFiberContext(isTest, isTest ? this._config.beforeTest : this._config.beforeHook, hookArgsFn, isTest ? this._config.afterTest : this._config.afterHook, hookArgsFn, fnName, this._cid); }); this.options(options, { context, file, mocha, options }); } wrapHook(hookName) { return () => utils_1.executeHooksWithArgs(hookName, this._config[hookName], [this.prepareMessage(hookName)]).catch((e) => { log.error(`Error in ${hookName} hook: ${e.stack.slice(7)}`); }); } prepareMessage(hookName) { var _a, _b; const params = { type: hookName }; switch (hookName) { case 'beforeSuite': case 'afterSuite': params.payload = (_a = this._runner) === null || _a === void 0 ? void 0 : _a.suite.suites[0]; break; case 'beforeTest': case 'afterTest': params.payload = (_b = this._runner) === null || _b === void 0 ? void 0 : _b.test; break; } return this.formatMessage(params); } formatMessage(params) { var _a, _b, _c; let message = { type: params.type }; const mochaAllHooksIfPresent = (_b = (_a = params.payload) === null || _a === void 0 ? void 0 : _a.title) === null || _b === void 0 ? void 0 : _b.match(/^"(before|after)( all| each)?" hook/); if (params.err) { if (params.err && params.err.message && params.err.message.includes(constants_1.MOCHA_TIMEOUT_MESSAGE)) { const replacement = util_1.format(constants_1.MOCHA_TIMEOUT_MESSAGE_REPLACEMENT, params.payload.parent.title, params.payload.title); params.err.message = params.err.message.replace(constants_1.MOCHA_TIMEOUT_MESSAGE, replacement); params.err.stack = params.err.stack.replace(constants_1.MOCHA_TIMEOUT_MESSAGE, replacement); } message.error = { name: params.err.name, message: params.err.message, stack: params.err.stack, type: params.err.type || params.err.name, expected: params.err.expected, actual: params.err.actual }; if (mochaAllHooksIfPresent) { message.type = 'hook:end'; } } if (params.payload) { message.title = params.payload.title; message.parent = params.payload.parent ? params.payload.parent.title : null; message.fullTitle = params.payload.fullTitle ? params.payload.fullTitle() : message.parent + ' ' + message.title; message.pending = params.payload.pending || false; message.file = params.payload.file; message.duration = params.payload.duration; if (params.payload.ctx && params.payload.ctx.currentTest) { message.currentTest = params.payload.ctx.currentTest.title; } if (params.type.match(/Test/)) { message.passed = (params.payload.state === 'passed'); } if (((_c = params.payload.parent) === null || _c === void 0 ? void 0 : _c.title) && mochaAllHooksIfPresent) { const hookName = mochaAllHooksIfPresent[0]; message.title = `${hookName} for ${params.payload.parent.title}`; } if (params.payload.context) { message.context = params.payload.context; } } return message; } requireExternalModules(modules, context) { modules.forEach(module => { if (!module) { return; } module = module.replace(/.*:/, ''); if (module.substr(0, 1) === '.') { module = path_1.default.join(process.cwd(), module); } utils_2.loadModule(module, context); }); } emit(event, payload, err) { if (payload.root) return; let message = this.formatMessage({ type: event, payload, err }); message.cid = this._cid; message.specs = this._specs; message.uid = this.getUID(message); this._reporter.emit(message.type, message); } getSyncEventIdStart(type) { const prop = `_${type}Cnt`; const suiteId = this._suiteIds[this._suiteIds.length - 1]; const cnt = this[prop].has(suiteId) ? this[prop].get(suiteId) || 0 : 0; this[prop].set(suiteId, cnt + 1); return `${type}-${suiteId}-${cnt}`; } getSyncEventIdEnd(type) { const prop = `_${type}Cnt`; const suiteId = this._suiteIds[this._suiteIds.length - 1]; const cnt = this[prop].get(suiteId) - 1; return `${type}-${suiteId}-${cnt}`; } getUID(message) { if (message.type === 'suite:start') { const suiteCnt = this._suiteCnt.has(this._level.toString()) ? this._suiteCnt.get(this._level.toString()) : 0; const suiteId = `suite-${this._level}-${suiteCnt}`; if (this._suiteCnt.has(this._level.toString())) { this._suiteCnt.set(this._level.toString(), this._suiteCnt.get(this._level.toString()) + 1); } else { this._suiteCnt.set(this._level.toString(), 1); } this._suiteIds.push(`${this._level}${suiteCnt}`); this._level++; return suiteId; } if (message.type === 'suite:end') { this._level--; const suiteCnt = this._suiteCnt.get(this._level.toString()) - 1; const suiteId = `suite-${this._level}-${suiteCnt}`; this._suiteIds.pop(); return suiteId; } if (message.type === 'hook:start') { return this.getSyncEventIdStart('hook'); } if (message.type === 'hook:end') { return this.getSyncEventIdEnd('hook'); } if (['test:start', 'test:pending'].includes(message.type)) { return this.getSyncEventIdStart('test'); } if (['test:end', 'test:pass', 'test:fail', 'test:retry'].includes(message.type)) { return this.getSyncEventIdEnd('test'); } throw new Error(`Unknown message type : ${message.type}`); } } exports.MochaAdapter = MochaAdapter; const adapterFactory = {}; exports.adapterFactory = adapterFactory; adapterFactory.init = async function (...args) { const adapter = new MochaAdapter(...args); const instance = await adapter.init(); return instance; }; exports.default = adapterFactory;