diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index d88c3948..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "pages"] - path = pages - url = https://github.com/pivotal/jasmine.git diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 02037164..708b3b70 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -123,6 +123,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) { j$.SpyRegistry = jRequire.SpyRegistry(j$); j$.SpyStrategy = jRequire.SpyStrategy(j$); j$.StringMatching = jRequire.StringMatching(j$); + j$.StringContaining = jRequire.StringContaining(j$); j$.UserContext = jRequire.UserContext(j$); j$.Suite = jRequire.Suite(j$); j$.Timer = jRequire.Timer(); @@ -424,7 +425,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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. * @name jasmine.any * @since 1.3.0 @@ -436,7 +437,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 not `null` and not `undefined`. * @name jasmine.anything * @since 2.2.0 @@ -447,7 +448,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 `true` or anything truthy. * @name jasmine.truthy * @since 3.1.0 @@ -458,7 +459,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 `null`, `undefined`, `0`, `false` or anything falsey. * @name jasmine.falsy * @since 3.1.0 @@ -469,7 +470,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 empty. * @name jasmine.empty * @since 3.1.0 @@ -480,7 +481,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 not empty. * @name jasmine.notEmpty * @since 3.1.0 @@ -491,7 +492,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 contains at least the keys and values. * @name jasmine.objectContaining * @since 1.3.0 @@ -503,7 +504,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 is a `String` that matches the `RegExp` or `String`. * @name jasmine.stringMatching * @since 2.2.0 @@ -515,7 +516,19 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 is a `String` that contains the specified `String`. + * @name jasmine.stringContaining + * @since 3.10.0 + * @function + * @param {String} expected + */ + j$.stringContaining = function(expected) { + return new j$.StringContaining(expected); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, 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 is an `Array` that contains at least the elements in the sample. * @name jasmine.arrayContaining * @since 2.2.0 @@ -527,7 +540,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 is an `Array` that contains all of the elements in the sample in any order. * @name jasmine.arrayWithExactContents * @since 2.8.0 @@ -539,7 +552,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 every key/value pair in the sample passes the deep equality comparison * with at least one key/value pair in the actual value being compared * @name jasmine.mapContaining @@ -552,7 +565,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 every item in the sample passes the deep equality comparison * with at least one item in the actual value being compared * @name jasmine.setContaining @@ -734,6 +747,7 @@ getJasmineRequireObj().Spec = function(j$) { /** * @interface Spec * @see Configuration#specFilter + * @since 2.0.0 */ function Spec(attrs) { this.expectationFactory = attrs.expectationFactory; @@ -744,6 +758,7 @@ getJasmineRequireObj().Spec = function(j$) { * @name Spec#id * @readonly * @type {string} + * @since 2.0.0 */ this.id = attrs.id; /** @@ -751,6 +766,7 @@ getJasmineRequireObj().Spec = function(j$) { * @name Spec#description * @readonly * @type {string} + * @since 2.0.0 */ this.description = attrs.description || ''; this.queueableFn = attrs.queueableFn; @@ -798,7 +814,8 @@ getJasmineRequireObj().Spec = function(j$) { * @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} - */ + * @since 2.0.0 +x */ this.result = { id: this.id, description: this.description, @@ -966,6 +983,7 @@ getJasmineRequireObj().Spec = function(j$) { * @name Spec#getFullName * @function * @returns {string} + * @since 2.0.0 */ Spec.prototype.getFullName = function() { return this.getSpecName(this); @@ -1869,6 +1887,7 @@ getJasmineRequireObj().Env = function(j$) { * @function * @name Env#topSuite * @return {Suite} the root suite + * @since 2.0.0 */ this.topSuite = function() { return j$.deprecatingSuiteProxy(topSuite, null, this); @@ -2061,6 +2080,7 @@ getJasmineRequireObj().Env = function(j$) { * @typedef JasmineStartedInfo * @property {Int} totalSpecsDefined - The total number of specs defined in this suite. * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. + * @since 2.0.0 */ reporter.jasmineStarted( { @@ -2099,6 +2119,7 @@ getJasmineRequireObj().Env = function(j$) { * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. * @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level. * @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level. + * @since 2.4.0 */ reporter.jasmineDone( { @@ -3090,6 +3111,31 @@ getJasmineRequireObj().SetContaining = function(j$) { return SetContaining; }; +getJasmineRequireObj().StringContaining = function(j$) { + function StringContaining(expected) { + if (!j$.isString_(expected)) { + throw new Error('Expected is not a String'); + } + + this.expected = expected; + } + + StringContaining.prototype.asymmetricMatch = function(other) { + if (!j$.isString_(other)) { + // Arrays, etc. don't match no matter what their indexOf returns. + return false; + } + + return other.indexOf(this.expected) !== -1; + }; + + StringContaining.prototype.jasmineToString = function() { + return ''; + }; + + return StringContaining; +}; + getJasmineRequireObj().StringMatching = function(j$) { function StringMatching(expected) { if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { @@ -3301,6 +3347,7 @@ getJasmineRequireObj().CallTracker = function(j$) { /** * Get the "this" object that was passed to a specific invocation of this spy. * @name Spy#calls#thisFor + * @since 3.8.0 * @function * @param {Integer} index The 0-based invocation index. * @return {Object?} @@ -3462,6 +3509,7 @@ getJasmineRequireObj().Clock = function() { /** * @class Clock + * @since 1.3.0 * @classdesc Jasmine's mock clock is used when testing time dependent code.
* _Note:_ Do not construct this directly. You can get the current clock with * {@link jasmine.clock}. @@ -4398,6 +4446,7 @@ getJasmineRequireObj().Expectation = function(j$) { * Otherwise evaluate the matcher. * @member * @name async-matchers#already + * @since 3.8.0 * @type {async-matchers} * @example * await expectAsync(myPromise).already.toBeResolved(); @@ -6076,9 +6125,33 @@ getJasmineRequireObj().MatchersUtil = function(j$) { /** * @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. + * objects. Examples include jasmine.any() and jasmine.stringMatching(). Jasmine + * includes a number of built-in asymmetric equality testers, such as + * {@link jasmine.objectContaining}. User-defined asymmetric equality testers are + * also supported. + * + * Asymmetric equality testers work with any matcher, including user-defined + * custom matchers, that uses {@link MatchersUtil#equals} or + * {@link MatchersUtil#contains}. + * + * @example + * function numberDivisibleBy(divisor) { + * return { + * asymmetricMatch: function(n) { + * return typeof n === 'number' && n % divisor === 0; + * }, + * jasmineToString: function() { + * return ``; + * } + * }; + * } + * + * var actual = { + * n: 2, + * otherFields: "don't care" + * }; + * + * expect(actual).toEqual(jasmine.objectContaining({n: numberDivisibleBy(2)})); * @see custom_asymmetric_equality_testers * @since 2.0.0 */ @@ -9906,6 +9979,7 @@ getJasmineRequireObj().Suite = function(j$) { /** * @interface Suite * @see Env#topSuite + * @since 2.0.0 */ function Suite(attrs) { this.env = attrs.env; @@ -9914,6 +9988,7 @@ getJasmineRequireObj().Suite = function(j$) { * @name Suite#id * @readonly * @type {string} + * @since 2.0.0 */ this.id = attrs.id; /** @@ -9928,6 +10003,7 @@ getJasmineRequireObj().Suite = function(j$) { * @name Suite#description * @readonly * @type {string} + * @since 2.0.0 */ this.description = attrs.description; this.expectationFactory = attrs.expectationFactory; @@ -9946,6 +10022,7 @@ getJasmineRequireObj().Suite = function(j$) { * The suite's children. * @name Suite#children * @type {Array.<(Spec|Suite)>} + * @since 2.0.0 */ this.children = []; @@ -9959,6 +10036,7 @@ getJasmineRequireObj().Suite = function(j$) { * @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} + * @since 2.0.0 */ this.result = { id: this.id, @@ -9989,6 +10067,7 @@ getJasmineRequireObj().Suite = function(j$) { * @name Suite#getFullName * @function * @returns {string} + * @since 2.0.0 */ Suite.prototype.getFullName = function() { var fullName = []; diff --git a/spec/core/asymmetric_equality/StringContainingSpec.js b/spec/core/asymmetric_equality/StringContainingSpec.js new file mode 100644 index 00000000..aef1f48e --- /dev/null +++ b/spec/core/asymmetric_equality/StringContainingSpec.js @@ -0,0 +1,27 @@ +describe('StringContaining', function() { + it('searches for a provided substring when the expected is a String', function() { + var matcher = new jasmineUnderTest.StringContaining('foo'); + + expect(matcher.asymmetricMatch('barfoobaz')).toBe(true); + expect(matcher.asymmetricMatch('barbaz')).toBe(false); + }); + + it('raises an Error when the expected is not a String', function() { + expect(function() { + new jasmineUnderTest.StringContaining(/foo/); + }).toThrowError(/not a String/); + }); + + it('fails when the actual is not a String', function() { + var matcher = new jasmineUnderTest.StringContaining('x'); + expect(matcher.asymmetricMatch(['x'])).toBe(false); + }); + + it("jasmineToString's itself", function() { + var matching = new jasmineUnderTest.StringContaining('foo'); + + expect(matching.jasmineToString()).toEqual( + '' + ); + }); +}); diff --git a/spec/core/integration/AsymmetricEqualityTestersSpec.js b/spec/core/integration/AsymmetricEqualityTestersSpec.js index 3d12f15d..7ab1c77f 100644 --- a/spec/core/integration/AsymmetricEqualityTestersSpec.js +++ b/spec/core/integration/AsymmetricEqualityTestersSpec.js @@ -231,6 +231,16 @@ describe('Asymmetric equality testers (Integration)', function() { }); }); + describe('stringContaining', function() { + verifyPasses(function(env) { + env.expect('foo').toEqual(jasmineUnderTest.stringContaining('o')); + }); + + verifyFails(function(env) { + env.expect('bar').toEqual(jasmineUnderTest.stringContaining('o')); + }); + }); + describe('truthy', function() { verifyPasses(function(env) { env.expect(true).toEqual(jasmineUnderTest.truthy()); diff --git a/src/core/CallTracker.js b/src/core/CallTracker.js index 128790fe..fa9ebeaa 100644 --- a/src/core/CallTracker.js +++ b/src/core/CallTracker.js @@ -52,6 +52,7 @@ getJasmineRequireObj().CallTracker = function(j$) { /** * Get the "this" object that was passed to a specific invocation of this spy. * @name Spy#calls#thisFor + * @since 3.8.0 * @function * @param {Integer} index The 0-based invocation index. * @return {Object?} diff --git a/src/core/Clock.js b/src/core/Clock.js index 30c1ca7a..1bb593e1 100644 --- a/src/core/Clock.js +++ b/src/core/Clock.js @@ -7,6 +7,7 @@ getJasmineRequireObj().Clock = function() { /** * @class Clock + * @since 1.3.0 * @classdesc Jasmine's mock clock is used when testing time dependent code.
* _Note:_ Do not construct this directly. You can get the current clock with * {@link jasmine.clock}. diff --git a/src/core/Env.js b/src/core/Env.js index 6a28a34e..89e8232b 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -814,6 +814,7 @@ getJasmineRequireObj().Env = function(j$) { * @function * @name Env#topSuite * @return {Suite} the root suite + * @since 2.0.0 */ this.topSuite = function() { return j$.deprecatingSuiteProxy(topSuite, null, this); @@ -1006,6 +1007,7 @@ getJasmineRequireObj().Env = function(j$) { * @typedef JasmineStartedInfo * @property {Int} totalSpecsDefined - The total number of specs defined in this suite. * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. + * @since 2.0.0 */ reporter.jasmineStarted( { @@ -1044,6 +1046,7 @@ getJasmineRequireObj().Env = function(j$) { * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. * @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level. * @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level. + * @since 2.4.0 */ reporter.jasmineDone( { diff --git a/src/core/Expectation.js b/src/core/Expectation.js index 0309ec34..5545e1b0 100644 --- a/src/core/Expectation.js +++ b/src/core/Expectation.js @@ -126,6 +126,7 @@ getJasmineRequireObj().Expectation = function(j$) { * Otherwise evaluate the matcher. * @member * @name async-matchers#already + * @since 3.8.0 * @type {async-matchers} * @example * await expectAsync(myPromise).already.toBeResolved(); diff --git a/src/core/Spec.js b/src/core/Spec.js index 60946906..6e997550 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -2,6 +2,7 @@ getJasmineRequireObj().Spec = function(j$) { /** * @interface Spec * @see Configuration#specFilter + * @since 2.0.0 */ function Spec(attrs) { this.expectationFactory = attrs.expectationFactory; @@ -12,6 +13,7 @@ getJasmineRequireObj().Spec = function(j$) { * @name Spec#id * @readonly * @type {string} + * @since 2.0.0 */ this.id = attrs.id; /** @@ -19,6 +21,7 @@ getJasmineRequireObj().Spec = function(j$) { * @name Spec#description * @readonly * @type {string} + * @since 2.0.0 */ this.description = attrs.description || ''; this.queueableFn = attrs.queueableFn; @@ -66,7 +69,8 @@ getJasmineRequireObj().Spec = function(j$) { * @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} - */ + * @since 2.0.0 +x */ this.result = { id: this.id, description: this.description, @@ -234,6 +238,7 @@ getJasmineRequireObj().Spec = function(j$) { * @name Spec#getFullName * @function * @returns {string} + * @since 2.0.0 */ Spec.prototype.getFullName = function() { return this.getSpecName(this); diff --git a/src/core/Suite.js b/src/core/Suite.js index ce873ad3..abf48b6a 100644 --- a/src/core/Suite.js +++ b/src/core/Suite.js @@ -2,6 +2,7 @@ getJasmineRequireObj().Suite = function(j$) { /** * @interface Suite * @see Env#topSuite + * @since 2.0.0 */ function Suite(attrs) { this.env = attrs.env; @@ -10,6 +11,7 @@ getJasmineRequireObj().Suite = function(j$) { * @name Suite#id * @readonly * @type {string} + * @since 2.0.0 */ this.id = attrs.id; /** @@ -24,6 +26,7 @@ getJasmineRequireObj().Suite = function(j$) { * @name Suite#description * @readonly * @type {string} + * @since 2.0.0 */ this.description = attrs.description; this.expectationFactory = attrs.expectationFactory; @@ -42,6 +45,7 @@ getJasmineRequireObj().Suite = function(j$) { * The suite's children. * @name Suite#children * @type {Array.<(Spec|Suite)>} + * @since 2.0.0 */ this.children = []; @@ -55,6 +59,7 @@ getJasmineRequireObj().Suite = function(j$) { * @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} + * @since 2.0.0 */ this.result = { id: this.id, @@ -85,6 +90,7 @@ getJasmineRequireObj().Suite = function(j$) { * @name Suite#getFullName * @function * @returns {string} + * @since 2.0.0 */ Suite.prototype.getFullName = function() { var fullName = []; diff --git a/src/core/asymmetric_equality/StringContaining.js b/src/core/asymmetric_equality/StringContaining.js new file mode 100644 index 00000000..16f0c4e6 --- /dev/null +++ b/src/core/asymmetric_equality/StringContaining.js @@ -0,0 +1,24 @@ +getJasmineRequireObj().StringContaining = function(j$) { + function StringContaining(expected) { + if (!j$.isString_(expected)) { + throw new Error('Expected is not a String'); + } + + this.expected = expected; + } + + StringContaining.prototype.asymmetricMatch = function(other) { + if (!j$.isString_(other)) { + // Arrays, etc. don't match no matter what their indexOf returns. + return false; + } + + return other.indexOf(this.expected) !== -1; + }; + + StringContaining.prototype.jasmineToString = function() { + return ''; + }; + + return StringContaining; +}; diff --git a/src/core/base.js b/src/core/base.js index 89cd2b6e..ec5a6383 100644 --- a/src/core/base.js +++ b/src/core/base.js @@ -229,7 +229,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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. * @name jasmine.any * @since 1.3.0 @@ -241,7 +241,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 not `null` and not `undefined`. * @name jasmine.anything * @since 2.2.0 @@ -252,7 +252,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 `true` or anything truthy. * @name jasmine.truthy * @since 3.1.0 @@ -263,7 +263,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 `null`, `undefined`, `0`, `false` or anything falsey. * @name jasmine.falsy * @since 3.1.0 @@ -274,7 +274,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 empty. * @name jasmine.empty * @since 3.1.0 @@ -285,7 +285,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 not empty. * @name jasmine.notEmpty * @since 3.1.0 @@ -296,7 +296,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 contains at least the keys and values. * @name jasmine.objectContaining * @since 1.3.0 @@ -308,7 +308,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 is a `String` that matches the `RegExp` or `String`. * @name jasmine.stringMatching * @since 2.2.0 @@ -320,7 +320,19 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 is a `String` that contains the specified `String`. + * @name jasmine.stringContaining + * @since 3.10.0 + * @function + * @param {String} expected + */ + j$.stringContaining = function(expected) { + return new j$.StringContaining(expected); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, 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 is an `Array` that contains at least the elements in the sample. * @name jasmine.arrayContaining * @since 2.2.0 @@ -332,7 +344,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 is an `Array` that contains all of the elements in the sample in any order. * @name jasmine.arrayWithExactContents * @since 2.8.0 @@ -344,7 +356,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 every key/value pair in the sample passes the deep equality comparison * with at least one key/value pair in the actual value being compared * @name jasmine.mapContaining @@ -357,7 +369,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) { }; /** - * 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}), + * Get an {@link AsymmetricEqualityTester}, 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 every item in the sample passes the deep equality comparison * with at least one item in the actual value being compared * @name jasmine.setContaining diff --git a/src/core/matchers/matchersUtil.js b/src/core/matchers/matchersUtil.js index 16c2d32e..bba9797c 100644 --- a/src/core/matchers/matchersUtil.js +++ b/src/core/matchers/matchersUtil.js @@ -683,9 +683,33 @@ getJasmineRequireObj().MatchersUtil = function(j$) { /** * @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. + * objects. Examples include jasmine.any() and jasmine.stringMatching(). Jasmine + * includes a number of built-in asymmetric equality testers, such as + * {@link jasmine.objectContaining}. User-defined asymmetric equality testers are + * also supported. + * + * Asymmetric equality testers work with any matcher, including user-defined + * custom matchers, that uses {@link MatchersUtil#equals} or + * {@link MatchersUtil#contains}. + * + * @example + * function numberDivisibleBy(divisor) { + * return { + * asymmetricMatch: function(n) { + * return typeof n === 'number' && n % divisor === 0; + * }, + * jasmineToString: function() { + * return `
`; + * } + * }; + * } + * + * var actual = { + * n: 2, + * otherFields: "don't care" + * }; + * + * expect(actual).toEqual(jasmine.objectContaining({n: numberDivisibleBy(2)})); * @see custom_asymmetric_equality_testers * @since 2.0.0 */ diff --git a/src/core/requireCore.js b/src/core/requireCore.js index 6372fd68..1a2ee522 100644 --- a/src/core/requireCore.js +++ b/src/core/requireCore.js @@ -101,6 +101,7 @@ var getJasmineRequireObj = (function(jasmineGlobal) { j$.SpyRegistry = jRequire.SpyRegistry(j$); j$.SpyStrategy = jRequire.SpyStrategy(j$); j$.StringMatching = jRequire.StringMatching(j$); + j$.StringContaining = jRequire.StringContaining(j$); j$.UserContext = jRequire.UserContext(j$); j$.Suite = jRequire.Suite(j$); j$.Timer = jRequire.Timer();