qunit-1.14.0.js 60 KB

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