Merge branch '3.99' into 4.0

This commit is contained in:
Steve Gravrock
2021-07-10 09:13:28 -07:00
36 changed files with 1043 additions and 277 deletions

View File

@@ -6,9 +6,11 @@ getJasmineRequireObj().Clock = function() {
typeof process.versions.node === 'string';
/**
* _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}.
* @class Clock
* @classdesc Jasmine's mock clock is used when testing time dependent code.
* @classdesc Jasmine's mock clock is used when testing time dependent code.<br>
* _Note:_ Do not construct this directly. You can get the current clock with
* {@link jasmine.clock}.
* @hideconstructor
*/
function Clock(global, delayedFunctionSchedulerFactory, mockDate) {
var self = this,

View File

@@ -1,10 +1,11 @@
getJasmineRequireObj().Env = function(j$) {
/**
* _Note:_ Do not construct this directly, Jasmine will make one during booting.
* @name Env
* @class Env
* @since 2.0.0
* @classdesc The Jasmine environment
* @constructor
* @classdesc The Jasmine environment.<br>
* _Note:_ Do not construct this directly. You can obtain the Env instance by
* calling {@link jasmine.getEnv}.
* @hideconstructor
*/
function Env(options) {
options = options || {};
@@ -35,7 +36,8 @@ getJasmineRequireObj().Env = function(j$) {
/**
* This represents the available options to configure Jasmine.
* Options that are not provided will use their default values
* Options that are not provided will use their default values.
* @see Env#configure
* @interface Configuration
* @since 3.3.0
*/

View File

@@ -46,6 +46,12 @@ getJasmineRequireObj().Expectation = function(j$) {
* Asynchronous matchers that operate on an actual value which is a promise,
* and return a promise.
*
* Most async matchers will wait indefinitely for the promise to be resolved
* or rejected, resulting in a spec timeout if that never happens. If you
* expect that the promise will already be resolved or rejected at the time
* the matcher is called, you can use the {@link async-matchers#already}
* modifier to get a faster failure with a more helpful message.
*
* Note: Specs must await the result of each async matcher, return the
* promise returned by the matcher, or return a promise that's derived from
* the one returned by the matcher. Otherwise the matcher will not be
@@ -115,6 +121,23 @@ getJasmineRequireObj().Expectation = function(j$) {
}
});
/**
* Fail as soon as possible if the actual is pending.
* Otherwise evaluate the matcher.
* @member
* @name async-matchers#already
* @type {async-matchers}
* @example
* await expectAsync(myPromise).already.toBeResolved();
* @example
* return expectAsync(myPromise).already.toBeResolved();
*/
Object.defineProperty(AsyncExpectation.prototype, 'already', {
get: function() {
return addFilter(this, expectSettledPromiseFilter);
}
});
function wrapSyncCompare(name, matcherFactory) {
return function() {
var result = this.expector.compare(name, matcherFactory, arguments);
@@ -193,6 +216,27 @@ getJasmineRequireObj().Expectation = function(j$) {
buildFailureMessage: negatedFailureMessage
};
var expectSettledPromiseFilter = {
selectComparisonFunc: function(matcher) {
return function(actual) {
var matcherArgs = arguments;
return j$.isPending_(actual).then(function(isPending) {
if (isPending) {
return {
pass: false,
message:
'Expected a promise to be settled (via ' +
'expectAsync(...).already) but it was pending.'
};
} else {
return matcher.compare.apply(null, matcherArgs);
}
});
};
}
};
function ContextAddingFilter(message) {
this.message = message;
}

View File

@@ -13,9 +13,11 @@ getJasmineRequireObj().Spy = function(j$) {
});
/**
* _Note:_ Do not construct this directly, use {@link spyOn}, {@link spyOnProperty}, {@link jasmine.createSpy}, or {@link jasmine.createSpyObj}
* @constructor
* @name Spy
* @classdesc _Note:_ Do not construct this directly. Use {@link spyOn},
* {@link spyOnProperty}, {@link jasmine.createSpy}, or
* {@link jasmine.createSpyObj} instead.
* @class Spy
* @hideconstructor
*/
function Spy(
name,

View File

@@ -163,7 +163,7 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
return spy;
};
this.spyOnAllFunctions = function(obj) {
this.spyOnAllFunctions = function(obj, includeNonEnumerable) {
if (j$.util.isUndefined(obj)) {
throw new Error(
'spyOnAllFunctions could not find an object to spy upon'
@@ -171,30 +171,27 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
}
var pointer = obj,
props = [],
prop,
descriptor;
propsToSpyOn = [],
properties,
propertiesToSkip = [];
while (pointer) {
for (prop in pointer) {
if (
Object.prototype.hasOwnProperty.call(pointer, prop) &&
pointer[prop] instanceof Function
) {
descriptor = Object.getOwnPropertyDescriptor(pointer, prop);
if (
(descriptor.writable || descriptor.set) &&
descriptor.configurable
) {
props.push(prop);
}
}
}
while (
pointer &&
(!includeNonEnumerable || pointer !== Object.prototype)
) {
properties = getProps(pointer, includeNonEnumerable);
properties = properties.filter(function(prop) {
return propertiesToSkip.indexOf(prop) === -1;
});
propertiesToSkip = propertiesToSkip.concat(properties);
propsToSpyOn = propsToSpyOn.concat(
getSpyableFunctionProps(pointer, properties)
);
pointer = Object.getPrototypeOf(pointer);
}
for (var i = 0; i < props.length; i++) {
this.spyOn(obj, props[i]);
for (var i = 0; i < propsToSpyOn.length; i++) {
this.spyOn(obj, propsToSpyOn[i]);
}
return obj;
@@ -209,5 +206,49 @@ getJasmineRequireObj().SpyRegistry = function(j$) {
};
}
function getProps(obj, includeNonEnumerable) {
var enumerableProperties = Object.keys(obj);
if (!includeNonEnumerable) {
return enumerableProperties;
}
return Object.getOwnPropertyNames(obj).filter(function(prop) {
return (
prop !== 'constructor' ||
enumerableProperties.indexOf('constructor') > -1
);
});
}
function getSpyableFunctionProps(obj, propertiesToCheck) {
var props = [],
prop;
for (var i = 0; i < propertiesToCheck.length; i++) {
prop = propertiesToCheck[i];
if (
Object.prototype.hasOwnProperty.call(obj, prop) &&
isSpyableProp(obj, prop)
) {
props.push(prop);
}
}
return props;
}
function isSpyableProp(obj, prop) {
var value, descriptor;
try {
value = obj[prop];
} catch (e) {
return false;
}
if (value instanceof Function) {
descriptor = Object.getOwnPropertyDescriptor(obj, prop);
return (descriptor.writable || descriptor.set) && descriptor.configurable;
}
return false;
}
return SpyRegistry;
};

View File

@@ -201,6 +201,19 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
return matches ? matches[1] : '<anonymous>';
};
j$.isPending_ = function(promise) {
var sentinel = {};
// eslint-disable-next-line compat/compat
return Promise.race([promise, Promise.resolve(sentinel)]).then(
function(result) {
return result === sentinel;
},
function() {
return false;
}
);
};
/**
* Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
* that will succeed if the actual value being compared is an instance of the specified class/constructor.

View File

@@ -10,7 +10,7 @@ getJasmineRequireObj().toBeResolved = function(j$) {
* @example
* return expectAsync(aPromise).toBeResolved();
*/
return function toBeResolved() {
return function toBeResolved(matchersUtil) {
return {
compare: function(actual) {
if (!j$.isPromiseLike(actual)) {
@@ -21,8 +21,15 @@ getJasmineRequireObj().toBeResolved = function(j$) {
function() {
return { pass: true };
},
function() {
return { pass: false };
function(e) {
return {
pass: false,
message:
'Expected a promise to be resolved but it was ' +
'rejected with ' +
matchersUtil.pp(e) +
'.'
};
}
);
}

View File

@@ -45,10 +45,14 @@ getJasmineRequireObj().toBeResolvedTo = function(j$) {
};
}
},
function() {
function(e) {
return {
pass: false,
message: prefix(false) + ' but it was rejected.'
message:
prefix(false) +
' but it was rejected with ' +
matchersUtil.pp(e) +
'.'
};
}
);

View File

@@ -1,10 +1,10 @@
getJasmineRequireObj().MatchersUtil = function(j$) {
/**
* @class MatchersUtil
* @classdesc Utilities for use in implementing matchers.<br>
* _Note:_ Do not construct this directly. Jasmine will construct one and
* pass it to matchers and asymmetric equality testers.
* @name MatchersUtil
* @classdesc Utilities for use in implementing matchers
* @constructor
* @hideconstructor
*/
function MatchersUtil(options) {
options = options || {};
@@ -599,3 +599,28 @@ getJasmineRequireObj().MatchersUtil = function(j$) {
return MatchersUtil;
};
/**
* @interface AsymmetricEqualityTester
* @classdesc An asymmetric equality tester is an object that can match multiple
* objects. Examples include jasmine.any() and jasmine.stringMatching().
* User-defined asymmetric equality testers can also be defined and used in
* expectations.
* @see custom_asymmetric_equality_testers
* @since 2.0.0
*/
/**
* Determines whether a value matches this tester
* @function
* @name AsymmetricEqualityTester#asymmetricMatch
* @param value {any} The value to test
* @param matchersUtil {MatchersUtil} utilities for testing equality, etc
* @return {Boolean}
*/
/**
* Returns a string representation of this tester to use in matcher failure messages
* @function
* @name AsymmetricEqualityTester#jasmineToString
* @param pp {function} Function that takes a value and returns a pretty-printed representation
* @return {String}
*/

View File

@@ -285,10 +285,11 @@ getJasmineRequireObj().interface = function(jasmine, env) {
* @function
* @global
* @param {Object} obj - The object upon which to install the {@link Spy}s
* @param {boolean} includeNonEnumerable - Whether or not to add spies to non-enumerable properties
* @returns {Object} the spied object
*/
spyOnAllFunctions: function(obj) {
return env.spyOnAllFunctions(obj);
spyOnAllFunctions: function(obj, includeNonEnumerable) {
return env.spyOnAllFunctions(obj, includeNonEnumerable);
},
jsApiReporter: new jasmine.JsApiReporter({

View File

@@ -177,7 +177,10 @@ jasmineRequire.HtmlReporter = function(j$) {
' of ' +
totalSpecsDefined +
' specs - run all';
var skippedLink = addToExistingQueryString('spec', '');
// include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906
var skippedLink =
(window.location.pathname || '') +
addToExistingQueryString('spec', '');
alert.appendChild(
createDom(
'span',
@@ -545,7 +548,7 @@ jasmineRequire.HtmlReporter = function(j$) {
);
throwCheckbox.checked = config.oneFailurePerSpec;
throwCheckbox.onclick = function() {
navigateWithNewParam('throwFailures', !config.oneFailurePerSpec);
navigateWithNewParam('oneFailurePerSpec', !config.oneFailurePerSpec);
};
var randomCheckbox = optionsMenuDom.querySelector(
@@ -615,7 +618,11 @@ jasmineRequire.HtmlReporter = function(j$) {
suite = suite.parent;
}
return addToExistingQueryString('spec', els.join(' '));
// include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906
return (
(window.location.pathname || '') +
addToExistingQueryString('spec', els.join(' '))
);
}
function addDeprecationWarnings(result, runnableType) {
@@ -718,11 +725,19 @@ jasmineRequire.HtmlReporter = function(j$) {
}
function specHref(result) {
return addToExistingQueryString('spec', result.fullName);
// include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906
return (
(window.location.pathname || '') +
addToExistingQueryString('spec', result.fullName)
);
}
function seedHref(seed) {
return addToExistingQueryString('seed', seed);
// include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906
return (
(window.location.pathname || '') +
addToExistingQueryString('seed', seed)
);
}
function defaultQueryString(key, value) {