'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); exports.default = _default; var _assert = require('assert'); var _jestUtil = require('jest-util'); var _assertionErrorMessage = _interopRequireDefault( require('../assertionErrorMessage') ); var _isError = _interopRequireDefault(require('../isError')); var _queueRunner = _interopRequireDefault(require('../queueRunner')); var _treeProcessor = _interopRequireDefault(require('../treeProcessor')); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : {default: obj}; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _default(j$) { return class Env { constructor() { _defineProperty(this, 'specFilter', void 0); _defineProperty(this, 'catchExceptions', void 0); _defineProperty(this, 'throwOnExpectationFailure', void 0); _defineProperty(this, 'catchingExceptions', void 0); _defineProperty(this, 'topSuite', void 0); _defineProperty(this, 'fail', void 0); _defineProperty(this, 'pending', void 0); _defineProperty(this, 'afterAll', void 0); _defineProperty(this, 'fit', void 0); _defineProperty(this, 'throwingExpectationFailures', void 0); _defineProperty(this, 'randomizeTests', void 0); _defineProperty(this, 'randomTests', void 0); _defineProperty(this, 'seed', void 0); _defineProperty(this, 'execute', void 0); _defineProperty(this, 'fdescribe', void 0); _defineProperty(this, 'spyOn', void 0); _defineProperty(this, 'beforeEach', void 0); _defineProperty(this, 'afterEach', void 0); _defineProperty(this, 'clearReporters', void 0); _defineProperty(this, 'addReporter', void 0); _defineProperty(this, 'it', void 0); _defineProperty(this, 'xdescribe', void 0); _defineProperty(this, 'xit', void 0); _defineProperty(this, 'beforeAll', void 0); _defineProperty(this, 'todo', void 0); _defineProperty(this, 'provideFallbackReporter', void 0); _defineProperty(this, 'allowRespy', void 0); _defineProperty(this, 'describe', void 0); let totalSpecsDefined = 0; let catchExceptions = true; const realSetTimeout = global.setTimeout; const realClearTimeout = global.clearTimeout; const runnableResources = {}; const currentlyExecutingSuites = []; let currentSpec = null; let throwOnExpectationFailure = false; let random = false; let seed = null; let nextSpecId = 0; let nextSuiteId = 0; const getNextSpecId = function () { return 'spec' + nextSpecId++; }; const getNextSuiteId = function () { return 'suite' + nextSuiteId++; }; const topSuite = new j$.Suite({ id: getNextSuiteId(), description: '', getTestPath() { return j$.testPath; } }); let currentDeclarationSuite = topSuite; const currentSuite = function () { return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; }; const currentRunnable = function () { return currentSpec || currentSuite(); }; const reporter = new j$.ReportDispatcher([ 'jasmineStarted', 'jasmineDone', 'suiteStarted', 'suiteDone', 'specStarted', 'specDone' ]); this.specFilter = function () { return true; }; const defaultResourcesForRunnable = function (id, _parentRunnableId) { const resources = { spies: [] }; runnableResources[id] = resources; }; const clearResourcesForRunnable = function (id) { spyRegistry.clearSpies(); delete runnableResources[id]; }; const beforeAndAfterFns = function (suite) { return function () { let afters = []; let befores = []; while (suite) { befores = befores.concat(suite.beforeFns); afters = afters.concat(suite.afterFns); suite = suite.parentSuite; } return { befores: befores.reverse(), afters }; }; }; const getSpecName = function (spec, suite) { const fullName = [spec.description]; const suiteFullName = suite.getFullName(); if (suiteFullName !== '') { fullName.unshift(suiteFullName); } return fullName.join(' '); }; this.catchExceptions = function (value) { catchExceptions = !!value; return catchExceptions; }; this.catchingExceptions = function () { return catchExceptions; }; this.throwOnExpectationFailure = function (value) { throwOnExpectationFailure = !!value; }; this.throwingExpectationFailures = function () { return throwOnExpectationFailure; }; this.randomizeTests = function (value) { random = !!value; }; this.randomTests = function () { return random; }; this.seed = function (value) { if (value) { seed = value; } return seed; }; const queueRunnerFactory = options => { options.clearTimeout = realClearTimeout; options.fail = this.fail; options.setTimeout = realSetTimeout; return (0, _queueRunner.default)(options); }; this.topSuite = function () { return topSuite; }; const uncaught = err => { if (currentSpec) { currentSpec.onException(err); currentSpec.cancel(); } else { console.error('Unhandled error'); console.error(err.stack); } }; let oldListenersException; let oldListenersRejection; const executionSetup = function () { // Need to ensure we are the only ones handling these exceptions. oldListenersException = process.listeners('uncaughtException').slice(); oldListenersRejection = process.listeners('unhandledRejection').slice(); j$.process.removeAllListeners('uncaughtException'); j$.process.removeAllListeners('unhandledRejection'); j$.process.on('uncaughtException', uncaught); j$.process.on('unhandledRejection', uncaught); }; const executionTeardown = function () { j$.process.removeListener('uncaughtException', uncaught); j$.process.removeListener('unhandledRejection', uncaught); // restore previous exception handlers oldListenersException.forEach(listener => { j$.process.on('uncaughtException', listener); }); oldListenersRejection.forEach(listener => { j$.process.on('unhandledRejection', listener); }); }; this.execute = async function (runnablesToRun, suiteTree = topSuite) { if (!runnablesToRun) { if (focusedRunnables.length) { runnablesToRun = focusedRunnables; } else { runnablesToRun = [suiteTree.id]; } } if (currentlyExecutingSuites.length === 0) { executionSetup(); } const lastDeclarationSuite = currentDeclarationSuite; await (0, _treeProcessor.default)({ nodeComplete(suite) { if (!suite.disabled) { clearResourcesForRunnable(suite.id); } currentlyExecutingSuites.pop(); if (suite === topSuite) { reporter.jasmineDone({ failedExpectations: topSuite.result.failedExpectations }); } else { reporter.suiteDone(suite.getResult()); } }, nodeStart(suite) { currentlyExecutingSuites.push(suite); defaultResourcesForRunnable( suite.id, suite.parentSuite && suite.parentSuite.id ); if (suite === topSuite) { reporter.jasmineStarted({ totalSpecsDefined }); } else { reporter.suiteStarted(suite.result); } }, queueRunnerFactory, runnableIds: runnablesToRun, tree: suiteTree }); currentDeclarationSuite = lastDeclarationSuite; if (currentlyExecutingSuites.length === 0) { executionTeardown(); } }; this.addReporter = function (reporterToAdd) { reporter.addReporter(reporterToAdd); }; this.provideFallbackReporter = function (reporterToAdd) { reporter.provideFallbackReporter(reporterToAdd); }; this.clearReporters = function () { reporter.clearReporters(); }; const spyRegistry = new j$.SpyRegistry({ currentSpies() { if (!currentRunnable()) { throw new Error( 'Spies must be created in a before function or a spec' ); } return runnableResources[currentRunnable().id].spies; } }); this.allowRespy = function (allow) { spyRegistry.allowRespy(allow); }; this.spyOn = function (...args) { return spyRegistry.spyOn.apply(spyRegistry, args); }; const suiteFactory = function (description) { const suite = new j$.Suite({ id: getNextSuiteId(), description, parentSuite: currentDeclarationSuite, throwOnExpectationFailure, getTestPath() { return j$.testPath; } }); return suite; }; this.describe = function (description, specDefinitions) { const suite = suiteFactory(description); if (specDefinitions === undefined) { throw new Error( `Missing second argument. It must be a callback function.` ); } if (typeof specDefinitions !== 'function') { throw new Error( `Invalid second argument, ${specDefinitions}. It must be a callback function.` ); } if (specDefinitions.length > 0) { throw new Error('describe does not expect any arguments'); } if (currentDeclarationSuite.markedPending) { suite.pend(); } if (currentDeclarationSuite.markedTodo) { // @ts-expect-error TODO Possible error: Suite does not have todo method suite.todo(); } addSpecsToSuite(suite, specDefinitions); return suite; }; this.xdescribe = function (description, specDefinitions) { const suite = suiteFactory(description); suite.pend(); addSpecsToSuite(suite, specDefinitions); return suite; }; const focusedRunnables = []; this.fdescribe = function (description, specDefinitions) { const suite = suiteFactory(description); suite.isFocused = true; focusedRunnables.push(suite.id); unfocusAncestor(); addSpecsToSuite(suite, specDefinitions); return suite; }; const addSpecsToSuite = (suite, specDefinitions) => { const parentSuite = currentDeclarationSuite; parentSuite.addChild(suite); currentDeclarationSuite = suite; let declarationError = undefined; let describeReturnValue; try { describeReturnValue = specDefinitions.call(suite); } catch (e) { declarationError = e; } if ((0, _jestUtil.isPromise)(describeReturnValue)) { declarationError = new Error( 'Returning a Promise from "describe" is not supported. Tests must be defined synchronously.' ); } else if (describeReturnValue !== undefined) { declarationError = new Error( 'A "describe" callback must not return a value.' ); } if (declarationError) { this.it('encountered a declaration exception', () => { throw declarationError; }); } currentDeclarationSuite = parentSuite; }; function findFocusedAncestor(suite) { while (suite) { if (suite.isFocused) { return suite.id; } suite = suite.parentSuite; } return null; } function unfocusAncestor() { const focusedAncestor = findFocusedAncestor(currentDeclarationSuite); if (focusedAncestor) { for (let i = 0; i < focusedRunnables.length; i++) { if (focusedRunnables[i] === focusedAncestor) { focusedRunnables.splice(i, 1); break; } } } } const specFactory = (description, fn, suite, timeout) => { totalSpecsDefined++; const spec = new j$.Spec({ id: getNextSpecId(), beforeAndAfterFns: beforeAndAfterFns(suite), resultCallback: specResultCallback, getSpecName(spec) { return getSpecName(spec, suite); }, getTestPath() { return j$.testPath; }, onStart: specStarted, description, queueRunnerFactory, userContext() { return suite.clonedSharedUserContext(); }, queueableFn: { fn, timeout() { return timeout || j$._DEFAULT_TIMEOUT_INTERVAL; } }, throwOnExpectationFailure }); if (!this.specFilter(spec)) { spec.disable(); } return spec; function specResultCallback(result) { clearResourcesForRunnable(spec.id); currentSpec = null; reporter.specDone(result); } function specStarted(spec) { currentSpec = spec; defaultResourcesForRunnable(spec.id, suite.id); reporter.specStarted(spec.result); } }; this.it = function (description, fn, timeout) { if (typeof description !== 'string') { throw new Error( `Invalid first argument, ${description}. It must be a string.` ); } if (fn === undefined) { throw new Error( 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.' ); } if (typeof fn !== 'function') { throw new Error( `Invalid second argument, ${fn}. It must be a callback function.` ); } const spec = specFactory( description, fn, currentDeclarationSuite, timeout ); if (currentDeclarationSuite.markedPending) { spec.pend(); } // When a test is defined inside another, jasmine will not run it. // This check throws an error to warn the user about the edge-case. if (currentSpec !== null) { throw new Error( `Tests cannot be nested. Test "${spec.description}" cannot run because it is nested within "${currentSpec.description}".` ); } currentDeclarationSuite.addChild(spec); return spec; }; this.xit = function (...args) { const spec = this.it.apply(this, args); spec.pend('Temporarily disabled with xit'); return spec; }; this.todo = function () { const description = arguments[0]; if (arguments.length !== 1 || typeof description !== 'string') { throw new _jestUtil.ErrorWithStack( 'Todo must be called with only a description.', this.todo ); } const spec = specFactory( description, () => {}, currentDeclarationSuite ); if (currentDeclarationSuite.markedPending) { spec.pend(); } else { spec.todo(); } currentDeclarationSuite.addChild(spec); return spec; }; this.fit = function (description, fn, timeout) { const spec = specFactory( description, fn, currentDeclarationSuite, timeout ); currentDeclarationSuite.addChild(spec); if (currentDeclarationSuite.markedPending) { spec.pend(); } else { focusedRunnables.push(spec.id); } unfocusAncestor(); return spec; }; this.beforeEach = function (beforeEachFunction, timeout) { currentDeclarationSuite.beforeEach({ fn: beforeEachFunction, timeout() { return timeout || j$._DEFAULT_TIMEOUT_INTERVAL; } }); }; this.beforeAll = function (beforeAllFunction, timeout) { currentDeclarationSuite.beforeAll({ fn: beforeAllFunction, timeout() { return timeout || j$._DEFAULT_TIMEOUT_INTERVAL; } }); }; this.afterEach = function (afterEachFunction, timeout) { currentDeclarationSuite.afterEach({ fn: afterEachFunction, timeout() { return timeout || j$._DEFAULT_TIMEOUT_INTERVAL; } }); }; this.afterAll = function (afterAllFunction, timeout) { currentDeclarationSuite.afterAll({ fn: afterAllFunction, timeout() { return timeout || j$._DEFAULT_TIMEOUT_INTERVAL; } }); }; this.pending = function (message) { let fullMessage = j$.Spec.pendingSpecExceptionMessage; if (message) { fullMessage += message; } throw fullMessage; }; this.fail = function (error) { let checkIsError; let message; if ( error instanceof _assert.AssertionError || (error && error.name === _assert.AssertionError.name) ) { checkIsError = false; // @ts-expect-error TODO Possible error: j$.Spec does not have expand property message = (0, _assertionErrorMessage.default)(error, { expand: j$.Spec.expand }); } else { const check = (0, _isError.default)(error); checkIsError = check.isError; message = check.message; } const errorAsErrorObject = checkIsError ? error : new Error(message); const runnable = currentRunnable(); if (!runnable) { errorAsErrorObject.message = 'Caught error after test environment was torn down\n\n' + errorAsErrorObject.message; throw errorAsErrorObject; } runnable.addExpectationResult(false, { matcherName: '', passed: false, expected: '', actual: '', message, error: errorAsErrorObject }); }; } }; }