getJasmineRequireObj().Env = function(j$) {
/**
* @class Env
* @since 2.0.0
* @classdesc The Jasmine environment.
* _Note:_ Do not construct this directly. You can obtain the Env instance by
* calling {@link jasmine.getEnv}.
* @hideconstructor
*/
function Env(options) {
options = options || {};
const self = this;
const GlobalErrors = options.GlobalErrors || j$.GlobalErrors;
const global = options.global || j$.getGlobal();
const realSetTimeout = global.setTimeout;
const realClearTimeout = global.clearTimeout;
const clearStack = j$.getClearStack(global);
this.clock = new j$.Clock(
global,
function() {
return new j$.DelayedFunctionScheduler();
},
new j$.MockDate(global)
);
const globalErrors = new GlobalErrors(
undefined,
// Configuration is late-bound because GlobalErrors needs to be constructed
// before it's set to detect load-time errors in browsers
() => this.configuration()
);
const installGlobalErrors = (function() {
let installed = false;
return function() {
if (!installed) {
globalErrors.install();
installed = true;
}
};
})();
const runableResources = new j$.RunableResources({
getCurrentRunableId: function() {
const r = runner.currentRunable();
return r ? r.id : null;
},
globalErrors
});
let reportDispatcher;
let topSuite;
let runner;
let parallelLoadingState = null; // 'specs', 'helpers', or null for non-parallel
/**
* This represents the available options to configure Jasmine.
* Options that are not provided will use their default values.
* @see Env#configure
* @interface Configuration
* @since 3.3.0
*/
const config = {
/**
* Whether to randomize spec execution order
* @name Configuration#random
* @since 3.3.0
* @type Boolean
* @default true
*/
random: true,
/**
* Seed to use as the basis of randomization.
* Null causes the seed to be determined randomly at the start of execution.
* @name Configuration#seed
* @since 3.3.0
* @type (number|string)
* @default null
*/
seed: null,
/**
* Whether to stop execution of the suite after the first spec failure
*
*
In parallel mode, `stopOnSpecFailure` works on a "best effort" * basis. Jasmine will stop execution as soon as practical after a failure * but it might not be immediate.
* @name Configuration#stopOnSpecFailure * @since 3.9.0 * @type Boolean * @default false */ stopOnSpecFailure: false, /** * Whether to fail the spec if it ran no expectations. By default * a spec that ran no expectations is reported as passed. Setting this * to true will report such spec as a failure. * @name Configuration#failSpecWithNoExpectations * @since 3.5.0 * @type Boolean * @default false */ failSpecWithNoExpectations: false, /** * Whether to cause specs to only have one expectation failure. * @name Configuration#stopSpecOnExpectationFailure * @since 3.3.0 * @type Boolean * @default false */ stopSpecOnExpectationFailure: false, /** * A function that takes a spec and returns true if it should be executed * or false if it should be skipped. * @callback SpecFilter * @param {Spec} spec - The spec that the filter is being applied to. * @return boolean */ /** * Function to use to filter specs * @name Configuration#specFilter * @since 3.3.0 * @type SpecFilter * @default A function that always returns true. */ specFilter: function() { return true; }, /** * Whether reporters should hide disabled specs from their output. * Currently only supported by Jasmine's HTMLReporter * @name Configuration#hideDisabled * @since 3.3.0 * @type Boolean * @default false */ hideDisabled: false, /** * Clean closures when a suite is done running (done by clearing the stored function reference). * This prevents memory leaks, but you won't be able to run jasmine multiple times. * @name Configuration#autoCleanClosures * @since 3.10.0 * @type boolean * @default true */ autoCleanClosures: true, /** * Whether to forbid duplicate spec or suite names. If set to true, using * the same name multiple times in the same immediate parent suite is an * error. * @name Configuration#forbidDuplicateNames * @type boolean * @default false */ forbidDuplicateNames: false, /** * Whether to issue warnings for certain deprecated functionality * every time it's used. If not set or set to false, deprecation warnings * for methods that tend to be called frequently will be issued only once * or otherwise throttled to prevent the suite output from being flooded * with warnings. * @name Configuration#verboseDeprecations * @since 3.6.0 * @type Boolean * @default false */ verboseDeprecations: false, /** * Whether to detect late promise rejection handling during spec * execution. If this option is enabled, a promise rejection that triggers * the JavaScript runtime's unhandled rejection event will not be treated * as an error as long as it's handled before the spec finishes. * * This option is off by default because it imposes a performance penalty. * @name Configuration#detectLateRejectionHandling * @since 5.10.0 * @type Boolean * @default false */ detectLateRejectionHandling: false }; if (!options.suppressLoadErrors) { installGlobalErrors(); globalErrors.pushListener(function loadtimeErrorHandler(error, event) { topSuite.result.failedExpectations.push({ passed: false, globalErrorType: 'load', message: error ? error.message : event.message, stack: error && error.stack, filename: event && event.filename, lineno: event && event.lineno }); }); } /** * Configure your jasmine environment * @name Env#configure * @since 3.3.0 * @argument {Configuration} configuration * @function */ this.configure = function(configuration) { if (parallelLoadingState) { throw new Error( 'Jasmine cannot be configured via Env in parallel mode' ); } const booleanProps = [ 'random', 'failSpecWithNoExpectations', 'hideDisabled', 'stopOnSpecFailure', 'stopSpecOnExpectationFailure', 'autoCleanClosures', 'forbidDuplicateNames', 'detectLateRejectionHandling' ]; booleanProps.forEach(function(prop) { if (typeof configuration[prop] !== 'undefined') { config[prop] = !!configuration[prop]; } }); if (configuration.specFilter) { config.specFilter = configuration.specFilter; } if (typeof configuration.seed !== 'undefined') { config.seed = configuration.seed; } if (configuration.hasOwnProperty('verboseDeprecations')) { config.verboseDeprecations = configuration.verboseDeprecations; deprecator.verboseDeprecations(config.verboseDeprecations); } }; /** * Get the current configuration for your jasmine environment * @name Env#configuration * @since 3.3.0 * @function * @returns {Configuration} */ this.configuration = function() { const result = {}; for (const property in config) { result[property] = config[property]; } return result; }; this.setDefaultSpyStrategy = function(defaultStrategyFn) { runableResources.setDefaultSpyStrategy(defaultStrategyFn); }; this.addSpyStrategy = function(name, fn) { runableResources.customSpyStrategies()[name] = fn; }; this.addCustomEqualityTester = function(tester) { runableResources.customEqualityTesters().push(tester); }; this.addMatchers = function(matchersToAdd) { runableResources.addCustomMatchers(matchersToAdd); }; this.addAsyncMatchers = function(matchersToAdd) { runableResources.addCustomAsyncMatchers(matchersToAdd); }; this.addCustomObjectFormatter = function(formatter) { runableResources.customObjectFormatters().push(formatter); }; j$.Expectation.addCoreMatchers(j$.matchers); j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers); const expectationFactory = function(actual, spec) { return j$.Expectation.factory({ matchersUtil: runableResources.makeMatchersUtil(), customMatchers: runableResources.customMatchers(), actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { return spec.addExpectationResult(passed, result); } }; const handleThrowUnlessFailure = function(passed, result) { if (!passed) { /** * @interface * @name ThrowUnlessFailure * @extends Error * @description Represents a failure of an expectation evaluated with * {@link throwUnless}. Properties of this error are a subset of the * properties of {@link ExpectationResult} and have the same values. * * Note: The expected and actual properties are deprecated and may be removed * in a future release. In many Jasmine configurations they are passed * through JSON serialization and deserialization, which is inherently * lossy. In such cases, the expected and actual values may be placeholders * or approximations of the original objects. * * @property {String} matcherName - The name of the matcher that was executed for this expectation. * @property {String} message - The failure message for the expectation. * @property {Boolean} passed - Whether the expectation passed or failed. * @property {Object} expected - Deprecated. If the expectation failed, what was the expected value. * @property {Object} actual - Deprecated. If the expectation failed, what actual value was produced. */ const error = new Error(result.message); error.passed = result.passed; error.message = result.message; error.expected = result.expected; error.actual = result.actual; error.matcherName = result.matcherName; throw error; } }; const throwUnlessFactory = function(actual, spec) { return j$.Expectation.factory({ matchersUtil: runableResources.makeMatchersUtil(), customMatchers: runableResources.customMatchers(), actual: actual, addExpectationResult: handleThrowUnlessFailure }); }; const throwUnlessAsyncFactory = function(actual, spec) { return j$.Expectation.asyncFactory({ matchersUtil: runableResources.makeMatchersUtil(), customAsyncMatchers: runableResources.customAsyncMatchers(), actual: actual, addExpectationResult: handleThrowUnlessFailure }); }; // TODO: Unify recordLateError with recordLateExpectation? The extra // diagnostic info added by the latter is probably useful in most cases. function recordLateError(error) { const isExpectationResult = error.matcherName !== undefined && error.passed !== undefined; const result = isExpectationResult ? error : j$.buildExpectationResult({ error, passed: false, matcherName: '', expected: '', actual: '' }); routeLateFailure(result); } function recordLateExpectation(runable, runableType, result) { const delayedExpectationResult = {}; Object.keys(result).forEach(function(k) { delayedExpectationResult[k] = result[k]; }); delayedExpectationResult.passed = false; delayedExpectationResult.globalErrorType = 'lateExpectation'; delayedExpectationResult.message = runableType + ' "' + runable.getFullName() + '" ran a "' + result.matcherName + '" expectation after it finished.\n'; if (result.message) { delayedExpectationResult.message += 'Message: "' + result.message + '"\n'; } delayedExpectationResult.message += '1. Did you forget to return or await the result of expectAsync?\n' + '2. Was done() invoked before an async operation completed?\n' + '3. Did an expectation follow a call to done()?'; topSuite.result.failedExpectations.push(delayedExpectationResult); } function routeLateFailure(expectationResult) { // Report the result on the nearest ancestor suite that hasn't already // been reported done. for (let r = runner.currentRunable(); r; r = r.parentSuite) { if (!r.reportedDone) { if (r === topSuite) { expectationResult.globalErrorType = 'lateError'; } r.result.failedExpectations.push(expectationResult); return; } } // If we get here, all results have been reported and there's nothing we // can do except log the result and hope the user sees it. // eslint-disable-next-line no-console console.error('Jasmine received a result after the suite finished:'); // eslint-disable-next-line no-console console.error(expectationResult); } const asyncExpectationFactory = function(actual, spec, runableType) { return j$.Expectation.asyncFactory({ matchersUtil: runableResources.makeMatchersUtil(), customAsyncMatchers: runableResources.customAsyncMatchers(), actual: actual, addExpectationResult: addExpectationResult }); function addExpectationResult(passed, result) { if (runner.currentRunable() !== spec) { recordLateExpectation(spec, runableType, result); } return spec.addExpectationResult(passed, result); } }; /** * Causes a deprecation warning to be logged to the console and reported to * reporters. * * The optional second parameter is an object that can have either of the * following properties: * * omitStackTrace: Whether to omit the stack trace. Optional. Defaults to * false. This option is ignored if the deprecation is an Error. Set this * when the stack trace will not contain anything that helps the user find * the source of the deprecation. * * ignoreRunnable: Whether to log the deprecation on the root suite, ignoring * the spec or suite that's running when it happens. Optional. Defaults to * false. * * @name Env#deprecated * @since 2.99 * @function * @param {String|Error} deprecation The deprecation message * @param {Object} [options] Optional extra options, as described above */ this.deprecated = function(deprecation, options) { const runable = runner.currentRunable() || topSuite; deprecator.addDeprecationWarning(runable, deprecation, options); }; function runQueue(options) { options.clearStack = options.clearStack || clearStack; options.timeout = { setTimeout: realSetTimeout, clearTimeout: realClearTimeout }; options.fail = self.fail; options.globalErrors = globalErrors; options.onException = options.onException || function(e) { (runner.currentRunable() || topSuite).handleException(e); }; new j$.QueueRunner(options).execute(); } const suiteBuilder = new j$.SuiteBuilder({ env: this, expectationFactory, asyncExpectationFactory, onLateError: recordLateError, runQueue }); topSuite = suiteBuilder.topSuite; const deprecator = new j$.Deprecator(topSuite); /** * Provides the root suite, through which all suites and specs can be * accessed. * @function * @name Env#topSuite * @return {Suite} the root suite * @since 2.0.0 */ this.topSuite = function() { ensureNonParallel('topSuite'); return topSuite.metadata; }; /** * This represents the available reporter callback for an object passed to {@link Env#addReporter}. * @interface Reporter * @see custom_reporter */ reportDispatcher = new j$.ReportDispatcher( j$.reporterEvents, function(options) { options.SkipPolicy = j$.NeverSkipPolicy; return runQueue(options); }, recordLateError ); runner = new j$.Runner({ topSuite, totalSpecsDefined: () => suiteBuilder.totalSpecsDefined, focusedRunables: () => suiteBuilder.focusedRunables, runableResources, reportDispatcher, runQueue, TreeProcessor: j$.TreeProcessor, globalErrors, getConfig: () => config }); this.setParallelLoadingState = function(state) { parallelLoadingState = state; }; this.parallelReset = function() { suiteBuilder.parallelReset(); runner.parallelReset(); }; /** * Executes the specs. * * If called with no parameter or with a falsy parameter, * all specs will be executed except those that are excluded by a * [spec filter]{@link Configuration#specFilter} or other mechanism. If the * parameter is a list of spec/suite IDs, only those specs/suites will * be run. * * execute should not be called more than once unless the env has been * configured with `{autoCleanClosures: false}`. * * execute returns a promise. The promise will be resolved to the same * {@link JasmineDoneInfo|overall result} that's passed to a reporter's * `jasmineDone` method, even if the suite did not pass. To determine * whether the suite passed, check the value that the promise resolves to * or use a {@link Reporter}. The promise will be rejected in the case of * certain serious errors that prevent execution from starting. * * @name Env#execute * @since 2.0.0 * @function * @async * @param {(string[])=} runablesToRun IDs of suites and/or specs to run * @return {Promise