diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 023dec91..fef0a9e6 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -663,12 +663,6 @@ getJasmineRequireObj().Spec = function(j$) { this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.resultCallback = attrs.resultCallback || function() {}; - /** - * The unique ID of this spec. - * @name Spec#id - * @readonly - * @type {string} - */ this.id = attrs.id; this.description = attrs.description || ''; this.queueableFn = attrs.queueableFn; @@ -898,34 +892,43 @@ getJasmineRequireObj().Spec = function(j$) { * @interface Spec * @see Configuration#specFilter */ - Spec.prototype.buildMetadata = function() { - return { - /** - * The description passed to the {@link it} that created this spec. - * @name Spec#description - * @readonly - * @type {string} - */ - description: this.description, + Object.defineProperty(Spec.prototype, 'metadata', { + get: function() { + if (!this.metadata_) { + this.metadata_ = { + /** + * The unique ID of this spec. + * @name Spec#id + * @readonly + * @type {string} + */ + id: this.id, - /** - * The full description including all ancestors of this spec. - * @name Spec#getFullName - * @function - * @returns {string} - */ - getFullName: this.getFullName.bind(this) - }; - }; + /** + * The description passed to the {@link it} that created this spec. + * @name Spec#description + * @readonly + * @type {string} + */ + description: this.description, + + /** + * The full description including all ancestors of this spec. + * @name Spec#getFullName + * @function + * @returns {string} + */ + getFullName: this.getFullName.bind(this) + }; + } + + return this.metadata_; + } + }); return Spec; }; -if (typeof window == void 0 && typeof exports == 'object') { - /* globals exports */ - exports.Spec = jasmineRequire.Spec; -} - /*jshint bitwise: false*/ getJasmineRequireObj().Order = function() { @@ -1523,7 +1526,7 @@ getJasmineRequireObj().Env = function(j$) { * @return {Suite} the root suite */ this.topSuite = function() { - return topSuite.buildMetadata(null); + return topSuite.metadata; }; /** @@ -1906,7 +1909,7 @@ getJasmineRequireObj().Env = function(j$) { if (suite.parentSuite && !suite.children.length) { throw new Error('describe with no children (describe() or it())'); } - return suite; + return suite.metadata; }; this.xdescribe = function(description, specDefinitions) { @@ -1915,7 +1918,7 @@ getJasmineRequireObj().Env = function(j$) { var suite = suiteFactory(description); suite.pend(); addSpecsToSuite(suite, specDefinitions); - return suite; + return suite.metadata; }; var focusedRunnables = []; @@ -1930,7 +1933,7 @@ getJasmineRequireObj().Env = function(j$) { unfocusAncestor(); addSpecsToSuite(suite, specDefinitions); - return suite; + return suite.metadata; }; function addSpecsToSuite(suite, specDefinitions) { @@ -2020,7 +2023,7 @@ getJasmineRequireObj().Env = function(j$) { } }; - this.it = function(description, fn, timeout) { + this.it_ = function(description, fn, timeout) { ensureIsNotNested('it'); // it() sometimes doesn't have a fn argument, so only check the type if // it's given. @@ -2036,6 +2039,11 @@ getJasmineRequireObj().Env = function(j$) { return spec; }; + this.it = function(description, fn, timeout) { + const spec = this.it_(description, fn, timeout); + return spec.metadata; + }; + this.xit = function(description, fn, timeout) { ensureIsNotNested('xit'); // xit(), like it(), doesn't always have a fn argument, so only check the @@ -2043,9 +2051,9 @@ getJasmineRequireObj().Env = function(j$) { if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'xit'); } - var spec = this.it.apply(this, arguments); + var spec = this.it_.apply(this, arguments); spec.pend('Temporarily disabled with xit'); - return spec; + return spec.metadata; }; this.fit = function(description, fn, timeout) { @@ -2055,7 +2063,7 @@ getJasmineRequireObj().Env = function(j$) { currentDeclarationSuite.addChild(spec); focusedRunnables.push(spec.id); unfocusAncestor(); - return spec; + return spec.metadata; }; /** @@ -9045,12 +9053,6 @@ getJasmineRequireObj().StackTrace = function(j$) { getJasmineRequireObj().Suite = function(j$) { function Suite(attrs) { this.env = attrs.env; - /** - * The unique ID of this suite. - * @name Suite#id - * @readonly - * @type {string} - */ this.id = attrs.id; this.parentSuite = attrs.parentSuite; this.description = attrs.description; @@ -9235,49 +9237,68 @@ getJasmineRequireObj().Suite = function(j$) { ); }; - Suite.prototype.buildMetadata = function(parentMetadata) { + Object.defineProperty(Suite.prototype, 'metadata', { + get: function() { + if (!this.metadata_) { + this.metadata_ = new SuiteMetadata(this); + } + + return this.metadata_; + } + }); + + /** + * @interface Suite + * @see Env#topSuite + */ + function SuiteMetadata(suite) { + this.suite_ = suite; /** - * @interface Suite - * @see Env#topSuite + * The unique ID of this suite. + * @name Suite#id + * @readonly + * @type {string} */ - var result = { - /** - * The parent of this suite, or null if this is the top suite. - * @name Suite#parentSuite - * @readonly - * @type {Suite} - */ - parentSuite: parentMetadata, - - /** - * The description passed to the {@link describe} that created this suite. - * @name Suite#description - * @readonly - * @type {string} - */ - description: this.description, - - /** - * The full description including all ancestors of this suite. - * @name Suite#getFullName - * @function - * @returns {string} - */ - getFullName: this.getFullName.bind(this) - }; + this.id = suite.id; /** - * The suite's children. - * @name Suite#children - * @type {Array.<(Spec|Suite)>} + * The parent of this suite, or null if this is the top suite. + * @name Suite#parentSuite + * @readonly + * @type {Suite} */ - result.children = this.children.map(function(child) { - return child.buildMetadata(result); - }); + this.parentSuite = suite.parentSuite ? suite.parentSuite.metadata : null; - return result; + /** + * The description passed to the {@link describe} that created this suite. + * @name Suite#description + * @readonly + * @type {string} + */ + this.description = suite.description; + } + + /** + * The full description including all ancestors of this suite. + * @name Suite#getFullName + * @function + * @returns {string} + */ + SuiteMetadata.prototype.getFullName = function() { + return this.suite_.getFullName(); }; + /** + * The suite's children. + * @name Suite#children + * @type {Array.<(Spec|Suite)>} + */ + Object.defineProperty(SuiteMetadata.prototype, 'children', { + get: function() { + return this.suite_.children.map(child => child.metadata); + } + }); + function isFailure(args) { return !args[0]; } @@ -9285,11 +9306,6 @@ getJasmineRequireObj().Suite = function(j$) { return Suite; }; -if (typeof window == void 0 && typeof exports == 'object') { - /* globals exports */ - exports.Suite = jasmineRequire.Suite; -} - getJasmineRequireObj().Timer = function() { var defaultNow = (function(Date) { return function() { diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index be10480e..ae790a9c 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -90,7 +90,7 @@ describe('Env', function() { it('can configure specs to throw errors on expectation failures', function() { env.configure({ oneFailurePerSpec: true }); - spyOn(jasmineUnderTest, 'Spec'); + spyOn(jasmineUnderTest, 'Spec').and.callThrough(); env.it('foo', function() {}); expect(jasmineUnderTest.Spec).toHaveBeenCalledWith( jasmine.objectContaining({ @@ -162,7 +162,7 @@ describe('Env', function() { }); it('defaults to multiple failures for specs', function() { - spyOn(jasmineUnderTest, 'Spec'); + spyOn(jasmineUnderTest, 'Spec').and.callThrough(); env.it('bar', function() {}); expect(jasmineUnderTest.Spec).toHaveBeenCalledWith( jasmine.objectContaining({ @@ -181,7 +181,40 @@ describe('Env', function() { ); }); + function behavesLikeDescribe(methodName) { + it('returns a suite metadata object', function() { + let innerSuite; + let spec; + const suite = env.describe('outer suite', function() { + innerSuite = env.describe('inner suite', function() { + spec = env.it('a spec'); + }); + }); + + expect(suite.parentSuite).toEqual( + jasmine.objectContaining({ + description: 'Jasmine__TopLevel__Suite' + }) + ); + expect(suite.parentSuite.pend).toBeUndefined(); + expect(suite.pend).toBeUndefined(); + expect(suite.description).toEqual('outer suite'); + expect(suite.getFullName()).toEqual('outer suite'); + expect(suite.id).toBeInstanceOf(String); + expect(suite.id).not.toEqual(''); + expect(suite.children.length).toEqual(1); + expect(suite.children[0]).toBe(innerSuite); + expect(innerSuite.children.length).toEqual(1); + expect(innerSuite.children[0]).toBe(spec); + expect(innerSuite.getFullName()).toEqual('outer suite inner suite'); + expect(innerSuite.parentSuite).toBe(suite); + expect(spec.getFullName()).toEqual('outer suite inner suite a spec'); + }); + } + describe('#describe', function() { + behavesLikeDescribe('describe'); + it('throws an error when given arguments', function() { expect(function() { env.describe('done method', function(done) {}); @@ -232,7 +265,41 @@ describe('Env', function() { }); }); + describe('#fdescribe', function() { + behavesLikeDescribe('fdescribe'); + }); + + describe('xdescribe', function() { + behavesLikeDescribe('xdescribe'); + }); + + function behavesLikeIt(methodName) { + it('returns a spec metadata object', function() { + let spec; + + env.describe('a suite', function() { + spec = env[methodName]('a spec', function() {}); + }); + + expect(spec.description) + .withContext('description') + .toEqual('a spec'); + expect(spec.getFullName()) + .withContext('getFullName') + .toEqual('a suite a spec'); + expect(spec.id) + .withContext('id') + .toBeInstanceOf(String); + expect(spec.id) + .withContext('id') + .not.toEqual(''); + expect(spec.pend).toBeFalsy(); + }); + } + describe('#it', function() { + behavesLikeIt('it'); + it('throws an error when it receives a non-fn argument', function() { expect(function() { env.it('undefined arg', null); @@ -255,9 +322,11 @@ describe('Env', function() { }); describe('#xit', function() { + behavesLikeIt('xit'); + it('calls spec.pend with "Temporarily disabled with xit"', function() { var pendSpy = jasmine.createSpy(); - spyOn(env, 'it').and.returnValue({ + spyOn(env, 'it_').and.returnValue({ pend: pendSpy }); env.xit('foo', function() {}); @@ -286,6 +355,8 @@ describe('Env', function() { }); describe('#fit', function() { + behavesLikeIt('fit'); + it('throws an error when it receives a non-fn argument', function() { expect(function() { env.fit('undefined arg', undefined); diff --git a/src/core/Env.js b/src/core/Env.js index ef26385a..d019fc4b 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -549,7 +549,7 @@ getJasmineRequireObj().Env = function(j$) { * @return {Suite} the root suite */ this.topSuite = function() { - return topSuite.buildMetadata(null); + return topSuite.metadata; }; /** @@ -932,7 +932,7 @@ getJasmineRequireObj().Env = function(j$) { if (suite.parentSuite && !suite.children.length) { throw new Error('describe with no children (describe() or it())'); } - return suite; + return suite.metadata; }; this.xdescribe = function(description, specDefinitions) { @@ -941,7 +941,7 @@ getJasmineRequireObj().Env = function(j$) { var suite = suiteFactory(description); suite.pend(); addSpecsToSuite(suite, specDefinitions); - return suite; + return suite.metadata; }; var focusedRunnables = []; @@ -956,7 +956,7 @@ getJasmineRequireObj().Env = function(j$) { unfocusAncestor(); addSpecsToSuite(suite, specDefinitions); - return suite; + return suite.metadata; }; function addSpecsToSuite(suite, specDefinitions) { @@ -1046,7 +1046,7 @@ getJasmineRequireObj().Env = function(j$) { } }; - this.it = function(description, fn, timeout) { + this.it_ = function(description, fn, timeout) { ensureIsNotNested('it'); // it() sometimes doesn't have a fn argument, so only check the type if // it's given. @@ -1062,6 +1062,11 @@ getJasmineRequireObj().Env = function(j$) { return spec; }; + this.it = function(description, fn, timeout) { + const spec = this.it_(description, fn, timeout); + return spec.metadata; + }; + this.xit = function(description, fn, timeout) { ensureIsNotNested('xit'); // xit(), like it(), doesn't always have a fn argument, so only check the @@ -1069,9 +1074,9 @@ getJasmineRequireObj().Env = function(j$) { if (arguments.length > 1 && typeof fn !== 'undefined') { ensureIsFunctionOrAsync(fn, 'xit'); } - var spec = this.it.apply(this, arguments); + var spec = this.it_.apply(this, arguments); spec.pend('Temporarily disabled with xit'); - return spec; + return spec.metadata; }; this.fit = function(description, fn, timeout) { @@ -1081,7 +1086,7 @@ getJasmineRequireObj().Env = function(j$) { currentDeclarationSuite.addChild(spec); focusedRunnables.push(spec.id); unfocusAncestor(); - return spec; + return spec.metadata; }; /** diff --git a/src/core/Spec.js b/src/core/Spec.js index 20ee9041..82d7e235 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -3,12 +3,6 @@ getJasmineRequireObj().Spec = function(j$) { this.expectationFactory = attrs.expectationFactory; this.asyncExpectationFactory = attrs.asyncExpectationFactory; this.resultCallback = attrs.resultCallback || function() {}; - /** - * The unique ID of this spec. - * @name Spec#id - * @readonly - * @type {string} - */ this.id = attrs.id; this.description = attrs.description || ''; this.queueableFn = attrs.queueableFn; @@ -238,30 +232,39 @@ getJasmineRequireObj().Spec = function(j$) { * @interface Spec * @see Configuration#specFilter */ - Spec.prototype.buildMetadata = function() { - return { - /** - * The description passed to the {@link it} that created this spec. - * @name Spec#description - * @readonly - * @type {string} - */ - description: this.description, + Object.defineProperty(Spec.prototype, 'metadata', { + get: function() { + if (!this.metadata_) { + this.metadata_ = { + /** + * The unique ID of this spec. + * @name Spec#id + * @readonly + * @type {string} + */ + id: this.id, - /** - * The full description including all ancestors of this spec. - * @name Spec#getFullName - * @function - * @returns {string} - */ - getFullName: this.getFullName.bind(this) - }; - }; + /** + * The description passed to the {@link it} that created this spec. + * @name Spec#description + * @readonly + * @type {string} + */ + description: this.description, + + /** + * The full description including all ancestors of this spec. + * @name Spec#getFullName + * @function + * @returns {string} + */ + getFullName: this.getFullName.bind(this) + }; + } + + return this.metadata_; + } + }); return Spec; }; - -if (typeof window == void 0 && typeof exports == 'object') { - /* globals exports */ - exports.Spec = jasmineRequire.Spec; -} diff --git a/src/core/Suite.js b/src/core/Suite.js index 0403dc02..a15be857 100644 --- a/src/core/Suite.js +++ b/src/core/Suite.js @@ -1,12 +1,6 @@ getJasmineRequireObj().Suite = function(j$) { function Suite(attrs) { this.env = attrs.env; - /** - * The unique ID of this suite. - * @name Suite#id - * @readonly - * @type {string} - */ this.id = attrs.id; this.parentSuite = attrs.parentSuite; this.description = attrs.description; @@ -191,57 +185,71 @@ getJasmineRequireObj().Suite = function(j$) { ); }; - Suite.prototype.buildMetadata = function(parentMetadata) { + Object.defineProperty(Suite.prototype, 'metadata', { + get: function() { + if (!this.metadata_) { + this.metadata_ = new SuiteMetadata(this); + } + + return this.metadata_; + } + }); + + /** + * @interface Suite + * @see Env#topSuite + */ + function SuiteMetadata(suite) { + this.suite_ = suite; /** - * @interface Suite - * @see Env#topSuite + * The unique ID of this suite. + * @name Suite#id + * @readonly + * @type {string} */ - var result = { - /** - * The parent of this suite, or null if this is the top suite. - * @name Suite#parentSuite - * @readonly - * @type {Suite} - */ - parentSuite: parentMetadata, - - /** - * The description passed to the {@link describe} that created this suite. - * @name Suite#description - * @readonly - * @type {string} - */ - description: this.description, - - /** - * The full description including all ancestors of this suite. - * @name Suite#getFullName - * @function - * @returns {string} - */ - getFullName: this.getFullName.bind(this) - }; + this.id = suite.id; /** - * The suite's children. - * @name Suite#children - * @type {Array.<(Spec|Suite)>} + * The parent of this suite, or null if this is the top suite. + * @name Suite#parentSuite + * @readonly + * @type {Suite} */ - result.children = this.children.map(function(child) { - return child.buildMetadata(result); - }); + this.parentSuite = suite.parentSuite ? suite.parentSuite.metadata : null; - return result; + /** + * The description passed to the {@link describe} that created this suite. + * @name Suite#description + * @readonly + * @type {string} + */ + this.description = suite.description; + } + + /** + * The full description including all ancestors of this suite. + * @name Suite#getFullName + * @function + * @returns {string} + */ + SuiteMetadata.prototype.getFullName = function() { + return this.suite_.getFullName(); }; + /** + * The suite's children. + * @name Suite#children + * @type {Array.<(Spec|Suite)>} + */ + Object.defineProperty(SuiteMetadata.prototype, 'children', { + get: function() { + return this.suite_.children.map(child => child.metadata); + } + }); + function isFailure(args) { return !args[0]; } return Suite; }; - -if (typeof window == void 0 && typeof exports == 'object') { - /* globals exports */ - exports.Suite = jasmineRequire.Suite; -}