Merge branch 'main' into 3.99

This commit is contained in:
Steve Gravrock
2020-09-14 18:39:32 -07:00
82 changed files with 2725 additions and 1286 deletions

View File

@@ -620,12 +620,22 @@ getJasmineRequireObj().Env = function(j$) {
this.deprecated = function(deprecation) {
var runnable = currentRunnable() || topSuite;
var context;
if (runnable === topSuite) {
context = '';
} else if (runnable === currentSuite()) {
context = ' (in suite: ' + runnable.getFullName() + ')';
} else {
context = ' (in spec: ' + runnable.getFullName() + ')';
}
runnable.addDeprecationWarning(deprecation);
if (
typeof console !== 'undefined' &&
typeof console.error === 'function'
) {
console.error('DEPRECATION:', deprecation);
console.error('DEPRECATION: ' + deprecation + context);
}
};
@@ -767,7 +777,8 @@ getJasmineRequireObj().Env = function(j$) {
queueRunnerFactory
);
this.execute = function(runnablesToRun) {
// Both params are optional.
this.execute = function(runnablesToRun, onComplete) {
installGlobalErrors();
if (!runnablesToRun) {
@@ -875,7 +886,11 @@ getJasmineRequireObj().Env = function(j$) {
failedExpectations: topSuite.result.failedExpectations,
deprecationWarnings: topSuite.result.deprecationWarnings
},
function() {}
function() {
if (onComplete) {
onComplete();
}
}
);
});
}
@@ -1014,6 +1029,7 @@ getJasmineRequireObj().Env = function(j$) {
id: getNextSuiteId(),
description: description,
parentSuite: currentDeclarationSuite,
timer: new j$.Timer(),
expectationFactory: expectationFactory,
asyncExpectationFactory: suiteAsyncExpectationFactory,
expectationResultFactory: expectationResultFactory,
@@ -1192,6 +1208,40 @@ getJasmineRequireObj().Env = function(j$) {
return spec;
};
/**
* Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult}
* @name Env#setSpecProperty
* @since 3.6.0
* @function
* @param {String} key The name of the property
* @param {*} value The value of the property
*/
this.setSpecProperty = function(key, value) {
if (!currentRunnable() || currentRunnable() == currentSuite()) {
throw new Error(
"'setSpecProperty' was used when there was no current spec"
);
}
currentRunnable().setSpecProperty(key, value);
};
/**
* Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult}
* @name Env#setSuiteProperty
* @since 3.6.0
* @function
* @param {String} key The name of the property
* @param {*} value The value of the property
*/
this.setSuiteProperty = function(key, value) {
if (!currentSuite()) {
throw new Error(
"'setSuiteProperty' was used when there was no current suite"
);
}
currentSuite().setSuiteProperty(key, value);
};
this.expect = function(actual) {
if (!currentRunnable()) {
throw new Error(

View File

@@ -96,7 +96,17 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
handlers.push(listener);
};
this.popListener = function popListener() {
this.popListener = function popListener(listener) {
if (!listener) {
throw new Error('popListener expects a listener');
}
if (listener !== handlers[handlers.length - 1]) {
throw new Error(
'popListener was passed a different listener than the current one'
);
}
handlers.pop();
};
}

View File

@@ -6,7 +6,7 @@ getJasmineRequireObj().JsApiReporter = function(j$) {
* @hideconstructor
*/
function JsApiReporter(options) {
var timer = options.timer || j$.noopTimer,
var timer = options.timer || new j$.Timer(),
status = 'loaded';
this.started = false;

View File

@@ -1,4 +1,6 @@
getJasmineRequireObj().QueueRunner = function(j$) {
var nextid = 1;
function StopExecutionError() {}
StopExecutionError.prototype = new Error();
j$.StopExecutionError = StopExecutionError;
@@ -18,6 +20,7 @@ getJasmineRequireObj().QueueRunner = function(j$) {
function emptyFn() {}
function QueueRunner(attrs) {
this.id_ = nextid++;
var queueableFns = attrs.queueableFns || [];
this.queueableFns = queueableFns.concat(attrs.cleanupFns || []);
this.firstCleanupIx = queueableFns.length;
@@ -120,7 +123,8 @@ getJasmineRequireObj().QueueRunner = function(j$) {
}),
errored = false,
queueableFn = self.queueableFns[iterativeIndex],
timeoutId;
timeoutId,
maybeThenable;
next.fail = function nextFail() {
self.fail.apply(null, arguments);
@@ -148,7 +152,7 @@ getJasmineRequireObj().QueueRunner = function(j$) {
try {
if (queueableFn.fn.length === 0) {
var maybeThenable = queueableFn.fn.call(self.userContext);
maybeThenable = queueableFn.fn.call(self.userContext);
if (maybeThenable && j$.isFunction_(maybeThenable.then)) {
maybeThenable.then(next, onPromiseRejection);
@@ -156,7 +160,8 @@ getJasmineRequireObj().QueueRunner = function(j$) {
return { completedSynchronously: false };
}
} else {
queueableFn.fn.call(self.userContext, next);
maybeThenable = queueableFn.fn.call(self.userContext, next);
this.diagnoseConflictingAsync_(queueableFn.fn, maybeThenable);
completedSynchronously = false;
return { completedSynchronously: false };
}
@@ -209,5 +214,28 @@ getJasmineRequireObj().QueueRunner = function(j$) {
});
};
QueueRunner.prototype.diagnoseConflictingAsync_ = function(fn, retval) {
if (retval && j$.isFunction_(retval.then)) {
// Issue a warning that matches the user's code
if (j$.isAsyncFunction_(fn)) {
this.deprecated(
'An asynchronous before/it/after ' +
'function was defined with the async keyword but also took a ' +
'done callback. This is not supported and will stop working in' +
' the future. Either remove the done callback (recommended) or ' +
'remove the async keyword.'
);
} else {
this.deprecated(
'An asynchronous before/it/after ' +
'function took a done callback but also returned a promise. ' +
'This is not supported and will stop working in the future. ' +
'Either remove the done callback (recommended) or change the ' +
'function to not return a promise.'
);
}
}
};
return QueueRunner;
};

View File

@@ -31,7 +31,7 @@ getJasmineRequireObj().Spec = function(j$) {
return true;
};
this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
this.timer = attrs.timer || j$.noopTimer;
this.timer = attrs.timer || new j$.Timer();
if (!this.queueableFn.fn) {
this.pend();
@@ -48,6 +48,7 @@ getJasmineRequireObj().Spec = function(j$) {
* @property {String} pendingReason - If the spec is {@link pending}, this will be the reason.
* @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec.
* @property {number} duration - The time in ms used by the spec execution, including any before/afterEach.
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty}
*/
this.result = {
id: this.id,
@@ -57,7 +58,8 @@ getJasmineRequireObj().Spec = function(j$) {
passedExpectations: [],
deprecationWarnings: [],
pendingReason: '',
duration: null
duration: null,
properties: null
};
}
@@ -74,6 +76,11 @@ getJasmineRequireObj().Spec = function(j$) {
}
};
Spec.prototype.setSpecProperty = function(key, value) {
this.result.properties = this.result.properties || {};
this.result.properties[key] = value;
};
Spec.prototype.expect = function(actual) {
return this.expectationFactory(actual, this);
};
@@ -96,6 +103,7 @@ getJasmineRequireObj().Spec = function(j$) {
fn: function(done) {
self.queueableFn.fn = null;
self.result.status = self.status(excluded, failSpecWithNoExp);
self.result.duration = self.timer.elapsed();
self.resultCallback(self.result, done);
}
};
@@ -111,7 +119,6 @@ getJasmineRequireObj().Spec = function(j$) {
self.onException.apply(self, arguments);
},
onComplete: function() {
self.result.duration = self.timer.elapsed();
onComplete(
self.result.status === 'failed' &&
new j$.StopExecutionError('spec failed')

View File

@@ -150,10 +150,10 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
* @name SpyStrategy#throwError
* @since 2.0.0
* @function
* @param {Error|String} something Thing to throw
* @param {Error|Object|String} something Thing to throw
*/
SpyStrategy.prototype.throwError = function(something) {
var error = something instanceof Error ? something : new Error(something);
var error = j$.isString_(something) ? new Error(something) : something;
this.plan = function() {
throw error;
};
@@ -168,7 +168,13 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
* @param {Function} fn The function to invoke with the passed parameters.
*/
SpyStrategy.prototype.callFake = function(fn) {
if (!(j$.isFunction_(fn) || j$.isAsyncFunction_(fn))) {
if (
!(
j$.isFunction_(fn) ||
j$.isAsyncFunction_(fn) ||
j$.isGeneratorFunction_(fn)
)
) {
throw new Error(
'Argument passed to callFake should be a function, got ' + fn
);

View File

@@ -14,7 +14,7 @@ getJasmineRequireObj().Suite = function(j$) {
this.beforeAllFns = [];
this.afterAllFns = [];
this.timer = attrs.timer || j$.noopTimer;
this.timer = attrs.timer || new j$.Timer();
this.children = [];
@@ -27,6 +27,7 @@ getJasmineRequireObj().Suite = function(j$) {
* @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite.
* @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite.
* @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach.
* @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty}
*/
this.result = {
id: this.id,
@@ -34,10 +35,16 @@ getJasmineRequireObj().Suite = function(j$) {
fullName: this.getFullName(),
failedExpectations: [],
deprecationWarnings: [],
duration: null
duration: null,
properties: null
};
}
Suite.prototype.setSuiteProperty = function(key, value) {
this.result.properties = this.result.properties || {};
this.result.properties[key] = value;
};
Suite.prototype.expect = function(actual) {
return this.expectationFactory(actual, this);
};

View File

@@ -22,12 +22,3 @@ getJasmineRequireObj().Timer = function() {
return Timer;
};
getJasmineRequireObj().noopTimer = function() {
return {
start: function() {},
elapsed: function() {
return 0;
}
};
};

View File

@@ -66,7 +66,9 @@ getJasmineRequireObj().asymmetricEqualityTesterArgCompatShim = function(j$) {
for (i = 0; i < props.length; i++) {
k = props[i];
if (k !== 'length') {
// Skip length (dealt with above), and anything that collides with
// MatchesUtil e.g. an Array.prototype.contains method added by user code
if (k !== 'length' && !self[k]) {
copyAndDeprecate(self, Array.prototype, k);
}
}
@@ -97,7 +99,7 @@ getJasmineRequireObj().asymmetricEqualityTesterArgCompatShim = function(j$) {
});
}
props = Object.getOwnPropertyDescriptors(Array.prototype);
props = Object.getOwnPropertyDescriptors(Array.prototype); // eslint-disable-line compat/compat
a = [];
for (k in props) {

View File

@@ -76,6 +76,10 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
return j$.isA_('AsyncFunction', value);
};
j$.isGeneratorFunction_ = function(value) {
return j$.isA_('GeneratorFunction', value);
};
j$.isTypedArray_ = function(value) {
return (
j$.isA_('Float32Array', value) ||
@@ -148,6 +152,24 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
);
};
j$.isWeakMap = function(obj) {
return (
obj !== null &&
typeof obj !== 'undefined' &&
typeof jasmineGlobal.WeakMap !== 'undefined' &&
obj.constructor === jasmineGlobal.WeakMap
);
};
j$.isDataView = function(obj) {
return (
obj !== null &&
typeof obj !== 'undefined' &&
typeof jasmineGlobal.DataView !== 'undefined' &&
obj.constructor === jasmineGlobal.DataView
);
};
j$.isPromise = function(obj) {
return (
typeof jasmineGlobal.Promise !== 'undefined' &&

View File

@@ -72,12 +72,7 @@ getJasmineRequireObj().DiffBuilder = function (j$) {
};
function dereferencePath(objectPath, actual, expected, pp) {
var i, asymmetricResult
for (i = 0; i < objectPath.components.length; i++) {
actual = actual[objectPath.components[i]];
expected = expected[objectPath.components[i]];
function handleAsymmetricExpected() {
if (j$.isAsymmetricEqualityTester_(expected) && j$.isFunction_(expected.valuesForDiff_)) {
var asymmetricResult = expected.valuesForDiff_(actual, pp);
expected = asymmetricResult.self;
@@ -85,6 +80,15 @@ getJasmineRequireObj().DiffBuilder = function (j$) {
}
}
var i;
handleAsymmetricExpected();
for (i = 0; i < objectPath.components.length; i++) {
actual = actual[objectPath.components[i]];
expected = expected[objectPath.components[i]];
handleAsymmetricExpected();
}
return {actual: actual, expected: expected};
}

View File

@@ -0,0 +1,26 @@
/* eslint-disable compat/compat */
getJasmineRequireObj().toBePending = function(j$) {
/**
* Expect a promise to be pending, i.e. the promise is neither resolved nor rejected.
* @function
* @async
* @name async-matchers#toBePending
* @since 3.6
* @example
* await expectAsync(aPromise).toBePending();
*/
return function toBePending() {
return {
compare: function(actual) {
if (!j$.isPromiseLike(actual)) {
throw new Error('Expected toBePending to be called on a promise.');
}
var want = {};
return Promise.race([actual, Promise.resolve(want)]).then(
function(got) { return {pass: want === got}; },
function() { return {pass: false}; }
);
}
};
};
};

View File

@@ -3,7 +3,6 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
* _Note:_ Do not construct this directly. Jasmine will construct one and
* pass it to matchers and asymmetric equality testers.
* @name MatchersUtil
* @since 2.0.0
* @classdesc Utilities for use in implementing matchers
* @constructor
*/
@@ -15,6 +14,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
* taking into account the current set of custom value formatters.
* @function
* @name MatchersUtil#pp
* @since 3.6.0
* @param {*} value The value to pretty-print
* @return {string} The pretty-printed value
*/
@@ -26,6 +26,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
* logic as {@link MatchersUtil#equals}.
* @function
* @name MatchersUtil#contains
* @since 2.0.0
* @param {*} haystack The collection to search
* @param {*} needle The value to search for
* @param [customTesters] An array of custom equality testers. Deprecated.
@@ -125,6 +126,7 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
* Determines whether two values are deeply equal to each other.
* @function
* @name MatchersUtil#equals
* @since 2.0.0
* @param {*} a The first value to compare
* @param {*} b The second value to compare
* @param [customTesters] An array of custom equality testers. Deprecated.

View File

@@ -1,5 +1,6 @@
getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) {
var availableMatchers = [
'toBePending',
'toBeResolved',
'toBeRejected',
'toBeResolvedTo',

View File

@@ -20,8 +20,10 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
'toBeUndefined',
'toContain',
'toEqual',
'toHaveSize',
'toHaveBeenCalled',
'toHaveBeenCalledBefore',
'toHaveBeenCalledOnceWith',
'toHaveBeenCalledTimes',
'toHaveBeenCalledWith',
'toHaveClass',

View File

@@ -0,0 +1,69 @@
getJasmineRequireObj().toHaveBeenCalledOnceWith = function (j$) {
var getErrorMsg = j$.formatErrorMsg('<toHaveBeenCalledOnceWith>', 'expect(<spyObj>).toHaveBeenCalledOnceWith(...arguments)');
/**
* {@link expect} the actual (a {@link Spy}) to have been called exactly once, and exactly with the particular arguments.
* @function
* @name matchers#toHaveBeenCalledOnceWith
* @since 3.6.0
* @param {...Object} - The arguments to look for
* @example
* expect(mySpy).toHaveBeenCalledOnceWith('foo', 'bar', 2);
*/
function toHaveBeenCalledOnceWith(util) {
return {
compare: function () {
var args = Array.prototype.slice.call(arguments, 0),
actual = args[0],
expectedArgs = args.slice(1);
if (!j$.isSpy(actual)) {
throw new Error(getErrorMsg('Expected a spy, but got ' + util.pp(actual) + '.'));
}
var prettyPrintedCalls = actual.calls.allArgs().map(function (argsForCall) {
return ' ' + util.pp(argsForCall);
});
if (actual.calls.count() === 1 && util.contains(actual.calls.allArgs(), expectedArgs)) {
return {
pass: true,
message: 'Expected spy ' + actual.and.identity + ' to have been called 0 times, multiple times, or once, but with arguments different from:\n'
+ ' ' + util.pp(expectedArgs) + '\n'
+ 'But the actual call was:\n'
+ prettyPrintedCalls.join(',\n') + '.\n\n'
};
}
function getDiffs() {
return actual.calls.allArgs().map(function (argsForCall, callIx) {
var diffBuilder = new j$.DiffBuilder();
util.equals(argsForCall, expectedArgs, diffBuilder);
return diffBuilder.getMessage();
});
}
function butString() {
switch (actual.calls.count()) {
case 0:
return 'But it was never called.\n\n';
case 1:
return 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n' + getDiffs().join('\n') + '\n\n';
default:
return 'But the actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n';
}
}
return {
pass: false,
message: 'Expected spy ' + actual.and.identity + ' to have been called only once, and with given args:\n'
+ ' ' + util.pp(expectedArgs) + '\n'
+ butString()
};
}
};
}
return toHaveBeenCalledOnceWith;
};

View File

@@ -0,0 +1,42 @@
getJasmineRequireObj().toHaveSize = function(j$) {
/**
* {@link expect} the actual size to be equal to the expected, using array-like length or object keys size.
* @function
* @name matchers#toHaveSize
* @since 3.6.0
* @param {Object} expected - Expected size
* @example
* array = [1,2];
* expect(array).toHaveSize(2);
*/
function toHaveSize() {
return {
compare: function(actual, expected) {
var result = {
pass: false
};
if (j$.isA_('WeakSet', actual) || j$.isWeakMap(actual) || j$.isDataView(actual)) {
throw new Error('Cannot get size of ' + actual + '.');
}
if (j$.isSet(actual) || j$.isMap(actual)) {
result.pass = actual.size === expected;
} else if (isLength(actual.length)) {
result.pass = actual.length === expected;
} else {
result.pass = Object.keys(actual).length === expected;
}
return result;
}
};
}
var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; // eslint-disable-line compat/compat
function isLength(value) {
return (typeof value == 'number') && value > -1 && value % 1 === 0 && value <= MAX_SAFE_INTEGER;
}
return toHaveSize;
};

View File

@@ -49,7 +49,6 @@ var getJasmineRequireObj = (function(jasmineGlobal) {
j$.Expector = jRequire.Expector(j$);
j$.Expectation = jRequire.Expectation(j$);
j$.buildExpectationResult = jRequire.buildExpectationResult(j$);
j$.noopTimer = jRequire.noopTimer();
j$.JsApiReporter = jRequire.JsApiReporter(j$);
j$.asymmetricEqualityTesterArgCompatShim = jRequire.asymmetricEqualityTesterArgCompatShim(
j$

View File

@@ -168,6 +168,30 @@ getJasmineRequireObj().interface = function(jasmine, env) {
return env.afterAll.apply(env, arguments);
},
/**
* Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult}
* @name setSpecProperty
* @since 3.6.0
* @function
* @param {String} key The name of the property
* @param {*} value The value of the property
*/
setSpecProperty: function(key, value) {
return env.setSpecProperty(key, value);
},
/**
* Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult}
* @name setSuiteProperty
* @since 3.6.0
* @function
* @param {String} key The name of the property
* @param {*} value The value of the property
*/
setSuiteProperty: function(key, value) {
return env.setSuiteProperty(key, value);
},
/**
* Create an expectation for a spec.
* @name expect
@@ -327,7 +351,7 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @since 3.6.0
* @function
* @param {Function} formatter - A function which takes a value to format and returns a string if it knows how to format it, and `undefined` otherwise.
* @see custom_object_formatter
* @see custom_object_formatters
*/
jasmine.addCustomObjectFormatter = function(formatter) {
return env.addCustomObjectFormatter(formatter);

View File

@@ -101,7 +101,7 @@ jasmineRequire.HtmlReporter = function(j$) {
if (result.status === 'failed') {
failures.push(failureDom(result));
}
addDeprecationWarnings(result);
addDeprecationWarnings(result, 'suite');
};
this.specStarted = function(result) {
@@ -137,7 +137,7 @@ jasmineRequire.HtmlReporter = function(j$) {
failures.push(failureDom(result));
}
addDeprecationWarnings(result);
addDeprecationWarnings(result, 'spec');
};
this.displaySpecInCorrectFormat = function(result) {
@@ -276,14 +276,27 @@ jasmineRequire.HtmlReporter = function(j$) {
addDeprecationWarnings(doneResult);
var warningBarClassName = 'jasmine-bar jasmine-warning';
for (i = 0; i < deprecationWarnings.length; i++) {
var warning = deprecationWarnings[i];
var context;
switch (deprecationWarnings[i].runnableType) {
case 'spec':
context = '(in spec: ' + deprecationWarnings[i].runnableName + ')';
break;
case 'suite':
context = '(in suite: ' + deprecationWarnings[i].runnableName + ')';
break;
default:
context = '';
}
alert.appendChild(
createDom(
'span',
{ className: warningBarClassName },
'DEPRECATION: ' + warning
{ className: 'jasmine-bar jasmine-warning' },
'DEPRECATION: ' + deprecationWarnings[i].message,
createDom('br'),
context
)
);
}
@@ -321,9 +334,11 @@ jasmineRequire.HtmlReporter = function(j$) {
find('.jasmine-failures-menu').onclick = function() {
setMenuModeTo('jasmine-failure-list');
return false;
};
find('.jasmine-spec-list-menu').onclick = function() {
setMenuModeTo('jasmine-spec-list');
return false;
};
setMenuModeTo('jasmine-failure-list');
@@ -592,12 +607,16 @@ jasmineRequire.HtmlReporter = function(j$) {
return addToExistingQueryString('spec', els.join(' '));
}
function addDeprecationWarnings(result) {
function addDeprecationWarnings(result, runnableType) {
if (result && result.deprecationWarnings) {
for (var i = 0; i < result.deprecationWarnings.length; i++) {
var warning = result.deprecationWarnings[i].message;
if (!j$.util.arrayContains(warning)) {
deprecationWarnings.push(warning);
deprecationWarnings.push({
message: warning,
runnableName: result.fullName,
runnableType: runnableType
});
}
}
}

View File

@@ -1,3 +1,5 @@
var jasmineRequire = window.jasmineRequire || require('./jasmine.js');
jasmineRequire.html = function(j$) {
j$.ResultsNode = jasmineRequire.ResultsNode();
j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);