UNPKG

codeceptjs

Version:

Supercharged End 2 End Testing Framework for NodeJS

233 lines (199 loc) 6.38 kB
const escapeRe = require('escape-string-regexp') const { test, setup, teardown, suiteSetup, suiteTeardown, injected } = require('./asyncWrapper') const ScenarioConfig = require('./scenarioConfig') const FeatureConfig = require('./featureConfig') const addDataContext = require('../data/context') const { createTest } = require('./test') const { createSuite } = require('./suite') const { HookConfig, AfterSuiteHook, AfterHook, BeforeSuiteHook, BeforeHook } = require('./hooks') const setContextTranslation = context => { const container = require('../container') const contexts = container.translation().value('contexts') if (contexts) { for (const key of Object.keys(contexts)) { if (context[key]) { context[contexts[key]] = context[key] } } } } /** * Codecept-style interface: * * Feature('login'); * * Scenario('login as regular user', ({I}) { * I.fillField(); * I.click(); * I.see('Hello, '+data.login); * }); * * @param {Mocha.Suite} suite Root suite. * @ignore */ module.exports = function (suite) { const suites = [suite] suite.timeout(0) let afterAllHooks let afterEachHooks let afterAllHooksAreLoaded let afterEachHooksAreLoaded suite.on('pre-require', (context, file, mocha) => { const common = require('mocha/lib/interfaces/common')(suites, context, mocha) const addScenario = function (title, opts = {}, fn) { const suite = suites[0] if (typeof opts === 'function' && !fn) { fn = opts opts = {} } if (suite.pending) { fn = null } const test = createTest(title, fn) test.file = file test.addToSuite(suite) test.applyOptions(opts) return new ScenarioConfig(test) } // create dispatcher context.BeforeAll = common.before context.AfterAll = common.after context.run = mocha.options.delay && common.runWithSuite(suite) /** * Describe a "suite" with the given `title` * and callback `fn` containing nested suites * and/or tests. * @global * @param {string} title * @param {Object<string, *>} [opts] * @returns {FeatureConfig} */ context.Feature = function (title, opts) { if (suites.length > 1) { suites.shift() } afterAllHooks = [] afterEachHooks = [] afterAllHooksAreLoaded = false afterEachHooksAreLoaded = false const suite = createSuite(suites[0], title) suite.applyOptions(opts) suite.file = file suites.unshift(suite) suite.beforeEach('codeceptjs.before', () => setup(suite)) afterEachHooks.push(['finalize codeceptjs', () => teardown(suite)]) suite.beforeAll('codeceptjs.beforeSuite', () => suiteSetup(suite)) afterAllHooks.push(['codeceptjs.afterSuite', () => suiteTeardown(suite)]) return new FeatureConfig(suite) } /** * Exclusive test suite - runs only this feature. * @global * @kind constant * @type {CodeceptJS.IFeature} */ context.Feature.only = function (title, opts) { const reString = `^${escapeRe(`${title}:`)}` mocha.grep(new RegExp(reString)) process.env.FEATURE_ONLY = true return context.Feature(title, opts) } /** * Pending test suite. * @global * @kind constant * @type {CodeceptJS.IFeature} */ context.xFeature = context.Feature.skip = function (title, opts) { const skipInfo = { skipped: true, message: 'Skipped due to "skip" on Feature.', } return context.Feature(title, { ...opts, skipInfo }) } context.BeforeSuite = function (fn) { suites[0].beforeAll('BeforeSuite', injected(fn, suites[0], 'beforeSuite')) return new HookConfig(new BeforeSuiteHook({ suite: suites[0] })) } context.AfterSuite = function (fn) { afterAllHooks.unshift(['AfterSuite', injected(fn, suites[0], 'afterSuite')]) return new HookConfig(new AfterSuiteHook({ suite: suites[0] })) } context.Background = context.Before = function (fn) { suites[0].beforeEach('Before', injected(fn, suites[0], 'before')) return new HookConfig(new BeforeHook({ suite: suites[0] })) } context.After = function (fn) { afterEachHooks.unshift(['After', injected(fn, suites[0], 'after')]) return new HookConfig(new AfterHook({ suite: suites[0] })) } /** * Describe a specification or test-case * with the given `title` and callback `fn` * acting as a thunk. * @ignore */ context.Scenario = addScenario /** * Exclusive test-case. * @ignore */ context.Scenario.only = function (title, opts, fn) { const reString = `^${escapeRe(`${suites[0].title}: ${title}`.replace(/( \| {.+})?$/g, ''))}` mocha.grep(new RegExp(reString)) process.env.SCENARIO_ONLY = true return addScenario(title, opts, fn) } /** * Pending test case. * @global * @kind constant * @type {CodeceptJS.IScenario} */ context.xScenario = context.Scenario.skip = function (title, opts = {}, fn) { if (typeof opts === 'function' && !fn) { opts = {} } return context.Scenario(title, opts) } /** * Pending test case with message: 'Test not implemented!'. * @global * @kind constant * @type {CodeceptJS.IScenario} */ context.Scenario.todo = function (title, opts = {}, fn) { if (typeof opts === 'function' && !fn) { fn = opts opts = {} } const skipInfo = { message: 'Test not implemented!', description: fn ? fn.toString() : '', } return context.Scenario(title, { ...opts, skipInfo }) } /** * For translation */ setContextTranslation(context) addDataContext(context) }) suite.on('post-require', () => { /** * load hooks from arrays to suite to prevent reordering */ if (!afterEachHooksAreLoaded && Array.isArray(afterEachHooks)) { afterEachHooks.forEach(hook => { suites[0].afterEach(hook[0], hook[1]) }) afterEachHooksAreLoaded = true } if (!afterAllHooksAreLoaded && Array.isArray(afterAllHooks)) { afterAllHooks.forEach(hook => { suites[0].afterAll(hook[0], hook[1]) }) afterAllHooksAreLoaded = true } }) }