From 19b73469dd4d64ce56a6db2d830d99be93ed3608 Mon Sep 17 00:00:00 2001 From: "Davis W. Frank" Date: Sun, 28 Jun 2009 13:36:51 -0700 Subject: [PATCH] dwf: more doc commits --- README.markdown | 8 +- lib/jasmine.js | 567 +++++++++++++++++++++++++++++++++++----- src/ActionCollection.js | 12 + src/Env.js | 5 - src/Matchers.js | 81 +++++- src/NestedResults.js | 33 ++- src/PrettyPrinter.js | 9 + src/Spec.js | 24 +- src/Suite.js | 2 +- src/base.js | 390 +++++++++++++++++++++++---- src/util.js | 11 +- 11 files changed, 1007 insertions(+), 135 deletions(-) diff --git a/README.markdown b/README.markdown index ee1c745..d469e77 100644 --- a/README.markdown +++ b/README.markdown @@ -67,8 +67,6 @@ Jasmine has several built-in matchers. Here are a few: `toBeFalsy()` returns true if the object or primitive evaluates to false -`wasNotCalledWith()` returns true if the object is a spy and was called with the passed arguments - `toContain()` returns true if an array or string contains the passed variable. `toNotContain()` returns true if an array or string does not contain the passed variable. @@ -164,13 +162,13 @@ timeout before the next block is run. You supply a time to wait before the next }); runs(function () { - this.expects_that(this.foo).should_equal(0); + this.expects(this.foo).toEqual(0); }); waits(500); runs(function () { - this.expects_that(this.foo).should_equal(1); + this.expects(this.foo).toEqual(1); }); }); @@ -252,7 +250,7 @@ Similarly, there is an afterEach declaration. It takes a function that is run a }); ### Nested Describes -Jasmine now supports nested describes. An example: +Jasmine supports nested describes. An example: describe('some suite', function () { diff --git a/lib/jasmine.js b/lib/jasmine.js index 8db9054..f1579fa 100644 --- a/lib/jasmine.js +++ b/lib/jasmine.js @@ -1,14 +1,25 @@ -/* - * Jasmine internal classes & objects +/** + * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. + * + * @namespace */ - -/** @namespace */ var jasmine = {}; +/** + * @private + */ jasmine.unimplementedMethod_ = function() { throw new Error("unimplemented method"); }; +/** + * Allows for bound functions to be comapred. Internal use only. + * + * @ignore + * @private + * @param base {Object} bound 'this' for the function + * @param name {Function} function to find + */ jasmine.bindOriginal_ = function(base, name) { var original = base[name]; return function() { @@ -35,82 +46,270 @@ jasmine.ExpectationResult = function(passed, message, details) { this.trace = new Error(message); // todo: test better }; +/** + * Getter for the Jasmine environment. Ensures one gets created + */ jasmine.getEnv = function() { return jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); }; +/** + * @ignore + * @private + * @param value + * @returns {Boolean} + */ jasmine.isArray_ = function(value) { return value && - typeof value === 'object' && - typeof value.length === 'number' && - typeof value.splice === 'function' && - !(value.propertyIsEnumerable('length')); + typeof value === 'object' && + typeof value.length === 'number' && + typeof value.splice === 'function' && + !(value.propertyIsEnumerable('length')); }; +/** + * Pretty printer for expecations. Takes any object and turns it into a human-readable string. + * + * @param value {Object} an object to be outputted + * @returns {String} + */ jasmine.pp = function(value) { var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); stringPrettyPrinter.format(value); return stringPrettyPrinter.string; }; +/** + * Returns true if the object is a DOM Node. + * + * @param {Object} obj object to check + * @returns {Boolean} + */ jasmine.isDomNode = function(obj) { return obj['nodeType'] > 0; }; +/** + * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. + * + * @example + * // don't care about which function is passed in, as long as it's a function + * expect(mySpy).wasCalledWith(jasmine.any(Function)); + * + * @param {Class} clazz + * @returns matchable object of the type clazz + */ jasmine.any = function(clazz) { return new jasmine.Matchers.Any(clazz); }; +/** + * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. + * + * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine + * expectation syntax. Spies can be checked if they were called or not and what the calling params were. + * + * A Spy has the following mehtod: wasCalled, callCount, mostRecentCall, and argsForCall (see docs) + * Spies are torn down at the end of every spec. + * + * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. + * + * @example + * // a stub + * var myStub = jasmine.createSpy('myStub'); // can be used anywhere + * + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // actual foo.not will not be called, execution stops + * spyOn(foo, 'not'); + + // foo.not spied upon, execution will continue to implementation + * spyOn(foo, 'not').andCallThrough(); + * + * // fake example + * var foo = { + * not: function(bool) { return !bool; } + * } + * + * // foo.not(val) will return val + * spyOn(foo, 'not').andCallFake(function(value) {return value;}); + * + * // mock example + * foo.not(7 == 7); + * expect(foo.not).wasCalled(); + * expect(foo.not).wasCalledWith(true); + * + * @constructor + * @see spyOn, jasmine.createSpy, jasmine.createSpyObj + * @param {String} name + */ +jasmine.Spy = function(name) { + /** + * The name of the spy, if provided. + */ + this.identity = name || 'unknown'; + /** + * Is this Object a spy? + */ + this.isSpy = true; + /** + * The acutal function this spy stubs. + */ + this.plan = function() {}; + /** + * Tracking of the most recent call to the spy. + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy.mostRecentCall.args = [1, 2]; + */ + this.mostRecentCall = {}; + + /** + * Holds arguments for each call to the spy, indexed by call count + * @example + * var mySpy = jasmine.createSpy('foo'); + * mySpy(1, 2); + * mySpy(7, 8); + * mySpy.mostRecentCall.args = [7, 8]; + * mySpy.argsForCall[0] = [1, 2]; + * mySpy.argsForCall[1] = [7, 8]; + */ + this.argsForCall = []; +}; + +/** + * Tells a spy to call through to the actual implemenatation. + * + * @example + * var foo = { + * bar: function() { // do some stuff } + * } + * + * // defining a spy on an existing property: foo.bar + * spyOn(foo, 'bar').andCallThrough(); + */ +jasmine.Spy.prototype.andCallThrough = function() { + this.plan = this.originalValue; + return this; +}; + +/** + * For setting the return value of a spy. + * + * @example + * // defining a spy from scratch: foo() returns 'baz' + * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); + * + * // defining a spy on an existing property: foo.bar() returns 'baz' + * spyOn(foo, 'bar').andReturn('baz'); + * + * @param {Object} value + */ +jasmine.Spy.prototype.andReturn = function(value) { + this.plan = function() { + return value; + }; + return this; +}; + +/** + * For throwing an exception when a spy is called. + * + * @example + * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' + * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); + * + * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' + * spyOn(foo, 'bar').andThrow('baz'); + * + * @param {String} exceptionMsg + */ +jasmine.Spy.prototype.andThrow = function(exceptionMsg) { + this.plan = function() { + throw exceptionMsg; + }; + return this; +}; + +/** + * Calls an alternate implementation when a spy is called. + * + * @example + * var baz = function() { + * // do some stuff, return something + * } + * // defining a spy from scratch: foo() calls the function baz + * var foo = jasmine.createSpy('spy on foo').andCall(baz); + * + * // defining a spy on an existing property: foo.bar() calls an anonymnous function + * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); + * + * @param {Function} fakeFunc + */ +jasmine.Spy.prototype.andCallFake = function(fakeFunc) { + this.plan = fakeFunc; + return this; +}; + +/** + * Resets all of a spy's the tracking variables so that it can be used again. + * + * @example + * spyOn(foo, 'bar'); + * + * foo.bar(); + * + * expect(foo.bar.callCount).toEqual(1); + * + * foo.bar.reset(); + * + * expect(foo.bar.callCount).toEqual(0); + */ +jasmine.Spy.prototype.reset = function() { + this.wasCalled = false; + this.callCount = 0; + this.argsForCall = []; + this.mostRecentCall = {}; +}; + jasmine.createSpy = function(name) { + var spyObj = function() { spyObj.wasCalled = true; spyObj.callCount++; var args = jasmine.util.argsToArray(arguments); - spyObj.mostRecentCall = { - object: this, - args: args - }; + //spyObj.mostRecentCall = { + // object: this, + // args: args + //}; + spyObj.mostRecentCall.object = this; + spyObj.mostRecentCall.args = args; spyObj.argsForCall.push(args); return spyObj.plan.apply(this, arguments); }; - spyObj.identity = name || 'unknown'; - spyObj.isSpy = true; - - spyObj.plan = function() { - }; - - spyObj.andCallThrough = function() { - spyObj.plan = spyObj.originalValue; - return spyObj; - }; - spyObj.andReturn = function(value) { - spyObj.plan = function() { - return value; - }; - return spyObj; - }; - spyObj.andThrow = function(exceptionMsg) { - spyObj.plan = function() { - throw exceptionMsg; - }; - return spyObj; - }; - spyObj.andCallFake = function(fakeFunc) { - spyObj.plan = fakeFunc; - return spyObj; - }; - spyObj.reset = function() { - spyObj.wasCalled = false; - spyObj.callCount = 0; - spyObj.argsForCall = []; - spyObj.mostRecentCall = {}; - }; + var spy = new jasmine.Spy(name); + + for(var prop in spy) { + spyObj[prop] = spy[prop]; + } + spyObj.reset(); return spyObj; }; +/** + * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something + * large in one call. + * + * @param {String} baseName name of spy class + * @param {Array} methodNames array of names of methods to make spies + */ jasmine.createSpyObj = function(baseName, methodNames) { var obj = {}; for (var i = 0; i < methodNames.length; i++) { @@ -123,50 +322,146 @@ jasmine.log = function(message) { jasmine.getEnv().currentSpec.getResults().log(message); }; +/** + * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. + * + * @example + * // spy example + * var foo = { + * not: function(bool) { return !bool; } + * } + * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops + * + * @see jasmine.createSpy + * @param obj + * @param methodName + * @returns a Jasmine spy that can be chained with all spy methods + */ var spyOn = function(obj, methodName) { return jasmine.getEnv().currentSpec.spyOn(obj, methodName); }; +/** + * Creates a Jasmine spec that will be added to the current suite. + * + * // TODO: pending tests + * + * @example + * it('should be true', function() { + * expect(true).toEqual(true); + * }); + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ var it = function(desc, func) { return jasmine.getEnv().it(desc, func); }; +/** + * Creates a disabled Jasmine spec. + * + * A convenience method that allows existing specs to be disabled temporarily during development. + * + * @param {String} desc description of this specification + * @param {Function} func defines the preconditions and expectations of the spec + */ var xit = function(desc, func) { return jasmine.getEnv().xit(desc, func); }; +/** + * Starts a chain for a Jasmine expectation. + * + * It is passed an Object that is the actual value and should chain to one of the many + * jasmine.Matchers functions. + * + * @param {Object} actual Actual value to test against and expected value + */ var expect = function(actual) { return jasmine.getEnv().currentSpec.expect(actual); }; +/** + * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. + * + * @param {Function} func Function that defines part of a jasmine spec. + */ var runs = function(func) { jasmine.getEnv().currentSpec.runs(func); }; +/** + * Waits for a timeout before moving to the next runs()-defined block. + * @param {Number} timeout + */ var waits = function(timeout) { jasmine.getEnv().currentSpec.waits(timeout); }; +/** + * Waits for the latchFunction to return true before proceeding to the next runs()-defined block. + * + * @param {Number} timeout + * @param {Function} latchFunction + * @param {String} message + */ var waitsFor = function(timeout, latchFunction, message) { jasmine.getEnv().currentSpec.waitsFor(timeout, latchFunction, message); }; +/** + * A function that is called before each spec in a suite. + * + * Used for spec setup, including validating assumptions. + * + * @param {Function} beforeEachFunction + */ var beforeEach = function(beforeEachFunction) { jasmine.getEnv().beforeEach(beforeEachFunction); }; +/** + * A function that is called after each spec in a suite. + * + * Used for restoring any state that is hijacked during spec execution. + * + * @param {Function} afterEachFunction + */ var afterEach = function(afterEachFunction) { jasmine.getEnv().afterEach(afterEachFunction); }; +/** + * Defines a suite of specifications. + * + * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared + * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization + * of setup in some tests. + * + * @example + * // TODO: a simple suite + * + * // TODO: a simple suite with a nested describe block + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ var describe = function(description, specDefinitions) { return jasmine.getEnv().describe(description, specDefinitions); }; +/** + * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. + * + * @param {String} description A string, usually the class under test. + * @param {Function} specDefinitions function that defines several specs. + */ var xdescribe = function(description, specDefinitions) { return jasmine.getEnv().xdescribe(description, specDefinitions); }; + jasmine.XmlHttpRequest = XMLHttpRequest; // Provide the XMLHttpRequest class for IE 5.x-6.x: @@ -190,6 +485,13 @@ if (typeof XMLHttpRequest == "undefined") jasmine.XmlHttpRequest = function() { throw new Error("This browser does not support XMLHttpRequest."); }; +/** + * Adds suite files to an HTML document so that they are executed, thus adding them to the current + * Jasmine environment. + * + * @param {String} url path to the file to include + * @param {Boolean} opt_global + */ jasmine.include = function(url, opt_global) { if (opt_global) { document.write('