You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

716 lines
19 KiB

'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports.default = jasmineEnv;
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 jasmineEnv(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
});
};
}
};
}