qunit.js 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932
  1. /**
  2. * QUnit v1.9.0 - A JavaScript Unit Testing Framework
  3. *
  4. * http://docs.jquery.com/QUnit
  5. *
  6. * Copyright (c) 2012 John Resig, Jörn Zaefferer
  7. * Dual licensed under the MIT (MIT-LICENSE.txt)
  8. * or GPL (GPL-LICENSE.txt) licenses.
  9. */
  10. (function( window ) {
  11. var QUnit,
  12. config,
  13. onErrorFnPrev,
  14. testId = 0,
  15. fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
  16. toString = Object.prototype.toString,
  17. hasOwn = Object.prototype.hasOwnProperty,
  18. defined = {
  19. setTimeout: typeof window.setTimeout !== "undefined",
  20. sessionStorage: (function() {
  21. var x = "qunit-test-string";
  22. try {
  23. sessionStorage.setItem( x, x );
  24. sessionStorage.removeItem( x );
  25. return true;
  26. } catch( e ) {
  27. return false;
  28. }
  29. }())
  30. };
  31. function Test( settings ) {
  32. extend( this, settings );
  33. this.assertions = [];
  34. this.testNumber = ++Test.count;
  35. }
  36. Test.count = 0;
  37. Test.prototype = {
  38. init: function() {
  39. var a, b, li,
  40. tests = id( "qunit-tests" );
  41. if ( tests ) {
  42. b = document.createElement( "strong" );
  43. b.innerHTML = this.name;
  44. // `a` initialized at top of scope
  45. a = document.createElement( "a" );
  46. a.innerHTML = "Rerun";
  47. a.href = QUnit.url({ testNumber: this.testNumber });
  48. li = document.createElement( "li" );
  49. li.appendChild( b );
  50. li.appendChild( a );
  51. li.className = "running";
  52. li.id = this.id = "qunit-test-output" + testId++;
  53. tests.appendChild( li );
  54. }
  55. },
  56. setup: function() {
  57. if ( this.module !== config.previousModule ) {
  58. if ( config.previousModule ) {
  59. runLoggingCallbacks( "moduleDone", QUnit, {
  60. name: config.previousModule,
  61. failed: config.moduleStats.bad,
  62. passed: config.moduleStats.all - config.moduleStats.bad,
  63. total: config.moduleStats.all
  64. });
  65. }
  66. config.previousModule = this.module;
  67. config.moduleStats = { all: 0, bad: 0 };
  68. runLoggingCallbacks( "moduleStart", QUnit, {
  69. name: this.module
  70. });
  71. } else if ( config.autorun ) {
  72. runLoggingCallbacks( "moduleStart", QUnit, {
  73. name: this.module
  74. });
  75. }
  76. config.current = this;
  77. this.testEnvironment = extend({
  78. setup: function() {},
  79. teardown: function() {}
  80. }, this.moduleTestEnvironment );
  81. runLoggingCallbacks( "testStart", QUnit, {
  82. name: this.testName,
  83. module: this.module
  84. });
  85. // allow utility functions to access the current test environment
  86. // TODO why??
  87. QUnit.current_testEnvironment = this.testEnvironment;
  88. if ( !config.pollution ) {
  89. saveGlobal();
  90. }
  91. if ( config.notrycatch ) {
  92. this.testEnvironment.setup.call( this.testEnvironment );
  93. return;
  94. }
  95. try {
  96. this.testEnvironment.setup.call( this.testEnvironment );
  97. } catch( e ) {
  98. QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
  99. }
  100. },
  101. run: function() {
  102. config.current = this;
  103. var running = id( "qunit-testresult" );
  104. if ( running ) {
  105. running.innerHTML = "Running: <br/>" + this.name;
  106. }
  107. if ( this.async ) {
  108. QUnit.stop();
  109. }
  110. if ( config.notrycatch ) {
  111. this.callback.call( this.testEnvironment, QUnit.assert );
  112. return;
  113. }
  114. try {
  115. this.callback.call( this.testEnvironment, QUnit.assert );
  116. } catch( e ) {
  117. QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace( e, 0 ) );
  118. // else next test will carry the responsibility
  119. saveGlobal();
  120. // Restart the tests if they're blocking
  121. if ( config.blocking ) {
  122. QUnit.start();
  123. }
  124. }
  125. },
  126. teardown: function() {
  127. config.current = this;
  128. if ( config.notrycatch ) {
  129. this.testEnvironment.teardown.call( this.testEnvironment );
  130. return;
  131. } else {
  132. try {
  133. this.testEnvironment.teardown.call( this.testEnvironment );
  134. } catch( e ) {
  135. QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
  136. }
  137. }
  138. checkPollution();
  139. },
  140. finish: function() {
  141. config.current = this;
  142. if ( config.requireExpects && this.expected == null ) {
  143. QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
  144. } else if ( this.expected != null && this.expected != this.assertions.length ) {
  145. QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
  146. } else if ( this.expected == null && !this.assertions.length ) {
  147. QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
  148. }
  149. var assertion, a, b, i, li, ol,
  150. test = this,
  151. good = 0,
  152. bad = 0,
  153. tests = id( "qunit-tests" );
  154. config.stats.all += this.assertions.length;
  155. config.moduleStats.all += this.assertions.length;
  156. if ( tests ) {
  157. ol = document.createElement( "ol" );
  158. for ( i = 0; i < this.assertions.length; i++ ) {
  159. assertion = this.assertions[i];
  160. li = document.createElement( "li" );
  161. li.className = assertion.result ? "pass" : "fail";
  162. li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
  163. ol.appendChild( li );
  164. if ( assertion.result ) {
  165. good++;
  166. } else {
  167. bad++;
  168. config.stats.bad++;
  169. config.moduleStats.bad++;
  170. }
  171. }
  172. // store result when possible
  173. if ( QUnit.config.reorder && defined.sessionStorage ) {
  174. if ( bad ) {
  175. sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
  176. } else {
  177. sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
  178. }
  179. }
  180. if ( bad === 0 ) {
  181. ol.style.display = "none";
  182. }
  183. // `b` initialized at top of scope
  184. b = document.createElement( "strong" );
  185. b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
  186. addEvent(b, "click", function() {
  187. var next = b.nextSibling.nextSibling,
  188. display = next.style.display;
  189. next.style.display = display === "none" ? "block" : "none";
  190. });
  191. addEvent(b, "dblclick", function( e ) {
  192. var target = e && e.target ? e.target : window.event.srcElement;
  193. if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
  194. target = target.parentNode;
  195. }
  196. if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
  197. window.location = QUnit.url({ testNumber: test.testNumber });
  198. }
  199. });
  200. // `li` initialized at top of scope
  201. li = id( this.id );
  202. li.className = bad ? "fail" : "pass";
  203. li.removeChild( li.firstChild );
  204. a = li.firstChild;
  205. li.appendChild( b );
  206. li.appendChild ( a );
  207. li.appendChild( ol );
  208. } else {
  209. for ( i = 0; i < this.assertions.length; i++ ) {
  210. if ( !this.assertions[i].result ) {
  211. bad++;
  212. config.stats.bad++;
  213. config.moduleStats.bad++;
  214. }
  215. }
  216. }
  217. runLoggingCallbacks( "testDone", QUnit, {
  218. name: this.testName,
  219. module: this.module,
  220. failed: bad,
  221. passed: this.assertions.length - bad,
  222. total: this.assertions.length
  223. });
  224. QUnit.reset();
  225. config.current = undefined;
  226. },
  227. queue: function() {
  228. var bad,
  229. test = this;
  230. synchronize(function() {
  231. test.init();
  232. });
  233. function run() {
  234. // each of these can by async
  235. synchronize(function() {
  236. test.setup();
  237. });
  238. synchronize(function() {
  239. test.run();
  240. });
  241. synchronize(function() {
  242. test.teardown();
  243. });
  244. synchronize(function() {
  245. test.finish();
  246. });
  247. }
  248. // `bad` initialized at top of scope
  249. // defer when previous test run passed, if storage is available
  250. bad = QUnit.config.reorder && defined.sessionStorage &&
  251. +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
  252. if ( bad ) {
  253. run();
  254. } else {
  255. synchronize( run, true );
  256. }
  257. }
  258. };
  259. // Root QUnit object.
  260. // `QUnit` initialized at top of scope
  261. QUnit = {
  262. // call on start of module test to prepend name to all tests
  263. module: function( name, testEnvironment ) {
  264. config.currentModule = name;
  265. config.currentModuleTestEnviroment = testEnvironment;
  266. },
  267. asyncTest: function( testName, expected, callback ) {
  268. if ( arguments.length === 2 ) {
  269. callback = expected;
  270. expected = null;
  271. }
  272. QUnit.test( testName, expected, callback, true );
  273. },
  274. test: function( testName, expected, callback, async ) {
  275. var test,
  276. name = "<span class='test-name'>" + escapeInnerText( testName ) + "</span>";
  277. if ( arguments.length === 2 ) {
  278. callback = expected;
  279. expected = null;
  280. }
  281. if ( config.currentModule ) {
  282. name = "<span class='module-name'>" + config.currentModule + "</span>: " + name;
  283. }
  284. test = new Test({
  285. name: name,
  286. testName: testName,
  287. expected: expected,
  288. async: async,
  289. callback: callback,
  290. module: config.currentModule,
  291. moduleTestEnvironment: config.currentModuleTestEnviroment,
  292. stack: sourceFromStacktrace( 2 )
  293. });
  294. if ( !validTest( test ) ) {
  295. return;
  296. }
  297. test.queue();
  298. },
  299. // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
  300. expect: function( asserts ) {
  301. config.current.expected = asserts;
  302. },
  303. start: function( count ) {
  304. config.semaphore -= count || 1;
  305. // don't start until equal number of stop-calls
  306. if ( config.semaphore > 0 ) {
  307. return;
  308. }
  309. // ignore if start is called more often then stop
  310. if ( config.semaphore < 0 ) {
  311. config.semaphore = 0;
  312. }
  313. // A slight delay, to avoid any current callbacks
  314. if ( defined.setTimeout ) {
  315. window.setTimeout(function() {
  316. if ( config.semaphore > 0 ) {
  317. return;
  318. }
  319. if ( config.timeout ) {
  320. clearTimeout( config.timeout );
  321. }
  322. config.blocking = false;
  323. process( true );
  324. }, 13);
  325. } else {
  326. config.blocking = false;
  327. process( true );
  328. }
  329. },
  330. stop: function( count ) {
  331. config.semaphore += count || 1;
  332. config.blocking = true;
  333. if ( config.testTimeout && defined.setTimeout ) {
  334. clearTimeout( config.timeout );
  335. config.timeout = window.setTimeout(function() {
  336. QUnit.ok( false, "Test timed out" );
  337. config.semaphore = 1;
  338. QUnit.start();
  339. }, config.testTimeout );
  340. }
  341. }
  342. };
  343. // Asssert helpers
  344. // All of these must call either QUnit.push() or manually do:
  345. // - runLoggingCallbacks( "log", .. );
  346. // - config.current.assertions.push({ .. });
  347. QUnit.assert = {
  348. /**
  349. * Asserts rough true-ish result.
  350. * @name ok
  351. * @function
  352. * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
  353. */
  354. ok: function( result, msg ) {
  355. if ( !config.current ) {
  356. throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
  357. }
  358. result = !!result;
  359. var source,
  360. details = {
  361. result: result,
  362. message: msg
  363. };
  364. msg = escapeInnerText( msg || (result ? "okay" : "failed" ) );
  365. msg = "<span class='test-message'>" + msg + "</span>";
  366. if ( !result ) {
  367. source = sourceFromStacktrace( 2 );
  368. if ( source ) {
  369. details.source = source;
  370. msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr></table>";
  371. }
  372. }
  373. runLoggingCallbacks( "log", QUnit, details );
  374. config.current.assertions.push({
  375. result: result,
  376. message: msg
  377. });
  378. },
  379. /**
  380. * Assert that the first two arguments are equal, with an optional message.
  381. * Prints out both actual and expected values.
  382. * @name equal
  383. * @function
  384. * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
  385. */
  386. equal: function( actual, expected, message ) {
  387. QUnit.push( expected == actual, actual, expected, message );
  388. },
  389. /**
  390. * @name notEqual
  391. * @function
  392. */
  393. notEqual: function( actual, expected, message ) {
  394. QUnit.push( expected != actual, actual, expected, message );
  395. },
  396. /**
  397. * @name deepEqual
  398. * @function
  399. */
  400. deepEqual: function( actual, expected, message ) {
  401. QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
  402. },
  403. /**
  404. * @name notDeepEqual
  405. * @function
  406. */
  407. notDeepEqual: function( actual, expected, message ) {
  408. QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
  409. },
  410. /**
  411. * @name strictEqual
  412. * @function
  413. */
  414. strictEqual: function( actual, expected, message ) {
  415. QUnit.push( expected === actual, actual, expected, message );
  416. },
  417. /**
  418. * @name notStrictEqual
  419. * @function
  420. */
  421. notStrictEqual: function( actual, expected, message ) {
  422. QUnit.push( expected !== actual, actual, expected, message );
  423. },
  424. throws: function( block, expected, message ) {
  425. var actual,
  426. ok = false;
  427. // 'expected' is optional
  428. if ( typeof expected === "string" ) {
  429. message = expected;
  430. expected = null;
  431. }
  432. config.current.ignoreGlobalErrors = true;
  433. try {
  434. block.call( config.current.testEnvironment );
  435. } catch (e) {
  436. actual = e;
  437. }
  438. config.current.ignoreGlobalErrors = false;
  439. if ( actual ) {
  440. // we don't want to validate thrown error
  441. if ( !expected ) {
  442. ok = true;
  443. // expected is a regexp
  444. } else if ( QUnit.objectType( expected ) === "regexp" ) {
  445. ok = expected.test( actual );
  446. // expected is a constructor
  447. } else if ( actual instanceof expected ) {
  448. ok = true;
  449. // expected is a validation function which returns true is validation passed
  450. } else if ( expected.call( {}, actual ) === true ) {
  451. ok = true;
  452. }
  453. QUnit.push( ok, actual, null, message );
  454. } else {
  455. QUnit.pushFailure( message, null, 'No exception was thrown.' );
  456. }
  457. }
  458. };
  459. /**
  460. * @deprecate since 1.8.0
  461. * Kept assertion helpers in root for backwards compatibility
  462. */
  463. extend( QUnit, QUnit.assert );
  464. /**
  465. * @deprecated since 1.9.0
  466. * Kept global "raises()" for backwards compatibility
  467. */
  468. QUnit.raises = QUnit.assert.throws;
  469. /**
  470. * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
  471. * Kept to avoid TypeErrors for undefined methods.
  472. */
  473. QUnit.equals = function() {
  474. QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
  475. };
  476. QUnit.same = function() {
  477. QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
  478. };
  479. // We want access to the constructor's prototype
  480. (function() {
  481. function F() {}
  482. F.prototype = QUnit;
  483. QUnit = new F();
  484. // Make F QUnit's constructor so that we can add to the prototype later
  485. QUnit.constructor = F;
  486. }());
  487. /**
  488. * Config object: Maintain internal state
  489. * Later exposed as QUnit.config
  490. * `config` initialized at top of scope
  491. */
  492. config = {
  493. // The queue of tests to run
  494. queue: [],
  495. // block until document ready
  496. blocking: true,
  497. // when enabled, show only failing tests
  498. // gets persisted through sessionStorage and can be changed in UI via checkbox
  499. hidepassed: false,
  500. // by default, run previously failed tests first
  501. // very useful in combination with "Hide passed tests" checked
  502. reorder: true,
  503. // by default, modify document.title when suite is done
  504. altertitle: true,
  505. // when enabled, all tests must call expect()
  506. requireExpects: false,
  507. // add checkboxes that are persisted in the query-string
  508. // when enabled, the id is set to `true` as a `QUnit.config` property
  509. urlConfig: [
  510. {
  511. id: "noglobals",
  512. label: "Check for Globals",
  513. tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
  514. },
  515. {
  516. id: "notrycatch",
  517. label: "No try-catch",
  518. tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
  519. }
  520. ],
  521. // logging callback queues
  522. begin: [],
  523. done: [],
  524. log: [],
  525. testStart: [],
  526. testDone: [],
  527. moduleStart: [],
  528. moduleDone: []
  529. };
  530. // Initialize more QUnit.config and QUnit.urlParams
  531. (function() {
  532. var i,
  533. location = window.location || { search: "", protocol: "file:" },
  534. params = location.search.slice( 1 ).split( "&" ),
  535. length = params.length,
  536. urlParams = {},
  537. current;
  538. if ( params[ 0 ] ) {
  539. for ( i = 0; i < length; i++ ) {
  540. current = params[ i ].split( "=" );
  541. current[ 0 ] = decodeURIComponent( current[ 0 ] );
  542. // allow just a key to turn on a flag, e.g., test.html?noglobals
  543. current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
  544. urlParams[ current[ 0 ] ] = current[ 1 ];
  545. }
  546. }
  547. QUnit.urlParams = urlParams;
  548. // String search anywhere in moduleName+testName
  549. config.filter = urlParams.filter;
  550. // Exact match of the module name
  551. config.module = urlParams.module;
  552. config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
  553. // Figure out if we're running the tests from a server or not
  554. QUnit.isLocal = location.protocol === "file:";
  555. }());
  556. // Export global variables, unless an 'exports' object exists,
  557. // in that case we assume we're in CommonJS (dealt with on the bottom of the script)
  558. if ( typeof exports === "undefined" ) {
  559. extend( window, QUnit );
  560. // Expose QUnit object
  561. window.QUnit = QUnit;
  562. }
  563. // Extend QUnit object,
  564. // these after set here because they should not be exposed as global functions
  565. extend( QUnit, {
  566. config: config,
  567. // Initialize the configuration options
  568. init: function() {
  569. extend( config, {
  570. stats: { all: 0, bad: 0 },
  571. moduleStats: { all: 0, bad: 0 },
  572. started: +new Date(),
  573. updateRate: 1000,
  574. blocking: false,
  575. autostart: true,
  576. autorun: false,
  577. filter: "",
  578. queue: [],
  579. semaphore: 0
  580. });
  581. var tests, banner, result,
  582. qunit = id( "qunit" );
  583. if ( qunit ) {
  584. qunit.innerHTML =
  585. "<h1 id='qunit-header'>" + escapeInnerText( document.title ) + "</h1>" +
  586. "<h2 id='qunit-banner'></h2>" +
  587. "<div id='qunit-testrunner-toolbar'></div>" +
  588. "<h2 id='qunit-userAgent'></h2>" +
  589. "<ol id='qunit-tests'></ol>";
  590. }
  591. tests = id( "qunit-tests" );
  592. banner = id( "qunit-banner" );
  593. result = id( "qunit-testresult" );
  594. if ( tests ) {
  595. tests.innerHTML = "";
  596. }
  597. if ( banner ) {
  598. banner.className = "";
  599. }
  600. if ( result ) {
  601. result.parentNode.removeChild( result );
  602. }
  603. if ( tests ) {
  604. result = document.createElement( "p" );
  605. result.id = "qunit-testresult";
  606. result.className = "result";
  607. tests.parentNode.insertBefore( result, tests );
  608. result.innerHTML = "Running...<br/>&nbsp;";
  609. }
  610. },
  611. // Resets the test setup. Useful for tests that modify the DOM.
  612. // If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
  613. reset: function() {
  614. var fixture;
  615. if ( window.jQuery ) {
  616. jQuery( "#qunit-fixture" ).html( config.fixture );
  617. } else {
  618. fixture = id( "qunit-fixture" );
  619. if ( fixture ) {
  620. fixture.innerHTML = config.fixture;
  621. }
  622. }
  623. },
  624. // Trigger an event on an element.
  625. // @example triggerEvent( document.body, "click" );
  626. triggerEvent: function( elem, type, event ) {
  627. if ( document.createEvent ) {
  628. event = document.createEvent( "MouseEvents" );
  629. event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
  630. 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  631. elem.dispatchEvent( event );
  632. } else if ( elem.fireEvent ) {
  633. elem.fireEvent( "on" + type );
  634. }
  635. },
  636. // Safe object type checking
  637. is: function( type, obj ) {
  638. return QUnit.objectType( obj ) == type;
  639. },
  640. objectType: function( obj ) {
  641. if ( typeof obj === "undefined" ) {
  642. return "undefined";
  643. // consider: typeof null === object
  644. }
  645. if ( obj === null ) {
  646. return "null";
  647. }
  648. var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || "";
  649. switch ( type ) {
  650. case "Number":
  651. if ( isNaN(obj) ) {
  652. return "nan";
  653. }
  654. return "number";
  655. case "String":
  656. case "Boolean":
  657. case "Array":
  658. case "Date":
  659. case "RegExp":
  660. case "Function":
  661. return type.toLowerCase();
  662. }
  663. if ( typeof obj === "object" ) {
  664. return "object";
  665. }
  666. return undefined;
  667. },
  668. push: function( result, actual, expected, message ) {
  669. if ( !config.current ) {
  670. throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
  671. }
  672. var output, source,
  673. details = {
  674. result: result,
  675. message: message,
  676. actual: actual,
  677. expected: expected
  678. };
  679. message = escapeInnerText( message ) || ( result ? "okay" : "failed" );
  680. message = "<span class='test-message'>" + message + "</span>";
  681. output = message;
  682. if ( !result ) {
  683. expected = escapeInnerText( QUnit.jsDump.parse(expected) );
  684. actual = escapeInnerText( QUnit.jsDump.parse(actual) );
  685. output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
  686. if ( actual != expected ) {
  687. output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
  688. output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
  689. }
  690. source = sourceFromStacktrace();
  691. if ( source ) {
  692. details.source = source;
  693. output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
  694. }
  695. output += "</table>";
  696. }
  697. runLoggingCallbacks( "log", QUnit, details );
  698. config.current.assertions.push({
  699. result: !!result,
  700. message: output
  701. });
  702. },
  703. pushFailure: function( message, source, actual ) {
  704. if ( !config.current ) {
  705. throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
  706. }
  707. var output,
  708. details = {
  709. result: false,
  710. message: message
  711. };
  712. message = escapeInnerText( message ) || "error";
  713. message = "<span class='test-message'>" + message + "</span>";
  714. output = message;
  715. output += "<table>";
  716. if ( actual ) {
  717. output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeInnerText( actual ) + "</pre></td></tr>";
  718. }
  719. if ( source ) {
  720. details.source = source;
  721. output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
  722. }
  723. output += "</table>";
  724. runLoggingCallbacks( "log", QUnit, details );
  725. config.current.assertions.push({
  726. result: false,
  727. message: output
  728. });
  729. },
  730. url: function( params ) {
  731. params = extend( extend( {}, QUnit.urlParams ), params );
  732. var key,
  733. querystring = "?";
  734. for ( key in params ) {
  735. if ( !hasOwn.call( params, key ) ) {
  736. continue;
  737. }
  738. querystring += encodeURIComponent( key ) + "=" +
  739. encodeURIComponent( params[ key ] ) + "&";
  740. }
  741. return window.location.pathname + querystring.slice( 0, -1 );
  742. },
  743. extend: extend,
  744. id: id,
  745. addEvent: addEvent
  746. // load, equiv, jsDump, diff: Attached later
  747. });
  748. /**
  749. * @deprecated: Created for backwards compatibility with test runner that set the hook function
  750. * into QUnit.{hook}, instead of invoking it and passing the hook function.
  751. * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
  752. * Doing this allows us to tell if the following methods have been overwritten on the actual
  753. * QUnit object.
  754. */
  755. extend( QUnit.constructor.prototype, {
  756. // Logging callbacks; all receive a single argument with the listed properties
  757. // run test/logs.html for any related changes
  758. begin: registerLoggingCallback( "begin" ),
  759. // done: { failed, passed, total, runtime }
  760. done: registerLoggingCallback( "done" ),
  761. // log: { result, actual, expected, message }
  762. log: registerLoggingCallback( "log" ),
  763. // testStart: { name }
  764. testStart: registerLoggingCallback( "testStart" ),
  765. // testDone: { name, failed, passed, total }
  766. testDone: registerLoggingCallback( "testDone" ),
  767. // moduleStart: { name }
  768. moduleStart: registerLoggingCallback( "moduleStart" ),
  769. // moduleDone: { name, failed, passed, total }
  770. moduleDone: registerLoggingCallback( "moduleDone" )
  771. });
  772. if ( typeof document === "undefined" || document.readyState === "complete" ) {
  773. config.autorun = true;
  774. }
  775. QUnit.load = function() {
  776. runLoggingCallbacks( "begin", QUnit, {} );
  777. // Initialize the config, saving the execution queue
  778. var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxes,
  779. urlConfigHtml = "",
  780. oldconfig = extend( {}, config );
  781. QUnit.init();
  782. extend(config, oldconfig);
  783. config.blocking = false;
  784. len = config.urlConfig.length;
  785. for ( i = 0; i < len; i++ ) {
  786. val = config.urlConfig[i];
  787. if ( typeof val === "string" ) {
  788. val = {
  789. id: val,
  790. label: val,
  791. tooltip: "[no tooltip available]"
  792. };
  793. }
  794. config[ val.id ] = QUnit.urlParams[ val.id ];
  795. urlConfigHtml += "<input id='qunit-urlconfig-" + val.id + "' name='" + val.id + "' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) + " title='" + val.tooltip + "'><label for='qunit-urlconfig-" + val.id + "' title='" + val.tooltip + "'>" + val.label + "</label>";
  796. }
  797. // `userAgent` initialized at top of scope
  798. userAgent = id( "qunit-userAgent" );
  799. if ( userAgent ) {
  800. userAgent.innerHTML = navigator.userAgent;
  801. }
  802. // `banner` initialized at top of scope
  803. banner = id( "qunit-header" );
  804. if ( banner ) {
  805. banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
  806. }
  807. // `toolbar` initialized at top of scope
  808. toolbar = id( "qunit-testrunner-toolbar" );
  809. if ( toolbar ) {
  810. // `filter` initialized at top of scope
  811. filter = document.createElement( "input" );
  812. filter.type = "checkbox";
  813. filter.id = "qunit-filter-pass";
  814. addEvent( filter, "click", function() {
  815. var tmp,
  816. ol = document.getElementById( "qunit-tests" );
  817. if ( filter.checked ) {
  818. ol.className = ol.className + " hidepass";
  819. } else {
  820. tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
  821. ol.className = tmp.replace( / hidepass /, " " );
  822. }
  823. if ( defined.sessionStorage ) {
  824. if (filter.checked) {
  825. sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
  826. } else {
  827. sessionStorage.removeItem( "qunit-filter-passed-tests" );
  828. }
  829. }
  830. });
  831. if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
  832. filter.checked = true;
  833. // `ol` initialized at top of scope
  834. ol = document.getElementById( "qunit-tests" );
  835. ol.className = ol.className + " hidepass";
  836. }
  837. toolbar.appendChild( filter );
  838. // `label` initialized at top of scope
  839. label = document.createElement( "label" );
  840. label.setAttribute( "for", "qunit-filter-pass" );
  841. label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." );
  842. label.innerHTML = "Hide passed tests";
  843. toolbar.appendChild( label );
  844. urlConfigCheckboxes = document.createElement( 'span' );
  845. urlConfigCheckboxes.innerHTML = urlConfigHtml;
  846. addEvent( urlConfigCheckboxes, "change", function( event ) {
  847. var params = {};
  848. params[ event.target.name ] = event.target.checked ? true : undefined;
  849. window.location = QUnit.url( params );
  850. });
  851. toolbar.appendChild( urlConfigCheckboxes );
  852. }
  853. // `main` initialized at top of scope
  854. main = id( "qunit-fixture" );
  855. if ( main ) {
  856. config.fixture = main.innerHTML;
  857. }
  858. if ( config.autostart ) {
  859. QUnit.start();
  860. }
  861. };
  862. addEvent( window, "load", QUnit.load );
  863. // `onErrorFnPrev` initialized at top of scope
  864. // Preserve other handlers
  865. onErrorFnPrev = window.onerror;
  866. // Cover uncaught exceptions
  867. // Returning true will surpress the default browser handler,
  868. // returning false will let it run.
  869. window.onerror = function ( error, filePath, linerNr ) {
  870. var ret = false;
  871. if ( onErrorFnPrev ) {
  872. ret = onErrorFnPrev( error, filePath, linerNr );
  873. }
  874. // Treat return value as window.onerror itself does,
  875. // Only do our handling if not surpressed.
  876. if ( ret !== true ) {
  877. if ( QUnit.config.current ) {
  878. if ( QUnit.config.current.ignoreGlobalErrors ) {
  879. return true;
  880. }
  881. QUnit.pushFailure( error, filePath + ":" + linerNr );
  882. } else {
  883. QUnit.test( "global failure", function() {
  884. QUnit.pushFailure( error, filePath + ":" + linerNr );
  885. });
  886. }
  887. return false;
  888. }
  889. return ret;
  890. };
  891. function done() {
  892. config.autorun = true;
  893. // Log the last module results
  894. if ( config.currentModule ) {
  895. runLoggingCallbacks( "moduleDone", QUnit, {
  896. name: config.currentModule,
  897. failed: config.moduleStats.bad,
  898. passed: config.moduleStats.all - config.moduleStats.bad,
  899. total: config.moduleStats.all
  900. });
  901. }
  902. var i, key,
  903. banner = id( "qunit-banner" ),
  904. tests = id( "qunit-tests" ),
  905. runtime = +new Date() - config.started,
  906. passed = config.stats.all - config.stats.bad,
  907. html = [
  908. "Tests completed in ",
  909. runtime,
  910. " milliseconds.<br/>",
  911. "<span class='passed'>",
  912. passed,
  913. "</span> tests of <span class='total'>",
  914. config.stats.all,
  915. "</span> passed, <span class='failed'>",
  916. config.stats.bad,
  917. "</span> failed."
  918. ].join( "" );
  919. if ( banner ) {
  920. banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
  921. }
  922. if ( tests ) {
  923. id( "qunit-testresult" ).innerHTML = html;
  924. }
  925. if ( config.altertitle && typeof document !== "undefined" && document.title ) {
  926. // show ✖ for good, ✔ for bad suite result in title
  927. // use escape sequences in case file gets loaded with non-utf-8-charset
  928. document.title = [
  929. ( config.stats.bad ? "\u2716" : "\u2714" ),
  930. document.title.replace( /^[\u2714\u2716] /i, "" )
  931. ].join( " " );
  932. }
  933. // clear own sessionStorage items if all tests passed
  934. if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
  935. // `key` & `i` initialized at top of scope
  936. for ( i = 0; i < sessionStorage.length; i++ ) {
  937. key = sessionStorage.key( i++ );
  938. if ( key.indexOf( "qunit-test-" ) === 0 ) {
  939. sessionStorage.removeItem( key );
  940. }
  941. }
  942. }
  943. runLoggingCallbacks( "done", QUnit, {
  944. failed: config.stats.bad,
  945. passed: passed,
  946. total: config.stats.all,
  947. runtime: runtime
  948. });
  949. }
  950. /** @return Boolean: true if this test should be ran */
  951. function validTest( test ) {
  952. var include,
  953. filter = config.filter && config.filter.toLowerCase(),
  954. module = config.module && config.module.toLowerCase(),
  955. fullName = (test.module + ": " + test.testName).toLowerCase();
  956. if ( config.testNumber ) {
  957. return test.testNumber === config.testNumber;
  958. }
  959. if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
  960. return false;
  961. }
  962. if ( !filter ) {
  963. return true;
  964. }
  965. include = filter.charAt( 0 ) !== "!";
  966. if ( !include ) {
  967. filter = filter.slice( 1 );
  968. }
  969. // If the filter matches, we need to honour include
  970. if ( fullName.indexOf( filter ) !== -1 ) {
  971. return include;
  972. }
  973. // Otherwise, do the opposite
  974. return !include;
  975. }
  976. // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
  977. // Later Safari and IE10 are supposed to support error.stack as well
  978. // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
  979. function extractStacktrace( e, offset ) {
  980. offset = offset === undefined ? 3 : offset;
  981. var stack, include, i, regex;
  982. if ( e.stacktrace ) {
  983. // Opera
  984. return e.stacktrace.split( "\n" )[ offset + 3 ];
  985. } else if ( e.stack ) {
  986. // Firefox, Chrome
  987. stack = e.stack.split( "\n" );
  988. if (/^error$/i.test( stack[0] ) ) {
  989. stack.shift();
  990. }
  991. if ( fileName ) {
  992. include = [];
  993. for ( i = offset; i < stack.length; i++ ) {
  994. if ( stack[ i ].indexOf( fileName ) != -1 ) {
  995. break;
  996. }
  997. include.push( stack[ i ] );
  998. }
  999. if ( include.length ) {
  1000. return include.join( "\n" );
  1001. }
  1002. }
  1003. return stack[ offset ];
  1004. } else if ( e.sourceURL ) {
  1005. // Safari, PhantomJS
  1006. // hopefully one day Safari provides actual stacktraces
  1007. // exclude useless self-reference for generated Error objects
  1008. if ( /qunit.js$/.test( e.sourceURL ) ) {
  1009. return;
  1010. }
  1011. // for actual exceptions, this is useful
  1012. return e.sourceURL + ":" + e.line;
  1013. }
  1014. }
  1015. function sourceFromStacktrace( offset ) {
  1016. try {
  1017. throw new Error();
  1018. } catch ( e ) {
  1019. return extractStacktrace( e, offset );
  1020. }
  1021. }
  1022. function escapeInnerText( s ) {
  1023. if ( !s ) {
  1024. return "";
  1025. }
  1026. s = s + "";
  1027. return s.replace( /[\&<>]/g, function( s ) {
  1028. switch( s ) {
  1029. case "&": return "&amp;";
  1030. case "<": return "&lt;";
  1031. case ">": return "&gt;";
  1032. default: return s;
  1033. }
  1034. });
  1035. }
  1036. function synchronize( callback, last ) {
  1037. config.queue.push( callback );
  1038. if ( config.autorun && !config.blocking ) {
  1039. process( last );
  1040. }
  1041. }
  1042. function process( last ) {
  1043. function next() {
  1044. process( last );
  1045. }
  1046. var start = new Date().getTime();
  1047. config.depth = config.depth ? config.depth + 1 : 1;
  1048. while ( config.queue.length && !config.blocking ) {
  1049. if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
  1050. config.queue.shift()();
  1051. } else {
  1052. window.setTimeout( next, 13 );
  1053. break;
  1054. }
  1055. }
  1056. config.depth--;
  1057. if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
  1058. done();
  1059. }
  1060. }
  1061. function saveGlobal() {
  1062. config.pollution = [];
  1063. if ( config.noglobals ) {
  1064. for ( var key in window ) {
  1065. // in Opera sometimes DOM element ids show up here, ignore them
  1066. if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) {
  1067. continue;
  1068. }
  1069. config.pollution.push( key );
  1070. }
  1071. }
  1072. }
  1073. function checkPollution( name ) {
  1074. var newGlobals,
  1075. deletedGlobals,
  1076. old = config.pollution;
  1077. saveGlobal();
  1078. newGlobals = diff( config.pollution, old );
  1079. if ( newGlobals.length > 0 ) {
  1080. QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
  1081. }
  1082. deletedGlobals = diff( old, config.pollution );
  1083. if ( deletedGlobals.length > 0 ) {
  1084. QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
  1085. }
  1086. }
  1087. // returns a new Array with the elements that are in a but not in b
  1088. function diff( a, b ) {
  1089. var i, j,
  1090. result = a.slice();
  1091. for ( i = 0; i < result.length; i++ ) {
  1092. for ( j = 0; j < b.length; j++ ) {
  1093. if ( result[i] === b[j] ) {
  1094. result.splice( i, 1 );
  1095. i--;
  1096. break;
  1097. }
  1098. }
  1099. }
  1100. return result;
  1101. }
  1102. function extend( a, b ) {
  1103. for ( var prop in b ) {
  1104. if ( b[ prop ] === undefined ) {
  1105. delete a[ prop ];
  1106. // Avoid "Member not found" error in IE8 caused by setting window.constructor
  1107. } else if ( prop !== "constructor" || a !== window ) {
  1108. a[ prop ] = b[ prop ];
  1109. }
  1110. }
  1111. return a;
  1112. }
  1113. function addEvent( elem, type, fn ) {
  1114. if ( elem.addEventListener ) {
  1115. elem.addEventListener( type, fn, false );
  1116. } else if ( elem.attachEvent ) {
  1117. elem.attachEvent( "on" + type, fn );
  1118. } else {
  1119. fn();
  1120. }
  1121. }
  1122. function id( name ) {
  1123. return !!( typeof document !== "undefined" && document && document.getElementById ) &&
  1124. document.getElementById( name );
  1125. }
  1126. function registerLoggingCallback( key ) {
  1127. return function( callback ) {
  1128. config[key].push( callback );
  1129. };
  1130. }
  1131. // Supports deprecated method of completely overwriting logging callbacks
  1132. function runLoggingCallbacks( key, scope, args ) {
  1133. //debugger;
  1134. var i, callbacks;
  1135. if ( QUnit.hasOwnProperty( key ) ) {
  1136. QUnit[ key ].call(scope, args );
  1137. } else {
  1138. callbacks = config[ key ];
  1139. for ( i = 0; i < callbacks.length; i++ ) {
  1140. callbacks[ i ].call( scope, args );
  1141. }
  1142. }
  1143. }
  1144. // Test for equality any JavaScript type.
  1145. // Author: Philippe Rathé <prathe@gmail.com>
  1146. QUnit.equiv = (function() {
  1147. // Call the o related callback with the given arguments.
  1148. function bindCallbacks( o, callbacks, args ) {
  1149. var prop = QUnit.objectType( o );
  1150. if ( prop ) {
  1151. if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
  1152. return callbacks[ prop ].apply( callbacks, args );
  1153. } else {
  1154. return callbacks[ prop ]; // or undefined
  1155. }
  1156. }
  1157. }
  1158. // the real equiv function
  1159. var innerEquiv,
  1160. // stack to decide between skip/abort functions
  1161. callers = [],
  1162. // stack to avoiding loops from circular referencing
  1163. parents = [],
  1164. getProto = Object.getPrototypeOf || function ( obj ) {
  1165. return obj.__proto__;
  1166. },
  1167. callbacks = (function () {
  1168. // for string, boolean, number and null
  1169. function useStrictEquality( b, a ) {
  1170. if ( b instanceof a.constructor || a instanceof b.constructor ) {
  1171. // to catch short annotaion VS 'new' annotation of a
  1172. // declaration
  1173. // e.g. var i = 1;
  1174. // var j = new Number(1);
  1175. return a == b;
  1176. } else {
  1177. return a === b;
  1178. }
  1179. }
  1180. return {
  1181. "string": useStrictEquality,
  1182. "boolean": useStrictEquality,
  1183. "number": useStrictEquality,
  1184. "null": useStrictEquality,
  1185. "undefined": useStrictEquality,
  1186. "nan": function( b ) {
  1187. return isNaN( b );
  1188. },
  1189. "date": function( b, a ) {
  1190. return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
  1191. },
  1192. "regexp": function( b, a ) {
  1193. return QUnit.objectType( b ) === "regexp" &&
  1194. // the regex itself
  1195. a.source === b.source &&
  1196. // and its modifers
  1197. a.global === b.global &&
  1198. // (gmi) ...
  1199. a.ignoreCase === b.ignoreCase &&
  1200. a.multiline === b.multiline;
  1201. },
  1202. // - skip when the property is a method of an instance (OOP)
  1203. // - abort otherwise,
  1204. // initial === would have catch identical references anyway
  1205. "function": function() {
  1206. var caller = callers[callers.length - 1];
  1207. return caller !== Object && typeof caller !== "undefined";
  1208. },
  1209. "array": function( b, a ) {
  1210. var i, j, len, loop;
  1211. // b could be an object literal here
  1212. if ( QUnit.objectType( b ) !== "array" ) {
  1213. return false;
  1214. }
  1215. len = a.length;
  1216. if ( len !== b.length ) {
  1217. // safe and faster
  1218. return false;
  1219. }
  1220. // track reference to avoid circular references
  1221. parents.push( a );
  1222. for ( i = 0; i < len; i++ ) {
  1223. loop = false;
  1224. for ( j = 0; j < parents.length; j++ ) {
  1225. if ( parents[j] === a[i] ) {
  1226. loop = true;// dont rewalk array
  1227. }
  1228. }
  1229. if ( !loop && !innerEquiv(a[i], b[i]) ) {
  1230. parents.pop();
  1231. return false;
  1232. }
  1233. }
  1234. parents.pop();
  1235. return true;
  1236. },
  1237. "object": function( b, a ) {
  1238. var i, j, loop,
  1239. // Default to true
  1240. eq = true,
  1241. aProperties = [],
  1242. bProperties = [];
  1243. // comparing constructors is more strict than using
  1244. // instanceof
  1245. if ( a.constructor !== b.constructor ) {
  1246. // Allow objects with no prototype to be equivalent to
  1247. // objects with Object as their constructor.
  1248. if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
  1249. ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
  1250. return false;
  1251. }
  1252. }
  1253. // stack constructor before traversing properties
  1254. callers.push( a.constructor );
  1255. // track reference to avoid circular references
  1256. parents.push( a );
  1257. for ( i in a ) { // be strict: don't ensures hasOwnProperty
  1258. // and go deep
  1259. loop = false;
  1260. for ( j = 0; j < parents.length; j++ ) {
  1261. if ( parents[j] === a[i] ) {
  1262. // don't go down the same path twice
  1263. loop = true;
  1264. }
  1265. }
  1266. aProperties.push(i); // collect a's properties
  1267. if (!loop && !innerEquiv( a[i], b[i] ) ) {
  1268. eq = false;
  1269. break;
  1270. }
  1271. }
  1272. callers.pop(); // unstack, we are done
  1273. parents.pop();
  1274. for ( i in b ) {
  1275. bProperties.push( i ); // collect b's properties
  1276. }
  1277. // Ensures identical properties name
  1278. return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
  1279. }
  1280. };
  1281. }());
  1282. innerEquiv = function() { // can take multiple arguments
  1283. var args = [].slice.apply( arguments );
  1284. if ( args.length < 2 ) {
  1285. return true; // end transition
  1286. }
  1287. return (function( a, b ) {
  1288. if ( a === b ) {
  1289. return true; // catch the most you can
  1290. } else if ( a === null || b === null || typeof a === "undefined" ||
  1291. typeof b === "undefined" ||
  1292. QUnit.objectType(a) !== QUnit.objectType(b) ) {
  1293. return false; // don't lose time with error prone cases
  1294. } else {
  1295. return bindCallbacks(a, callbacks, [ b, a ]);
  1296. }
  1297. // apply transition with (1..n) arguments
  1298. }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) );
  1299. };
  1300. return innerEquiv;
  1301. }());
  1302. /**
  1303. * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
  1304. * http://flesler.blogspot.com Licensed under BSD
  1305. * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
  1306. *
  1307. * @projectDescription Advanced and extensible data dumping for Javascript.
  1308. * @version 1.0.0
  1309. * @author Ariel Flesler
  1310. * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
  1311. */
  1312. QUnit.jsDump = (function() {
  1313. function quote( str ) {
  1314. return '"' + str.toString().replace( /"/g, '\\"' ) + '"';
  1315. }
  1316. function literal( o ) {
  1317. return o + "";
  1318. }
  1319. function join( pre, arr, post ) {
  1320. var s = jsDump.separator(),
  1321. base = jsDump.indent(),
  1322. inner = jsDump.indent(1);
  1323. if ( arr.join ) {
  1324. arr = arr.join( "," + s + inner );
  1325. }
  1326. if ( !arr ) {
  1327. return pre + post;
  1328. }
  1329. return [ pre, inner + arr, base + post ].join(s);
  1330. }
  1331. function array( arr, stack ) {
  1332. var i = arr.length, ret = new Array(i);
  1333. this.up();
  1334. while ( i-- ) {
  1335. ret[i] = this.parse( arr[i] , undefined , stack);
  1336. }
  1337. this.down();
  1338. return join( "[", ret, "]" );
  1339. }
  1340. var reName = /^function (\w+)/,
  1341. jsDump = {
  1342. parse: function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance
  1343. stack = stack || [ ];
  1344. var inStack, res,
  1345. parser = this.parsers[ type || this.typeOf(obj) ];
  1346. type = typeof parser;
  1347. inStack = inArray( obj, stack );
  1348. if ( inStack != -1 ) {
  1349. return "recursion(" + (inStack - stack.length) + ")";
  1350. }
  1351. //else
  1352. if ( type == "function" ) {
  1353. stack.push( obj );
  1354. res = parser.call( this, obj, stack );
  1355. stack.pop();
  1356. return res;
  1357. }
  1358. // else
  1359. return ( type == "string" ) ? parser : this.parsers.error;
  1360. },
  1361. typeOf: function( obj ) {
  1362. var type;
  1363. if ( obj === null ) {
  1364. type = "null";
  1365. } else if ( typeof obj === "undefined" ) {
  1366. type = "undefined";
  1367. } else if ( QUnit.is( "regexp", obj) ) {
  1368. type = "regexp";
  1369. } else if ( QUnit.is( "date", obj) ) {
  1370. type = "date";
  1371. } else if ( QUnit.is( "function", obj) ) {
  1372. type = "function";
  1373. } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
  1374. type = "window";
  1375. } else if ( obj.nodeType === 9 ) {
  1376. type = "document";
  1377. } else if ( obj.nodeType ) {
  1378. type = "node";
  1379. } else if (
  1380. // native arrays
  1381. toString.call( obj ) === "[object Array]" ||
  1382. // NodeList objects
  1383. ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
  1384. ) {
  1385. type = "array";
  1386. } else {
  1387. type = typeof obj;
  1388. }
  1389. return type;
  1390. },
  1391. separator: function() {
  1392. return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
  1393. },
  1394. indent: function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
  1395. if ( !this.multiline ) {
  1396. return "";
  1397. }
  1398. var chr = this.indentChar;
  1399. if ( this.HTML ) {
  1400. chr = chr.replace( /\t/g, " " ).replace( / /g, "&nbsp;" );
  1401. }
  1402. return new Array( this._depth_ + (extra||0) ).join(chr);
  1403. },
  1404. up: function( a ) {
  1405. this._depth_ += a || 1;
  1406. },
  1407. down: function( a ) {
  1408. this._depth_ -= a || 1;
  1409. },
  1410. setParser: function( name, parser ) {
  1411. this.parsers[name] = parser;
  1412. },
  1413. // The next 3 are exposed so you can use them
  1414. quote: quote,
  1415. literal: literal,
  1416. join: join,
  1417. //
  1418. _depth_: 1,
  1419. // This is the list of parsers, to modify them, use jsDump.setParser
  1420. parsers: {
  1421. window: "[Window]",
  1422. document: "[Document]",
  1423. error: "[ERROR]", //when no parser is found, shouldn"t happen
  1424. unknown: "[Unknown]",
  1425. "null": "null",
  1426. "undefined": "undefined",
  1427. "function": function( fn ) {
  1428. var ret = "function",
  1429. name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];//functions never have name in IE
  1430. if ( name ) {
  1431. ret += " " + name;
  1432. }
  1433. ret += "( ";
  1434. ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
  1435. return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
  1436. },
  1437. array: array,
  1438. nodelist: array,
  1439. "arguments": array,
  1440. object: function( map, stack ) {
  1441. var ret = [ ], keys, key, val, i;
  1442. QUnit.jsDump.up();
  1443. if ( Object.keys ) {
  1444. keys = Object.keys( map );
  1445. } else {
  1446. keys = [];
  1447. for ( key in map ) {
  1448. keys.push( key );
  1449. }
  1450. }
  1451. keys.sort();
  1452. for ( i = 0; i < keys.length; i++ ) {
  1453. key = keys[ i ];
  1454. val = map[ key ];
  1455. ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
  1456. }
  1457. QUnit.jsDump.down();
  1458. return join( "{", ret, "}" );
  1459. },
  1460. node: function( node ) {
  1461. var a, val,
  1462. open = QUnit.jsDump.HTML ? "&lt;" : "<",
  1463. close = QUnit.jsDump.HTML ? "&gt;" : ">",
  1464. tag = node.nodeName.toLowerCase(),
  1465. ret = open + tag;
  1466. for ( a in QUnit.jsDump.DOMAttrs ) {
  1467. val = node[ QUnit.jsDump.DOMAttrs[a] ];
  1468. if ( val ) {
  1469. ret += " " + a + "=" + QUnit.jsDump.parse( val, "attribute" );
  1470. }
  1471. }
  1472. return ret + close + open + "/" + tag + close;
  1473. },
  1474. functionArgs: function( fn ) {//function calls it internally, it's the arguments part of the function
  1475. var args,
  1476. l = fn.length;
  1477. if ( !l ) {
  1478. return "";
  1479. }
  1480. args = new Array(l);
  1481. while ( l-- ) {
  1482. args[l] = String.fromCharCode(97+l);//97 is 'a'
  1483. }
  1484. return " " + args.join( ", " ) + " ";
  1485. },
  1486. key: quote, //object calls it internally, the key part of an item in a map
  1487. functionCode: "[code]", //function calls it internally, it's the content of the function
  1488. attribute: quote, //node calls it internally, it's an html attribute value
  1489. string: quote,
  1490. date: quote,
  1491. regexp: literal, //regex
  1492. number: literal,
  1493. "boolean": literal
  1494. },
  1495. DOMAttrs: {
  1496. //attributes to dump from nodes, name=>realName
  1497. id: "id",
  1498. name: "name",
  1499. "class": "className"
  1500. },
  1501. HTML: false,//if true, entities are escaped ( <, >, \t, space and \n )
  1502. indentChar: " ",//indentation unit
  1503. multiline: true //if true, items in a collection, are separated by a \n, else just a space.
  1504. };
  1505. return jsDump;
  1506. }());
  1507. // from Sizzle.js
  1508. function getText( elems ) {
  1509. var i, elem,
  1510. ret = "";
  1511. for ( i = 0; elems[i]; i++ ) {
  1512. elem = elems[i];
  1513. // Get the text from text nodes and CDATA nodes
  1514. if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
  1515. ret += elem.nodeValue;
  1516. // Traverse everything else, except comment nodes
  1517. } else if ( elem.nodeType !== 8 ) {
  1518. ret += getText( elem.childNodes );
  1519. }
  1520. }
  1521. return ret;
  1522. }
  1523. // from jquery.js
  1524. function inArray( elem, array ) {
  1525. if ( array.indexOf ) {
  1526. return array.indexOf( elem );
  1527. }
  1528. for ( var i = 0, length = array.length; i < length; i++ ) {
  1529. if ( array[ i ] === elem ) {
  1530. return i;
  1531. }
  1532. }
  1533. return -1;
  1534. }
  1535. /*
  1536. * Javascript Diff Algorithm
  1537. * By John Resig (http://ejohn.org/)
  1538. * Modified by Chu Alan "sprite"
  1539. *
  1540. * Released under the MIT license.
  1541. *
  1542. * More Info:
  1543. * http://ejohn.org/projects/javascript-diff-algorithm/
  1544. *
  1545. * Usage: QUnit.diff(expected, actual)
  1546. *
  1547. * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
  1548. */
  1549. QUnit.diff = (function() {
  1550. function diff( o, n ) {
  1551. var i,
  1552. ns = {},
  1553. os = {};
  1554. for ( i = 0; i < n.length; i++ ) {
  1555. if ( ns[ n[i] ] == null ) {
  1556. ns[ n[i] ] = {
  1557. rows: [],
  1558. o: null
  1559. };
  1560. }
  1561. ns[ n[i] ].rows.push( i );
  1562. }
  1563. for ( i = 0; i < o.length; i++ ) {
  1564. if ( os[ o[i] ] == null ) {
  1565. os[ o[i] ] = {
  1566. rows: [],
  1567. n: null
  1568. };
  1569. }
  1570. os[ o[i] ].rows.push( i );
  1571. }
  1572. for ( i in ns ) {
  1573. if ( !hasOwn.call( ns, i ) ) {
  1574. continue;
  1575. }
  1576. if ( ns[i].rows.length == 1 && typeof os[i] != "undefined" && os[i].rows.length == 1 ) {
  1577. n[ ns[i].rows[0] ] = {
  1578. text: n[ ns[i].rows[0] ],
  1579. row: os[i].rows[0]
  1580. };
  1581. o[ os[i].rows[0] ] = {
  1582. text: o[ os[i].rows[0] ],
  1583. row: ns[i].rows[0]
  1584. };
  1585. }
  1586. }
  1587. for ( i = 0; i < n.length - 1; i++ ) {
  1588. if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
  1589. n[ i + 1 ] == o[ n[i].row + 1 ] ) {
  1590. n[ i + 1 ] = {
  1591. text: n[ i + 1 ],
  1592. row: n[i].row + 1
  1593. };
  1594. o[ n[i].row + 1 ] = {
  1595. text: o[ n[i].row + 1 ],
  1596. row: i + 1
  1597. };
  1598. }
  1599. }
  1600. for ( i = n.length - 1; i > 0; i-- ) {
  1601. if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
  1602. n[ i - 1 ] == o[ n[i].row - 1 ]) {
  1603. n[ i - 1 ] = {
  1604. text: n[ i - 1 ],
  1605. row: n[i].row - 1
  1606. };
  1607. o[ n[i].row - 1 ] = {
  1608. text: o[ n[i].row - 1 ],
  1609. row: i - 1
  1610. };
  1611. }
  1612. }
  1613. return {
  1614. o: o,
  1615. n: n
  1616. };
  1617. }
  1618. return function( o, n ) {
  1619. o = o.replace( /\s+$/, "" );
  1620. n = n.replace( /\s+$/, "" );
  1621. var i, pre,
  1622. str = "",
  1623. out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
  1624. oSpace = o.match(/\s+/g),
  1625. nSpace = n.match(/\s+/g);
  1626. if ( oSpace == null ) {
  1627. oSpace = [ " " ];
  1628. }
  1629. else {
  1630. oSpace.push( " " );
  1631. }
  1632. if ( nSpace == null ) {
  1633. nSpace = [ " " ];
  1634. }
  1635. else {
  1636. nSpace.push( " " );
  1637. }
  1638. if ( out.n.length === 0 ) {
  1639. for ( i = 0; i < out.o.length; i++ ) {
  1640. str += "<del>" + out.o[i] + oSpace[i] + "</del>";
  1641. }
  1642. }
  1643. else {
  1644. if ( out.n[0].text == null ) {
  1645. for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
  1646. str += "<del>" + out.o[n] + oSpace[n] + "</del>";
  1647. }
  1648. }
  1649. for ( i = 0; i < out.n.length; i++ ) {
  1650. if (out.n[i].text == null) {
  1651. str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
  1652. }
  1653. else {
  1654. // `pre` initialized at top of scope
  1655. pre = "";
  1656. for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
  1657. pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
  1658. }
  1659. str += " " + out.n[i].text + nSpace[i] + pre;
  1660. }
  1661. }
  1662. }
  1663. return str;
  1664. };
  1665. }());
  1666. // for CommonJS enviroments, export everything
  1667. if ( typeof exports !== "undefined" ) {
  1668. extend(exports, QUnit);
  1669. }
  1670. // get at whatever the global object is, like window in browsers
  1671. }( (function() {return this;}.call()) ));