jasmine.js 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530
  1. var isCommonJS = typeof window == "undefined";
  2. /**
  3. * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
  4. *
  5. * @namespace
  6. */
  7. var jasmine = {};
  8. if (isCommonJS) exports.jasmine = jasmine;
  9. /**
  10. * @private
  11. */
  12. jasmine.unimplementedMethod_ = function() {
  13. throw new Error("unimplemented method");
  14. };
  15. /**
  16. * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
  17. * a plain old variable and may be redefined by somebody else.
  18. *
  19. * @private
  20. */
  21. jasmine.undefined = jasmine.___undefined___;
  22. /**
  23. * Show diagnostic messages in the console if set to true
  24. *
  25. */
  26. jasmine.VERBOSE = false;
  27. /**
  28. * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
  29. *
  30. */
  31. jasmine.DEFAULT_UPDATE_INTERVAL = 250;
  32. /**
  33. * Default timeout interval in milliseconds for waitsFor() blocks.
  34. */
  35. jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
  36. jasmine.getGlobal = function() {
  37. function getGlobal() {
  38. return this;
  39. }
  40. return getGlobal();
  41. };
  42. /**
  43. * Allows for bound functions to be compared. Internal use only.
  44. *
  45. * @ignore
  46. * @private
  47. * @param base {Object} bound 'this' for the function
  48. * @param name {Function} function to find
  49. */
  50. jasmine.bindOriginal_ = function(base, name) {
  51. var original = base[name];
  52. if (original.apply) {
  53. return function() {
  54. return original.apply(base, arguments);
  55. };
  56. } else {
  57. // IE support
  58. return jasmine.getGlobal()[name];
  59. }
  60. };
  61. jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
  62. jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
  63. jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
  64. jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
  65. jasmine.MessageResult = function(values) {
  66. this.type = 'log';
  67. this.values = values;
  68. this.trace = new Error(); // todo: test better
  69. };
  70. jasmine.MessageResult.prototype.toString = function() {
  71. var text = "";
  72. for (var i = 0; i < this.values.length; i++) {
  73. if (i > 0) text += " ";
  74. if (jasmine.isString_(this.values[i])) {
  75. text += this.values[i];
  76. } else {
  77. text += jasmine.pp(this.values[i]);
  78. }
  79. }
  80. return text;
  81. };
  82. jasmine.ExpectationResult = function(params) {
  83. this.type = 'expect';
  84. this.matcherName = params.matcherName;
  85. this.passed_ = params.passed;
  86. this.expected = params.expected;
  87. this.actual = params.actual;
  88. this.message = this.passed_ ? 'Passed.' : params.message;
  89. var trace = (params.trace || new Error(this.message));
  90. this.trace = this.passed_ ? '' : trace;
  91. };
  92. jasmine.ExpectationResult.prototype.toString = function () {
  93. return this.message;
  94. };
  95. jasmine.ExpectationResult.prototype.passed = function () {
  96. return this.passed_;
  97. };
  98. /**
  99. * Getter for the Jasmine environment. Ensures one gets created
  100. */
  101. jasmine.getEnv = function() {
  102. var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
  103. return env;
  104. };
  105. /**
  106. * @ignore
  107. * @private
  108. * @param value
  109. * @returns {Boolean}
  110. */
  111. jasmine.isArray_ = function(value) {
  112. return jasmine.isA_("Array", value);
  113. };
  114. /**
  115. * @ignore
  116. * @private
  117. * @param value
  118. * @returns {Boolean}
  119. */
  120. jasmine.isString_ = function(value) {
  121. return jasmine.isA_("String", value);
  122. };
  123. /**
  124. * @ignore
  125. * @private
  126. * @param value
  127. * @returns {Boolean}
  128. */
  129. jasmine.isNumber_ = function(value) {
  130. return jasmine.isA_("Number", value);
  131. };
  132. /**
  133. * @ignore
  134. * @private
  135. * @param {String} typeName
  136. * @param value
  137. * @returns {Boolean}
  138. */
  139. jasmine.isA_ = function(typeName, value) {
  140. return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
  141. };
  142. /**
  143. * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
  144. *
  145. * @param value {Object} an object to be outputted
  146. * @returns {String}
  147. */
  148. jasmine.pp = function(value) {
  149. var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
  150. stringPrettyPrinter.format(value);
  151. return stringPrettyPrinter.string;
  152. };
  153. /**
  154. * Returns true if the object is a DOM Node.
  155. *
  156. * @param {Object} obj object to check
  157. * @returns {Boolean}
  158. */
  159. jasmine.isDomNode = function(obj) {
  160. return obj.nodeType > 0;
  161. };
  162. /**
  163. * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
  164. *
  165. * @example
  166. * // don't care about which function is passed in, as long as it's a function
  167. * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
  168. *
  169. * @param {Class} clazz
  170. * @returns matchable object of the type clazz
  171. */
  172. jasmine.any = function(clazz) {
  173. return new jasmine.Matchers.Any(clazz);
  174. };
  175. /**
  176. * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
  177. * attributes on the object.
  178. *
  179. * @example
  180. * // don't care about any other attributes than foo.
  181. * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
  182. *
  183. * @param sample {Object} sample
  184. * @returns matchable object for the sample
  185. */
  186. jasmine.objectContaining = function (sample) {
  187. return new jasmine.Matchers.ObjectContaining(sample);
  188. };
  189. /**
  190. * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
  191. *
  192. * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
  193. * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
  194. *
  195. * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
  196. *
  197. * Spies are torn down at the end of every spec.
  198. *
  199. * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
  200. *
  201. * @example
  202. * // a stub
  203. * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
  204. *
  205. * // spy example
  206. * var foo = {
  207. * not: function(bool) { return !bool; }
  208. * }
  209. *
  210. * // actual foo.not will not be called, execution stops
  211. * spyOn(foo, 'not');
  212. // foo.not spied upon, execution will continue to implementation
  213. * spyOn(foo, 'not').andCallThrough();
  214. *
  215. * // fake example
  216. * var foo = {
  217. * not: function(bool) { return !bool; }
  218. * }
  219. *
  220. * // foo.not(val) will return val
  221. * spyOn(foo, 'not').andCallFake(function(value) {return value;});
  222. *
  223. * // mock example
  224. * foo.not(7 == 7);
  225. * expect(foo.not).toHaveBeenCalled();
  226. * expect(foo.not).toHaveBeenCalledWith(true);
  227. *
  228. * @constructor
  229. * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
  230. * @param {String} name
  231. */
  232. jasmine.Spy = function(name) {
  233. /**
  234. * The name of the spy, if provided.
  235. */
  236. this.identity = name || 'unknown';
  237. /**
  238. * Is this Object a spy?
  239. */
  240. this.isSpy = true;
  241. /**
  242. * The actual function this spy stubs.
  243. */
  244. this.plan = function() {
  245. };
  246. /**
  247. * Tracking of the most recent call to the spy.
  248. * @example
  249. * var mySpy = jasmine.createSpy('foo');
  250. * mySpy(1, 2);
  251. * mySpy.mostRecentCall.args = [1, 2];
  252. */
  253. this.mostRecentCall = {};
  254. /**
  255. * Holds arguments for each call to the spy, indexed by call count
  256. * @example
  257. * var mySpy = jasmine.createSpy('foo');
  258. * mySpy(1, 2);
  259. * mySpy(7, 8);
  260. * mySpy.mostRecentCall.args = [7, 8];
  261. * mySpy.argsForCall[0] = [1, 2];
  262. * mySpy.argsForCall[1] = [7, 8];
  263. */
  264. this.argsForCall = [];
  265. this.calls = [];
  266. };
  267. /**
  268. * Tells a spy to call through to the actual implemenatation.
  269. *
  270. * @example
  271. * var foo = {
  272. * bar: function() { // do some stuff }
  273. * }
  274. *
  275. * // defining a spy on an existing property: foo.bar
  276. * spyOn(foo, 'bar').andCallThrough();
  277. */
  278. jasmine.Spy.prototype.andCallThrough = function() {
  279. this.plan = this.originalValue;
  280. return this;
  281. };
  282. /**
  283. * For setting the return value of a spy.
  284. *
  285. * @example
  286. * // defining a spy from scratch: foo() returns 'baz'
  287. * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
  288. *
  289. * // defining a spy on an existing property: foo.bar() returns 'baz'
  290. * spyOn(foo, 'bar').andReturn('baz');
  291. *
  292. * @param {Object} value
  293. */
  294. jasmine.Spy.prototype.andReturn = function(value) {
  295. this.plan = function() {
  296. return value;
  297. };
  298. return this;
  299. };
  300. /**
  301. * For throwing an exception when a spy is called.
  302. *
  303. * @example
  304. * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
  305. * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
  306. *
  307. * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
  308. * spyOn(foo, 'bar').andThrow('baz');
  309. *
  310. * @param {String} exceptionMsg
  311. */
  312. jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
  313. this.plan = function() {
  314. throw exceptionMsg;
  315. };
  316. return this;
  317. };
  318. /**
  319. * Calls an alternate implementation when a spy is called.
  320. *
  321. * @example
  322. * var baz = function() {
  323. * // do some stuff, return something
  324. * }
  325. * // defining a spy from scratch: foo() calls the function baz
  326. * var foo = jasmine.createSpy('spy on foo').andCall(baz);
  327. *
  328. * // defining a spy on an existing property: foo.bar() calls an anonymnous function
  329. * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
  330. *
  331. * @param {Function} fakeFunc
  332. */
  333. jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
  334. this.plan = fakeFunc;
  335. return this;
  336. };
  337. /**
  338. * Resets all of a spy's the tracking variables so that it can be used again.
  339. *
  340. * @example
  341. * spyOn(foo, 'bar');
  342. *
  343. * foo.bar();
  344. *
  345. * expect(foo.bar.callCount).toEqual(1);
  346. *
  347. * foo.bar.reset();
  348. *
  349. * expect(foo.bar.callCount).toEqual(0);
  350. */
  351. jasmine.Spy.prototype.reset = function() {
  352. this.wasCalled = false;
  353. this.callCount = 0;
  354. this.argsForCall = [];
  355. this.calls = [];
  356. this.mostRecentCall = {};
  357. };
  358. jasmine.createSpy = function(name) {
  359. var spyObj = function() {
  360. spyObj.wasCalled = true;
  361. spyObj.callCount++;
  362. var args = jasmine.util.argsToArray(arguments);
  363. spyObj.mostRecentCall.object = this;
  364. spyObj.mostRecentCall.args = args;
  365. spyObj.argsForCall.push(args);
  366. spyObj.calls.push({object: this, args: args});
  367. return spyObj.plan.apply(this, arguments);
  368. };
  369. var spy = new jasmine.Spy(name);
  370. for (var prop in spy) {
  371. spyObj[prop] = spy[prop];
  372. }
  373. spyObj.reset();
  374. return spyObj;
  375. };
  376. /**
  377. * Determines whether an object is a spy.
  378. *
  379. * @param {jasmine.Spy|Object} putativeSpy
  380. * @returns {Boolean}
  381. */
  382. jasmine.isSpy = function(putativeSpy) {
  383. return putativeSpy && putativeSpy.isSpy;
  384. };
  385. /**
  386. * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
  387. * large in one call.
  388. *
  389. * @param {String} baseName name of spy class
  390. * @param {Array} methodNames array of names of methods to make spies
  391. */
  392. jasmine.createSpyObj = function(baseName, methodNames) {
  393. if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
  394. throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
  395. }
  396. var obj = {};
  397. for (var i = 0; i < methodNames.length; i++) {
  398. obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
  399. }
  400. return obj;
  401. };
  402. /**
  403. * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
  404. *
  405. * Be careful not to leave calls to <code>jasmine.log</code> in production code.
  406. */
  407. jasmine.log = function() {
  408. var spec = jasmine.getEnv().currentSpec;
  409. spec.log.apply(spec, arguments);
  410. };
  411. /**
  412. * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
  413. *
  414. * @example
  415. * // spy example
  416. * var foo = {
  417. * not: function(bool) { return !bool; }
  418. * }
  419. * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
  420. *
  421. * @see jasmine.createSpy
  422. * @param obj
  423. * @param methodName
  424. * @returns a Jasmine spy that can be chained with all spy methods
  425. */
  426. var spyOn = function(obj, methodName) {
  427. return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
  428. };
  429. if (isCommonJS) exports.spyOn = spyOn;
  430. /**
  431. * Creates a Jasmine spec that will be added to the current suite.
  432. *
  433. * // TODO: pending tests
  434. *
  435. * @example
  436. * it('should be true', function() {
  437. * expect(true).toEqual(true);
  438. * });
  439. *
  440. * @param {String} desc description of this specification
  441. * @param {Function} func defines the preconditions and expectations of the spec
  442. */
  443. var it = function(desc, func) {
  444. return jasmine.getEnv().it(desc, func);
  445. };
  446. if (isCommonJS) exports.it = it;
  447. /**
  448. * Creates a <em>disabled</em> Jasmine spec.
  449. *
  450. * A convenience method that allows existing specs to be disabled temporarily during development.
  451. *
  452. * @param {String} desc description of this specification
  453. * @param {Function} func defines the preconditions and expectations of the spec
  454. */
  455. var xit = function(desc, func) {
  456. return jasmine.getEnv().xit(desc, func);
  457. };
  458. if (isCommonJS) exports.xit = xit;
  459. /**
  460. * Starts a chain for a Jasmine expectation.
  461. *
  462. * It is passed an Object that is the actual value and should chain to one of the many
  463. * jasmine.Matchers functions.
  464. *
  465. * @param {Object} actual Actual value to test against and expected value
  466. */
  467. var expect = function(actual) {
  468. return jasmine.getEnv().currentSpec.expect(actual);
  469. };
  470. if (isCommonJS) exports.expect = expect;
  471. /**
  472. * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
  473. *
  474. * @param {Function} func Function that defines part of a jasmine spec.
  475. */
  476. var runs = function(func) {
  477. jasmine.getEnv().currentSpec.runs(func);
  478. };
  479. if (isCommonJS) exports.runs = runs;
  480. /**
  481. * Waits a fixed time period before moving to the next block.
  482. *
  483. * @deprecated Use waitsFor() instead
  484. * @param {Number} timeout milliseconds to wait
  485. */
  486. var waits = function(timeout) {
  487. jasmine.getEnv().currentSpec.waits(timeout);
  488. };
  489. if (isCommonJS) exports.waits = waits;
  490. /**
  491. * Waits for the latchFunction to return true before proceeding to the next block.
  492. *
  493. * @param {Function} latchFunction
  494. * @param {String} optional_timeoutMessage
  495. * @param {Number} optional_timeout
  496. */
  497. var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
  498. jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
  499. };
  500. if (isCommonJS) exports.waitsFor = waitsFor;
  501. /**
  502. * A function that is called before each spec in a suite.
  503. *
  504. * Used for spec setup, including validating assumptions.
  505. *
  506. * @param {Function} beforeEachFunction
  507. */
  508. var beforeEach = function(beforeEachFunction) {
  509. jasmine.getEnv().beforeEach(beforeEachFunction);
  510. };
  511. if (isCommonJS) exports.beforeEach = beforeEach;
  512. /**
  513. * A function that is called after each spec in a suite.
  514. *
  515. * Used for restoring any state that is hijacked during spec execution.
  516. *
  517. * @param {Function} afterEachFunction
  518. */
  519. var afterEach = function(afterEachFunction) {
  520. jasmine.getEnv().afterEach(afterEachFunction);
  521. };
  522. if (isCommonJS) exports.afterEach = afterEach;
  523. /**
  524. * Defines a suite of specifications.
  525. *
  526. * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
  527. * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
  528. * of setup in some tests.
  529. *
  530. * @example
  531. * // TODO: a simple suite
  532. *
  533. * // TODO: a simple suite with a nested describe block
  534. *
  535. * @param {String} description A string, usually the class under test.
  536. * @param {Function} specDefinitions function that defines several specs.
  537. */
  538. var describe = function(description, specDefinitions) {
  539. return jasmine.getEnv().describe(description, specDefinitions);
  540. };
  541. if (isCommonJS) exports.describe = describe;
  542. /**
  543. * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
  544. *
  545. * @param {String} description A string, usually the class under test.
  546. * @param {Function} specDefinitions function that defines several specs.
  547. */
  548. var xdescribe = function(description, specDefinitions) {
  549. return jasmine.getEnv().xdescribe(description, specDefinitions);
  550. };
  551. if (isCommonJS) exports.xdescribe = xdescribe;
  552. // Provide the XMLHttpRequest class for IE 5.x-6.x:
  553. jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
  554. function tryIt(f) {
  555. try {
  556. return f();
  557. } catch(e) {
  558. }
  559. return null;
  560. }
  561. var xhr = tryIt(function() {
  562. return new ActiveXObject("Msxml2.XMLHTTP.6.0");
  563. }) ||
  564. tryIt(function() {
  565. return new ActiveXObject("Msxml2.XMLHTTP.3.0");
  566. }) ||
  567. tryIt(function() {
  568. return new ActiveXObject("Msxml2.XMLHTTP");
  569. }) ||
  570. tryIt(function() {
  571. return new ActiveXObject("Microsoft.XMLHTTP");
  572. });
  573. if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
  574. return xhr;
  575. } : XMLHttpRequest;
  576. /**
  577. * @namespace
  578. */
  579. jasmine.util = {};
  580. /**
  581. * Declare that a child class inherit it's prototype from the parent class.
  582. *
  583. * @private
  584. * @param {Function} childClass
  585. * @param {Function} parentClass
  586. */
  587. jasmine.util.inherit = function(childClass, parentClass) {
  588. /**
  589. * @private
  590. */
  591. var subclass = function() {
  592. };
  593. subclass.prototype = parentClass.prototype;
  594. childClass.prototype = new subclass();
  595. };
  596. jasmine.util.formatException = function(e) {
  597. var lineNumber;
  598. if (e.line) {
  599. lineNumber = e.line;
  600. }
  601. else if (e.lineNumber) {
  602. lineNumber = e.lineNumber;
  603. }
  604. var file;
  605. if (e.sourceURL) {
  606. file = e.sourceURL;
  607. }
  608. else if (e.fileName) {
  609. file = e.fileName;
  610. }
  611. var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
  612. if (file && lineNumber) {
  613. message += ' in ' + file + ' (line ' + lineNumber + ')';
  614. }
  615. return message;
  616. };
  617. jasmine.util.htmlEscape = function(str) {
  618. if (!str) return str;
  619. return str.replace(/&/g, '&amp;')
  620. .replace(/</g, '&lt;')
  621. .replace(/>/g, '&gt;');
  622. };
  623. jasmine.util.argsToArray = function(args) {
  624. var arrayOfArgs = [];
  625. for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
  626. return arrayOfArgs;
  627. };
  628. jasmine.util.extend = function(destination, source) {
  629. for (var property in source) destination[property] = source[property];
  630. return destination;
  631. };
  632. /**
  633. * Environment for Jasmine
  634. *
  635. * @constructor
  636. */
  637. jasmine.Env = function() {
  638. this.currentSpec = null;
  639. this.currentSuite = null;
  640. this.currentRunner_ = new jasmine.Runner(this);
  641. this.reporter = new jasmine.MultiReporter();
  642. this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
  643. this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
  644. this.lastUpdate = 0;
  645. this.specFilter = function() {
  646. return true;
  647. };
  648. this.nextSpecId_ = 0;
  649. this.nextSuiteId_ = 0;
  650. this.equalityTesters_ = [];
  651. // wrap matchers
  652. this.matchersClass = function() {
  653. jasmine.Matchers.apply(this, arguments);
  654. };
  655. jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
  656. jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
  657. };
  658. jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
  659. jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
  660. jasmine.Env.prototype.setInterval = jasmine.setInterval;
  661. jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
  662. /**
  663. * @returns an object containing jasmine version build info, if set.
  664. */
  665. jasmine.Env.prototype.version = function () {
  666. if (jasmine.version_) {
  667. return jasmine.version_;
  668. } else {
  669. throw new Error('Version not set');
  670. }
  671. };
  672. /**
  673. * @returns string containing jasmine version build info, if set.
  674. */
  675. jasmine.Env.prototype.versionString = function() {
  676. if (!jasmine.version_) {
  677. return "version unknown";
  678. }
  679. var version = this.version();
  680. var versionString = version.major + "." + version.minor + "." + version.build;
  681. if (version.release_candidate) {
  682. versionString += ".rc" + version.release_candidate;
  683. }
  684. versionString += " revision " + version.revision;
  685. return versionString;
  686. };
  687. /**
  688. * @returns a sequential integer starting at 0
  689. */
  690. jasmine.Env.prototype.nextSpecId = function () {
  691. return this.nextSpecId_++;
  692. };
  693. /**
  694. * @returns a sequential integer starting at 0
  695. */
  696. jasmine.Env.prototype.nextSuiteId = function () {
  697. return this.nextSuiteId_++;
  698. };
  699. /**
  700. * Register a reporter to receive status updates from Jasmine.
  701. * @param {jasmine.Reporter} reporter An object which will receive status updates.
  702. */
  703. jasmine.Env.prototype.addReporter = function(reporter) {
  704. this.reporter.addReporter(reporter);
  705. };
  706. jasmine.Env.prototype.execute = function() {
  707. this.currentRunner_.execute();
  708. };
  709. jasmine.Env.prototype.describe = function(description, specDefinitions) {
  710. var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
  711. var parentSuite = this.currentSuite;
  712. if (parentSuite) {
  713. parentSuite.add(suite);
  714. } else {
  715. this.currentRunner_.add(suite);
  716. }
  717. this.currentSuite = suite;
  718. var declarationError = null;
  719. try {
  720. specDefinitions.call(suite);
  721. } catch(e) {
  722. declarationError = e;
  723. }
  724. if (declarationError) {
  725. this.it("encountered a declaration exception", function() {
  726. throw declarationError;
  727. });
  728. }
  729. this.currentSuite = parentSuite;
  730. return suite;
  731. };
  732. jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
  733. if (this.currentSuite) {
  734. this.currentSuite.beforeEach(beforeEachFunction);
  735. } else {
  736. this.currentRunner_.beforeEach(beforeEachFunction);
  737. }
  738. };
  739. jasmine.Env.prototype.currentRunner = function () {
  740. return this.currentRunner_;
  741. };
  742. jasmine.Env.prototype.afterEach = function(afterEachFunction) {
  743. if (this.currentSuite) {
  744. this.currentSuite.afterEach(afterEachFunction);
  745. } else {
  746. this.currentRunner_.afterEach(afterEachFunction);
  747. }
  748. };
  749. jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
  750. return {
  751. execute: function() {
  752. }
  753. };
  754. };
  755. jasmine.Env.prototype.it = function(description, func) {
  756. var spec = new jasmine.Spec(this, this.currentSuite, description);
  757. this.currentSuite.add(spec);
  758. this.currentSpec = spec;
  759. if (func) {
  760. spec.runs(func);
  761. }
  762. return spec;
  763. };
  764. jasmine.Env.prototype.xit = function(desc, func) {
  765. return {
  766. id: this.nextSpecId(),
  767. runs: function() {
  768. }
  769. };
  770. };
  771. jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
  772. if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
  773. return true;
  774. }
  775. a.__Jasmine_been_here_before__ = b;
  776. b.__Jasmine_been_here_before__ = a;
  777. var hasKey = function(obj, keyName) {
  778. return obj !== null && obj[keyName] !== jasmine.undefined;
  779. };
  780. for (var property in b) {
  781. if (!hasKey(a, property) && hasKey(b, property)) {
  782. mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
  783. }
  784. }
  785. for (property in a) {
  786. if (!hasKey(b, property) && hasKey(a, property)) {
  787. mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
  788. }
  789. }
  790. for (property in b) {
  791. if (property == '__Jasmine_been_here_before__') continue;
  792. if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
  793. mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
  794. }
  795. }
  796. if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
  797. mismatchValues.push("arrays were not the same length");
  798. }
  799. delete a.__Jasmine_been_here_before__;
  800. delete b.__Jasmine_been_here_before__;
  801. return (mismatchKeys.length === 0 && mismatchValues.length === 0);
  802. };
  803. jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
  804. mismatchKeys = mismatchKeys || [];
  805. mismatchValues = mismatchValues || [];
  806. for (var i = 0; i < this.equalityTesters_.length; i++) {
  807. var equalityTester = this.equalityTesters_[i];
  808. var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
  809. if (result !== jasmine.undefined) return result;
  810. }
  811. if (a === b) return true;
  812. if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
  813. return (a == jasmine.undefined && b == jasmine.undefined);
  814. }
  815. if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
  816. return a === b;
  817. }
  818. if (a instanceof Date && b instanceof Date) {
  819. return a.getTime() == b.getTime();
  820. }
  821. if (a.jasmineMatches) {
  822. return a.jasmineMatches(b);
  823. }
  824. if (b.jasmineMatches) {
  825. return b.jasmineMatches(a);
  826. }
  827. if (a instanceof jasmine.Matchers.ObjectContaining) {
  828. return a.matches(b);
  829. }
  830. if (b instanceof jasmine.Matchers.ObjectContaining) {
  831. return b.matches(a);
  832. }
  833. if (jasmine.isString_(a) && jasmine.isString_(b)) {
  834. return (a == b);
  835. }
  836. if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
  837. return (a == b);
  838. }
  839. if (typeof a === "object" && typeof b === "object") {
  840. return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
  841. }
  842. //Straight check
  843. return (a === b);
  844. };
  845. jasmine.Env.prototype.contains_ = function(haystack, needle) {
  846. if (jasmine.isArray_(haystack)) {
  847. for (var i = 0; i < haystack.length; i++) {
  848. if (this.equals_(haystack[i], needle)) return true;
  849. }
  850. return false;
  851. }
  852. return haystack.indexOf(needle) >= 0;
  853. };
  854. jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
  855. this.equalityTesters_.push(equalityTester);
  856. };
  857. /** No-op base class for Jasmine reporters.
  858. *
  859. * @constructor
  860. */
  861. jasmine.Reporter = function() {
  862. };
  863. //noinspection JSUnusedLocalSymbols
  864. jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
  865. };
  866. //noinspection JSUnusedLocalSymbols
  867. jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
  868. };
  869. //noinspection JSUnusedLocalSymbols
  870. jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
  871. };
  872. //noinspection JSUnusedLocalSymbols
  873. jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
  874. };
  875. //noinspection JSUnusedLocalSymbols
  876. jasmine.Reporter.prototype.reportSpecResults = function(spec) {
  877. };
  878. //noinspection JSUnusedLocalSymbols
  879. jasmine.Reporter.prototype.log = function(str) {
  880. };
  881. /**
  882. * Blocks are functions with executable code that make up a spec.
  883. *
  884. * @constructor
  885. * @param {jasmine.Env} env
  886. * @param {Function} func
  887. * @param {jasmine.Spec} spec
  888. */
  889. jasmine.Block = function(env, func, spec) {
  890. this.env = env;
  891. this.func = func;
  892. this.spec = spec;
  893. };
  894. jasmine.Block.prototype.execute = function(onComplete) {
  895. try {
  896. this.func.apply(this.spec);
  897. } catch (e) {
  898. this.spec.fail(e);
  899. }
  900. onComplete();
  901. };
  902. /** JavaScript API reporter.
  903. *
  904. * @constructor
  905. */
  906. jasmine.JsApiReporter = function() {
  907. this.started = false;
  908. this.finished = false;
  909. this.suites_ = [];
  910. this.results_ = {};
  911. };
  912. jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
  913. this.started = true;
  914. var suites = runner.topLevelSuites();
  915. for (var i = 0; i < suites.length; i++) {
  916. var suite = suites[i];
  917. this.suites_.push(this.summarize_(suite));
  918. }
  919. };
  920. jasmine.JsApiReporter.prototype.suites = function() {
  921. return this.suites_;
  922. };
  923. jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
  924. var isSuite = suiteOrSpec instanceof jasmine.Suite;
  925. var summary = {
  926. id: suiteOrSpec.id,
  927. name: suiteOrSpec.description,
  928. type: isSuite ? 'suite' : 'spec',
  929. children: []
  930. };
  931. if (isSuite) {
  932. var children = suiteOrSpec.children();
  933. for (var i = 0; i < children.length; i++) {
  934. summary.children.push(this.summarize_(children[i]));
  935. }
  936. }
  937. return summary;
  938. };
  939. jasmine.JsApiReporter.prototype.results = function() {
  940. return this.results_;
  941. };
  942. jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
  943. return this.results_[specId];
  944. };
  945. //noinspection JSUnusedLocalSymbols
  946. jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
  947. this.finished = true;
  948. };
  949. //noinspection JSUnusedLocalSymbols
  950. jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
  951. };
  952. //noinspection JSUnusedLocalSymbols
  953. jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
  954. this.results_[spec.id] = {
  955. messages: spec.results().getItems(),
  956. result: spec.results().failedCount > 0 ? "failed" : "passed"
  957. };
  958. };
  959. //noinspection JSUnusedLocalSymbols
  960. jasmine.JsApiReporter.prototype.log = function(str) {
  961. };
  962. jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
  963. var results = {};
  964. for (var i = 0; i < specIds.length; i++) {
  965. var specId = specIds[i];
  966. results[specId] = this.summarizeResult_(this.results_[specId]);
  967. }
  968. return results;
  969. };
  970. jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
  971. var summaryMessages = [];
  972. var messagesLength = result.messages.length;
  973. for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
  974. var resultMessage = result.messages[messageIndex];
  975. summaryMessages.push({
  976. text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
  977. passed: resultMessage.passed ? resultMessage.passed() : true,
  978. type: resultMessage.type,
  979. message: resultMessage.message,
  980. trace: {
  981. stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
  982. }
  983. });
  984. }
  985. return {
  986. result : result.result,
  987. messages : summaryMessages
  988. };
  989. };
  990. /**
  991. * @constructor
  992. * @param {jasmine.Env} env
  993. * @param actual
  994. * @param {jasmine.Spec} spec
  995. */
  996. jasmine.Matchers = function(env, actual, spec, opt_isNot) {
  997. this.env = env;
  998. this.actual = actual;
  999. this.spec = spec;
  1000. this.isNot = opt_isNot || false;
  1001. this.reportWasCalled_ = false;
  1002. };
  1003. // todo: @deprecated as of Jasmine 0.11, remove soon [xw]
  1004. jasmine.Matchers.pp = function(str) {
  1005. throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
  1006. };
  1007. // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
  1008. jasmine.Matchers.prototype.report = function(result, failing_message, details) {
  1009. throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
  1010. };
  1011. jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
  1012. for (var methodName in prototype) {
  1013. if (methodName == 'report') continue;
  1014. var orig = prototype[methodName];
  1015. matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
  1016. }
  1017. };
  1018. jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
  1019. return function() {
  1020. var matcherArgs = jasmine.util.argsToArray(arguments);
  1021. var result = matcherFunction.apply(this, arguments);
  1022. if (this.isNot) {
  1023. result = !result;
  1024. }
  1025. if (this.reportWasCalled_) return result;
  1026. var message;
  1027. if (!result) {
  1028. if (this.message) {
  1029. message = this.message.apply(this, arguments);
  1030. if (jasmine.isArray_(message)) {
  1031. message = message[this.isNot ? 1 : 0];
  1032. }
  1033. } else {
  1034. var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
  1035. message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
  1036. if (matcherArgs.length > 0) {
  1037. for (var i = 0; i < matcherArgs.length; i++) {
  1038. if (i > 0) message += ",";
  1039. message += " " + jasmine.pp(matcherArgs[i]);
  1040. }
  1041. }
  1042. message += ".";
  1043. }
  1044. }
  1045. var expectationResult = new jasmine.ExpectationResult({
  1046. matcherName: matcherName,
  1047. passed: result,
  1048. expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
  1049. actual: this.actual,
  1050. message: message
  1051. });
  1052. this.spec.addMatcherResult(expectationResult);
  1053. return jasmine.undefined;
  1054. };
  1055. };
  1056. /**
  1057. * toBe: compares the actual to the expected using ===
  1058. * @param expected
  1059. */
  1060. jasmine.Matchers.prototype.toBe = function(expected) {
  1061. return this.actual === expected;
  1062. };
  1063. /**
  1064. * toNotBe: compares the actual to the expected using !==
  1065. * @param expected
  1066. * @deprecated as of 1.0. Use not.toBe() instead.
  1067. */
  1068. jasmine.Matchers.prototype.toNotBe = function(expected) {
  1069. return this.actual !== expected;
  1070. };
  1071. /**
  1072. * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
  1073. *
  1074. * @param expected
  1075. */
  1076. jasmine.Matchers.prototype.toEqual = function(expected) {
  1077. return this.env.equals_(this.actual, expected);
  1078. };
  1079. /**
  1080. * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
  1081. * @param expected
  1082. * @deprecated as of 1.0. Use not.toEqual() instead.
  1083. */
  1084. jasmine.Matchers.prototype.toNotEqual = function(expected) {
  1085. return !this.env.equals_(this.actual, expected);
  1086. };
  1087. /**
  1088. * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
  1089. * a pattern or a String.
  1090. *
  1091. * @param expected
  1092. */
  1093. jasmine.Matchers.prototype.toMatch = function(expected) {
  1094. return new RegExp(expected).test(this.actual);
  1095. };
  1096. /**
  1097. * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
  1098. * @param expected
  1099. * @deprecated as of 1.0. Use not.toMatch() instead.
  1100. */
  1101. jasmine.Matchers.prototype.toNotMatch = function(expected) {
  1102. return !(new RegExp(expected).test(this.actual));
  1103. };
  1104. /**
  1105. * Matcher that compares the actual to jasmine.undefined.
  1106. */
  1107. jasmine.Matchers.prototype.toBeDefined = function() {
  1108. return (this.actual !== jasmine.undefined);
  1109. };
  1110. /**
  1111. * Matcher that compares the actual to jasmine.undefined.
  1112. */
  1113. jasmine.Matchers.prototype.toBeUndefined = function() {
  1114. return (this.actual === jasmine.undefined);
  1115. };
  1116. /**
  1117. * Matcher that compares the actual to null.
  1118. */
  1119. jasmine.Matchers.prototype.toBeNull = function() {
  1120. return (this.actual === null);
  1121. };
  1122. /**
  1123. * Matcher that boolean not-nots the actual.
  1124. */
  1125. jasmine.Matchers.prototype.toBeTruthy = function() {
  1126. return !!this.actual;
  1127. };
  1128. /**
  1129. * Matcher that boolean nots the actual.
  1130. */
  1131. jasmine.Matchers.prototype.toBeFalsy = function() {
  1132. return !this.actual;
  1133. };
  1134. /**
  1135. * Matcher that checks to see if the actual, a Jasmine spy, was called.
  1136. */
  1137. jasmine.Matchers.prototype.toHaveBeenCalled = function() {
  1138. if (arguments.length > 0) {
  1139. throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
  1140. }
  1141. if (!jasmine.isSpy(this.actual)) {
  1142. throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
  1143. }
  1144. this.message = function() {
  1145. return [
  1146. "Expected spy " + this.actual.identity + " to have been called.",
  1147. "Expected spy " + this.actual.identity + " not to have been called."
  1148. ];
  1149. };
  1150. return this.actual.wasCalled;
  1151. };
  1152. /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
  1153. jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
  1154. /**
  1155. * Matcher that checks to see if the actual, a Jasmine spy, was not called.
  1156. *
  1157. * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
  1158. */
  1159. jasmine.Matchers.prototype.wasNotCalled = function() {
  1160. if (arguments.length > 0) {
  1161. throw new Error('wasNotCalled does not take arguments');
  1162. }
  1163. if (!jasmine.isSpy(this.actual)) {
  1164. throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
  1165. }
  1166. this.message = function() {
  1167. return [
  1168. "Expected spy " + this.actual.identity + " to not have been called.",
  1169. "Expected spy " + this.actual.identity + " to have been called."
  1170. ];
  1171. };
  1172. return !this.actual.wasCalled;
  1173. };
  1174. /**
  1175. * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
  1176. *
  1177. * @example
  1178. *
  1179. */
  1180. jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
  1181. var expectedArgs = jasmine.util.argsToArray(arguments);
  1182. if (!jasmine.isSpy(this.actual)) {
  1183. throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
  1184. }
  1185. this.message = function() {
  1186. if (this.actual.callCount === 0) {
  1187. // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
  1188. return [
  1189. "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
  1190. "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
  1191. ];
  1192. } else {
  1193. return [
  1194. "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
  1195. "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
  1196. ];
  1197. }
  1198. };
  1199. return this.env.contains_(this.actual.argsForCall, expectedArgs);
  1200. };
  1201. /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
  1202. jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
  1203. /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
  1204. jasmine.Matchers.prototype.wasNotCalledWith = function() {
  1205. var expectedArgs = jasmine.util.argsToArray(arguments);
  1206. if (!jasmine.isSpy(this.actual)) {
  1207. throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
  1208. }
  1209. this.message = function() {
  1210. return [
  1211. "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
  1212. "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
  1213. ];
  1214. };
  1215. return !this.env.contains_(this.actual.argsForCall, expectedArgs);
  1216. };
  1217. /**
  1218. * Matcher that checks that the expected item is an element in the actual Array.
  1219. *
  1220. * @param {Object} expected
  1221. */
  1222. jasmine.Matchers.prototype.toContain = function(expected) {
  1223. return this.env.contains_(this.actual, expected);
  1224. };
  1225. /**
  1226. * Matcher that checks that the expected item is NOT an element in the actual Array.
  1227. *
  1228. * @param {Object} expected
  1229. * @deprecated as of 1.0. Use not.toContain() instead.
  1230. */
  1231. jasmine.Matchers.prototype.toNotContain = function(expected) {
  1232. return !this.env.contains_(this.actual, expected);
  1233. };
  1234. jasmine.Matchers.prototype.toBeLessThan = function(expected) {
  1235. return this.actual < expected;
  1236. };
  1237. jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
  1238. return this.actual > expected;
  1239. };
  1240. /**
  1241. * Matcher that checks that the expected item is equal to the actual item
  1242. * up to a given level of decimal precision (default 2).
  1243. *
  1244. * @param {Number} expected
  1245. * @param {Number} precision
  1246. */
  1247. jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
  1248. if (!(precision === 0)) {
  1249. precision = precision || 2;
  1250. }
  1251. var multiplier = Math.pow(10, precision);
  1252. var actual = Math.round(this.actual * multiplier);
  1253. expected = Math.round(expected * multiplier);
  1254. return expected == actual;
  1255. };
  1256. /**
  1257. * Matcher that checks that the expected exception was thrown by the actual.
  1258. *
  1259. * @param {String} expected
  1260. */
  1261. jasmine.Matchers.prototype.toThrow = function(expected) {
  1262. var result = false;
  1263. var exception;
  1264. if (typeof this.actual != 'function') {
  1265. throw new Error('Actual is not a function');
  1266. }
  1267. try {
  1268. this.actual();
  1269. } catch (e) {
  1270. exception = e;
  1271. }
  1272. if (exception) {
  1273. result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
  1274. }
  1275. var not = this.isNot ? "not " : "";
  1276. this.message = function() {
  1277. if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
  1278. return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
  1279. } else {
  1280. return "Expected function to throw an exception.";
  1281. }
  1282. };
  1283. return result;
  1284. };
  1285. jasmine.Matchers.Any = function(expectedClass) {
  1286. this.expectedClass = expectedClass;
  1287. };
  1288. jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {
  1289. if (this.expectedClass == String) {
  1290. return typeof other == 'string' || other instanceof String;
  1291. }
  1292. if (this.expectedClass == Number) {
  1293. return typeof other == 'number' || other instanceof Number;
  1294. }
  1295. if (this.expectedClass == Function) {
  1296. return typeof other == 'function' || other instanceof Function;
  1297. }
  1298. if (this.expectedClass == Object) {
  1299. return typeof other == 'object';
  1300. }
  1301. return other instanceof this.expectedClass;
  1302. };
  1303. jasmine.Matchers.Any.prototype.jasmineToString = function() {
  1304. return '<jasmine.any(' + this.expectedClass + ')>';
  1305. };
  1306. jasmine.Matchers.ObjectContaining = function (sample) {
  1307. this.sample = sample;
  1308. };
  1309. jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
  1310. mismatchKeys = mismatchKeys || [];
  1311. mismatchValues = mismatchValues || [];
  1312. var env = jasmine.getEnv();
  1313. var hasKey = function(obj, keyName) {
  1314. return obj != null && obj[keyName] !== jasmine.undefined;
  1315. };
  1316. for (var property in this.sample) {
  1317. if (!hasKey(other, property) && hasKey(this.sample, property)) {
  1318. mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
  1319. }
  1320. else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
  1321. mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
  1322. }
  1323. }
  1324. return (mismatchKeys.length === 0 && mismatchValues.length === 0);
  1325. };
  1326. jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
  1327. return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>";
  1328. };
  1329. // Mock setTimeout, clearTimeout
  1330. // Contributed by Pivotal Computer Systems, www.pivotalsf.com
  1331. jasmine.FakeTimer = function() {
  1332. this.reset();
  1333. var self = this;
  1334. self.setTimeout = function(funcToCall, millis) {
  1335. self.timeoutsMade++;
  1336. self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
  1337. return self.timeoutsMade;
  1338. };
  1339. self.setInterval = function(funcToCall, millis) {
  1340. self.timeoutsMade++;
  1341. self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
  1342. return self.timeoutsMade;
  1343. };
  1344. self.clearTimeout = function(timeoutKey) {
  1345. self.scheduledFunctions[timeoutKey] = jasmine.undefined;
  1346. };
  1347. self.clearInterval = function(timeoutKey) {
  1348. self.scheduledFunctions[timeoutKey] = jasmine.undefined;
  1349. };
  1350. };
  1351. jasmine.FakeTimer.prototype.reset = function() {
  1352. this.timeoutsMade = 0;
  1353. this.scheduledFunctions = {};
  1354. this.nowMillis = 0;
  1355. };
  1356. jasmine.FakeTimer.prototype.tick = function(millis) {
  1357. var oldMillis = this.nowMillis;
  1358. var newMillis = oldMillis + millis;
  1359. this.runFunctionsWithinRange(oldMillis, newMillis);
  1360. this.nowMillis = newMillis;
  1361. };
  1362. jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
  1363. var scheduledFunc;
  1364. var funcsToRun = [];
  1365. for (var timeoutKey in this.scheduledFunctions) {
  1366. scheduledFunc = this.scheduledFunctions[timeoutKey];
  1367. if (scheduledFunc != jasmine.undefined &&
  1368. scheduledFunc.runAtMillis >= oldMillis &&
  1369. scheduledFunc.runAtMillis <= nowMillis) {
  1370. funcsToRun.push(scheduledFunc);
  1371. this.scheduledFunctions[timeoutKey] = jasmine.undefined;
  1372. }
  1373. }
  1374. if (funcsToRun.length > 0) {
  1375. funcsToRun.sort(function(a, b) {
  1376. return a.runAtMillis - b.runAtMillis;
  1377. });
  1378. for (var i = 0; i < funcsToRun.length; ++i) {
  1379. try {
  1380. var funcToRun = funcsToRun[i];
  1381. this.nowMillis = funcToRun.runAtMillis;
  1382. funcToRun.funcToCall();
  1383. if (funcToRun.recurring) {
  1384. this.scheduleFunction(funcToRun.timeoutKey,
  1385. funcToRun.funcToCall,
  1386. funcToRun.millis,
  1387. true);
  1388. }
  1389. } catch(e) {
  1390. }
  1391. }
  1392. this.runFunctionsWithinRange(oldMillis, nowMillis);
  1393. }
  1394. };
  1395. jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
  1396. this.scheduledFunctions[timeoutKey] = {
  1397. runAtMillis: this.nowMillis + millis,
  1398. funcToCall: funcToCall,
  1399. recurring: recurring,
  1400. timeoutKey: timeoutKey,
  1401. millis: millis
  1402. };
  1403. };
  1404. /**
  1405. * @namespace
  1406. */
  1407. jasmine.Clock = {
  1408. defaultFakeTimer: new jasmine.FakeTimer(),
  1409. reset: function() {
  1410. jasmine.Clock.assertInstalled();
  1411. jasmine.Clock.defaultFakeTimer.reset();
  1412. },
  1413. tick: function(millis) {
  1414. jasmine.Clock.assertInstalled();
  1415. jasmine.Clock.defaultFakeTimer.tick(millis);
  1416. },
  1417. runFunctionsWithinRange: function(oldMillis, nowMillis) {
  1418. jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
  1419. },
  1420. scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
  1421. jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
  1422. },
  1423. useMock: function() {
  1424. if (!jasmine.Clock.isInstalled()) {
  1425. var spec = jasmine.getEnv().currentSpec;
  1426. spec.after(jasmine.Clock.uninstallMock);
  1427. jasmine.Clock.installMock();
  1428. }
  1429. },
  1430. installMock: function() {
  1431. jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
  1432. },
  1433. uninstallMock: function() {
  1434. jasmine.Clock.assertInstalled();
  1435. jasmine.Clock.installed = jasmine.Clock.real;
  1436. },
  1437. real: {
  1438. setTimeout: jasmine.getGlobal().setTimeout,
  1439. clearTimeout: jasmine.getGlobal().clearTimeout,
  1440. setInterval: jasmine.getGlobal().setInterval,
  1441. clearInterval: jasmine.getGlobal().clearInterval
  1442. },
  1443. assertInstalled: function() {
  1444. if (!jasmine.Clock.isInstalled()) {
  1445. throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
  1446. }
  1447. },
  1448. isInstalled: function() {
  1449. return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
  1450. },
  1451. installed: null
  1452. };
  1453. jasmine.Clock.installed = jasmine.Clock.real;
  1454. //else for IE support
  1455. jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
  1456. if (jasmine.Clock.installed.setTimeout.apply) {
  1457. return jasmine.Clock.installed.setTimeout.apply(this, arguments);
  1458. } else {
  1459. return jasmine.Clock.installed.setTimeout(funcToCall, millis);
  1460. }
  1461. };
  1462. jasmine.getGlobal().setInterval = function(funcToCall, millis) {
  1463. if (jasmine.Clock.installed.setInterval.apply) {
  1464. return jasmine.Clock.installed.setInterval.apply(this, arguments);
  1465. } else {
  1466. return jasmine.Clock.installed.setInterval(funcToCall, millis);
  1467. }
  1468. };
  1469. jasmine.getGlobal().clearTimeout = function(timeoutKey) {
  1470. if (jasmine.Clock.installed.clearTimeout.apply) {
  1471. return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
  1472. } else {
  1473. return jasmine.Clock.installed.clearTimeout(timeoutKey);
  1474. }
  1475. };
  1476. jasmine.getGlobal().clearInterval = function(timeoutKey) {
  1477. if (jasmine.Clock.installed.clearTimeout.apply) {
  1478. return jasmine.Clock.installed.clearInterval.apply(this, arguments);
  1479. } else {
  1480. return jasmine.Clock.installed.clearInterval(timeoutKey);
  1481. }
  1482. };
  1483. /**
  1484. * @constructor
  1485. */
  1486. jasmine.MultiReporter = function() {
  1487. this.subReporters_ = [];
  1488. };
  1489. jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
  1490. jasmine.MultiReporter.prototype.addReporter = function(reporter) {
  1491. this.subReporters_.push(reporter);
  1492. };
  1493. (function() {
  1494. var functionNames = [
  1495. "reportRunnerStarting",
  1496. "reportRunnerResults",
  1497. "reportSuiteResults",
  1498. "reportSpecStarting",
  1499. "reportSpecResults",
  1500. "log"
  1501. ];
  1502. for (var i = 0; i < functionNames.length; i++) {
  1503. var functionName = functionNames[i];
  1504. jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
  1505. return function() {
  1506. for (var j = 0; j < this.subReporters_.length; j++) {
  1507. var subReporter = this.subReporters_[j];
  1508. if (subReporter[functionName]) {
  1509. subReporter[functionName].apply(subReporter, arguments);
  1510. }
  1511. }
  1512. };
  1513. })(functionName);
  1514. }
  1515. })();
  1516. /**
  1517. * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
  1518. *
  1519. * @constructor
  1520. */
  1521. jasmine.NestedResults = function() {
  1522. /**
  1523. * The total count of results
  1524. */
  1525. this.totalCount = 0;
  1526. /**
  1527. * Number of passed results
  1528. */
  1529. this.passedCount = 0;
  1530. /**
  1531. * Number of failed results
  1532. */
  1533. this.failedCount = 0;
  1534. /**
  1535. * Was this suite/spec skipped?
  1536. */
  1537. this.skipped = false;
  1538. /**
  1539. * @ignore
  1540. */
  1541. this.items_ = [];
  1542. };
  1543. /**
  1544. * Roll up the result counts.
  1545. *
  1546. * @param result
  1547. */
  1548. jasmine.NestedResults.prototype.rollupCounts = function(result) {
  1549. this.totalCount += result.totalCount;
  1550. this.passedCount += result.passedCount;
  1551. this.failedCount += result.failedCount;
  1552. };
  1553. /**
  1554. * Adds a log message.
  1555. * @param values Array of message parts which will be concatenated later.
  1556. */
  1557. jasmine.NestedResults.prototype.log = function(values) {
  1558. this.items_.push(new jasmine.MessageResult(values));
  1559. };
  1560. /**
  1561. * Getter for the results: message & results.
  1562. */
  1563. jasmine.NestedResults.prototype.getItems = function() {
  1564. return this.items_;
  1565. };
  1566. /**
  1567. * Adds a result, tracking counts (total, passed, & failed)
  1568. * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
  1569. */
  1570. jasmine.NestedResults.prototype.addResult = function(result) {
  1571. if (result.type != 'log') {
  1572. if (result.items_) {
  1573. this.rollupCounts(result);
  1574. } else {
  1575. this.totalCount++;
  1576. if (result.passed()) {
  1577. this.passedCount++;
  1578. } else {
  1579. this.failedCount++;
  1580. }
  1581. }
  1582. }
  1583. this.items_.push(result);
  1584. };
  1585. /**
  1586. * @returns {Boolean} True if <b>everything</b> below passed
  1587. */
  1588. jasmine.NestedResults.prototype.passed = function() {
  1589. return this.passedCount === this.totalCount;
  1590. };
  1591. /**
  1592. * Base class for pretty printing for expectation results.
  1593. */
  1594. jasmine.PrettyPrinter = function() {
  1595. this.ppNestLevel_ = 0;
  1596. };
  1597. /**
  1598. * Formats a value in a nice, human-readable string.
  1599. *
  1600. * @param value
  1601. */
  1602. jasmine.PrettyPrinter.prototype.format = function(value) {
  1603. if (this.ppNestLevel_ > 40) {
  1604. throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
  1605. }
  1606. this.ppNestLevel_++;
  1607. try {
  1608. if (value === jasmine.undefined) {
  1609. this.emitScalar('undefined');
  1610. } else if (value === null) {
  1611. this.emitScalar('null');
  1612. } else if (value === jasmine.getGlobal()) {
  1613. this.emitScalar('<global>');
  1614. } else if (value.jasmineToString) {
  1615. this.emitScalar(value.jasmineToString());
  1616. } else if (typeof value === 'string') {
  1617. this.emitString(value);
  1618. } else if (jasmine.isSpy(value)) {
  1619. this.emitScalar("spy on " + value.identity);
  1620. } else if (value instanceof RegExp) {
  1621. this.emitScalar(value.toString());
  1622. } else if (typeof value === 'function') {
  1623. this.emitScalar('Function');
  1624. } else if (typeof value.nodeType === 'number') {
  1625. this.emitScalar('HTMLNode');
  1626. } else if (value instanceof Date) {
  1627. this.emitScalar('Date(' + value + ')');
  1628. } else if (value.__Jasmine_been_here_before__) {
  1629. this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
  1630. } else if (jasmine.isArray_(value) || typeof value == 'object') {
  1631. value.__Jasmine_been_here_before__ = true;
  1632. if (jasmine.isArray_(value)) {
  1633. this.emitArray(value);
  1634. } else {
  1635. this.emitObject(value);
  1636. }
  1637. delete value.__Jasmine_been_here_before__;
  1638. } else {
  1639. this.emitScalar(value.toString());
  1640. }
  1641. } finally {
  1642. this.ppNestLevel_--;
  1643. }
  1644. };
  1645. jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
  1646. for (var property in obj) {
  1647. if (property == '__Jasmine_been_here_before__') continue;
  1648. fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined &&
  1649. obj.__lookupGetter__(property) !== null) : false);
  1650. }
  1651. };
  1652. jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
  1653. jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
  1654. jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
  1655. jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
  1656. jasmine.StringPrettyPrinter = function() {
  1657. jasmine.PrettyPrinter.call(this);
  1658. this.string = '';
  1659. };
  1660. jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
  1661. jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
  1662. this.append(value);
  1663. };
  1664. jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
  1665. this.append("'" + value + "'");
  1666. };
  1667. jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
  1668. this.append('[ ');
  1669. for (var i = 0; i < array.length; i++) {
  1670. if (i > 0) {
  1671. this.append(', ');
  1672. }
  1673. this.format(array[i]);
  1674. }
  1675. this.append(' ]');
  1676. };
  1677. jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
  1678. var self = this;
  1679. this.append('{ ');
  1680. var first = true;
  1681. this.iterateObject(obj, function(property, isGetter) {
  1682. if (first) {
  1683. first = false;
  1684. } else {
  1685. self.append(', ');
  1686. }
  1687. self.append(property);
  1688. self.append(' : ');
  1689. if (isGetter) {
  1690. self.append('<getter>');
  1691. } else {
  1692. self.format(obj[property]);
  1693. }
  1694. });
  1695. this.append(' }');
  1696. };
  1697. jasmine.StringPrettyPrinter.prototype.append = function(value) {
  1698. this.string += value;
  1699. };
  1700. jasmine.Queue = function(env) {
  1701. this.env = env;
  1702. this.blocks = [];
  1703. this.running = false;
  1704. this.index = 0;
  1705. this.offset = 0;
  1706. this.abort = false;
  1707. };
  1708. jasmine.Queue.prototype.addBefore = function(block) {
  1709. this.blocks.unshift(block);
  1710. };
  1711. jasmine.Queue.prototype.add = function(block) {
  1712. this.blocks.push(block);
  1713. };
  1714. jasmine.Queue.prototype.insertNext = function(block) {
  1715. this.blocks.splice((this.index + this.offset + 1), 0, block);
  1716. this.offset++;
  1717. };
  1718. jasmine.Queue.prototype.start = function(onComplete) {
  1719. this.running = true;
  1720. this.onComplete = onComplete;
  1721. this.next_();
  1722. };
  1723. jasmine.Queue.prototype.isRunning = function() {
  1724. return this.running;
  1725. };
  1726. jasmine.Queue.LOOP_DONT_RECURSE = true;
  1727. jasmine.Queue.prototype.next_ = function() {
  1728. var self = this;
  1729. var goAgain = true;
  1730. while (goAgain) {
  1731. goAgain = false;
  1732. if (self.index < self.blocks.length && !this.abort) {
  1733. var calledSynchronously = true;
  1734. var completedSynchronously = false;
  1735. var onComplete = function () {
  1736. if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
  1737. completedSynchronously = true;
  1738. return;
  1739. }
  1740. if (self.blocks[self.index].abort) {
  1741. self.abort = true;
  1742. }
  1743. self.offset = 0;
  1744. self.index++;
  1745. var now = new Date().getTime();
  1746. if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
  1747. self.env.lastUpdate = now;
  1748. self.env.setTimeout(function() {
  1749. self.next_();
  1750. }, 0);
  1751. } else {
  1752. if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
  1753. goAgain = true;
  1754. } else {
  1755. self.next_();
  1756. }
  1757. }
  1758. };
  1759. self.blocks[self.index].execute(onComplete);
  1760. calledSynchronously = false;
  1761. if (completedSynchronously) {
  1762. onComplete();
  1763. }
  1764. } else {
  1765. self.running = false;
  1766. if (self.onComplete) {
  1767. self.onComplete();
  1768. }
  1769. }
  1770. }
  1771. };
  1772. jasmine.Queue.prototype.results = function() {
  1773. var results = new jasmine.NestedResults();
  1774. for (var i = 0; i < this.blocks.length; i++) {
  1775. if (this.blocks[i].results) {
  1776. results.addResult(this.blocks[i].results());
  1777. }
  1778. }
  1779. return results;
  1780. };
  1781. /**
  1782. * Runner
  1783. *
  1784. * @constructor
  1785. * @param {jasmine.Env} env
  1786. */
  1787. jasmine.Runner = function(env) {
  1788. var self = this;
  1789. self.env = env;
  1790. self.queue = new jasmine.Queue(env);
  1791. self.before_ = [];
  1792. self.after_ = [];
  1793. self.suites_ = [];
  1794. };
  1795. jasmine.Runner.prototype.execute = function() {
  1796. var self = this;
  1797. if (self.env.reporter.reportRunnerStarting) {
  1798. self.env.reporter.reportRunnerStarting(this);
  1799. }
  1800. self.queue.start(function () {
  1801. self.finishCallback();
  1802. });
  1803. };
  1804. jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
  1805. beforeEachFunction.typeName = 'beforeEach';
  1806. this.before_.splice(0,0,beforeEachFunction);
  1807. };
  1808. jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
  1809. afterEachFunction.typeName = 'afterEach';
  1810. this.after_.splice(0,0,afterEachFunction);
  1811. };
  1812. jasmine.Runner.prototype.finishCallback = function() {
  1813. this.env.reporter.reportRunnerResults(this);
  1814. };
  1815. jasmine.Runner.prototype.addSuite = function(suite) {
  1816. this.suites_.push(suite);
  1817. };
  1818. jasmine.Runner.prototype.add = function(block) {
  1819. if (block instanceof jasmine.Suite) {
  1820. this.addSuite(block);
  1821. }
  1822. this.queue.add(block);
  1823. };
  1824. jasmine.Runner.prototype.specs = function () {
  1825. var suites = this.suites();
  1826. var specs = [];
  1827. for (var i = 0; i < suites.length; i++) {
  1828. specs = specs.concat(suites[i].specs());
  1829. }
  1830. return specs;
  1831. };
  1832. jasmine.Runner.prototype.suites = function() {
  1833. return this.suites_;
  1834. };
  1835. jasmine.Runner.prototype.topLevelSuites = function() {
  1836. var topLevelSuites = [];
  1837. for (var i = 0; i < this.suites_.length; i++) {
  1838. if (!this.suites_[i].parentSuite) {
  1839. topLevelSuites.push(this.suites_[i]);
  1840. }
  1841. }
  1842. return topLevelSuites;
  1843. };
  1844. jasmine.Runner.prototype.results = function() {
  1845. return this.queue.results();
  1846. };
  1847. /**
  1848. * Internal representation of a Jasmine specification, or test.
  1849. *
  1850. * @constructor
  1851. * @param {jasmine.Env} env
  1852. * @param {jasmine.Suite} suite
  1853. * @param {String} description
  1854. */
  1855. jasmine.Spec = function(env, suite, description) {
  1856. if (!env) {
  1857. throw new Error('jasmine.Env() required');
  1858. }
  1859. if (!suite) {
  1860. throw new Error('jasmine.Suite() required');
  1861. }
  1862. var spec = this;
  1863. spec.id = env.nextSpecId ? env.nextSpecId() : null;
  1864. spec.env = env;
  1865. spec.suite = suite;
  1866. spec.description = description;
  1867. spec.queue = new jasmine.Queue(env);
  1868. spec.afterCallbacks = [];
  1869. spec.spies_ = [];
  1870. spec.results_ = new jasmine.NestedResults();
  1871. spec.results_.description = description;
  1872. spec.matchersClass = null;
  1873. };
  1874. jasmine.Spec.prototype.getFullName = function() {
  1875. return this.suite.getFullName() + ' ' + this.description + '.';
  1876. };
  1877. jasmine.Spec.prototype.results = function() {
  1878. return this.results_;
  1879. };
  1880. /**
  1881. * All parameters are pretty-printed and concatenated together, then written to the spec's output.
  1882. *
  1883. * Be careful not to leave calls to <code>jasmine.log</code> in production code.
  1884. */
  1885. jasmine.Spec.prototype.log = function() {
  1886. return this.results_.log(arguments);
  1887. };
  1888. jasmine.Spec.prototype.runs = function (func) {
  1889. var block = new jasmine.Block(this.env, func, this);
  1890. this.addToQueue(block);
  1891. return this;
  1892. };
  1893. jasmine.Spec.prototype.addToQueue = function (block) {
  1894. if (this.queue.isRunning()) {
  1895. this.queue.insertNext(block);
  1896. } else {
  1897. this.queue.add(block);
  1898. }
  1899. };
  1900. /**
  1901. * @param {jasmine.ExpectationResult} result
  1902. */
  1903. jasmine.Spec.prototype.addMatcherResult = function(result) {
  1904. this.results_.addResult(result);
  1905. };
  1906. jasmine.Spec.prototype.expect = function(actual) {
  1907. var positive = new (this.getMatchersClass_())(this.env, actual, this);
  1908. positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
  1909. return positive;
  1910. };
  1911. /**
  1912. * Waits a fixed time period before moving to the next block.
  1913. *
  1914. * @deprecated Use waitsFor() instead
  1915. * @param {Number} timeout milliseconds to wait
  1916. */
  1917. jasmine.Spec.prototype.waits = function(timeout) {
  1918. var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
  1919. this.addToQueue(waitsFunc);
  1920. return this;
  1921. };
  1922. /**
  1923. * Waits for the latchFunction to return true before proceeding to the next block.
  1924. *
  1925. * @param {Function} latchFunction
  1926. * @param {String} optional_timeoutMessage
  1927. * @param {Number} optional_timeout
  1928. */
  1929. jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
  1930. var latchFunction_ = null;
  1931. var optional_timeoutMessage_ = null;
  1932. var optional_timeout_ = null;
  1933. for (var i = 0; i < arguments.length; i++) {
  1934. var arg = arguments[i];
  1935. switch (typeof arg) {
  1936. case 'function':
  1937. latchFunction_ = arg;
  1938. break;
  1939. case 'string':
  1940. optional_timeoutMessage_ = arg;
  1941. break;
  1942. case 'number':
  1943. optional_timeout_ = arg;
  1944. break;
  1945. }
  1946. }
  1947. var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
  1948. this.addToQueue(waitsForFunc);
  1949. return this;
  1950. };
  1951. jasmine.Spec.prototype.fail = function (e) {
  1952. var expectationResult = new jasmine.ExpectationResult({
  1953. passed: false,
  1954. message: e ? jasmine.util.formatException(e) : 'Exception',
  1955. trace: { stack: e.stack }
  1956. });
  1957. this.results_.addResult(expectationResult);
  1958. };
  1959. jasmine.Spec.prototype.getMatchersClass_ = function() {
  1960. return this.matchersClass || this.env.matchersClass;
  1961. };
  1962. jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
  1963. var parent = this.getMatchersClass_();
  1964. var newMatchersClass = function() {
  1965. parent.apply(this, arguments);
  1966. };
  1967. jasmine.util.inherit(newMatchersClass, parent);
  1968. jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
  1969. this.matchersClass = newMatchersClass;
  1970. };
  1971. jasmine.Spec.prototype.finishCallback = function() {
  1972. this.env.reporter.reportSpecResults(this);
  1973. };
  1974. jasmine.Spec.prototype.finish = function(onComplete) {
  1975. this.removeAllSpies();
  1976. this.finishCallback();
  1977. if (onComplete) {
  1978. onComplete();
  1979. }
  1980. };
  1981. jasmine.Spec.prototype.after = function(doAfter) {
  1982. if (this.queue.isRunning()) {
  1983. this.queue.add(new jasmine.Block(this.env, doAfter, this));
  1984. } else {
  1985. this.afterCallbacks.unshift(doAfter);
  1986. }
  1987. };
  1988. jasmine.Spec.prototype.execute = function(onComplete) {
  1989. var spec = this;
  1990. if (!spec.env.specFilter(spec)) {
  1991. spec.results_.skipped = true;
  1992. spec.finish(onComplete);
  1993. return;
  1994. }
  1995. this.env.reporter.reportSpecStarting(this);
  1996. spec.env.currentSpec = spec;
  1997. spec.addBeforesAndAftersToQueue();
  1998. spec.queue.start(function () {
  1999. spec.finish(onComplete);
  2000. });
  2001. };
  2002. jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
  2003. var runner = this.env.currentRunner();
  2004. var i;
  2005. for (var suite = this.suite; suite; suite = suite.parentSuite) {
  2006. for (i = 0; i < suite.before_.length; i++) {
  2007. this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
  2008. }
  2009. }
  2010. for (i = 0; i < runner.before_.length; i++) {
  2011. this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
  2012. }
  2013. for (i = 0; i < this.afterCallbacks.length; i++) {
  2014. this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
  2015. }
  2016. for (suite = this.suite; suite; suite = suite.parentSuite) {
  2017. for (i = 0; i < suite.after_.length; i++) {
  2018. this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
  2019. }
  2020. }
  2021. for (i = 0; i < runner.after_.length; i++) {
  2022. this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
  2023. }
  2024. };
  2025. jasmine.Spec.prototype.explodes = function() {
  2026. throw 'explodes function should not have been called';
  2027. };
  2028. jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
  2029. if (obj == jasmine.undefined) {
  2030. throw "spyOn could not find an object to spy upon for " + methodName + "()";
  2031. }
  2032. if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
  2033. throw methodName + '() method does not exist';
  2034. }
  2035. if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
  2036. throw new Error(methodName + ' has already been spied upon');
  2037. }
  2038. var spyObj = jasmine.createSpy(methodName);
  2039. this.spies_.push(spyObj);
  2040. spyObj.baseObj = obj;
  2041. spyObj.methodName = methodName;
  2042. spyObj.originalValue = obj[methodName];
  2043. obj[methodName] = spyObj;
  2044. return spyObj;
  2045. };
  2046. jasmine.Spec.prototype.removeAllSpies = function() {
  2047. for (var i = 0; i < this.spies_.length; i++) {
  2048. var spy = this.spies_[i];
  2049. spy.baseObj[spy.methodName] = spy.originalValue;
  2050. }
  2051. this.spies_ = [];
  2052. };
  2053. /**
  2054. * Internal representation of a Jasmine suite.
  2055. *
  2056. * @constructor
  2057. * @param {jasmine.Env} env
  2058. * @param {String} description
  2059. * @param {Function} specDefinitions
  2060. * @param {jasmine.Suite} parentSuite
  2061. */
  2062. jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
  2063. var self = this;
  2064. self.id = env.nextSuiteId ? env.nextSuiteId() : null;
  2065. self.description = description;
  2066. self.queue = new jasmine.Queue(env);
  2067. self.parentSuite = parentSuite;
  2068. self.env = env;
  2069. self.before_ = [];
  2070. self.after_ = [];
  2071. self.children_ = [];
  2072. self.suites_ = [];
  2073. self.specs_ = [];
  2074. };
  2075. jasmine.Suite.prototype.getFullName = function() {
  2076. var fullName = this.description;
  2077. for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
  2078. fullName = parentSuite.description + ' ' + fullName;
  2079. }
  2080. return fullName;
  2081. };
  2082. jasmine.Suite.prototype.finish = function(onComplete) {
  2083. this.env.reporter.reportSuiteResults(this);
  2084. this.finished = true;
  2085. if (typeof(onComplete) == 'function') {
  2086. onComplete();
  2087. }
  2088. };
  2089. jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
  2090. beforeEachFunction.typeName = 'beforeEach';
  2091. this.before_.unshift(beforeEachFunction);
  2092. };
  2093. jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
  2094. afterEachFunction.typeName = 'afterEach';
  2095. this.after_.unshift(afterEachFunction);
  2096. };
  2097. jasmine.Suite.prototype.results = function() {
  2098. return this.queue.results();
  2099. };
  2100. jasmine.Suite.prototype.add = function(suiteOrSpec) {
  2101. this.children_.push(suiteOrSpec);
  2102. if (suiteOrSpec instanceof jasmine.Suite) {
  2103. this.suites_.push(suiteOrSpec);
  2104. this.env.currentRunner().addSuite(suiteOrSpec);
  2105. } else {
  2106. this.specs_.push(suiteOrSpec);
  2107. }
  2108. this.queue.add(suiteOrSpec);
  2109. };
  2110. jasmine.Suite.prototype.specs = function() {
  2111. return this.specs_;
  2112. };
  2113. jasmine.Suite.prototype.suites = function() {
  2114. return this.suites_;
  2115. };
  2116. jasmine.Suite.prototype.children = function() {
  2117. return this.children_;
  2118. };
  2119. jasmine.Suite.prototype.execute = function(onComplete) {
  2120. var self = this;
  2121. this.queue.start(function () {
  2122. self.finish(onComplete);
  2123. });
  2124. };
  2125. jasmine.WaitsBlock = function(env, timeout, spec) {
  2126. this.timeout = timeout;
  2127. jasmine.Block.call(this, env, null, spec);
  2128. };
  2129. jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
  2130. jasmine.WaitsBlock.prototype.execute = function (onComplete) {
  2131. if (jasmine.VERBOSE) {
  2132. this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
  2133. }
  2134. this.env.setTimeout(function () {
  2135. onComplete();
  2136. }, this.timeout);
  2137. };
  2138. /**
  2139. * A block which waits for some condition to become true, with timeout.
  2140. *
  2141. * @constructor
  2142. * @extends jasmine.Block
  2143. * @param {jasmine.Env} env The Jasmine environment.
  2144. * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
  2145. * @param {Function} latchFunction A function which returns true when the desired condition has been met.
  2146. * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
  2147. * @param {jasmine.Spec} spec The Jasmine spec.
  2148. */
  2149. jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
  2150. this.timeout = timeout || env.defaultTimeoutInterval;
  2151. this.latchFunction = latchFunction;
  2152. this.message = message;
  2153. this.totalTimeSpentWaitingForLatch = 0;
  2154. jasmine.Block.call(this, env, null, spec);
  2155. };
  2156. jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
  2157. jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
  2158. jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
  2159. if (jasmine.VERBOSE) {
  2160. this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
  2161. }
  2162. var latchFunctionResult;
  2163. try {
  2164. latchFunctionResult = this.latchFunction.apply(this.spec);
  2165. } catch (e) {
  2166. this.spec.fail(e);
  2167. onComplete();
  2168. return;
  2169. }
  2170. if (latchFunctionResult) {
  2171. onComplete();
  2172. } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
  2173. var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
  2174. this.spec.fail({
  2175. name: 'timeout',
  2176. message: message
  2177. });
  2178. this.abort = true;
  2179. onComplete();
  2180. } else {
  2181. this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
  2182. var self = this;
  2183. this.env.setTimeout(function() {
  2184. self.execute(onComplete);
  2185. }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
  2186. }
  2187. };
  2188. jasmine.version_= {
  2189. "major": 1,
  2190. "minor": 2,
  2191. "build": 0,
  2192. "revision": 1333557965,
  2193. "release_candidate": 3
  2194. };