qunit.js 148 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260
  1. /*!
  2. * QUnit 2.6.2
  3. * https://qunitjs.com/
  4. *
  5. * Copyright jQuery Foundation and other contributors
  6. * Released under the MIT license
  7. * https://jquery.org/license
  8. *
  9. * Date: 2018-08-19T19:37Z
  10. */
  11. (function (global$1) {
  12. 'use strict';
  13. global$1 = global$1 && global$1.hasOwnProperty('default') ? global$1['default'] : global$1;
  14. var window = global$1.window;
  15. var self$1 = global$1.self;
  16. var console = global$1.console;
  17. var setTimeout = global$1.setTimeout;
  18. var clearTimeout = global$1.clearTimeout;
  19. var document = window && window.document;
  20. var navigator = window && window.navigator;
  21. var localSessionStorage = function () {
  22. var x = "qunit-test-string";
  23. try {
  24. global$1.sessionStorage.setItem(x, x);
  25. global$1.sessionStorage.removeItem(x);
  26. return global$1.sessionStorage;
  27. } catch (e) {
  28. return undefined;
  29. }
  30. }();
  31. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  32. return typeof obj;
  33. } : function (obj) {
  34. return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  35. };
  36. var classCallCheck = function (instance, Constructor) {
  37. if (!(instance instanceof Constructor)) {
  38. throw new TypeError("Cannot call a class as a function");
  39. }
  40. };
  41. var createClass = function () {
  42. function defineProperties(target, props) {
  43. for (var i = 0; i < props.length; i++) {
  44. var descriptor = props[i];
  45. descriptor.enumerable = descriptor.enumerable || false;
  46. descriptor.configurable = true;
  47. if ("value" in descriptor) descriptor.writable = true;
  48. Object.defineProperty(target, descriptor.key, descriptor);
  49. }
  50. }
  51. return function (Constructor, protoProps, staticProps) {
  52. if (protoProps) defineProperties(Constructor.prototype, protoProps);
  53. if (staticProps) defineProperties(Constructor, staticProps);
  54. return Constructor;
  55. };
  56. }();
  57. var toConsumableArray = function (arr) {
  58. if (Array.isArray(arr)) {
  59. for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
  60. return arr2;
  61. } else {
  62. return Array.from(arr);
  63. }
  64. };
  65. var toString = Object.prototype.toString;
  66. var hasOwn = Object.prototype.hasOwnProperty;
  67. var now = Date.now || function () {
  68. return new Date().getTime();
  69. };
  70. var defined = {
  71. document: window && window.document !== undefined,
  72. setTimeout: setTimeout !== undefined
  73. };
  74. // Returns a new Array with the elements that are in a but not in b
  75. function diff(a, b) {
  76. var i,
  77. j,
  78. result = a.slice();
  79. for (i = 0; i < result.length; i++) {
  80. for (j = 0; j < b.length; j++) {
  81. if (result[i] === b[j]) {
  82. result.splice(i, 1);
  83. i--;
  84. break;
  85. }
  86. }
  87. }
  88. return result;
  89. }
  90. /**
  91. * Determines whether an element exists in a given array or not.
  92. *
  93. * @method inArray
  94. * @param {Any} elem
  95. * @param {Array} array
  96. * @return {Boolean}
  97. */
  98. function inArray(elem, array) {
  99. return array.indexOf(elem) !== -1;
  100. }
  101. /**
  102. * Makes a clone of an object using only Array or Object as base,
  103. * and copies over the own enumerable properties.
  104. *
  105. * @param {Object} obj
  106. * @return {Object} New object with only the own properties (recursively).
  107. */
  108. function objectValues(obj) {
  109. var key,
  110. val,
  111. vals = is("array", obj) ? [] : {};
  112. for (key in obj) {
  113. if (hasOwn.call(obj, key)) {
  114. val = obj[key];
  115. vals[key] = val === Object(val) ? objectValues(val) : val;
  116. }
  117. }
  118. return vals;
  119. }
  120. function extend(a, b, undefOnly) {
  121. for (var prop in b) {
  122. if (hasOwn.call(b, prop)) {
  123. if (b[prop] === undefined) {
  124. delete a[prop];
  125. } else if (!(undefOnly && typeof a[prop] !== "undefined")) {
  126. a[prop] = b[prop];
  127. }
  128. }
  129. }
  130. return a;
  131. }
  132. function objectType(obj) {
  133. if (typeof obj === "undefined") {
  134. return "undefined";
  135. }
  136. // Consider: typeof null === object
  137. if (obj === null) {
  138. return "null";
  139. }
  140. var match = toString.call(obj).match(/^\[object\s(.*)\]$/),
  141. type = match && match[1];
  142. switch (type) {
  143. case "Number":
  144. if (isNaN(obj)) {
  145. return "nan";
  146. }
  147. return "number";
  148. case "String":
  149. case "Boolean":
  150. case "Array":
  151. case "Set":
  152. case "Map":
  153. case "Date":
  154. case "RegExp":
  155. case "Function":
  156. case "Symbol":
  157. return type.toLowerCase();
  158. default:
  159. return typeof obj === "undefined" ? "undefined" : _typeof(obj);
  160. }
  161. }
  162. // Safe object type checking
  163. function is(type, obj) {
  164. return objectType(obj) === type;
  165. }
  166. // Based on Java's String.hashCode, a simple but not
  167. // rigorously collision resistant hashing function
  168. function generateHash(module, testName) {
  169. var str = module + "\x1C" + testName;
  170. var hash = 0;
  171. for (var i = 0; i < str.length; i++) {
  172. hash = (hash << 5) - hash + str.charCodeAt(i);
  173. hash |= 0;
  174. }
  175. // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
  176. // strictly necessary but increases user understanding that the id is a SHA-like hash
  177. var hex = (0x100000000 + hash).toString(16);
  178. if (hex.length < 8) {
  179. hex = "0000000" + hex;
  180. }
  181. return hex.slice(-8);
  182. }
  183. // Test for equality any JavaScript type.
  184. // Authors: Philippe Rathé <prathe@gmail.com>, David Chan <david@troi.org>
  185. var equiv = (function () {
  186. // Value pairs queued for comparison. Used for breadth-first processing order, recursion
  187. // detection and avoiding repeated comparison (see below for details).
  188. // Elements are { a: val, b: val }.
  189. var pairs = [];
  190. var getProto = Object.getPrototypeOf || function (obj) {
  191. return obj.__proto__;
  192. };
  193. function useStrictEquality(a, b) {
  194. // This only gets called if a and b are not strict equal, and is used to compare on
  195. // the primitive values inside object wrappers. For example:
  196. // `var i = 1;`
  197. // `var j = new Number(1);`
  198. // Neither a nor b can be null, as a !== b and they have the same type.
  199. if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") {
  200. a = a.valueOf();
  201. }
  202. if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") {
  203. b = b.valueOf();
  204. }
  205. return a === b;
  206. }
  207. function compareConstructors(a, b) {
  208. var protoA = getProto(a);
  209. var protoB = getProto(b);
  210. // Comparing constructors is more strict than using `instanceof`
  211. if (a.constructor === b.constructor) {
  212. return true;
  213. }
  214. // Ref #851
  215. // If the obj prototype descends from a null constructor, treat it
  216. // as a null prototype.
  217. if (protoA && protoA.constructor === null) {
  218. protoA = null;
  219. }
  220. if (protoB && protoB.constructor === null) {
  221. protoB = null;
  222. }
  223. // Allow objects with no prototype to be equivalent to
  224. // objects with Object as their constructor.
  225. if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) {
  226. return true;
  227. }
  228. return false;
  229. }
  230. function getRegExpFlags(regexp) {
  231. return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
  232. }
  233. function isContainer(val) {
  234. return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1;
  235. }
  236. function breadthFirstCompareChild(a, b) {
  237. // If a is a container not reference-equal to b, postpone the comparison to the
  238. // end of the pairs queue -- unless (a, b) has been seen before, in which case skip
  239. // over the pair.
  240. if (a === b) {
  241. return true;
  242. }
  243. if (!isContainer(a)) {
  244. return typeEquiv(a, b);
  245. }
  246. if (pairs.every(function (pair) {
  247. return pair.a !== a || pair.b !== b;
  248. })) {
  249. // Not yet started comparing this pair
  250. pairs.push({ a: a, b: b });
  251. }
  252. return true;
  253. }
  254. var callbacks = {
  255. "string": useStrictEquality,
  256. "boolean": useStrictEquality,
  257. "number": useStrictEquality,
  258. "null": useStrictEquality,
  259. "undefined": useStrictEquality,
  260. "symbol": useStrictEquality,
  261. "date": useStrictEquality,
  262. "nan": function nan() {
  263. return true;
  264. },
  265. "regexp": function regexp(a, b) {
  266. return a.source === b.source &&
  267. // Include flags in the comparison
  268. getRegExpFlags(a) === getRegExpFlags(b);
  269. },
  270. // abort (identical references / instance methods were skipped earlier)
  271. "function": function _function() {
  272. return false;
  273. },
  274. "array": function array(a, b) {
  275. var i, len;
  276. len = a.length;
  277. if (len !== b.length) {
  278. // Safe and faster
  279. return false;
  280. }
  281. for (i = 0; i < len; i++) {
  282. // Compare non-containers; queue non-reference-equal containers
  283. if (!breadthFirstCompareChild(a[i], b[i])) {
  284. return false;
  285. }
  286. }
  287. return true;
  288. },
  289. // Define sets a and b to be equivalent if for each element aVal in a, there
  290. // is some element bVal in b such that aVal and bVal are equivalent. Element
  291. // repetitions are not counted, so these are equivalent:
  292. // a = new Set( [ {}, [], [] ] );
  293. // b = new Set( [ {}, {}, [] ] );
  294. "set": function set$$1(a, b) {
  295. var innerEq,
  296. outerEq = true;
  297. if (a.size !== b.size) {
  298. // This optimization has certain quirks because of the lack of
  299. // repetition counting. For instance, adding the same
  300. // (reference-identical) element to two equivalent sets can
  301. // make them non-equivalent.
  302. return false;
  303. }
  304. a.forEach(function (aVal) {
  305. // Short-circuit if the result is already known. (Using for...of
  306. // with a break clause would be cleaner here, but it would cause
  307. // a syntax error on older Javascript implementations even if
  308. // Set is unused)
  309. if (!outerEq) {
  310. return;
  311. }
  312. innerEq = false;
  313. b.forEach(function (bVal) {
  314. var parentPairs;
  315. // Likewise, short-circuit if the result is already known
  316. if (innerEq) {
  317. return;
  318. }
  319. // Swap out the global pairs list, as the nested call to
  320. // innerEquiv will clobber its contents
  321. parentPairs = pairs;
  322. if (innerEquiv(bVal, aVal)) {
  323. innerEq = true;
  324. }
  325. // Replace the global pairs list
  326. pairs = parentPairs;
  327. });
  328. if (!innerEq) {
  329. outerEq = false;
  330. }
  331. });
  332. return outerEq;
  333. },
  334. // Define maps a and b to be equivalent if for each key-value pair (aKey, aVal)
  335. // in a, there is some key-value pair (bKey, bVal) in b such that
  336. // [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not
  337. // counted, so these are equivalent:
  338. // a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] );
  339. // b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] );
  340. "map": function map(a, b) {
  341. var innerEq,
  342. outerEq = true;
  343. if (a.size !== b.size) {
  344. // This optimization has certain quirks because of the lack of
  345. // repetition counting. For instance, adding the same
  346. // (reference-identical) key-value pair to two equivalent maps
  347. // can make them non-equivalent.
  348. return false;
  349. }
  350. a.forEach(function (aVal, aKey) {
  351. // Short-circuit if the result is already known. (Using for...of
  352. // with a break clause would be cleaner here, but it would cause
  353. // a syntax error on older Javascript implementations even if
  354. // Map is unused)
  355. if (!outerEq) {
  356. return;
  357. }
  358. innerEq = false;
  359. b.forEach(function (bVal, bKey) {
  360. var parentPairs;
  361. // Likewise, short-circuit if the result is already known
  362. if (innerEq) {
  363. return;
  364. }
  365. // Swap out the global pairs list, as the nested call to
  366. // innerEquiv will clobber its contents
  367. parentPairs = pairs;
  368. if (innerEquiv([bVal, bKey], [aVal, aKey])) {
  369. innerEq = true;
  370. }
  371. // Replace the global pairs list
  372. pairs = parentPairs;
  373. });
  374. if (!innerEq) {
  375. outerEq = false;
  376. }
  377. });
  378. return outerEq;
  379. },
  380. "object": function object(a, b) {
  381. var i,
  382. aProperties = [],
  383. bProperties = [];
  384. if (compareConstructors(a, b) === false) {
  385. return false;
  386. }
  387. // Be strict: don't ensure hasOwnProperty and go deep
  388. for (i in a) {
  389. // Collect a's properties
  390. aProperties.push(i);
  391. // Skip OOP methods that look the same
  392. if (a.constructor !== Object && typeof a.constructor !== "undefined" && typeof a[i] === "function" && typeof b[i] === "function" && a[i].toString() === b[i].toString()) {
  393. continue;
  394. }
  395. // Compare non-containers; queue non-reference-equal containers
  396. if (!breadthFirstCompareChild(a[i], b[i])) {
  397. return false;
  398. }
  399. }
  400. for (i in b) {
  401. // Collect b's properties
  402. bProperties.push(i);
  403. }
  404. // Ensures identical properties name
  405. return typeEquiv(aProperties.sort(), bProperties.sort());
  406. }
  407. };
  408. function typeEquiv(a, b) {
  409. var type = objectType(a);
  410. // Callbacks for containers will append to the pairs queue to achieve breadth-first
  411. // search order. The pairs queue is also used to avoid reprocessing any pair of
  412. // containers that are reference-equal to a previously visited pair (a special case
  413. // this being recursion detection).
  414. //
  415. // Because of this approach, once typeEquiv returns a false value, it should not be
  416. // called again without clearing the pair queue else it may wrongly report a visited
  417. // pair as being equivalent.
  418. return objectType(b) === type && callbacks[type](a, b);
  419. }
  420. function innerEquiv(a, b) {
  421. var i, pair;
  422. // We're done when there's nothing more to compare
  423. if (arguments.length < 2) {
  424. return true;
  425. }
  426. // Clear the global pair queue and add the top-level values being compared
  427. pairs = [{ a: a, b: b }];
  428. for (i = 0; i < pairs.length; i++) {
  429. pair = pairs[i];
  430. // Perform type-specific comparison on any pairs that are not strictly
  431. // equal. For container types, that comparison will postpone comparison
  432. // of any sub-container pair to the end of the pair queue. This gives
  433. // breadth-first search order. It also avoids the reprocessing of
  434. // reference-equal siblings, cousins etc, which can have a significant speed
  435. // impact when comparing a container of small objects each of which has a
  436. // reference to the same (singleton) large object.
  437. if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) {
  438. return false;
  439. }
  440. }
  441. // ...across all consecutive argument pairs
  442. return arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1));
  443. }
  444. return function () {
  445. var result = innerEquiv.apply(undefined, arguments);
  446. // Release any retained objects
  447. pairs.length = 0;
  448. return result;
  449. };
  450. })();
  451. /**
  452. * Config object: Maintain internal state
  453. * Later exposed as QUnit.config
  454. * `config` initialized at top of scope
  455. */
  456. var config = {
  457. // The queue of tests to run
  458. queue: [],
  459. // Block until document ready
  460. blocking: true,
  461. // By default, run previously failed tests first
  462. // very useful in combination with "Hide passed tests" checked
  463. reorder: true,
  464. // By default, modify document.title when suite is done
  465. altertitle: true,
  466. // HTML Reporter: collapse every test except the first failing test
  467. // If false, all failing tests will be expanded
  468. collapse: true,
  469. // By default, scroll to top of the page when suite is done
  470. scrolltop: true,
  471. // Depth up-to which object will be dumped
  472. maxDepth: 5,
  473. // When enabled, all tests must call expect()
  474. requireExpects: false,
  475. // Placeholder for user-configurable form-exposed URL parameters
  476. urlConfig: [],
  477. // Set of all modules.
  478. modules: [],
  479. // The first unnamed module
  480. currentModule: {
  481. name: "",
  482. tests: [],
  483. childModules: [],
  484. testsRun: 0,
  485. unskippedTestsRun: 0,
  486. hooks: {
  487. before: [],
  488. beforeEach: [],
  489. afterEach: [],
  490. after: []
  491. }
  492. },
  493. callbacks: {},
  494. // The storage module to use for reordering tests
  495. storage: localSessionStorage
  496. };
  497. // take a predefined QUnit.config and extend the defaults
  498. var globalConfig = window && window.QUnit && window.QUnit.config;
  499. // only extend the global config if there is no QUnit overload
  500. if (window && window.QUnit && !window.QUnit.version) {
  501. extend(config, globalConfig);
  502. }
  503. // Push a loose unnamed module to the modules collection
  504. config.modules.push(config.currentModule);
  505. // Based on jsDump by Ariel Flesler
  506. // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
  507. var dump = (function () {
  508. function quote(str) {
  509. return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
  510. }
  511. function literal(o) {
  512. return o + "";
  513. }
  514. function join(pre, arr, post) {
  515. var s = dump.separator(),
  516. base = dump.indent(),
  517. inner = dump.indent(1);
  518. if (arr.join) {
  519. arr = arr.join("," + s + inner);
  520. }
  521. if (!arr) {
  522. return pre + post;
  523. }
  524. return [pre, inner + arr, base + post].join(s);
  525. }
  526. function array(arr, stack) {
  527. var i = arr.length,
  528. ret = new Array(i);
  529. if (dump.maxDepth && dump.depth > dump.maxDepth) {
  530. return "[object Array]";
  531. }
  532. this.up();
  533. while (i--) {
  534. ret[i] = this.parse(arr[i], undefined, stack);
  535. }
  536. this.down();
  537. return join("[", ret, "]");
  538. }
  539. function isArray(obj) {
  540. return (
  541. //Native Arrays
  542. toString.call(obj) === "[object Array]" ||
  543. // NodeList objects
  544. typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
  545. );
  546. }
  547. var reName = /^function (\w+)/,
  548. dump = {
  549. // The objType is used mostly internally, you can fix a (custom) type in advance
  550. parse: function parse(obj, objType, stack) {
  551. stack = stack || [];
  552. var res,
  553. parser,
  554. parserType,
  555. objIndex = stack.indexOf(obj);
  556. if (objIndex !== -1) {
  557. return "recursion(" + (objIndex - stack.length) + ")";
  558. }
  559. objType = objType || this.typeOf(obj);
  560. parser = this.parsers[objType];
  561. parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser);
  562. if (parserType === "function") {
  563. stack.push(obj);
  564. res = parser.call(this, obj, stack);
  565. stack.pop();
  566. return res;
  567. }
  568. return parserType === "string" ? parser : this.parsers.error;
  569. },
  570. typeOf: function typeOf(obj) {
  571. var type;
  572. if (obj === null) {
  573. type = "null";
  574. } else if (typeof obj === "undefined") {
  575. type = "undefined";
  576. } else if (is("regexp", obj)) {
  577. type = "regexp";
  578. } else if (is("date", obj)) {
  579. type = "date";
  580. } else if (is("function", obj)) {
  581. type = "function";
  582. } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
  583. type = "window";
  584. } else if (obj.nodeType === 9) {
  585. type = "document";
  586. } else if (obj.nodeType) {
  587. type = "node";
  588. } else if (isArray(obj)) {
  589. type = "array";
  590. } else if (obj.constructor === Error.prototype.constructor) {
  591. type = "error";
  592. } else {
  593. type = typeof obj === "undefined" ? "undefined" : _typeof(obj);
  594. }
  595. return type;
  596. },
  597. separator: function separator() {
  598. if (this.multiline) {
  599. return this.HTML ? "<br />" : "\n";
  600. } else {
  601. return this.HTML ? "&#160;" : " ";
  602. }
  603. },
  604. // Extra can be a number, shortcut for increasing-calling-decreasing
  605. indent: function indent(extra) {
  606. if (!this.multiline) {
  607. return "";
  608. }
  609. var chr = this.indentChar;
  610. if (this.HTML) {
  611. chr = chr.replace(/\t/g, " ").replace(/ /g, "&#160;");
  612. }
  613. return new Array(this.depth + (extra || 0)).join(chr);
  614. },
  615. up: function up(a) {
  616. this.depth += a || 1;
  617. },
  618. down: function down(a) {
  619. this.depth -= a || 1;
  620. },
  621. setParser: function setParser(name, parser) {
  622. this.parsers[name] = parser;
  623. },
  624. // The next 3 are exposed so you can use them
  625. quote: quote,
  626. literal: literal,
  627. join: join,
  628. depth: 1,
  629. maxDepth: config.maxDepth,
  630. // This is the list of parsers, to modify them, use dump.setParser
  631. parsers: {
  632. window: "[Window]",
  633. document: "[Document]",
  634. error: function error(_error) {
  635. return "Error(\"" + _error.message + "\")";
  636. },
  637. unknown: "[Unknown]",
  638. "null": "null",
  639. "undefined": "undefined",
  640. "function": function _function(fn) {
  641. var ret = "function",
  642. // Functions never have name in IE
  643. name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
  644. if (name) {
  645. ret += " " + name;
  646. }
  647. ret += "(";
  648. ret = [ret, dump.parse(fn, "functionArgs"), "){"].join("");
  649. return join(ret, dump.parse(fn, "functionCode"), "}");
  650. },
  651. array: array,
  652. nodelist: array,
  653. "arguments": array,
  654. object: function object(map, stack) {
  655. var keys,
  656. key,
  657. val,
  658. i,
  659. nonEnumerableProperties,
  660. ret = [];
  661. if (dump.maxDepth && dump.depth > dump.maxDepth) {
  662. return "[object Object]";
  663. }
  664. dump.up();
  665. keys = [];
  666. for (key in map) {
  667. keys.push(key);
  668. }
  669. // Some properties are not always enumerable on Error objects.
  670. nonEnumerableProperties = ["message", "name"];
  671. for (i in nonEnumerableProperties) {
  672. key = nonEnumerableProperties[i];
  673. if (key in map && !inArray(key, keys)) {
  674. keys.push(key);
  675. }
  676. }
  677. keys.sort();
  678. for (i = 0; i < keys.length; i++) {
  679. key = keys[i];
  680. val = map[key];
  681. ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack));
  682. }
  683. dump.down();
  684. return join("{", ret, "}");
  685. },
  686. node: function node(_node) {
  687. var len,
  688. i,
  689. val,
  690. open = dump.HTML ? "&lt;" : "<",
  691. close = dump.HTML ? "&gt;" : ">",
  692. tag = _node.nodeName.toLowerCase(),
  693. ret = open + tag,
  694. attrs = _node.attributes;
  695. if (attrs) {
  696. for (i = 0, len = attrs.length; i < len; i++) {
  697. val = attrs[i].nodeValue;
  698. // IE6 includes all attributes in .attributes, even ones not explicitly
  699. // set. Those have values like undefined, null, 0, false, "" or
  700. // "inherit".
  701. if (val && val !== "inherit") {
  702. ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute");
  703. }
  704. }
  705. }
  706. ret += close;
  707. // Show content of TextNode or CDATASection
  708. if (_node.nodeType === 3 || _node.nodeType === 4) {
  709. ret += _node.nodeValue;
  710. }
  711. return ret + open + "/" + tag + close;
  712. },
  713. // Function calls it internally, it's the arguments part of the function
  714. functionArgs: function functionArgs(fn) {
  715. var args,
  716. l = fn.length;
  717. if (!l) {
  718. return "";
  719. }
  720. args = new Array(l);
  721. while (l--) {
  722. // 97 is 'a'
  723. args[l] = String.fromCharCode(97 + l);
  724. }
  725. return " " + args.join(", ") + " ";
  726. },
  727. // Object calls it internally, the key part of an item in a map
  728. key: quote,
  729. // Function calls it internally, it's the content of the function
  730. functionCode: "[code]",
  731. // Node calls it internally, it's a html attribute value
  732. attribute: quote,
  733. string: quote,
  734. date: quote,
  735. regexp: literal,
  736. number: literal,
  737. "boolean": literal,
  738. symbol: function symbol(sym) {
  739. return sym.toString();
  740. }
  741. },
  742. // If true, entities are escaped ( <, >, \t, space and \n )
  743. HTML: false,
  744. // Indentation unit
  745. indentChar: " ",
  746. // If true, items in a collection, are separated by a \n, else just a space.
  747. multiline: true
  748. };
  749. return dump;
  750. })();
  751. var SuiteReport = function () {
  752. function SuiteReport(name, parentSuite) {
  753. classCallCheck(this, SuiteReport);
  754. this.name = name;
  755. this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
  756. this.tests = [];
  757. this.childSuites = [];
  758. if (parentSuite) {
  759. parentSuite.pushChildSuite(this);
  760. }
  761. }
  762. createClass(SuiteReport, [{
  763. key: "start",
  764. value: function start(recordTime) {
  765. if (recordTime) {
  766. this._startTime = Date.now();
  767. }
  768. return {
  769. name: this.name,
  770. fullName: this.fullName.slice(),
  771. tests: this.tests.map(function (test) {
  772. return test.start();
  773. }),
  774. childSuites: this.childSuites.map(function (suite) {
  775. return suite.start();
  776. }),
  777. testCounts: {
  778. total: this.getTestCounts().total
  779. }
  780. };
  781. }
  782. }, {
  783. key: "end",
  784. value: function end(recordTime) {
  785. if (recordTime) {
  786. this._endTime = Date.now();
  787. }
  788. return {
  789. name: this.name,
  790. fullName: this.fullName.slice(),
  791. tests: this.tests.map(function (test) {
  792. return test.end();
  793. }),
  794. childSuites: this.childSuites.map(function (suite) {
  795. return suite.end();
  796. }),
  797. testCounts: this.getTestCounts(),
  798. runtime: this.getRuntime(),
  799. status: this.getStatus()
  800. };
  801. }
  802. }, {
  803. key: "pushChildSuite",
  804. value: function pushChildSuite(suite) {
  805. this.childSuites.push(suite);
  806. }
  807. }, {
  808. key: "pushTest",
  809. value: function pushTest(test) {
  810. this.tests.push(test);
  811. }
  812. }, {
  813. key: "getRuntime",
  814. value: function getRuntime() {
  815. return this._endTime - this._startTime;
  816. }
  817. }, {
  818. key: "getTestCounts",
  819. value: function getTestCounts() {
  820. var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 };
  821. counts = this.tests.reduce(function (counts, test) {
  822. if (test.valid) {
  823. counts[test.getStatus()]++;
  824. counts.total++;
  825. }
  826. return counts;
  827. }, counts);
  828. return this.childSuites.reduce(function (counts, suite) {
  829. return suite.getTestCounts(counts);
  830. }, counts);
  831. }
  832. }, {
  833. key: "getStatus",
  834. value: function getStatus() {
  835. var _getTestCounts = this.getTestCounts(),
  836. total = _getTestCounts.total,
  837. failed = _getTestCounts.failed,
  838. skipped = _getTestCounts.skipped,
  839. todo = _getTestCounts.todo;
  840. if (failed) {
  841. return "failed";
  842. } else {
  843. if (skipped === total) {
  844. return "skipped";
  845. } else if (todo === total) {
  846. return "todo";
  847. } else {
  848. return "passed";
  849. }
  850. }
  851. }
  852. }]);
  853. return SuiteReport;
  854. }();
  855. var focused = false;
  856. var moduleStack = [];
  857. function createModule(name, testEnvironment, modifiers) {
  858. var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
  859. var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
  860. var parentSuite = parentModule ? parentModule.suiteReport : globalSuite;
  861. var skip = parentModule !== null && parentModule.skip || modifiers.skip;
  862. var todo = parentModule !== null && parentModule.todo || modifiers.todo;
  863. var module = {
  864. name: moduleName,
  865. parentModule: parentModule,
  866. tests: [],
  867. moduleId: generateHash(moduleName),
  868. testsRun: 0,
  869. unskippedTestsRun: 0,
  870. childModules: [],
  871. suiteReport: new SuiteReport(name, parentSuite),
  872. // Pass along `skip` and `todo` properties from parent module, in case
  873. // there is one, to childs. And use own otherwise.
  874. // This property will be used to mark own tests and tests of child suites
  875. // as either `skipped` or `todo`.
  876. skip: skip,
  877. todo: skip ? false : todo
  878. };
  879. var env = {};
  880. if (parentModule) {
  881. parentModule.childModules.push(module);
  882. extend(env, parentModule.testEnvironment);
  883. }
  884. extend(env, testEnvironment);
  885. module.testEnvironment = env;
  886. config.modules.push(module);
  887. return module;
  888. }
  889. function processModule(name, options, executeNow) {
  890. var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  891. if (objectType(options) === "function") {
  892. executeNow = options;
  893. options = undefined;
  894. }
  895. var module = createModule(name, options, modifiers);
  896. // Move any hooks to a 'hooks' object
  897. var testEnvironment = module.testEnvironment;
  898. var hooks = module.hooks = {};
  899. setHookFromEnvironment(hooks, testEnvironment, "before");
  900. setHookFromEnvironment(hooks, testEnvironment, "beforeEach");
  901. setHookFromEnvironment(hooks, testEnvironment, "afterEach");
  902. setHookFromEnvironment(hooks, testEnvironment, "after");
  903. var moduleFns = {
  904. before: setHookFunction(module, "before"),
  905. beforeEach: setHookFunction(module, "beforeEach"),
  906. afterEach: setHookFunction(module, "afterEach"),
  907. after: setHookFunction(module, "after")
  908. };
  909. var currentModule = config.currentModule;
  910. if (objectType(executeNow) === "function") {
  911. moduleStack.push(module);
  912. config.currentModule = module;
  913. executeNow.call(module.testEnvironment, moduleFns);
  914. moduleStack.pop();
  915. module = module.parentModule || currentModule;
  916. }
  917. config.currentModule = module;
  918. function setHookFromEnvironment(hooks, environment, name) {
  919. var potentialHook = environment[name];
  920. hooks[name] = typeof potentialHook === "function" ? [potentialHook] : [];
  921. delete environment[name];
  922. }
  923. function setHookFunction(module, hookName) {
  924. return function setHook(callback) {
  925. module.hooks[hookName].push(callback);
  926. };
  927. }
  928. }
  929. function module$1(name, options, executeNow) {
  930. if (focused) {
  931. return;
  932. }
  933. processModule(name, options, executeNow);
  934. }
  935. module$1.only = function () {
  936. if (focused) {
  937. return;
  938. }
  939. config.modules.length = 0;
  940. config.queue.length = 0;
  941. module$1.apply(undefined, arguments);
  942. focused = true;
  943. };
  944. module$1.skip = function (name, options, executeNow) {
  945. if (focused) {
  946. return;
  947. }
  948. processModule(name, options, executeNow, { skip: true });
  949. };
  950. module$1.todo = function (name, options, executeNow) {
  951. if (focused) {
  952. return;
  953. }
  954. processModule(name, options, executeNow, { todo: true });
  955. };
  956. var LISTENERS = Object.create(null);
  957. var SUPPORTED_EVENTS = ["runStart", "suiteStart", "testStart", "assertion", "testEnd", "suiteEnd", "runEnd"];
  958. /**
  959. * Emits an event with the specified data to all currently registered listeners.
  960. * Callbacks will fire in the order in which they are registered (FIFO). This
  961. * function is not exposed publicly; it is used by QUnit internals to emit
  962. * logging events.
  963. *
  964. * @private
  965. * @method emit
  966. * @param {String} eventName
  967. * @param {Object} data
  968. * @return {Void}
  969. */
  970. function emit(eventName, data) {
  971. if (objectType(eventName) !== "string") {
  972. throw new TypeError("eventName must be a string when emitting an event");
  973. }
  974. // Clone the callbacks in case one of them registers a new callback
  975. var originalCallbacks = LISTENERS[eventName];
  976. var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : [];
  977. for (var i = 0; i < callbacks.length; i++) {
  978. callbacks[i](data);
  979. }
  980. }
  981. /**
  982. * Registers a callback as a listener to the specified event.
  983. *
  984. * @public
  985. * @method on
  986. * @param {String} eventName
  987. * @param {Function} callback
  988. * @return {Void}
  989. */
  990. function on(eventName, callback) {
  991. if (objectType(eventName) !== "string") {
  992. throw new TypeError("eventName must be a string when registering a listener");
  993. } else if (!inArray(eventName, SUPPORTED_EVENTS)) {
  994. var events = SUPPORTED_EVENTS.join(", ");
  995. throw new Error("\"" + eventName + "\" is not a valid event; must be one of: " + events + ".");
  996. } else if (objectType(callback) !== "function") {
  997. throw new TypeError("callback must be a function when registering a listener");
  998. }
  999. if (!LISTENERS[eventName]) {
  1000. LISTENERS[eventName] = [];
  1001. }
  1002. // Don't register the same callback more than once
  1003. if (!inArray(callback, LISTENERS[eventName])) {
  1004. LISTENERS[eventName].push(callback);
  1005. }
  1006. }
  1007. // Register logging callbacks
  1008. function registerLoggingCallbacks(obj) {
  1009. var i,
  1010. l,
  1011. key,
  1012. callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"];
  1013. function registerLoggingCallback(key) {
  1014. var loggingCallback = function loggingCallback(callback) {
  1015. if (objectType(callback) !== "function") {
  1016. throw new Error("QUnit logging methods require a callback function as their first parameters.");
  1017. }
  1018. config.callbacks[key].push(callback);
  1019. };
  1020. return loggingCallback;
  1021. }
  1022. for (i = 0, l = callbackNames.length; i < l; i++) {
  1023. key = callbackNames[i];
  1024. // Initialize key collection of logging callback
  1025. if (objectType(config.callbacks[key]) === "undefined") {
  1026. config.callbacks[key] = [];
  1027. }
  1028. obj[key] = registerLoggingCallback(key);
  1029. }
  1030. }
  1031. function runLoggingCallbacks(key, args) {
  1032. var i, l, callbacks;
  1033. callbacks = config.callbacks[key];
  1034. for (i = 0, l = callbacks.length; i < l; i++) {
  1035. callbacks[i](args);
  1036. }
  1037. }
  1038. // Doesn't support IE9, it will return undefined on these browsers
  1039. // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
  1040. var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, "");
  1041. function extractStacktrace(e, offset) {
  1042. offset = offset === undefined ? 4 : offset;
  1043. var stack, include, i;
  1044. if (e && e.stack) {
  1045. stack = e.stack.split("\n");
  1046. if (/^error$/i.test(stack[0])) {
  1047. stack.shift();
  1048. }
  1049. if (fileName) {
  1050. include = [];
  1051. for (i = offset; i < stack.length; i++) {
  1052. if (stack[i].indexOf(fileName) !== -1) {
  1053. break;
  1054. }
  1055. include.push(stack[i]);
  1056. }
  1057. if (include.length) {
  1058. return include.join("\n");
  1059. }
  1060. }
  1061. return stack[offset];
  1062. }
  1063. }
  1064. function sourceFromStacktrace(offset) {
  1065. var error = new Error();
  1066. // Support: Safari <=7 only, IE <=10 - 11 only
  1067. // Not all browsers generate the `stack` property for `new Error()`, see also #636
  1068. if (!error.stack) {
  1069. try {
  1070. throw error;
  1071. } catch (err) {
  1072. error = err;
  1073. }
  1074. }
  1075. return extractStacktrace(error, offset);
  1076. }
  1077. var priorityCount = 0;
  1078. var unitSampler = void 0;
  1079. // This is a queue of functions that are tasks within a single test.
  1080. // After tests are dequeued from config.queue they are expanded into
  1081. // a set of tasks in this queue.
  1082. var taskQueue = [];
  1083. /**
  1084. * Advances the taskQueue to the next task. If the taskQueue is empty,
  1085. * process the testQueue
  1086. */
  1087. function advance() {
  1088. advanceTaskQueue();
  1089. if (!taskQueue.length) {
  1090. advanceTestQueue();
  1091. }
  1092. }
  1093. /**
  1094. * Advances the taskQueue to the next task if it is ready and not empty.
  1095. */
  1096. function advanceTaskQueue() {
  1097. var start = now();
  1098. config.depth = (config.depth || 0) + 1;
  1099. while (taskQueue.length && !config.blocking) {
  1100. var elapsedTime = now() - start;
  1101. if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) {
  1102. var task = taskQueue.shift();
  1103. task();
  1104. } else {
  1105. setTimeout(advance);
  1106. break;
  1107. }
  1108. }
  1109. config.depth--;
  1110. }
  1111. /**
  1112. * Advance the testQueue to the next test to process. Call done() if testQueue completes.
  1113. */
  1114. function advanceTestQueue() {
  1115. if (!config.blocking && !config.queue.length && config.depth === 0) {
  1116. done();
  1117. return;
  1118. }
  1119. var testTasks = config.queue.shift();
  1120. addToTaskQueue(testTasks());
  1121. if (priorityCount > 0) {
  1122. priorityCount--;
  1123. }
  1124. advance();
  1125. }
  1126. /**
  1127. * Enqueue the tasks for a test into the task queue.
  1128. * @param {Array} tasksArray
  1129. */
  1130. function addToTaskQueue(tasksArray) {
  1131. taskQueue.push.apply(taskQueue, toConsumableArray(tasksArray));
  1132. }
  1133. /**
  1134. * Return the number of tasks remaining in the task queue to be processed.
  1135. * @return {Number}
  1136. */
  1137. function taskQueueLength() {
  1138. return taskQueue.length;
  1139. }
  1140. /**
  1141. * Adds a test to the TestQueue for execution.
  1142. * @param {Function} testTasksFunc
  1143. * @param {Boolean} prioritize
  1144. * @param {String} seed
  1145. */
  1146. function addToTestQueue(testTasksFunc, prioritize, seed) {
  1147. if (prioritize) {
  1148. config.queue.splice(priorityCount++, 0, testTasksFunc);
  1149. } else if (seed) {
  1150. if (!unitSampler) {
  1151. unitSampler = unitSamplerGenerator(seed);
  1152. }
  1153. // Insert into a random position after all prioritized items
  1154. var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
  1155. config.queue.splice(priorityCount + index, 0, testTasksFunc);
  1156. } else {
  1157. config.queue.push(testTasksFunc);
  1158. }
  1159. }
  1160. /**
  1161. * Creates a seeded "sample" generator which is used for randomizing tests.
  1162. */
  1163. function unitSamplerGenerator(seed) {
  1164. // 32-bit xorshift, requires only a nonzero seed
  1165. // http://excamera.com/sphinx/article-xorshift.html
  1166. var sample = parseInt(generateHash(seed), 16) || -1;
  1167. return function () {
  1168. sample ^= sample << 13;
  1169. sample ^= sample >>> 17;
  1170. sample ^= sample << 5;
  1171. // ECMAScript has no unsigned number type
  1172. if (sample < 0) {
  1173. sample += 0x100000000;
  1174. }
  1175. return sample / 0x100000000;
  1176. };
  1177. }
  1178. /**
  1179. * This function is called when the ProcessingQueue is done processing all
  1180. * items. It handles emitting the final run events.
  1181. */
  1182. function done() {
  1183. var storage = config.storage;
  1184. ProcessingQueue.finished = true;
  1185. var runtime = now() - config.started;
  1186. var passed = config.stats.all - config.stats.bad;
  1187. if (config.stats.all === 0) {
  1188. if (config.filter && config.filter.length) {
  1189. throw new Error("No tests matched the filter \"" + config.filter + "\".");
  1190. }
  1191. if (config.module && config.module.length) {
  1192. throw new Error("No tests matched the module \"" + config.module + "\".");
  1193. }
  1194. if (config.moduleId && config.moduleId.length) {
  1195. throw new Error("No tests matched the moduleId \"" + config.moduleId + "\".");
  1196. }
  1197. if (config.testId && config.testId.length) {
  1198. throw new Error("No tests matched the testId \"" + config.testId + "\".");
  1199. }
  1200. throw new Error("No tests were run.");
  1201. }
  1202. emit("runEnd", globalSuite.end(true));
  1203. runLoggingCallbacks("done", {
  1204. passed: passed,
  1205. failed: config.stats.bad,
  1206. total: config.stats.all,
  1207. runtime: runtime
  1208. });
  1209. // Clear own storage items if all tests passed
  1210. if (storage && config.stats.bad === 0) {
  1211. for (var i = storage.length - 1; i >= 0; i--) {
  1212. var key = storage.key(i);
  1213. if (key.indexOf("qunit-test-") === 0) {
  1214. storage.removeItem(key);
  1215. }
  1216. }
  1217. }
  1218. }
  1219. var ProcessingQueue = {
  1220. finished: false,
  1221. add: addToTestQueue,
  1222. advance: advance,
  1223. taskCount: taskQueueLength
  1224. };
  1225. var TestReport = function () {
  1226. function TestReport(name, suite, options) {
  1227. classCallCheck(this, TestReport);
  1228. this.name = name;
  1229. this.suiteName = suite.name;
  1230. this.fullName = suite.fullName.concat(name);
  1231. this.runtime = 0;
  1232. this.assertions = [];
  1233. this.skipped = !!options.skip;
  1234. this.todo = !!options.todo;
  1235. this.valid = options.valid;
  1236. this._startTime = 0;
  1237. this._endTime = 0;
  1238. suite.pushTest(this);
  1239. }
  1240. createClass(TestReport, [{
  1241. key: "start",
  1242. value: function start(recordTime) {
  1243. if (recordTime) {
  1244. this._startTime = Date.now();
  1245. }
  1246. return {
  1247. name: this.name,
  1248. suiteName: this.suiteName,
  1249. fullName: this.fullName.slice()
  1250. };
  1251. }
  1252. }, {
  1253. key: "end",
  1254. value: function end(recordTime) {
  1255. if (recordTime) {
  1256. this._endTime = Date.now();
  1257. }
  1258. return extend(this.start(), {
  1259. runtime: this.getRuntime(),
  1260. status: this.getStatus(),
  1261. errors: this.getFailedAssertions(),
  1262. assertions: this.getAssertions()
  1263. });
  1264. }
  1265. }, {
  1266. key: "pushAssertion",
  1267. value: function pushAssertion(assertion) {
  1268. this.assertions.push(assertion);
  1269. }
  1270. }, {
  1271. key: "getRuntime",
  1272. value: function getRuntime() {
  1273. return this._endTime - this._startTime;
  1274. }
  1275. }, {
  1276. key: "getStatus",
  1277. value: function getStatus() {
  1278. if (this.skipped) {
  1279. return "skipped";
  1280. }
  1281. var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo;
  1282. if (!testPassed) {
  1283. return "failed";
  1284. } else if (this.todo) {
  1285. return "todo";
  1286. } else {
  1287. return "passed";
  1288. }
  1289. }
  1290. }, {
  1291. key: "getFailedAssertions",
  1292. value: function getFailedAssertions() {
  1293. return this.assertions.filter(function (assertion) {
  1294. return !assertion.passed;
  1295. });
  1296. }
  1297. }, {
  1298. key: "getAssertions",
  1299. value: function getAssertions() {
  1300. return this.assertions.slice();
  1301. }
  1302. // Remove actual and expected values from assertions. This is to prevent
  1303. // leaking memory throughout a test suite.
  1304. }, {
  1305. key: "slimAssertions",
  1306. value: function slimAssertions() {
  1307. this.assertions = this.assertions.map(function (assertion) {
  1308. delete assertion.actual;
  1309. delete assertion.expected;
  1310. return assertion;
  1311. });
  1312. }
  1313. }]);
  1314. return TestReport;
  1315. }();
  1316. var focused$1 = false;
  1317. function Test(settings) {
  1318. var i, l;
  1319. ++Test.count;
  1320. this.expected = null;
  1321. this.assertions = [];
  1322. this.semaphore = 0;
  1323. this.module = config.currentModule;
  1324. this.stack = sourceFromStacktrace(3);
  1325. this.steps = [];
  1326. this.timeout = undefined;
  1327. // If a module is skipped, all its tests and the tests of the child suites
  1328. // should be treated as skipped even if they are defined as `only` or `todo`.
  1329. // As for `todo` module, all its tests will be treated as `todo` except for
  1330. // tests defined as `skip` which will be left intact.
  1331. //
  1332. // So, if a test is defined as `todo` and is inside a skipped module, we should
  1333. // then treat that test as if was defined as `skip`.
  1334. if (this.module.skip) {
  1335. settings.skip = true;
  1336. settings.todo = false;
  1337. // Skipped tests should be left intact
  1338. } else if (this.module.todo && !settings.skip) {
  1339. settings.todo = true;
  1340. }
  1341. extend(this, settings);
  1342. this.testReport = new TestReport(settings.testName, this.module.suiteReport, {
  1343. todo: settings.todo,
  1344. skip: settings.skip,
  1345. valid: this.valid()
  1346. });
  1347. // Register unique strings
  1348. for (i = 0, l = this.module.tests; i < l.length; i++) {
  1349. if (this.module.tests[i].name === this.testName) {
  1350. this.testName += " ";
  1351. }
  1352. }
  1353. this.testId = generateHash(this.module.name, this.testName);
  1354. this.module.tests.push({
  1355. name: this.testName,
  1356. testId: this.testId,
  1357. skip: !!settings.skip
  1358. });
  1359. if (settings.skip) {
  1360. // Skipped tests will fully ignore any sent callback
  1361. this.callback = function () {};
  1362. this.async = false;
  1363. this.expected = 0;
  1364. } else {
  1365. if (typeof this.callback !== "function") {
  1366. var method = this.todo ? "todo" : "test";
  1367. // eslint-disable-next-line max-len
  1368. throw new TypeError("You must provide a function as a test callback to QUnit." + method + "(\"" + settings.testName + "\")");
  1369. }
  1370. this.assert = new Assert(this);
  1371. }
  1372. }
  1373. Test.count = 0;
  1374. function getNotStartedModules(startModule) {
  1375. var module = startModule,
  1376. modules = [];
  1377. while (module && module.testsRun === 0) {
  1378. modules.push(module);
  1379. module = module.parentModule;
  1380. }
  1381. return modules;
  1382. }
  1383. Test.prototype = {
  1384. before: function before() {
  1385. var i,
  1386. startModule,
  1387. module = this.module,
  1388. notStartedModules = getNotStartedModules(module);
  1389. for (i = notStartedModules.length - 1; i >= 0; i--) {
  1390. startModule = notStartedModules[i];
  1391. startModule.stats = { all: 0, bad: 0, started: now() };
  1392. emit("suiteStart", startModule.suiteReport.start(true));
  1393. runLoggingCallbacks("moduleStart", {
  1394. name: startModule.name,
  1395. tests: startModule.tests
  1396. });
  1397. }
  1398. config.current = this;
  1399. this.testEnvironment = extend({}, module.testEnvironment);
  1400. this.started = now();
  1401. emit("testStart", this.testReport.start(true));
  1402. runLoggingCallbacks("testStart", {
  1403. name: this.testName,
  1404. module: module.name,
  1405. testId: this.testId,
  1406. previousFailure: this.previousFailure
  1407. });
  1408. if (!config.pollution) {
  1409. saveGlobal();
  1410. }
  1411. },
  1412. run: function run() {
  1413. var promise;
  1414. config.current = this;
  1415. this.callbackStarted = now();
  1416. if (config.notrycatch) {
  1417. runTest(this);
  1418. return;
  1419. }
  1420. try {
  1421. runTest(this);
  1422. } catch (e) {
  1423. this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
  1424. // Else next test will carry the responsibility
  1425. saveGlobal();
  1426. // Restart the tests if they're blocking
  1427. if (config.blocking) {
  1428. internalRecover(this);
  1429. }
  1430. }
  1431. function runTest(test) {
  1432. promise = test.callback.call(test.testEnvironment, test.assert);
  1433. test.resolvePromise(promise);
  1434. // If the test has a "lock" on it, but the timeout is 0, then we push a
  1435. // failure as the test should be synchronous.
  1436. if (test.timeout === 0 && test.semaphore !== 0) {
  1437. pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2));
  1438. }
  1439. }
  1440. },
  1441. after: function after() {
  1442. checkPollution();
  1443. },
  1444. queueHook: function queueHook(hook, hookName, hookOwner) {
  1445. var _this = this;
  1446. var callHook = function callHook() {
  1447. var promise = hook.call(_this.testEnvironment, _this.assert);
  1448. _this.resolvePromise(promise, hookName);
  1449. };
  1450. var runHook = function runHook() {
  1451. if (hookName === "before") {
  1452. if (hookOwner.unskippedTestsRun !== 0) {
  1453. return;
  1454. }
  1455. _this.preserveEnvironment = true;
  1456. }
  1457. // The 'after' hook should only execute when there are not tests left and
  1458. // when the 'after' and 'finish' tasks are the only tasks left to process
  1459. if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && (config.queue.length > 0 || ProcessingQueue.taskCount() > 2)) {
  1460. return;
  1461. }
  1462. config.current = _this;
  1463. if (config.notrycatch) {
  1464. callHook();
  1465. return;
  1466. }
  1467. try {
  1468. callHook();
  1469. } catch (error) {
  1470. _this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0));
  1471. }
  1472. };
  1473. return runHook;
  1474. },
  1475. // Currently only used for module level hooks, can be used to add global level ones
  1476. hooks: function hooks(handler) {
  1477. var hooks = [];
  1478. function processHooks(test, module) {
  1479. if (module.parentModule) {
  1480. processHooks(test, module.parentModule);
  1481. }
  1482. if (module.hooks[handler].length) {
  1483. for (var i = 0; i < module.hooks[handler].length; i++) {
  1484. hooks.push(test.queueHook(module.hooks[handler][i], handler, module));
  1485. }
  1486. }
  1487. }
  1488. // Hooks are ignored on skipped tests
  1489. if (!this.skip) {
  1490. processHooks(this, this.module);
  1491. }
  1492. return hooks;
  1493. },
  1494. finish: function finish() {
  1495. config.current = this;
  1496. // Release the test callback to ensure that anything referenced has been
  1497. // released to be garbage collected.
  1498. this.callback = undefined;
  1499. if (this.steps.length) {
  1500. var stepsList = this.steps.join(", ");
  1501. this.pushFailure("Expected assert.verifySteps() to be called before end of test " + ("after using assert.step(). Unverified steps: " + stepsList), this.stack);
  1502. }
  1503. if (config.requireExpects && this.expected === null) {
  1504. this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack);
  1505. } else if (this.expected !== null && this.expected !== this.assertions.length) {
  1506. this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack);
  1507. } else if (this.expected === null && !this.assertions.length) {
  1508. this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack);
  1509. }
  1510. var i,
  1511. module = this.module,
  1512. moduleName = module.name,
  1513. testName = this.testName,
  1514. skipped = !!this.skip,
  1515. todo = !!this.todo,
  1516. bad = 0,
  1517. storage = config.storage;
  1518. this.runtime = now() - this.started;
  1519. config.stats.all += this.assertions.length;
  1520. module.stats.all += this.assertions.length;
  1521. for (i = 0; i < this.assertions.length; i++) {
  1522. if (!this.assertions[i].result) {
  1523. bad++;
  1524. config.stats.bad++;
  1525. module.stats.bad++;
  1526. }
  1527. }
  1528. notifyTestsRan(module, skipped);
  1529. // Store result when possible
  1530. if (storage) {
  1531. if (bad) {
  1532. storage.setItem("qunit-test-" + moduleName + "-" + testName, bad);
  1533. } else {
  1534. storage.removeItem("qunit-test-" + moduleName + "-" + testName);
  1535. }
  1536. }
  1537. // After emitting the js-reporters event we cleanup the assertion data to
  1538. // avoid leaking it. It is not used by the legacy testDone callbacks.
  1539. emit("testEnd", this.testReport.end(true));
  1540. this.testReport.slimAssertions();
  1541. runLoggingCallbacks("testDone", {
  1542. name: testName,
  1543. module: moduleName,
  1544. skipped: skipped,
  1545. todo: todo,
  1546. failed: bad,
  1547. passed: this.assertions.length - bad,
  1548. total: this.assertions.length,
  1549. runtime: skipped ? 0 : this.runtime,
  1550. // HTML Reporter use
  1551. assertions: this.assertions,
  1552. testId: this.testId,
  1553. // Source of Test
  1554. source: this.stack
  1555. });
  1556. if (module.testsRun === numberOfTests(module)) {
  1557. logSuiteEnd(module);
  1558. // Check if the parent modules, iteratively, are done. If that the case,
  1559. // we emit the `suiteEnd` event and trigger `moduleDone` callback.
  1560. var parent = module.parentModule;
  1561. while (parent && parent.testsRun === numberOfTests(parent)) {
  1562. logSuiteEnd(parent);
  1563. parent = parent.parentModule;
  1564. }
  1565. }
  1566. config.current = undefined;
  1567. function logSuiteEnd(module) {
  1568. // Reset `module.hooks` to ensure that anything referenced in these hooks
  1569. // has been released to be garbage collected.
  1570. module.hooks = {};
  1571. emit("suiteEnd", module.suiteReport.end(true));
  1572. runLoggingCallbacks("moduleDone", {
  1573. name: module.name,
  1574. tests: module.tests,
  1575. failed: module.stats.bad,
  1576. passed: module.stats.all - module.stats.bad,
  1577. total: module.stats.all,
  1578. runtime: now() - module.stats.started
  1579. });
  1580. }
  1581. },
  1582. preserveTestEnvironment: function preserveTestEnvironment() {
  1583. if (this.preserveEnvironment) {
  1584. this.module.testEnvironment = this.testEnvironment;
  1585. this.testEnvironment = extend({}, this.module.testEnvironment);
  1586. }
  1587. },
  1588. queue: function queue() {
  1589. var test = this;
  1590. if (!this.valid()) {
  1591. return;
  1592. }
  1593. function runTest() {
  1594. return [function () {
  1595. test.before();
  1596. }].concat(toConsumableArray(test.hooks("before")), [function () {
  1597. test.preserveTestEnvironment();
  1598. }], toConsumableArray(test.hooks("beforeEach")), [function () {
  1599. test.run();
  1600. }], toConsumableArray(test.hooks("afterEach").reverse()), toConsumableArray(test.hooks("after").reverse()), [function () {
  1601. test.after();
  1602. }, function () {
  1603. test.finish();
  1604. }]);
  1605. }
  1606. var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName);
  1607. // Prioritize previously failed tests, detected from storage
  1608. var prioritize = config.reorder && !!previousFailCount;
  1609. this.previousFailure = !!previousFailCount;
  1610. ProcessingQueue.add(runTest, prioritize, config.seed);
  1611. // If the queue has already finished, we manually process the new test
  1612. if (ProcessingQueue.finished) {
  1613. ProcessingQueue.advance();
  1614. }
  1615. },
  1616. pushResult: function pushResult(resultInfo) {
  1617. if (this !== config.current) {
  1618. throw new Error("Assertion occurred after test had finished.");
  1619. }
  1620. // Destructure of resultInfo = { result, actual, expected, message, negative }
  1621. var source,
  1622. details = {
  1623. module: this.module.name,
  1624. name: this.testName,
  1625. result: resultInfo.result,
  1626. message: resultInfo.message,
  1627. actual: resultInfo.actual,
  1628. testId: this.testId,
  1629. negative: resultInfo.negative || false,
  1630. runtime: now() - this.started,
  1631. todo: !!this.todo
  1632. };
  1633. if (hasOwn.call(resultInfo, "expected")) {
  1634. details.expected = resultInfo.expected;
  1635. }
  1636. if (!resultInfo.result) {
  1637. source = resultInfo.source || sourceFromStacktrace();
  1638. if (source) {
  1639. details.source = source;
  1640. }
  1641. }
  1642. this.logAssertion(details);
  1643. this.assertions.push({
  1644. result: !!resultInfo.result,
  1645. message: resultInfo.message
  1646. });
  1647. },
  1648. pushFailure: function pushFailure(message, source, actual) {
  1649. if (!(this instanceof Test)) {
  1650. throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2));
  1651. }
  1652. this.pushResult({
  1653. result: false,
  1654. message: message || "error",
  1655. actual: actual || null,
  1656. source: source
  1657. });
  1658. },
  1659. /**
  1660. * Log assertion details using both the old QUnit.log interface and
  1661. * QUnit.on( "assertion" ) interface.
  1662. *
  1663. * @private
  1664. */
  1665. logAssertion: function logAssertion(details) {
  1666. runLoggingCallbacks("log", details);
  1667. var assertion = {
  1668. passed: details.result,
  1669. actual: details.actual,
  1670. expected: details.expected,
  1671. message: details.message,
  1672. stack: details.source,
  1673. todo: details.todo
  1674. };
  1675. this.testReport.pushAssertion(assertion);
  1676. emit("assertion", assertion);
  1677. },
  1678. resolvePromise: function resolvePromise(promise, phase) {
  1679. var then,
  1680. resume,
  1681. message,
  1682. test = this;
  1683. if (promise != null) {
  1684. then = promise.then;
  1685. if (objectType(then) === "function") {
  1686. resume = internalStop(test);
  1687. if (config.notrycatch) {
  1688. then.call(promise, function () {
  1689. resume();
  1690. });
  1691. } else {
  1692. then.call(promise, function () {
  1693. resume();
  1694. }, function (error) {
  1695. message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
  1696. test.pushFailure(message, extractStacktrace(error, 0));
  1697. // Else next test will carry the responsibility
  1698. saveGlobal();
  1699. // Unblock
  1700. internalRecover(test);
  1701. });
  1702. }
  1703. }
  1704. }
  1705. },
  1706. valid: function valid() {
  1707. var filter = config.filter,
  1708. regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
  1709. module = config.module && config.module.toLowerCase(),
  1710. fullName = this.module.name + ": " + this.testName;
  1711. function moduleChainNameMatch(testModule) {
  1712. var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
  1713. if (testModuleName === module) {
  1714. return true;
  1715. } else if (testModule.parentModule) {
  1716. return moduleChainNameMatch(testModule.parentModule);
  1717. } else {
  1718. return false;
  1719. }
  1720. }
  1721. function moduleChainIdMatch(testModule) {
  1722. return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
  1723. }
  1724. // Internally-generated tests are always valid
  1725. if (this.callback && this.callback.validTest) {
  1726. return true;
  1727. }
  1728. if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
  1729. return false;
  1730. }
  1731. if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) {
  1732. return false;
  1733. }
  1734. if (module && !moduleChainNameMatch(this.module)) {
  1735. return false;
  1736. }
  1737. if (!filter) {
  1738. return true;
  1739. }
  1740. return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
  1741. },
  1742. regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
  1743. var regex = new RegExp(pattern, flags);
  1744. var match = regex.test(fullName);
  1745. return match !== exclude;
  1746. },
  1747. stringFilter: function stringFilter(filter, fullName) {
  1748. filter = filter.toLowerCase();
  1749. fullName = fullName.toLowerCase();
  1750. var include = filter.charAt(0) !== "!";
  1751. if (!include) {
  1752. filter = filter.slice(1);
  1753. }
  1754. // If the filter matches, we need to honour include
  1755. if (fullName.indexOf(filter) !== -1) {
  1756. return include;
  1757. }
  1758. // Otherwise, do the opposite
  1759. return !include;
  1760. }
  1761. };
  1762. function pushFailure() {
  1763. if (!config.current) {
  1764. throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2));
  1765. }
  1766. // Gets current test obj
  1767. var currentTest = config.current;
  1768. return currentTest.pushFailure.apply(currentTest, arguments);
  1769. }
  1770. function saveGlobal() {
  1771. config.pollution = [];
  1772. if (config.noglobals) {
  1773. for (var key in global$1) {
  1774. if (hasOwn.call(global$1, key)) {
  1775. // In Opera sometimes DOM element ids show up here, ignore them
  1776. if (/^qunit-test-output/.test(key)) {
  1777. continue;
  1778. }
  1779. config.pollution.push(key);
  1780. }
  1781. }
  1782. }
  1783. }
  1784. function checkPollution() {
  1785. var newGlobals,
  1786. deletedGlobals,
  1787. old = config.pollution;
  1788. saveGlobal();
  1789. newGlobals = diff(config.pollution, old);
  1790. if (newGlobals.length > 0) {
  1791. pushFailure("Introduced global variable(s): " + newGlobals.join(", "));
  1792. }
  1793. deletedGlobals = diff(old, config.pollution);
  1794. if (deletedGlobals.length > 0) {
  1795. pushFailure("Deleted global variable(s): " + deletedGlobals.join(", "));
  1796. }
  1797. }
  1798. // Will be exposed as QUnit.test
  1799. function test(testName, callback) {
  1800. if (focused$1) {
  1801. return;
  1802. }
  1803. var newTest = new Test({
  1804. testName: testName,
  1805. callback: callback
  1806. });
  1807. newTest.queue();
  1808. }
  1809. function todo(testName, callback) {
  1810. if (focused$1) {
  1811. return;
  1812. }
  1813. var newTest = new Test({
  1814. testName: testName,
  1815. callback: callback,
  1816. todo: true
  1817. });
  1818. newTest.queue();
  1819. }
  1820. // Will be exposed as QUnit.skip
  1821. function skip(testName) {
  1822. if (focused$1) {
  1823. return;
  1824. }
  1825. var test = new Test({
  1826. testName: testName,
  1827. skip: true
  1828. });
  1829. test.queue();
  1830. }
  1831. // Will be exposed as QUnit.only
  1832. function only(testName, callback) {
  1833. if (focused$1) {
  1834. return;
  1835. }
  1836. config.queue.length = 0;
  1837. focused$1 = true;
  1838. var newTest = new Test({
  1839. testName: testName,
  1840. callback: callback
  1841. });
  1842. newTest.queue();
  1843. }
  1844. // Put a hold on processing and return a function that will release it.
  1845. function internalStop(test) {
  1846. test.semaphore += 1;
  1847. config.blocking = true;
  1848. // Set a recovery timeout, if so configured.
  1849. if (defined.setTimeout) {
  1850. var timeoutDuration = void 0;
  1851. if (typeof test.timeout === "number") {
  1852. timeoutDuration = test.timeout;
  1853. } else if (typeof config.testTimeout === "number") {
  1854. timeoutDuration = config.testTimeout;
  1855. }
  1856. if (typeof timeoutDuration === "number" && timeoutDuration > 0) {
  1857. clearTimeout(config.timeout);
  1858. config.timeout = setTimeout(function () {
  1859. pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2));
  1860. internalRecover(test);
  1861. }, timeoutDuration);
  1862. }
  1863. }
  1864. var released = false;
  1865. return function resume() {
  1866. if (released) {
  1867. return;
  1868. }
  1869. released = true;
  1870. test.semaphore -= 1;
  1871. internalStart(test);
  1872. };
  1873. }
  1874. // Forcefully release all processing holds.
  1875. function internalRecover(test) {
  1876. test.semaphore = 0;
  1877. internalStart(test);
  1878. }
  1879. // Release a processing hold, scheduling a resumption attempt if no holds remain.
  1880. function internalStart(test) {
  1881. // If semaphore is non-numeric, throw error
  1882. if (isNaN(test.semaphore)) {
  1883. test.semaphore = 0;
  1884. pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2));
  1885. return;
  1886. }
  1887. // Don't start until equal number of stop-calls
  1888. if (test.semaphore > 0) {
  1889. return;
  1890. }
  1891. // Throw an Error if start is called more often than stop
  1892. if (test.semaphore < 0) {
  1893. test.semaphore = 0;
  1894. pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2));
  1895. return;
  1896. }
  1897. // Add a slight delay to allow more assertions etc.
  1898. if (defined.setTimeout) {
  1899. if (config.timeout) {
  1900. clearTimeout(config.timeout);
  1901. }
  1902. config.timeout = setTimeout(function () {
  1903. if (test.semaphore > 0) {
  1904. return;
  1905. }
  1906. if (config.timeout) {
  1907. clearTimeout(config.timeout);
  1908. }
  1909. begin();
  1910. });
  1911. } else {
  1912. begin();
  1913. }
  1914. }
  1915. function collectTests(module) {
  1916. var tests = [].concat(module.tests);
  1917. var modules = [].concat(toConsumableArray(module.childModules));
  1918. // Do a breadth-first traversal of the child modules
  1919. while (modules.length) {
  1920. var nextModule = modules.shift();
  1921. tests.push.apply(tests, nextModule.tests);
  1922. modules.push.apply(modules, toConsumableArray(nextModule.childModules));
  1923. }
  1924. return tests;
  1925. }
  1926. function numberOfTests(module) {
  1927. return collectTests(module).length;
  1928. }
  1929. function numberOfUnskippedTests(module) {
  1930. return collectTests(module).filter(function (test) {
  1931. return !test.skip;
  1932. }).length;
  1933. }
  1934. function notifyTestsRan(module, skipped) {
  1935. module.testsRun++;
  1936. if (!skipped) {
  1937. module.unskippedTestsRun++;
  1938. }
  1939. while (module = module.parentModule) {
  1940. module.testsRun++;
  1941. if (!skipped) {
  1942. module.unskippedTestsRun++;
  1943. }
  1944. }
  1945. }
  1946. /**
  1947. * Returns a function that proxies to the given method name on the globals
  1948. * console object. The proxy will also detect if the console doesn't exist and
  1949. * will appropriately no-op. This allows support for IE9, which doesn't have a
  1950. * console if the developer tools are not open.
  1951. */
  1952. function consoleProxy(method) {
  1953. return function () {
  1954. if (console) {
  1955. console[method].apply(console, arguments);
  1956. }
  1957. };
  1958. }
  1959. var Logger = {
  1960. warn: consoleProxy("warn")
  1961. };
  1962. var Assert = function () {
  1963. function Assert(testContext) {
  1964. classCallCheck(this, Assert);
  1965. this.test = testContext;
  1966. }
  1967. // Assert helpers
  1968. createClass(Assert, [{
  1969. key: "timeout",
  1970. value: function timeout(duration) {
  1971. if (typeof duration !== "number") {
  1972. throw new Error("You must pass a number as the duration to assert.timeout");
  1973. }
  1974. this.test.timeout = duration;
  1975. }
  1976. // Documents a "step", which is a string value, in a test as a passing assertion
  1977. }, {
  1978. key: "step",
  1979. value: function step(message) {
  1980. var assertionMessage = message;
  1981. var result = !!message;
  1982. this.test.steps.push(message);
  1983. if (objectType(message) === "undefined" || message === "") {
  1984. assertionMessage = "You must provide a message to assert.step";
  1985. } else if (objectType(message) !== "string") {
  1986. assertionMessage = "You must provide a string value to assert.step";
  1987. result = false;
  1988. }
  1989. this.pushResult({
  1990. result: result,
  1991. message: assertionMessage
  1992. });
  1993. }
  1994. // Verifies the steps in a test match a given array of string values
  1995. }, {
  1996. key: "verifySteps",
  1997. value: function verifySteps(steps, message) {
  1998. // Since the steps array is just string values, we can clone with slice
  1999. var actualStepsClone = this.test.steps.slice();
  2000. this.deepEqual(actualStepsClone, steps, message);
  2001. this.test.steps.length = 0;
  2002. }
  2003. // Specify the number of expected assertions to guarantee that failed test
  2004. // (no assertions are run at all) don't slip through.
  2005. }, {
  2006. key: "expect",
  2007. value: function expect(asserts) {
  2008. if (arguments.length === 1) {
  2009. this.test.expected = asserts;
  2010. } else {
  2011. return this.test.expected;
  2012. }
  2013. }
  2014. // Put a hold on processing and return a function that will release it a maximum of once.
  2015. }, {
  2016. key: "async",
  2017. value: function async(count) {
  2018. var test$$1 = this.test;
  2019. var popped = false,
  2020. acceptCallCount = count;
  2021. if (typeof acceptCallCount === "undefined") {
  2022. acceptCallCount = 1;
  2023. }
  2024. var resume = internalStop(test$$1);
  2025. return function done() {
  2026. if (config.current !== test$$1) {
  2027. throw Error("assert.async callback called after test finished.");
  2028. }
  2029. if (popped) {
  2030. test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
  2031. return;
  2032. }
  2033. acceptCallCount -= 1;
  2034. if (acceptCallCount > 0) {
  2035. return;
  2036. }
  2037. popped = true;
  2038. resume();
  2039. };
  2040. }
  2041. // Exports test.push() to the user API
  2042. // Alias of pushResult.
  2043. }, {
  2044. key: "push",
  2045. value: function push(result, actual, expected, message, negative) {
  2046. Logger.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult).");
  2047. var currentAssert = this instanceof Assert ? this : config.current.assert;
  2048. return currentAssert.pushResult({
  2049. result: result,
  2050. actual: actual,
  2051. expected: expected,
  2052. message: message,
  2053. negative: negative
  2054. });
  2055. }
  2056. }, {
  2057. key: "pushResult",
  2058. value: function pushResult(resultInfo) {
  2059. // Destructure of resultInfo = { result, actual, expected, message, negative }
  2060. var assert = this;
  2061. var currentTest = assert instanceof Assert && assert.test || config.current;
  2062. // Backwards compatibility fix.
  2063. // Allows the direct use of global exported assertions and QUnit.assert.*
  2064. // Although, it's use is not recommended as it can leak assertions
  2065. // to other tests from async tests, because we only get a reference to the current test,
  2066. // not exactly the test where assertion were intended to be called.
  2067. if (!currentTest) {
  2068. throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
  2069. }
  2070. if (!(assert instanceof Assert)) {
  2071. assert = currentTest.assert;
  2072. }
  2073. return assert.test.pushResult(resultInfo);
  2074. }
  2075. }, {
  2076. key: "ok",
  2077. value: function ok(result, message) {
  2078. if (!message) {
  2079. message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result);
  2080. }
  2081. this.pushResult({
  2082. result: !!result,
  2083. actual: result,
  2084. expected: true,
  2085. message: message
  2086. });
  2087. }
  2088. }, {
  2089. key: "notOk",
  2090. value: function notOk(result, message) {
  2091. if (!message) {
  2092. message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result);
  2093. }
  2094. this.pushResult({
  2095. result: !result,
  2096. actual: result,
  2097. expected: false,
  2098. message: message
  2099. });
  2100. }
  2101. }, {
  2102. key: "equal",
  2103. value: function equal(actual, expected, message) {
  2104. // eslint-disable-next-line eqeqeq
  2105. var result = expected == actual;
  2106. this.pushResult({
  2107. result: result,
  2108. actual: actual,
  2109. expected: expected,
  2110. message: message
  2111. });
  2112. }
  2113. }, {
  2114. key: "notEqual",
  2115. value: function notEqual(actual, expected, message) {
  2116. // eslint-disable-next-line eqeqeq
  2117. var result = expected != actual;
  2118. this.pushResult({
  2119. result: result,
  2120. actual: actual,
  2121. expected: expected,
  2122. message: message,
  2123. negative: true
  2124. });
  2125. }
  2126. }, {
  2127. key: "propEqual",
  2128. value: function propEqual(actual, expected, message) {
  2129. actual = objectValues(actual);
  2130. expected = objectValues(expected);
  2131. this.pushResult({
  2132. result: equiv(actual, expected),
  2133. actual: actual,
  2134. expected: expected,
  2135. message: message
  2136. });
  2137. }
  2138. }, {
  2139. key: "notPropEqual",
  2140. value: function notPropEqual(actual, expected, message) {
  2141. actual = objectValues(actual);
  2142. expected = objectValues(expected);
  2143. this.pushResult({
  2144. result: !equiv(actual, expected),
  2145. actual: actual,
  2146. expected: expected,
  2147. message: message,
  2148. negative: true
  2149. });
  2150. }
  2151. }, {
  2152. key: "deepEqual",
  2153. value: function deepEqual(actual, expected, message) {
  2154. this.pushResult({
  2155. result: equiv(actual, expected),
  2156. actual: actual,
  2157. expected: expected,
  2158. message: message
  2159. });
  2160. }
  2161. }, {
  2162. key: "notDeepEqual",
  2163. value: function notDeepEqual(actual, expected, message) {
  2164. this.pushResult({
  2165. result: !equiv(actual, expected),
  2166. actual: actual,
  2167. expected: expected,
  2168. message: message,
  2169. negative: true
  2170. });
  2171. }
  2172. }, {
  2173. key: "strictEqual",
  2174. value: function strictEqual(actual, expected, message) {
  2175. this.pushResult({
  2176. result: expected === actual,
  2177. actual: actual,
  2178. expected: expected,
  2179. message: message
  2180. });
  2181. }
  2182. }, {
  2183. key: "notStrictEqual",
  2184. value: function notStrictEqual(actual, expected, message) {
  2185. this.pushResult({
  2186. result: expected !== actual,
  2187. actual: actual,
  2188. expected: expected,
  2189. message: message,
  2190. negative: true
  2191. });
  2192. }
  2193. }, {
  2194. key: "throws",
  2195. value: function throws(block, expected, message) {
  2196. var actual = void 0,
  2197. result = false;
  2198. var currentTest = this instanceof Assert && this.test || config.current;
  2199. // 'expected' is optional unless doing string comparison
  2200. if (objectType(expected) === "string") {
  2201. if (message == null) {
  2202. message = expected;
  2203. expected = null;
  2204. } else {
  2205. throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary.");
  2206. }
  2207. }
  2208. currentTest.ignoreGlobalErrors = true;
  2209. try {
  2210. block.call(currentTest.testEnvironment);
  2211. } catch (e) {
  2212. actual = e;
  2213. }
  2214. currentTest.ignoreGlobalErrors = false;
  2215. if (actual) {
  2216. var expectedType = objectType(expected);
  2217. // We don't want to validate thrown error
  2218. if (!expected) {
  2219. result = true;
  2220. expected = null;
  2221. // Expected is a regexp
  2222. } else if (expectedType === "regexp") {
  2223. result = expected.test(errorString(actual));
  2224. // Expected is a constructor, maybe an Error constructor
  2225. } else if (expectedType === "function" && actual instanceof expected) {
  2226. result = true;
  2227. // Expected is an Error object
  2228. } else if (expectedType === "object") {
  2229. result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
  2230. // Expected is a validation function which returns true if validation passed
  2231. } else if (expectedType === "function" && expected.call({}, actual) === true) {
  2232. expected = null;
  2233. result = true;
  2234. }
  2235. }
  2236. currentTest.assert.pushResult({
  2237. result: result,
  2238. actual: actual,
  2239. expected: expected,
  2240. message: message
  2241. });
  2242. }
  2243. }, {
  2244. key: "rejects",
  2245. value: function rejects(promise, expected, message) {
  2246. var result = false;
  2247. var currentTest = this instanceof Assert && this.test || config.current;
  2248. // 'expected' is optional unless doing string comparison
  2249. if (objectType(expected) === "string") {
  2250. if (message === undefined) {
  2251. message = expected;
  2252. expected = undefined;
  2253. } else {
  2254. message = "assert.rejects does not accept a string value for the expected " + "argument.\nUse a non-string object value (e.g. validator function) instead " + "if necessary.";
  2255. currentTest.assert.pushResult({
  2256. result: false,
  2257. message: message
  2258. });
  2259. return;
  2260. }
  2261. }
  2262. var then = promise && promise.then;
  2263. if (objectType(then) !== "function") {
  2264. var _message = "The value provided to `assert.rejects` in " + "\"" + currentTest.testName + "\" was not a promise.";
  2265. currentTest.assert.pushResult({
  2266. result: false,
  2267. message: _message,
  2268. actual: promise
  2269. });
  2270. return;
  2271. }
  2272. var done = this.async();
  2273. return then.call(promise, function handleFulfillment() {
  2274. var message = "The promise returned by the `assert.rejects` callback in " + "\"" + currentTest.testName + "\" did not reject.";
  2275. currentTest.assert.pushResult({
  2276. result: false,
  2277. message: message,
  2278. actual: promise
  2279. });
  2280. done();
  2281. }, function handleRejection(actual) {
  2282. var expectedType = objectType(expected);
  2283. // We don't want to validate
  2284. if (expected === undefined) {
  2285. result = true;
  2286. expected = actual;
  2287. // Expected is a regexp
  2288. } else if (expectedType === "regexp") {
  2289. result = expected.test(errorString(actual));
  2290. // Expected is a constructor, maybe an Error constructor
  2291. } else if (expectedType === "function" && actual instanceof expected) {
  2292. result = true;
  2293. // Expected is an Error object
  2294. } else if (expectedType === "object") {
  2295. result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
  2296. // Expected is a validation function which returns true if validation passed
  2297. } else {
  2298. if (expectedType === "function") {
  2299. result = expected.call({}, actual) === true;
  2300. expected = null;
  2301. // Expected is some other invalid type
  2302. } else {
  2303. result = false;
  2304. message = "invalid expected value provided to `assert.rejects` " + "callback in \"" + currentTest.testName + "\": " + expectedType + ".";
  2305. }
  2306. }
  2307. currentTest.assert.pushResult({
  2308. result: result,
  2309. actual: actual,
  2310. expected: expected,
  2311. message: message
  2312. });
  2313. done();
  2314. });
  2315. }
  2316. }]);
  2317. return Assert;
  2318. }();
  2319. // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
  2320. // Known to us are: Closure Compiler, Narwhal
  2321. // eslint-disable-next-line dot-notation
  2322. Assert.prototype.raises = Assert.prototype["throws"];
  2323. /**
  2324. * Converts an error into a simple string for comparisons.
  2325. *
  2326. * @param {Error} error
  2327. * @return {String}
  2328. */
  2329. function errorString(error) {
  2330. var resultErrorString = error.toString();
  2331. if (resultErrorString.substring(0, 7) === "[object") {
  2332. var name = error.name ? error.name.toString() : "Error";
  2333. var message = error.message ? error.message.toString() : "";
  2334. if (name && message) {
  2335. return name + ": " + message;
  2336. } else if (name) {
  2337. return name;
  2338. } else if (message) {
  2339. return message;
  2340. } else {
  2341. return "Error";
  2342. }
  2343. } else {
  2344. return resultErrorString;
  2345. }
  2346. }
  2347. /* global module, exports, define */
  2348. function exportQUnit(QUnit) {
  2349. if (defined.document) {
  2350. // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
  2351. if (window.QUnit && window.QUnit.version) {
  2352. throw new Error("QUnit has already been defined.");
  2353. }
  2354. window.QUnit = QUnit;
  2355. }
  2356. // For nodejs
  2357. if (typeof module !== "undefined" && module && module.exports) {
  2358. module.exports = QUnit;
  2359. // For consistency with CommonJS environments' exports
  2360. module.exports.QUnit = QUnit;
  2361. }
  2362. // For CommonJS with exports, but without module.exports, like Rhino
  2363. if (typeof exports !== "undefined" && exports) {
  2364. exports.QUnit = QUnit;
  2365. }
  2366. if (typeof define === "function" && define.amd) {
  2367. define(function () {
  2368. return QUnit;
  2369. });
  2370. QUnit.config.autostart = false;
  2371. }
  2372. // For Web/Service Workers
  2373. if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) {
  2374. self$1.QUnit = QUnit;
  2375. }
  2376. }
  2377. // Handle an unhandled exception. By convention, returns true if further
  2378. // error handling should be suppressed and false otherwise.
  2379. // In this case, we will only suppress further error handling if the
  2380. // "ignoreGlobalErrors" configuration option is enabled.
  2381. function onError(error) {
  2382. for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  2383. args[_key - 1] = arguments[_key];
  2384. }
  2385. if (config.current) {
  2386. if (config.current.ignoreGlobalErrors) {
  2387. return true;
  2388. }
  2389. pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
  2390. } else {
  2391. test("global failure", extend(function () {
  2392. pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
  2393. }, { validTest: true }));
  2394. }
  2395. return false;
  2396. }
  2397. // Handle an unhandled rejection
  2398. function onUnhandledRejection(reason) {
  2399. var resultInfo = {
  2400. result: false,
  2401. message: reason.message || "error",
  2402. actual: reason,
  2403. source: reason.stack || sourceFromStacktrace(3)
  2404. };
  2405. var currentTest = config.current;
  2406. if (currentTest) {
  2407. currentTest.assert.pushResult(resultInfo);
  2408. } else {
  2409. test("global failure", extend(function (assert) {
  2410. assert.pushResult(resultInfo);
  2411. }, { validTest: true }));
  2412. }
  2413. }
  2414. var QUnit = {};
  2415. var globalSuite = new SuiteReport();
  2416. // The initial "currentModule" represents the global (or top-level) module that
  2417. // is not explicitly defined by the user, therefore we add the "globalSuite" to
  2418. // it since each module has a suiteReport associated with it.
  2419. config.currentModule.suiteReport = globalSuite;
  2420. var globalStartCalled = false;
  2421. var runStarted = false;
  2422. // Figure out if we're running the tests from a server or not
  2423. QUnit.isLocal = !(defined.document && window.location.protocol !== "file:");
  2424. // Expose the current QUnit version
  2425. QUnit.version = "2.6.2";
  2426. extend(QUnit, {
  2427. on: on,
  2428. module: module$1,
  2429. test: test,
  2430. todo: todo,
  2431. skip: skip,
  2432. only: only,
  2433. start: function start(count) {
  2434. var globalStartAlreadyCalled = globalStartCalled;
  2435. if (!config.current) {
  2436. globalStartCalled = true;
  2437. if (runStarted) {
  2438. throw new Error("Called start() while test already started running");
  2439. } else if (globalStartAlreadyCalled || count > 1) {
  2440. throw new Error("Called start() outside of a test context too many times");
  2441. } else if (config.autostart) {
  2442. throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
  2443. } else if (!config.pageLoaded) {
  2444. // The page isn't completely loaded yet, so we set autostart and then
  2445. // load if we're in Node or wait for the browser's load event.
  2446. config.autostart = true;
  2447. // Starts from Node even if .load was not previously called. We still return
  2448. // early otherwise we'll wind up "beginning" twice.
  2449. if (!defined.document) {
  2450. QUnit.load();
  2451. }
  2452. return;
  2453. }
  2454. } else {
  2455. throw new Error("QUnit.start cannot be called inside a test context.");
  2456. }
  2457. scheduleBegin();
  2458. },
  2459. config: config,
  2460. is: is,
  2461. objectType: objectType,
  2462. extend: extend,
  2463. load: function load() {
  2464. config.pageLoaded = true;
  2465. // Initialize the configuration options
  2466. extend(config, {
  2467. stats: { all: 0, bad: 0 },
  2468. started: 0,
  2469. updateRate: 1000,
  2470. autostart: true,
  2471. filter: ""
  2472. }, true);
  2473. if (!runStarted) {
  2474. config.blocking = false;
  2475. if (config.autostart) {
  2476. scheduleBegin();
  2477. }
  2478. }
  2479. },
  2480. stack: function stack(offset) {
  2481. offset = (offset || 0) + 2;
  2482. return sourceFromStacktrace(offset);
  2483. },
  2484. onError: onError,
  2485. onUnhandledRejection: onUnhandledRejection
  2486. });
  2487. QUnit.pushFailure = pushFailure;
  2488. QUnit.assert = Assert.prototype;
  2489. QUnit.equiv = equiv;
  2490. QUnit.dump = dump;
  2491. registerLoggingCallbacks(QUnit);
  2492. function scheduleBegin() {
  2493. runStarted = true;
  2494. // Add a slight delay to allow definition of more modules and tests.
  2495. if (defined.setTimeout) {
  2496. setTimeout(function () {
  2497. begin();
  2498. });
  2499. } else {
  2500. begin();
  2501. }
  2502. }
  2503. function begin() {
  2504. var i,
  2505. l,
  2506. modulesLog = [];
  2507. // If the test run hasn't officially begun yet
  2508. if (!config.started) {
  2509. // Record the time of the test run's beginning
  2510. config.started = now();
  2511. // Delete the loose unnamed module if unused.
  2512. if (config.modules[0].name === "" && config.modules[0].tests.length === 0) {
  2513. config.modules.shift();
  2514. }
  2515. // Avoid unnecessary information by not logging modules' test environments
  2516. for (i = 0, l = config.modules.length; i < l; i++) {
  2517. modulesLog.push({
  2518. name: config.modules[i].name,
  2519. tests: config.modules[i].tests
  2520. });
  2521. }
  2522. // The test run is officially beginning now
  2523. emit("runStart", globalSuite.start(true));
  2524. runLoggingCallbacks("begin", {
  2525. totalTests: Test.count,
  2526. modules: modulesLog
  2527. });
  2528. }
  2529. config.blocking = false;
  2530. ProcessingQueue.advance();
  2531. }
  2532. exportQUnit(QUnit);
  2533. (function () {
  2534. if (typeof window === "undefined" || typeof document === "undefined") {
  2535. return;
  2536. }
  2537. var config = QUnit.config,
  2538. hasOwn = Object.prototype.hasOwnProperty;
  2539. // Stores fixture HTML for resetting later
  2540. function storeFixture() {
  2541. // Avoid overwriting user-defined values
  2542. if (hasOwn.call(config, "fixture")) {
  2543. return;
  2544. }
  2545. var fixture = document.getElementById("qunit-fixture");
  2546. if (fixture) {
  2547. config.fixture = fixture.cloneNode(true);
  2548. }
  2549. }
  2550. QUnit.begin(storeFixture);
  2551. // Resets the fixture DOM element if available.
  2552. function resetFixture() {
  2553. if (config.fixture == null) {
  2554. return;
  2555. }
  2556. var fixture = document.getElementById("qunit-fixture");
  2557. var resetFixtureType = _typeof(config.fixture);
  2558. if (resetFixtureType === "string") {
  2559. // support user defined values for `config.fixture`
  2560. var newFixture = document.createElement("div");
  2561. newFixture.setAttribute("id", "qunit-fixture");
  2562. newFixture.innerHTML = config.fixture;
  2563. fixture.parentNode.replaceChild(newFixture, fixture);
  2564. } else {
  2565. var clonedFixture = config.fixture.cloneNode(true);
  2566. fixture.parentNode.replaceChild(clonedFixture, fixture);
  2567. }
  2568. }
  2569. QUnit.testStart(resetFixture);
  2570. })();
  2571. (function () {
  2572. // Only interact with URLs via window.location
  2573. var location = typeof window !== "undefined" && window.location;
  2574. if (!location) {
  2575. return;
  2576. }
  2577. var urlParams = getUrlParams();
  2578. QUnit.urlParams = urlParams;
  2579. // Match module/test by inclusion in an array
  2580. QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
  2581. QUnit.config.testId = [].concat(urlParams.testId || []);
  2582. // Exact case-insensitive match of the module name
  2583. QUnit.config.module = urlParams.module;
  2584. // Regular expression or case-insenstive substring match against "moduleName: testName"
  2585. QUnit.config.filter = urlParams.filter;
  2586. // Test order randomization
  2587. if (urlParams.seed === true) {
  2588. // Generate a random seed if the option is specified without a value
  2589. QUnit.config.seed = Math.random().toString(36).slice(2);
  2590. } else if (urlParams.seed) {
  2591. QUnit.config.seed = urlParams.seed;
  2592. }
  2593. // Add URL-parameter-mapped config values with UI form rendering data
  2594. QUnit.config.urlConfig.push({
  2595. id: "hidepassed",
  2596. label: "Hide passed tests",
  2597. tooltip: "Only show tests and assertions that fail. Stored as query-strings."
  2598. }, {
  2599. id: "noglobals",
  2600. label: "Check for Globals",
  2601. tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
  2602. }, {
  2603. id: "notrycatch",
  2604. label: "No try-catch",
  2605. tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
  2606. });
  2607. QUnit.begin(function () {
  2608. var i,
  2609. option,
  2610. urlConfig = QUnit.config.urlConfig;
  2611. for (i = 0; i < urlConfig.length; i++) {
  2612. // Options can be either strings or objects with nonempty "id" properties
  2613. option = QUnit.config.urlConfig[i];
  2614. if (typeof option !== "string") {
  2615. option = option.id;
  2616. }
  2617. if (QUnit.config[option] === undefined) {
  2618. QUnit.config[option] = urlParams[option];
  2619. }
  2620. }
  2621. });
  2622. function getUrlParams() {
  2623. var i, param, name, value;
  2624. var urlParams = Object.create(null);
  2625. var params = location.search.slice(1).split("&");
  2626. var length = params.length;
  2627. for (i = 0; i < length; i++) {
  2628. if (params[i]) {
  2629. param = params[i].split("=");
  2630. name = decodeQueryParam(param[0]);
  2631. // Allow just a key to turn on a flag, e.g., test.html?noglobals
  2632. value = param.length === 1 || decodeQueryParam(param.slice(1).join("="));
  2633. if (name in urlParams) {
  2634. urlParams[name] = [].concat(urlParams[name], value);
  2635. } else {
  2636. urlParams[name] = value;
  2637. }
  2638. }
  2639. }
  2640. return urlParams;
  2641. }
  2642. function decodeQueryParam(param) {
  2643. return decodeURIComponent(param.replace(/\+/g, "%20"));
  2644. }
  2645. })();
  2646. var stats = {
  2647. passedTests: 0,
  2648. failedTests: 0,
  2649. skippedTests: 0,
  2650. todoTests: 0
  2651. };
  2652. // Escape text for attribute or text content.
  2653. function escapeText(s) {
  2654. if (!s) {
  2655. return "";
  2656. }
  2657. s = s + "";
  2658. // Both single quotes and double quotes (for attributes)
  2659. return s.replace(/['"<>&]/g, function (s) {
  2660. switch (s) {
  2661. case "'":
  2662. return "&#039;";
  2663. case "\"":
  2664. return "&quot;";
  2665. case "<":
  2666. return "&lt;";
  2667. case ">":
  2668. return "&gt;";
  2669. case "&":
  2670. return "&amp;";
  2671. }
  2672. });
  2673. }
  2674. (function () {
  2675. // Don't load the HTML Reporter on non-browser environments
  2676. if (typeof window === "undefined" || !window.document) {
  2677. return;
  2678. }
  2679. var config = QUnit.config,
  2680. document$$1 = window.document,
  2681. collapseNext = false,
  2682. hasOwn = Object.prototype.hasOwnProperty,
  2683. unfilteredUrl = setUrl({ filter: undefined, module: undefined,
  2684. moduleId: undefined, testId: undefined }),
  2685. modulesList = [];
  2686. function addEvent(elem, type, fn) {
  2687. elem.addEventListener(type, fn, false);
  2688. }
  2689. function removeEvent(elem, type, fn) {
  2690. elem.removeEventListener(type, fn, false);
  2691. }
  2692. function addEvents(elems, type, fn) {
  2693. var i = elems.length;
  2694. while (i--) {
  2695. addEvent(elems[i], type, fn);
  2696. }
  2697. }
  2698. function hasClass(elem, name) {
  2699. return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0;
  2700. }
  2701. function addClass(elem, name) {
  2702. if (!hasClass(elem, name)) {
  2703. elem.className += (elem.className ? " " : "") + name;
  2704. }
  2705. }
  2706. function toggleClass(elem, name, force) {
  2707. if (force || typeof force === "undefined" && !hasClass(elem, name)) {
  2708. addClass(elem, name);
  2709. } else {
  2710. removeClass(elem, name);
  2711. }
  2712. }
  2713. function removeClass(elem, name) {
  2714. var set = " " + elem.className + " ";
  2715. // Class name may appear multiple times
  2716. while (set.indexOf(" " + name + " ") >= 0) {
  2717. set = set.replace(" " + name + " ", " ");
  2718. }
  2719. // Trim for prettiness
  2720. elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
  2721. }
  2722. function id(name) {
  2723. return document$$1.getElementById && document$$1.getElementById(name);
  2724. }
  2725. function abortTests() {
  2726. var abortButton = id("qunit-abort-tests-button");
  2727. if (abortButton) {
  2728. abortButton.disabled = true;
  2729. abortButton.innerHTML = "Aborting...";
  2730. }
  2731. QUnit.config.queue.length = 0;
  2732. return false;
  2733. }
  2734. function interceptNavigation(ev) {
  2735. applyUrlParams();
  2736. if (ev && ev.preventDefault) {
  2737. ev.preventDefault();
  2738. }
  2739. return false;
  2740. }
  2741. function getUrlConfigHtml() {
  2742. var i,
  2743. j,
  2744. val,
  2745. escaped,
  2746. escapedTooltip,
  2747. selection = false,
  2748. urlConfig = config.urlConfig,
  2749. urlConfigHtml = "";
  2750. for (i = 0; i < urlConfig.length; i++) {
  2751. // Options can be either strings or objects with nonempty "id" properties
  2752. val = config.urlConfig[i];
  2753. if (typeof val === "string") {
  2754. val = {
  2755. id: val,
  2756. label: val
  2757. };
  2758. }
  2759. escaped = escapeText(val.id);
  2760. escapedTooltip = escapeText(val.tooltip);
  2761. if (!val.value || typeof val.value === "string") {
  2762. urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'><input id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' type='checkbox'" + (val.value ? " value='" + escapeText(val.value) + "'" : "") + (config[val.id] ? " checked='checked'" : "") + " title='" + escapedTooltip + "' />" + escapeText(val.label) + "</label>";
  2763. } else {
  2764. urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
  2765. if (QUnit.is("array", val.value)) {
  2766. for (j = 0; j < val.value.length; j++) {
  2767. escaped = escapeText(val.value[j]);
  2768. urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>";
  2769. }
  2770. } else {
  2771. for (j in val.value) {
  2772. if (hasOwn.call(val.value, j)) {
  2773. urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>";
  2774. }
  2775. }
  2776. }
  2777. if (config[val.id] && !selection) {
  2778. escaped = escapeText(config[val.id]);
  2779. urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>";
  2780. }
  2781. urlConfigHtml += "</select>";
  2782. }
  2783. }
  2784. return urlConfigHtml;
  2785. }
  2786. // Handle "click" events on toolbar checkboxes and "change" for select menus.
  2787. // Updates the URL with the new state of `config.urlConfig` values.
  2788. function toolbarChanged() {
  2789. var updatedUrl,
  2790. value,
  2791. tests,
  2792. field = this,
  2793. params = {};
  2794. // Detect if field is a select menu or a checkbox
  2795. if ("selectedIndex" in field) {
  2796. value = field.options[field.selectedIndex].value || undefined;
  2797. } else {
  2798. value = field.checked ? field.defaultValue || true : undefined;
  2799. }
  2800. params[field.name] = value;
  2801. updatedUrl = setUrl(params);
  2802. // Check if we can apply the change without a page refresh
  2803. if ("hidepassed" === field.name && "replaceState" in window.history) {
  2804. QUnit.urlParams[field.name] = value;
  2805. config[field.name] = value || false;
  2806. tests = id("qunit-tests");
  2807. if (tests) {
  2808. toggleClass(tests, "hidepass", value || false);
  2809. }
  2810. window.history.replaceState(null, "", updatedUrl);
  2811. } else {
  2812. window.location = updatedUrl;
  2813. }
  2814. }
  2815. function setUrl(params) {
  2816. var key,
  2817. arrValue,
  2818. i,
  2819. querystring = "?",
  2820. location = window.location;
  2821. params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
  2822. for (key in params) {
  2823. // Skip inherited or undefined properties
  2824. if (hasOwn.call(params, key) && params[key] !== undefined) {
  2825. // Output a parameter for each value of this key
  2826. // (but usually just one)
  2827. arrValue = [].concat(params[key]);
  2828. for (i = 0; i < arrValue.length; i++) {
  2829. querystring += encodeURIComponent(key);
  2830. if (arrValue[i] !== true) {
  2831. querystring += "=" + encodeURIComponent(arrValue[i]);
  2832. }
  2833. querystring += "&";
  2834. }
  2835. }
  2836. }
  2837. return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1);
  2838. }
  2839. function applyUrlParams() {
  2840. var i,
  2841. selectedModules = [],
  2842. modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
  2843. filter = id("qunit-filter-input").value;
  2844. for (i = 0; i < modulesList.length; i++) {
  2845. if (modulesList[i].checked) {
  2846. selectedModules.push(modulesList[i].value);
  2847. }
  2848. }
  2849. window.location = setUrl({
  2850. filter: filter === "" ? undefined : filter,
  2851. moduleId: selectedModules.length === 0 ? undefined : selectedModules,
  2852. // Remove module and testId filter
  2853. module: undefined,
  2854. testId: undefined
  2855. });
  2856. }
  2857. function toolbarUrlConfigContainer() {
  2858. var urlConfigContainer = document$$1.createElement("span");
  2859. urlConfigContainer.innerHTML = getUrlConfigHtml();
  2860. addClass(urlConfigContainer, "qunit-url-config");
  2861. addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged);
  2862. addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged);
  2863. return urlConfigContainer;
  2864. }
  2865. function abortTestsButton() {
  2866. var button = document$$1.createElement("button");
  2867. button.id = "qunit-abort-tests-button";
  2868. button.innerHTML = "Abort";
  2869. addEvent(button, "click", abortTests);
  2870. return button;
  2871. }
  2872. function toolbarLooseFilter() {
  2873. var filter = document$$1.createElement("form"),
  2874. label = document$$1.createElement("label"),
  2875. input = document$$1.createElement("input"),
  2876. button = document$$1.createElement("button");
  2877. addClass(filter, "qunit-filter");
  2878. label.innerHTML = "Filter: ";
  2879. input.type = "text";
  2880. input.value = config.filter || "";
  2881. input.name = "filter";
  2882. input.id = "qunit-filter-input";
  2883. button.innerHTML = "Go";
  2884. label.appendChild(input);
  2885. filter.appendChild(label);
  2886. filter.appendChild(document$$1.createTextNode(" "));
  2887. filter.appendChild(button);
  2888. addEvent(filter, "submit", interceptNavigation);
  2889. return filter;
  2890. }
  2891. function moduleListHtml() {
  2892. var i,
  2893. checked,
  2894. html = "";
  2895. for (i = 0; i < config.modules.length; i++) {
  2896. if (config.modules[i].name !== "") {
  2897. checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1;
  2898. html += "<li><label class='clickable" + (checked ? " checked" : "") + "'><input type='checkbox' " + "value='" + config.modules[i].moduleId + "'" + (checked ? " checked='checked'" : "") + " />" + escapeText(config.modules[i].name) + "</label></li>";
  2899. }
  2900. }
  2901. return html;
  2902. }
  2903. function toolbarModuleFilter() {
  2904. var allCheckbox,
  2905. commit,
  2906. reset,
  2907. moduleFilter = document$$1.createElement("form"),
  2908. label = document$$1.createElement("label"),
  2909. moduleSearch = document$$1.createElement("input"),
  2910. dropDown = document$$1.createElement("div"),
  2911. actions = document$$1.createElement("span"),
  2912. dropDownList = document$$1.createElement("ul"),
  2913. dirty = false;
  2914. moduleSearch.id = "qunit-modulefilter-search";
  2915. moduleSearch.autocomplete = "off";
  2916. addEvent(moduleSearch, "input", searchInput);
  2917. addEvent(moduleSearch, "input", searchFocus);
  2918. addEvent(moduleSearch, "focus", searchFocus);
  2919. addEvent(moduleSearch, "click", searchFocus);
  2920. label.id = "qunit-modulefilter-search-container";
  2921. label.innerHTML = "Module: ";
  2922. label.appendChild(moduleSearch);
  2923. actions.id = "qunit-modulefilter-actions";
  2924. actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + ">All modules</label>";
  2925. allCheckbox = actions.lastChild.firstChild;
  2926. commit = actions.firstChild;
  2927. reset = commit.nextSibling;
  2928. addEvent(commit, "click", applyUrlParams);
  2929. dropDownList.id = "qunit-modulefilter-dropdown-list";
  2930. dropDownList.innerHTML = moduleListHtml();
  2931. dropDown.id = "qunit-modulefilter-dropdown";
  2932. dropDown.style.display = "none";
  2933. dropDown.appendChild(actions);
  2934. dropDown.appendChild(dropDownList);
  2935. addEvent(dropDown, "change", selectionChange);
  2936. selectionChange();
  2937. moduleFilter.id = "qunit-modulefilter";
  2938. moduleFilter.appendChild(label);
  2939. moduleFilter.appendChild(dropDown);
  2940. addEvent(moduleFilter, "submit", interceptNavigation);
  2941. addEvent(moduleFilter, "reset", function () {
  2942. // Let the reset happen, then update styles
  2943. window.setTimeout(selectionChange);
  2944. });
  2945. // Enables show/hide for the dropdown
  2946. function searchFocus() {
  2947. if (dropDown.style.display !== "none") {
  2948. return;
  2949. }
  2950. dropDown.style.display = "block";
  2951. addEvent(document$$1, "click", hideHandler);
  2952. addEvent(document$$1, "keydown", hideHandler);
  2953. // Hide on Escape keydown or outside-container click
  2954. function hideHandler(e) {
  2955. var inContainer = moduleFilter.contains(e.target);
  2956. if (e.keyCode === 27 || !inContainer) {
  2957. if (e.keyCode === 27 && inContainer) {
  2958. moduleSearch.focus();
  2959. }
  2960. dropDown.style.display = "none";
  2961. removeEvent(document$$1, "click", hideHandler);
  2962. removeEvent(document$$1, "keydown", hideHandler);
  2963. moduleSearch.value = "";
  2964. searchInput();
  2965. }
  2966. }
  2967. }
  2968. // Processes module search box input
  2969. function searchInput() {
  2970. var i,
  2971. item,
  2972. searchText = moduleSearch.value.toLowerCase(),
  2973. listItems = dropDownList.children;
  2974. for (i = 0; i < listItems.length; i++) {
  2975. item = listItems[i];
  2976. if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) {
  2977. item.style.display = "";
  2978. } else {
  2979. item.style.display = "none";
  2980. }
  2981. }
  2982. }
  2983. // Processes selection changes
  2984. function selectionChange(evt) {
  2985. var i,
  2986. item,
  2987. checkbox = evt && evt.target || allCheckbox,
  2988. modulesList = dropDownList.getElementsByTagName("input"),
  2989. selectedNames = [];
  2990. toggleClass(checkbox.parentNode, "checked", checkbox.checked);
  2991. dirty = false;
  2992. if (checkbox.checked && checkbox !== allCheckbox) {
  2993. allCheckbox.checked = false;
  2994. removeClass(allCheckbox.parentNode, "checked");
  2995. }
  2996. for (i = 0; i < modulesList.length; i++) {
  2997. item = modulesList[i];
  2998. if (!evt) {
  2999. toggleClass(item.parentNode, "checked", item.checked);
  3000. } else if (checkbox === allCheckbox && checkbox.checked) {
  3001. item.checked = false;
  3002. removeClass(item.parentNode, "checked");
  3003. }
  3004. dirty = dirty || item.checked !== item.defaultChecked;
  3005. if (item.checked) {
  3006. selectedNames.push(item.parentNode.textContent);
  3007. }
  3008. }
  3009. commit.style.display = reset.style.display = dirty ? "" : "none";
  3010. moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent;
  3011. moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent);
  3012. }
  3013. return moduleFilter;
  3014. }
  3015. function appendToolbar() {
  3016. var toolbar = id("qunit-testrunner-toolbar");
  3017. if (toolbar) {
  3018. toolbar.appendChild(toolbarUrlConfigContainer());
  3019. toolbar.appendChild(toolbarModuleFilter());
  3020. toolbar.appendChild(toolbarLooseFilter());
  3021. toolbar.appendChild(document$$1.createElement("div")).className = "clearfix";
  3022. }
  3023. }
  3024. function appendHeader() {
  3025. var header = id("qunit-header");
  3026. if (header) {
  3027. header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> ";
  3028. }
  3029. }
  3030. function appendBanner() {
  3031. var banner = id("qunit-banner");
  3032. if (banner) {
  3033. banner.className = "";
  3034. }
  3035. }
  3036. function appendTestResults() {
  3037. var tests = id("qunit-tests"),
  3038. result = id("qunit-testresult"),
  3039. controls;
  3040. if (result) {
  3041. result.parentNode.removeChild(result);
  3042. }
  3043. if (tests) {
  3044. tests.innerHTML = "";
  3045. result = document$$1.createElement("p");
  3046. result.id = "qunit-testresult";
  3047. result.className = "result";
  3048. tests.parentNode.insertBefore(result, tests);
  3049. result.innerHTML = "<div id=\"qunit-testresult-display\">Running...<br />&#160;</div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>";
  3050. controls = id("qunit-testresult-controls");
  3051. }
  3052. if (controls) {
  3053. controls.appendChild(abortTestsButton());
  3054. }
  3055. }
  3056. function appendFilteredTest() {
  3057. var testId = QUnit.config.testId;
  3058. if (!testId || testId.length <= 0) {
  3059. return "";
  3060. }
  3061. return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
  3062. }
  3063. function appendUserAgent() {
  3064. var userAgent = id("qunit-userAgent");
  3065. if (userAgent) {
  3066. userAgent.innerHTML = "";
  3067. userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
  3068. }
  3069. }
  3070. function appendInterface() {
  3071. var qunit = id("qunit");
  3072. if (qunit) {
  3073. qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document$$1.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
  3074. }
  3075. appendHeader();
  3076. appendBanner();
  3077. appendTestResults();
  3078. appendUserAgent();
  3079. appendToolbar();
  3080. }
  3081. function appendTestsList(modules) {
  3082. var i, l, x, z, test, moduleObj;
  3083. for (i = 0, l = modules.length; i < l; i++) {
  3084. moduleObj = modules[i];
  3085. for (x = 0, z = moduleObj.tests.length; x < z; x++) {
  3086. test = moduleObj.tests[x];
  3087. appendTest(test.name, test.testId, moduleObj.name);
  3088. }
  3089. }
  3090. }
  3091. function appendTest(name, testId, moduleName) {
  3092. var title,
  3093. rerunTrigger,
  3094. testBlock,
  3095. assertList,
  3096. tests = id("qunit-tests");
  3097. if (!tests) {
  3098. return;
  3099. }
  3100. title = document$$1.createElement("strong");
  3101. title.innerHTML = getNameHtml(name, moduleName);
  3102. rerunTrigger = document$$1.createElement("a");
  3103. rerunTrigger.innerHTML = "Rerun";
  3104. rerunTrigger.href = setUrl({ testId: testId });
  3105. testBlock = document$$1.createElement("li");
  3106. testBlock.appendChild(title);
  3107. testBlock.appendChild(rerunTrigger);
  3108. testBlock.id = "qunit-test-output-" + testId;
  3109. assertList = document$$1.createElement("ol");
  3110. assertList.className = "qunit-assert-list";
  3111. testBlock.appendChild(assertList);
  3112. tests.appendChild(testBlock);
  3113. }
  3114. // HTML Reporter initialization and load
  3115. QUnit.begin(function (details) {
  3116. var i, moduleObj, tests;
  3117. // Sort modules by name for the picker
  3118. for (i = 0; i < details.modules.length; i++) {
  3119. moduleObj = details.modules[i];
  3120. if (moduleObj.name) {
  3121. modulesList.push(moduleObj.name);
  3122. }
  3123. }
  3124. modulesList.sort(function (a, b) {
  3125. return a.localeCompare(b);
  3126. });
  3127. // Initialize QUnit elements
  3128. appendInterface();
  3129. appendTestsList(details.modules);
  3130. tests = id("qunit-tests");
  3131. if (tests && config.hidepassed) {
  3132. addClass(tests, "hidepass");
  3133. }
  3134. });
  3135. QUnit.done(function (details) {
  3136. var banner = id("qunit-banner"),
  3137. tests = id("qunit-tests"),
  3138. abortButton = id("qunit-abort-tests-button"),
  3139. totalTests = stats.passedTests + stats.skippedTests + stats.todoTests + stats.failedTests,
  3140. html = [totalTests, " tests completed in ", details.runtime, " milliseconds, with ", stats.failedTests, " failed, ", stats.skippedTests, " skipped, and ", stats.todoTests, " todo.<br />", "<span class='passed'>", details.passed, "</span> assertions of <span class='total'>", details.total, "</span> passed, <span class='failed'>", details.failed, "</span> failed."].join(""),
  3141. test,
  3142. assertLi,
  3143. assertList;
  3144. // Update remaing tests to aborted
  3145. if (abortButton && abortButton.disabled) {
  3146. html = "Tests aborted after " + details.runtime + " milliseconds.";
  3147. for (var i = 0; i < tests.children.length; i++) {
  3148. test = tests.children[i];
  3149. if (test.className === "" || test.className === "running") {
  3150. test.className = "aborted";
  3151. assertList = test.getElementsByTagName("ol")[0];
  3152. assertLi = document$$1.createElement("li");
  3153. assertLi.className = "fail";
  3154. assertLi.innerHTML = "Test aborted.";
  3155. assertList.appendChild(assertLi);
  3156. }
  3157. }
  3158. }
  3159. if (banner && (!abortButton || abortButton.disabled === false)) {
  3160. banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass";
  3161. }
  3162. if (abortButton) {
  3163. abortButton.parentNode.removeChild(abortButton);
  3164. }
  3165. if (tests) {
  3166. id("qunit-testresult-display").innerHTML = html;
  3167. }
  3168. if (config.altertitle && document$$1.title) {
  3169. // Show ✖ for good, ✔ for bad suite result in title
  3170. // use escape sequences in case file gets loaded with non-utf-8
  3171. // charset
  3172. document$$1.title = [stats.failedTests ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
  3173. }
  3174. // Scroll back to top to show results
  3175. if (config.scrolltop && window.scrollTo) {
  3176. window.scrollTo(0, 0);
  3177. }
  3178. });
  3179. function getNameHtml(name, module) {
  3180. var nameHtml = "";
  3181. if (module) {
  3182. nameHtml = "<span class='module-name'>" + escapeText(module) + "</span>: ";
  3183. }
  3184. nameHtml += "<span class='test-name'>" + escapeText(name) + "</span>";
  3185. return nameHtml;
  3186. }
  3187. QUnit.testStart(function (details) {
  3188. var running, testBlock, bad;
  3189. testBlock = id("qunit-test-output-" + details.testId);
  3190. if (testBlock) {
  3191. testBlock.className = "running";
  3192. } else {
  3193. // Report later registered tests
  3194. appendTest(details.name, details.testId, details.module);
  3195. }
  3196. running = id("qunit-testresult-display");
  3197. if (running) {
  3198. bad = QUnit.config.reorder && details.previousFailure;
  3199. running.innerHTML = [bad ? "Rerunning previously failed test: <br />" : "Running: <br />", getNameHtml(details.name, details.module)].join("");
  3200. }
  3201. });
  3202. function stripHtml(string) {
  3203. // Strip tags, html entity and whitespaces
  3204. return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/&quot;/g, "").replace(/\s+/g, "");
  3205. }
  3206. QUnit.log(function (details) {
  3207. var assertList,
  3208. assertLi,
  3209. message,
  3210. expected,
  3211. actual,
  3212. diff,
  3213. showDiff = false,
  3214. testItem = id("qunit-test-output-" + details.testId);
  3215. if (!testItem) {
  3216. return;
  3217. }
  3218. message = escapeText(details.message) || (details.result ? "okay" : "failed");
  3219. message = "<span class='test-message'>" + message + "</span>";
  3220. message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
  3221. // The pushFailure doesn't provide details.expected
  3222. // when it calls, it's implicit to also not show expected and diff stuff
  3223. // Also, we need to check details.expected existence, as it can exist and be undefined
  3224. if (!details.result && hasOwn.call(details, "expected")) {
  3225. if (details.negative) {
  3226. expected = "NOT " + QUnit.dump.parse(details.expected);
  3227. } else {
  3228. expected = QUnit.dump.parse(details.expected);
  3229. }
  3230. actual = QUnit.dump.parse(details.actual);
  3231. message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + "</pre></td></tr>";
  3232. if (actual !== expected) {
  3233. message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>";
  3234. if (typeof details.actual === "number" && typeof details.expected === "number") {
  3235. if (!isNaN(details.actual) && !isNaN(details.expected)) {
  3236. showDiff = true;
  3237. diff = details.actual - details.expected;
  3238. diff = (diff > 0 ? "+" : "") + diff;
  3239. }
  3240. } else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") {
  3241. diff = QUnit.diff(expected, actual);
  3242. // don't show diff if there is zero overlap
  3243. showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length;
  3244. }
  3245. if (showDiff) {
  3246. message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>";
  3247. }
  3248. } else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) {
  3249. message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " + " run with a higher max depth or <a href='" + escapeText(setUrl({ maxDepth: -1 })) + "'>" + "Rerun</a> without max depth.</p></td></tr>";
  3250. } else {
  3251. message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>";
  3252. }
  3253. if (details.source) {
  3254. message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>";
  3255. }
  3256. message += "</table>";
  3257. // This occurs when pushFailure is set and we have an extracted stack trace
  3258. } else if (!details.result && details.source) {
  3259. message += "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>" + "</table>";
  3260. }
  3261. assertList = testItem.getElementsByTagName("ol")[0];
  3262. assertLi = document$$1.createElement("li");
  3263. assertLi.className = details.result ? "pass" : "fail";
  3264. assertLi.innerHTML = message;
  3265. assertList.appendChild(assertLi);
  3266. });
  3267. QUnit.testDone(function (details) {
  3268. var testTitle,
  3269. time,
  3270. testItem,
  3271. assertList,
  3272. good,
  3273. bad,
  3274. testCounts,
  3275. skipped,
  3276. sourceName,
  3277. tests = id("qunit-tests");
  3278. if (!tests) {
  3279. return;
  3280. }
  3281. testItem = id("qunit-test-output-" + details.testId);
  3282. assertList = testItem.getElementsByTagName("ol")[0];
  3283. good = details.passed;
  3284. bad = details.failed;
  3285. // This test passed if it has no unexpected failed assertions
  3286. var testPassed = details.failed > 0 ? details.todo : !details.todo;
  3287. if (testPassed) {
  3288. // Collapse the passing tests
  3289. addClass(assertList, "qunit-collapsed");
  3290. } else if (config.collapse) {
  3291. if (!collapseNext) {
  3292. // Skip collapsing the first failing test
  3293. collapseNext = true;
  3294. } else {
  3295. // Collapse remaining tests
  3296. addClass(assertList, "qunit-collapsed");
  3297. }
  3298. }
  3299. // The testItem.firstChild is the test name
  3300. testTitle = testItem.firstChild;
  3301. testCounts = bad ? "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : "";
  3302. testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ")</b>";
  3303. if (details.skipped) {
  3304. stats.skippedTests++;
  3305. testItem.className = "skipped";
  3306. skipped = document$$1.createElement("em");
  3307. skipped.className = "qunit-skipped-label";
  3308. skipped.innerHTML = "skipped";
  3309. testItem.insertBefore(skipped, testTitle);
  3310. } else {
  3311. addEvent(testTitle, "click", function () {
  3312. toggleClass(assertList, "qunit-collapsed");
  3313. });
  3314. testItem.className = testPassed ? "pass" : "fail";
  3315. if (details.todo) {
  3316. var todoLabel = document$$1.createElement("em");
  3317. todoLabel.className = "qunit-todo-label";
  3318. todoLabel.innerHTML = "todo";
  3319. testItem.className += " todo";
  3320. testItem.insertBefore(todoLabel, testTitle);
  3321. }
  3322. time = document$$1.createElement("span");
  3323. time.className = "runtime";
  3324. time.innerHTML = details.runtime + " ms";
  3325. testItem.insertBefore(time, assertList);
  3326. if (!testPassed) {
  3327. stats.failedTests++;
  3328. } else if (details.todo) {
  3329. stats.todoTests++;
  3330. } else {
  3331. stats.passedTests++;
  3332. }
  3333. }
  3334. // Show the source of the test when showing assertions
  3335. if (details.source) {
  3336. sourceName = document$$1.createElement("p");
  3337. sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
  3338. addClass(sourceName, "qunit-source");
  3339. if (testPassed) {
  3340. addClass(sourceName, "qunit-collapsed");
  3341. }
  3342. addEvent(testTitle, "click", function () {
  3343. toggleClass(sourceName, "qunit-collapsed");
  3344. });
  3345. testItem.appendChild(sourceName);
  3346. }
  3347. });
  3348. // Avoid readyState issue with phantomjs
  3349. // Ref: #818
  3350. var notPhantom = function (p) {
  3351. return !(p && p.version && p.version.major > 0);
  3352. }(window.phantom);
  3353. if (notPhantom && document$$1.readyState === "complete") {
  3354. QUnit.load();
  3355. } else {
  3356. addEvent(window, "load", QUnit.load);
  3357. }
  3358. // Wrap window.onerror. We will call the original window.onerror to see if
  3359. // the existing handler fully handles the error; if not, we will call the
  3360. // QUnit.onError function.
  3361. var originalWindowOnError = window.onerror;
  3362. // Cover uncaught exceptions
  3363. // Returning true will suppress the default browser handler,
  3364. // returning false will let it run.
  3365. window.onerror = function (message, fileName, lineNumber) {
  3366. var ret = false;
  3367. if (originalWindowOnError) {
  3368. for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
  3369. args[_key - 3] = arguments[_key];
  3370. }
  3371. ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber].concat(args));
  3372. }
  3373. // Treat return value as window.onerror itself does,
  3374. // Only do our handling if not suppressed.
  3375. if (ret !== true) {
  3376. var error = {
  3377. message: message,
  3378. fileName: fileName,
  3379. lineNumber: lineNumber
  3380. };
  3381. ret = QUnit.onError(error);
  3382. }
  3383. return ret;
  3384. };
  3385. // Listen for unhandled rejections, and call QUnit.onUnhandledRejection
  3386. window.addEventListener("unhandledrejection", function (event) {
  3387. QUnit.onUnhandledRejection(event.reason);
  3388. });
  3389. })();
  3390. /*
  3391. * This file is a modified version of google-diff-match-patch's JavaScript implementation
  3392. * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
  3393. * modifications are licensed as more fully set forth in LICENSE.txt.
  3394. *
  3395. * The original source of google-diff-match-patch is attributable and licensed as follows:
  3396. *
  3397. * Copyright 2006 Google Inc.
  3398. * https://code.google.com/p/google-diff-match-patch/
  3399. *
  3400. * Licensed under the Apache License, Version 2.0 (the "License");
  3401. * you may not use this file except in compliance with the License.
  3402. * You may obtain a copy of the License at
  3403. *
  3404. * https://www.apache.org/licenses/LICENSE-2.0
  3405. *
  3406. * Unless required by applicable law or agreed to in writing, software
  3407. * distributed under the License is distributed on an "AS IS" BASIS,
  3408. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3409. * See the License for the specific language governing permissions and
  3410. * limitations under the License.
  3411. *
  3412. * More Info:
  3413. * https://code.google.com/p/google-diff-match-patch/
  3414. *
  3415. * Usage: QUnit.diff(expected, actual)
  3416. *
  3417. */
  3418. QUnit.diff = function () {
  3419. function DiffMatchPatch() {}
  3420. // DIFF FUNCTIONS
  3421. /**
  3422. * The data structure representing a diff is an array of tuples:
  3423. * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
  3424. * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
  3425. */
  3426. var DIFF_DELETE = -1,
  3427. DIFF_INSERT = 1,
  3428. DIFF_EQUAL = 0;
  3429. /**
  3430. * Find the differences between two texts. Simplifies the problem by stripping
  3431. * any common prefix or suffix off the texts before diffing.
  3432. * @param {string} text1 Old string to be diffed.
  3433. * @param {string} text2 New string to be diffed.
  3434. * @param {boolean=} optChecklines Optional speedup flag. If present and false,
  3435. * then don't run a line-level diff first to identify the changed areas.
  3436. * Defaults to true, which does a faster, slightly less optimal diff.
  3437. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  3438. */
  3439. DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) {
  3440. var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs;
  3441. // The diff must be complete in up to 1 second.
  3442. deadline = new Date().getTime() + 1000;
  3443. // Check for null inputs.
  3444. if (text1 === null || text2 === null) {
  3445. throw new Error("Null input. (DiffMain)");
  3446. }
  3447. // Check for equality (speedup).
  3448. if (text1 === text2) {
  3449. if (text1) {
  3450. return [[DIFF_EQUAL, text1]];
  3451. }
  3452. return [];
  3453. }
  3454. if (typeof optChecklines === "undefined") {
  3455. optChecklines = true;
  3456. }
  3457. checklines = optChecklines;
  3458. // Trim off common prefix (speedup).
  3459. commonlength = this.diffCommonPrefix(text1, text2);
  3460. commonprefix = text1.substring(0, commonlength);
  3461. text1 = text1.substring(commonlength);
  3462. text2 = text2.substring(commonlength);
  3463. // Trim off common suffix (speedup).
  3464. commonlength = this.diffCommonSuffix(text1, text2);
  3465. commonsuffix = text1.substring(text1.length - commonlength);
  3466. text1 = text1.substring(0, text1.length - commonlength);
  3467. text2 = text2.substring(0, text2.length - commonlength);
  3468. // Compute the diff on the middle block.
  3469. diffs = this.diffCompute(text1, text2, checklines, deadline);
  3470. // Restore the prefix and suffix.
  3471. if (commonprefix) {
  3472. diffs.unshift([DIFF_EQUAL, commonprefix]);
  3473. }
  3474. if (commonsuffix) {
  3475. diffs.push([DIFF_EQUAL, commonsuffix]);
  3476. }
  3477. this.diffCleanupMerge(diffs);
  3478. return diffs;
  3479. };
  3480. /**
  3481. * Reduce the number of edits by eliminating operationally trivial equalities.
  3482. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  3483. */
  3484. DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) {
  3485. var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel;
  3486. changes = false;
  3487. equalities = []; // Stack of indices where equalities are found.
  3488. equalitiesLength = 0; // Keeping our own length var is faster in JS.
  3489. /** @type {?string} */
  3490. lastequality = null;
  3491. // Always equal to diffs[equalities[equalitiesLength - 1]][1]
  3492. pointer = 0; // Index of current position.
  3493. // Is there an insertion operation before the last equality.
  3494. preIns = false;
  3495. // Is there a deletion operation before the last equality.
  3496. preDel = false;
  3497. // Is there an insertion operation after the last equality.
  3498. postIns = false;
  3499. // Is there a deletion operation after the last equality.
  3500. postDel = false;
  3501. while (pointer < diffs.length) {
  3502. // Equality found.
  3503. if (diffs[pointer][0] === DIFF_EQUAL) {
  3504. if (diffs[pointer][1].length < 4 && (postIns || postDel)) {
  3505. // Candidate found.
  3506. equalities[equalitiesLength++] = pointer;
  3507. preIns = postIns;
  3508. preDel = postDel;
  3509. lastequality = diffs[pointer][1];
  3510. } else {
  3511. // Not a candidate, and can never become one.
  3512. equalitiesLength = 0;
  3513. lastequality = null;
  3514. }
  3515. postIns = postDel = false;
  3516. // An insertion or deletion.
  3517. } else {
  3518. if (diffs[pointer][0] === DIFF_DELETE) {
  3519. postDel = true;
  3520. } else {
  3521. postIns = true;
  3522. }
  3523. /*
  3524. * Five types to be split:
  3525. * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
  3526. * <ins>A</ins>X<ins>C</ins><del>D</del>
  3527. * <ins>A</ins><del>B</del>X<ins>C</ins>
  3528. * <ins>A</del>X<ins>C</ins><del>D</del>
  3529. * <ins>A</ins><del>B</del>X<del>C</del>
  3530. */
  3531. if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) {
  3532. // Duplicate record.
  3533. diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
  3534. // Change second copy to insert.
  3535. diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
  3536. equalitiesLength--; // Throw away the equality we just deleted;
  3537. lastequality = null;
  3538. if (preIns && preDel) {
  3539. // No changes made which could affect previous entry, keep going.
  3540. postIns = postDel = true;
  3541. equalitiesLength = 0;
  3542. } else {
  3543. equalitiesLength--; // Throw away the previous equality.
  3544. pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
  3545. postIns = postDel = false;
  3546. }
  3547. changes = true;
  3548. }
  3549. }
  3550. pointer++;
  3551. }
  3552. if (changes) {
  3553. this.diffCleanupMerge(diffs);
  3554. }
  3555. };
  3556. /**
  3557. * Convert a diff array into a pretty HTML report.
  3558. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  3559. * @param {integer} string to be beautified.
  3560. * @return {string} HTML representation.
  3561. */
  3562. DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) {
  3563. var op,
  3564. data,
  3565. x,
  3566. html = [];
  3567. for (x = 0; x < diffs.length; x++) {
  3568. op = diffs[x][0]; // Operation (insert, delete, equal)
  3569. data = diffs[x][1]; // Text of change.
  3570. switch (op) {
  3571. case DIFF_INSERT:
  3572. html[x] = "<ins>" + escapeText(data) + "</ins>";
  3573. break;
  3574. case DIFF_DELETE:
  3575. html[x] = "<del>" + escapeText(data) + "</del>";
  3576. break;
  3577. case DIFF_EQUAL:
  3578. html[x] = "<span>" + escapeText(data) + "</span>";
  3579. break;
  3580. }
  3581. }
  3582. return html.join("");
  3583. };
  3584. /**
  3585. * Determine the common prefix of two strings.
  3586. * @param {string} text1 First string.
  3587. * @param {string} text2 Second string.
  3588. * @return {number} The number of characters common to the start of each
  3589. * string.
  3590. */
  3591. DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) {
  3592. var pointermid, pointermax, pointermin, pointerstart;
  3593. // Quick check for common null cases.
  3594. if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
  3595. return 0;
  3596. }
  3597. // Binary search.
  3598. // Performance analysis: https://neil.fraser.name/news/2007/10/09/
  3599. pointermin = 0;
  3600. pointermax = Math.min(text1.length, text2.length);
  3601. pointermid = pointermax;
  3602. pointerstart = 0;
  3603. while (pointermin < pointermid) {
  3604. if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
  3605. pointermin = pointermid;
  3606. pointerstart = pointermin;
  3607. } else {
  3608. pointermax = pointermid;
  3609. }
  3610. pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
  3611. }
  3612. return pointermid;
  3613. };
  3614. /**
  3615. * Determine the common suffix of two strings.
  3616. * @param {string} text1 First string.
  3617. * @param {string} text2 Second string.
  3618. * @return {number} The number of characters common to the end of each string.
  3619. */
  3620. DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) {
  3621. var pointermid, pointermax, pointermin, pointerend;
  3622. // Quick check for common null cases.
  3623. if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
  3624. return 0;
  3625. }
  3626. // Binary search.
  3627. // Performance analysis: https://neil.fraser.name/news/2007/10/09/
  3628. pointermin = 0;
  3629. pointermax = Math.min(text1.length, text2.length);
  3630. pointermid = pointermax;
  3631. pointerend = 0;
  3632. while (pointermin < pointermid) {
  3633. if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
  3634. pointermin = pointermid;
  3635. pointerend = pointermin;
  3636. } else {
  3637. pointermax = pointermid;
  3638. }
  3639. pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
  3640. }
  3641. return pointermid;
  3642. };
  3643. /**
  3644. * Find the differences between two texts. Assumes that the texts do not
  3645. * have any common prefix or suffix.
  3646. * @param {string} text1 Old string to be diffed.
  3647. * @param {string} text2 New string to be diffed.
  3648. * @param {boolean} checklines Speedup flag. If false, then don't run a
  3649. * line-level diff first to identify the changed areas.
  3650. * If true, then run a faster, slightly less optimal diff.
  3651. * @param {number} deadline Time when the diff should be complete by.
  3652. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  3653. * @private
  3654. */
  3655. DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) {
  3656. var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB;
  3657. if (!text1) {
  3658. // Just add some text (speedup).
  3659. return [[DIFF_INSERT, text2]];
  3660. }
  3661. if (!text2) {
  3662. // Just delete some text (speedup).
  3663. return [[DIFF_DELETE, text1]];
  3664. }
  3665. longtext = text1.length > text2.length ? text1 : text2;
  3666. shorttext = text1.length > text2.length ? text2 : text1;
  3667. i = longtext.indexOf(shorttext);
  3668. if (i !== -1) {
  3669. // Shorter text is inside the longer text (speedup).
  3670. diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
  3671. // Swap insertions for deletions if diff is reversed.
  3672. if (text1.length > text2.length) {
  3673. diffs[0][0] = diffs[2][0] = DIFF_DELETE;
  3674. }
  3675. return diffs;
  3676. }
  3677. if (shorttext.length === 1) {
  3678. // Single character string.
  3679. // After the previous speedup, the character can't be an equality.
  3680. return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
  3681. }
  3682. // Check to see if the problem can be split in two.
  3683. hm = this.diffHalfMatch(text1, text2);
  3684. if (hm) {
  3685. // A half-match was found, sort out the return data.
  3686. text1A = hm[0];
  3687. text1B = hm[1];
  3688. text2A = hm[2];
  3689. text2B = hm[3];
  3690. midCommon = hm[4];
  3691. // Send both pairs off for separate processing.
  3692. diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
  3693. diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
  3694. // Merge the results.
  3695. return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB);
  3696. }
  3697. if (checklines && text1.length > 100 && text2.length > 100) {
  3698. return this.diffLineMode(text1, text2, deadline);
  3699. }
  3700. return this.diffBisect(text1, text2, deadline);
  3701. };
  3702. /**
  3703. * Do the two texts share a substring which is at least half the length of the
  3704. * longer text?
  3705. * This speedup can produce non-minimal diffs.
  3706. * @param {string} text1 First string.
  3707. * @param {string} text2 Second string.
  3708. * @return {Array.<string>} Five element Array, containing the prefix of
  3709. * text1, the suffix of text1, the prefix of text2, the suffix of
  3710. * text2 and the common middle. Or null if there was no match.
  3711. * @private
  3712. */
  3713. DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) {
  3714. var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm;
  3715. longtext = text1.length > text2.length ? text1 : text2;
  3716. shorttext = text1.length > text2.length ? text2 : text1;
  3717. if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
  3718. return null; // Pointless.
  3719. }
  3720. dmp = this; // 'this' becomes 'window' in a closure.
  3721. /**
  3722. * Does a substring of shorttext exist within longtext such that the substring
  3723. * is at least half the length of longtext?
  3724. * Closure, but does not reference any external variables.
  3725. * @param {string} longtext Longer string.
  3726. * @param {string} shorttext Shorter string.
  3727. * @param {number} i Start index of quarter length substring within longtext.
  3728. * @return {Array.<string>} Five element Array, containing the prefix of
  3729. * longtext, the suffix of longtext, the prefix of shorttext, the suffix
  3730. * of shorttext and the common middle. Or null if there was no match.
  3731. * @private
  3732. */
  3733. function diffHalfMatchI(longtext, shorttext, i) {
  3734. var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
  3735. // Start with a 1/4 length substring at position i as a seed.
  3736. seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
  3737. j = -1;
  3738. bestCommon = "";
  3739. while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
  3740. prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j));
  3741. suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
  3742. if (bestCommon.length < suffixLength + prefixLength) {
  3743. bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
  3744. bestLongtextA = longtext.substring(0, i - suffixLength);
  3745. bestLongtextB = longtext.substring(i + prefixLength);
  3746. bestShorttextA = shorttext.substring(0, j - suffixLength);
  3747. bestShorttextB = shorttext.substring(j + prefixLength);
  3748. }
  3749. }
  3750. if (bestCommon.length * 2 >= longtext.length) {
  3751. return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon];
  3752. } else {
  3753. return null;
  3754. }
  3755. }
  3756. // First check if the second quarter is the seed for a half-match.
  3757. hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4));
  3758. // Check again based on the third quarter.
  3759. hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2));
  3760. if (!hm1 && !hm2) {
  3761. return null;
  3762. } else if (!hm2) {
  3763. hm = hm1;
  3764. } else if (!hm1) {
  3765. hm = hm2;
  3766. } else {
  3767. // Both matched. Select the longest.
  3768. hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
  3769. }
  3770. // A half-match was found, sort out the return data.
  3771. if (text1.length > text2.length) {
  3772. text1A = hm[0];
  3773. text1B = hm[1];
  3774. text2A = hm[2];
  3775. text2B = hm[3];
  3776. } else {
  3777. text2A = hm[0];
  3778. text2B = hm[1];
  3779. text1A = hm[2];
  3780. text1B = hm[3];
  3781. }
  3782. midCommon = hm[4];
  3783. return [text1A, text1B, text2A, text2B, midCommon];
  3784. };
  3785. /**
  3786. * Do a quick line-level diff on both strings, then rediff the parts for
  3787. * greater accuracy.
  3788. * This speedup can produce non-minimal diffs.
  3789. * @param {string} text1 Old string to be diffed.
  3790. * @param {string} text2 New string to be diffed.
  3791. * @param {number} deadline Time when the diff should be complete by.
  3792. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  3793. * @private
  3794. */
  3795. DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) {
  3796. var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j;
  3797. // Scan the text on a line-by-line basis first.
  3798. a = this.diffLinesToChars(text1, text2);
  3799. text1 = a.chars1;
  3800. text2 = a.chars2;
  3801. linearray = a.lineArray;
  3802. diffs = this.DiffMain(text1, text2, false, deadline);
  3803. // Convert the diff back to original text.
  3804. this.diffCharsToLines(diffs, linearray);
  3805. // Eliminate freak matches (e.g. blank lines)
  3806. this.diffCleanupSemantic(diffs);
  3807. // Rediff any replacement blocks, this time character-by-character.
  3808. // Add a dummy entry at the end.
  3809. diffs.push([DIFF_EQUAL, ""]);
  3810. pointer = 0;
  3811. countDelete = 0;
  3812. countInsert = 0;
  3813. textDelete = "";
  3814. textInsert = "";
  3815. while (pointer < diffs.length) {
  3816. switch (diffs[pointer][0]) {
  3817. case DIFF_INSERT:
  3818. countInsert++;
  3819. textInsert += diffs[pointer][1];
  3820. break;
  3821. case DIFF_DELETE:
  3822. countDelete++;
  3823. textDelete += diffs[pointer][1];
  3824. break;
  3825. case DIFF_EQUAL:
  3826. // Upon reaching an equality, check for prior redundancies.
  3827. if (countDelete >= 1 && countInsert >= 1) {
  3828. // Delete the offending records and add the merged ones.
  3829. diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert);
  3830. pointer = pointer - countDelete - countInsert;
  3831. a = this.DiffMain(textDelete, textInsert, false, deadline);
  3832. for (j = a.length - 1; j >= 0; j--) {
  3833. diffs.splice(pointer, 0, a[j]);
  3834. }
  3835. pointer = pointer + a.length;
  3836. }
  3837. countInsert = 0;
  3838. countDelete = 0;
  3839. textDelete = "";
  3840. textInsert = "";
  3841. break;
  3842. }
  3843. pointer++;
  3844. }
  3845. diffs.pop(); // Remove the dummy entry at the end.
  3846. return diffs;
  3847. };
  3848. /**
  3849. * Find the 'middle snake' of a diff, split the problem in two
  3850. * and return the recursively constructed diff.
  3851. * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
  3852. * @param {string} text1 Old string to be diffed.
  3853. * @param {string} text2 New string to be diffed.
  3854. * @param {number} deadline Time at which to bail if not yet complete.
  3855. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  3856. * @private
  3857. */
  3858. DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) {
  3859. var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
  3860. // Cache the text lengths to prevent multiple calls.
  3861. text1Length = text1.length;
  3862. text2Length = text2.length;
  3863. maxD = Math.ceil((text1Length + text2Length) / 2);
  3864. vOffset = maxD;
  3865. vLength = 2 * maxD;
  3866. v1 = new Array(vLength);
  3867. v2 = new Array(vLength);
  3868. // Setting all elements to -1 is faster in Chrome & Firefox than mixing
  3869. // integers and undefined.
  3870. for (x = 0; x < vLength; x++) {
  3871. v1[x] = -1;
  3872. v2[x] = -1;
  3873. }
  3874. v1[vOffset + 1] = 0;
  3875. v2[vOffset + 1] = 0;
  3876. delta = text1Length - text2Length;
  3877. // If the total number of characters is odd, then the front path will collide
  3878. // with the reverse path.
  3879. front = delta % 2 !== 0;
  3880. // Offsets for start and end of k loop.
  3881. // Prevents mapping of space beyond the grid.
  3882. k1start = 0;
  3883. k1end = 0;
  3884. k2start = 0;
  3885. k2end = 0;
  3886. for (d = 0; d < maxD; d++) {
  3887. // Bail out if deadline is reached.
  3888. if (new Date().getTime() > deadline) {
  3889. break;
  3890. }
  3891. // Walk the front path one step.
  3892. for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
  3893. k1Offset = vOffset + k1;
  3894. if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) {
  3895. x1 = v1[k1Offset + 1];
  3896. } else {
  3897. x1 = v1[k1Offset - 1] + 1;
  3898. }
  3899. y1 = x1 - k1;
  3900. while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) {
  3901. x1++;
  3902. y1++;
  3903. }
  3904. v1[k1Offset] = x1;
  3905. if (x1 > text1Length) {
  3906. // Ran off the right of the graph.
  3907. k1end += 2;
  3908. } else if (y1 > text2Length) {
  3909. // Ran off the bottom of the graph.
  3910. k1start += 2;
  3911. } else if (front) {
  3912. k2Offset = vOffset + delta - k1;
  3913. if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
  3914. // Mirror x2 onto top-left coordinate system.
  3915. x2 = text1Length - v2[k2Offset];
  3916. if (x1 >= x2) {
  3917. // Overlap detected.
  3918. return this.diffBisectSplit(text1, text2, x1, y1, deadline);
  3919. }
  3920. }
  3921. }
  3922. }
  3923. // Walk the reverse path one step.
  3924. for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
  3925. k2Offset = vOffset + k2;
  3926. if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) {
  3927. x2 = v2[k2Offset + 1];
  3928. } else {
  3929. x2 = v2[k2Offset - 1] + 1;
  3930. }
  3931. y2 = x2 - k2;
  3932. while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) {
  3933. x2++;
  3934. y2++;
  3935. }
  3936. v2[k2Offset] = x2;
  3937. if (x2 > text1Length) {
  3938. // Ran off the left of the graph.
  3939. k2end += 2;
  3940. } else if (y2 > text2Length) {
  3941. // Ran off the top of the graph.
  3942. k2start += 2;
  3943. } else if (!front) {
  3944. k1Offset = vOffset + delta - k2;
  3945. if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
  3946. x1 = v1[k1Offset];
  3947. y1 = vOffset + x1 - k1Offset;
  3948. // Mirror x2 onto top-left coordinate system.
  3949. x2 = text1Length - x2;
  3950. if (x1 >= x2) {
  3951. // Overlap detected.
  3952. return this.diffBisectSplit(text1, text2, x1, y1, deadline);
  3953. }
  3954. }
  3955. }
  3956. }
  3957. }
  3958. // Diff took too long and hit the deadline or
  3959. // number of diffs equals number of characters, no commonality at all.
  3960. return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
  3961. };
  3962. /**
  3963. * Given the location of the 'middle snake', split the diff in two parts
  3964. * and recurse.
  3965. * @param {string} text1 Old string to be diffed.
  3966. * @param {string} text2 New string to be diffed.
  3967. * @param {number} x Index of split point in text1.
  3968. * @param {number} y Index of split point in text2.
  3969. * @param {number} deadline Time at which to bail if not yet complete.
  3970. * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
  3971. * @private
  3972. */
  3973. DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) {
  3974. var text1a, text1b, text2a, text2b, diffs, diffsb;
  3975. text1a = text1.substring(0, x);
  3976. text2a = text2.substring(0, y);
  3977. text1b = text1.substring(x);
  3978. text2b = text2.substring(y);
  3979. // Compute both diffs serially.
  3980. diffs = this.DiffMain(text1a, text2a, false, deadline);
  3981. diffsb = this.DiffMain(text1b, text2b, false, deadline);
  3982. return diffs.concat(diffsb);
  3983. };
  3984. /**
  3985. * Reduce the number of edits by eliminating semantically trivial equalities.
  3986. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  3987. */
  3988. DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) {
  3989. var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
  3990. changes = false;
  3991. equalities = []; // Stack of indices where equalities are found.
  3992. equalitiesLength = 0; // Keeping our own length var is faster in JS.
  3993. /** @type {?string} */
  3994. lastequality = null;
  3995. // Always equal to diffs[equalities[equalitiesLength - 1]][1]
  3996. pointer = 0; // Index of current position.
  3997. // Number of characters that changed prior to the equality.
  3998. lengthInsertions1 = 0;
  3999. lengthDeletions1 = 0;
  4000. // Number of characters that changed after the equality.
  4001. lengthInsertions2 = 0;
  4002. lengthDeletions2 = 0;
  4003. while (pointer < diffs.length) {
  4004. if (diffs[pointer][0] === DIFF_EQUAL) {
  4005. // Equality found.
  4006. equalities[equalitiesLength++] = pointer;
  4007. lengthInsertions1 = lengthInsertions2;
  4008. lengthDeletions1 = lengthDeletions2;
  4009. lengthInsertions2 = 0;
  4010. lengthDeletions2 = 0;
  4011. lastequality = diffs[pointer][1];
  4012. } else {
  4013. // An insertion or deletion.
  4014. if (diffs[pointer][0] === DIFF_INSERT) {
  4015. lengthInsertions2 += diffs[pointer][1].length;
  4016. } else {
  4017. lengthDeletions2 += diffs[pointer][1].length;
  4018. }
  4019. // Eliminate an equality that is smaller or equal to the edits on both
  4020. // sides of it.
  4021. if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) {
  4022. // Duplicate record.
  4023. diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
  4024. // Change second copy to insert.
  4025. diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
  4026. // Throw away the equality we just deleted.
  4027. equalitiesLength--;
  4028. // Throw away the previous equality (it needs to be reevaluated).
  4029. equalitiesLength--;
  4030. pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
  4031. // Reset the counters.
  4032. lengthInsertions1 = 0;
  4033. lengthDeletions1 = 0;
  4034. lengthInsertions2 = 0;
  4035. lengthDeletions2 = 0;
  4036. lastequality = null;
  4037. changes = true;
  4038. }
  4039. }
  4040. pointer++;
  4041. }
  4042. // Normalize the diff.
  4043. if (changes) {
  4044. this.diffCleanupMerge(diffs);
  4045. }
  4046. // Find any overlaps between deletions and insertions.
  4047. // e.g: <del>abcxxx</del><ins>xxxdef</ins>
  4048. // -> <del>abc</del>xxx<ins>def</ins>
  4049. // e.g: <del>xxxabc</del><ins>defxxx</ins>
  4050. // -> <ins>def</ins>xxx<del>abc</del>
  4051. // Only extract an overlap if it is as big as the edit ahead or behind it.
  4052. pointer = 1;
  4053. while (pointer < diffs.length) {
  4054. if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
  4055. deletion = diffs[pointer - 1][1];
  4056. insertion = diffs[pointer][1];
  4057. overlapLength1 = this.diffCommonOverlap(deletion, insertion);
  4058. overlapLength2 = this.diffCommonOverlap(insertion, deletion);
  4059. if (overlapLength1 >= overlapLength2) {
  4060. if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) {
  4061. // Overlap found. Insert an equality and trim the surrounding edits.
  4062. diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]);
  4063. diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1);
  4064. diffs[pointer + 1][1] = insertion.substring(overlapLength1);
  4065. pointer++;
  4066. }
  4067. } else {
  4068. if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) {
  4069. // Reverse overlap found.
  4070. // Insert an equality and swap and trim the surrounding edits.
  4071. diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]);
  4072. diffs[pointer - 1][0] = DIFF_INSERT;
  4073. diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2);
  4074. diffs[pointer + 1][0] = DIFF_DELETE;
  4075. diffs[pointer + 1][1] = deletion.substring(overlapLength2);
  4076. pointer++;
  4077. }
  4078. }
  4079. pointer++;
  4080. }
  4081. pointer++;
  4082. }
  4083. };
  4084. /**
  4085. * Determine if the suffix of one string is the prefix of another.
  4086. * @param {string} text1 First string.
  4087. * @param {string} text2 Second string.
  4088. * @return {number} The number of characters common to the end of the first
  4089. * string and the start of the second string.
  4090. * @private
  4091. */
  4092. DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) {
  4093. var text1Length, text2Length, textLength, best, length, pattern, found;
  4094. // Cache the text lengths to prevent multiple calls.
  4095. text1Length = text1.length;
  4096. text2Length = text2.length;
  4097. // Eliminate the null case.
  4098. if (text1Length === 0 || text2Length === 0) {
  4099. return 0;
  4100. }
  4101. // Truncate the longer string.
  4102. if (text1Length > text2Length) {
  4103. text1 = text1.substring(text1Length - text2Length);
  4104. } else if (text1Length < text2Length) {
  4105. text2 = text2.substring(0, text1Length);
  4106. }
  4107. textLength = Math.min(text1Length, text2Length);
  4108. // Quick check for the worst case.
  4109. if (text1 === text2) {
  4110. return textLength;
  4111. }
  4112. // Start by looking for a single character match
  4113. // and increase length until no match is found.
  4114. // Performance analysis: https://neil.fraser.name/news/2010/11/04/
  4115. best = 0;
  4116. length = 1;
  4117. while (true) {
  4118. pattern = text1.substring(textLength - length);
  4119. found = text2.indexOf(pattern);
  4120. if (found === -1) {
  4121. return best;
  4122. }
  4123. length += found;
  4124. if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) {
  4125. best = length;
  4126. length++;
  4127. }
  4128. }
  4129. };
  4130. /**
  4131. * Split two texts into an array of strings. Reduce the texts to a string of
  4132. * hashes where each Unicode character represents one line.
  4133. * @param {string} text1 First string.
  4134. * @param {string} text2 Second string.
  4135. * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
  4136. * An object containing the encoded text1, the encoded text2 and
  4137. * the array of unique strings.
  4138. * The zeroth element of the array of unique strings is intentionally blank.
  4139. * @private
  4140. */
  4141. DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) {
  4142. var lineArray, lineHash, chars1, chars2;
  4143. lineArray = []; // E.g. lineArray[4] === 'Hello\n'
  4144. lineHash = {}; // E.g. lineHash['Hello\n'] === 4
  4145. // '\x00' is a valid character, but various debuggers don't like it.
  4146. // So we'll insert a junk entry to avoid generating a null character.
  4147. lineArray[0] = "";
  4148. /**
  4149. * Split a text into an array of strings. Reduce the texts to a string of
  4150. * hashes where each Unicode character represents one line.
  4151. * Modifies linearray and linehash through being a closure.
  4152. * @param {string} text String to encode.
  4153. * @return {string} Encoded string.
  4154. * @private
  4155. */
  4156. function diffLinesToCharsMunge(text) {
  4157. var chars, lineStart, lineEnd, lineArrayLength, line;
  4158. chars = "";
  4159. // Walk the text, pulling out a substring for each line.
  4160. // text.split('\n') would would temporarily double our memory footprint.
  4161. // Modifying text would create many large strings to garbage collect.
  4162. lineStart = 0;
  4163. lineEnd = -1;
  4164. // Keeping our own length variable is faster than looking it up.
  4165. lineArrayLength = lineArray.length;
  4166. while (lineEnd < text.length - 1) {
  4167. lineEnd = text.indexOf("\n", lineStart);
  4168. if (lineEnd === -1) {
  4169. lineEnd = text.length - 1;
  4170. }
  4171. line = text.substring(lineStart, lineEnd + 1);
  4172. lineStart = lineEnd + 1;
  4173. var lineHashExists = lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined;
  4174. if (lineHashExists) {
  4175. chars += String.fromCharCode(lineHash[line]);
  4176. } else {
  4177. chars += String.fromCharCode(lineArrayLength);
  4178. lineHash[line] = lineArrayLength;
  4179. lineArray[lineArrayLength++] = line;
  4180. }
  4181. }
  4182. return chars;
  4183. }
  4184. chars1 = diffLinesToCharsMunge(text1);
  4185. chars2 = diffLinesToCharsMunge(text2);
  4186. return {
  4187. chars1: chars1,
  4188. chars2: chars2,
  4189. lineArray: lineArray
  4190. };
  4191. };
  4192. /**
  4193. * Rehydrate the text in a diff from a string of line hashes to real lines of
  4194. * text.
  4195. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  4196. * @param {!Array.<string>} lineArray Array of unique strings.
  4197. * @private
  4198. */
  4199. DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) {
  4200. var x, chars, text, y;
  4201. for (x = 0; x < diffs.length; x++) {
  4202. chars = diffs[x][1];
  4203. text = [];
  4204. for (y = 0; y < chars.length; y++) {
  4205. text[y] = lineArray[chars.charCodeAt(y)];
  4206. }
  4207. diffs[x][1] = text.join("");
  4208. }
  4209. };
  4210. /**
  4211. * Reorder and merge like edit sections. Merge equalities.
  4212. * Any edit section can move as long as it doesn't cross an equality.
  4213. * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
  4214. */
  4215. DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) {
  4216. var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position;
  4217. diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end.
  4218. pointer = 0;
  4219. countDelete = 0;
  4220. countInsert = 0;
  4221. textDelete = "";
  4222. textInsert = "";
  4223. while (pointer < diffs.length) {
  4224. switch (diffs[pointer][0]) {
  4225. case DIFF_INSERT:
  4226. countInsert++;
  4227. textInsert += diffs[pointer][1];
  4228. pointer++;
  4229. break;
  4230. case DIFF_DELETE:
  4231. countDelete++;
  4232. textDelete += diffs[pointer][1];
  4233. pointer++;
  4234. break;
  4235. case DIFF_EQUAL:
  4236. // Upon reaching an equality, check for prior redundancies.
  4237. if (countDelete + countInsert > 1) {
  4238. if (countDelete !== 0 && countInsert !== 0) {
  4239. // Factor out any common prefixes.
  4240. commonlength = this.diffCommonPrefix(textInsert, textDelete);
  4241. if (commonlength !== 0) {
  4242. if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) {
  4243. diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength);
  4244. } else {
  4245. diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]);
  4246. pointer++;
  4247. }
  4248. textInsert = textInsert.substring(commonlength);
  4249. textDelete = textDelete.substring(commonlength);
  4250. }
  4251. // Factor out any common suffixies.
  4252. commonlength = this.diffCommonSuffix(textInsert, textDelete);
  4253. if (commonlength !== 0) {
  4254. diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1];
  4255. textInsert = textInsert.substring(0, textInsert.length - commonlength);
  4256. textDelete = textDelete.substring(0, textDelete.length - commonlength);
  4257. }
  4258. }
  4259. // Delete the offending records and add the merged ones.
  4260. if (countDelete === 0) {
  4261. diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]);
  4262. } else if (countInsert === 0) {
  4263. diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]);
  4264. } else {
  4265. diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]);
  4266. }
  4267. pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
  4268. } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
  4269. // Merge this equality with the previous one.
  4270. diffs[pointer - 1][1] += diffs[pointer][1];
  4271. diffs.splice(pointer, 1);
  4272. } else {
  4273. pointer++;
  4274. }
  4275. countInsert = 0;
  4276. countDelete = 0;
  4277. textDelete = "";
  4278. textInsert = "";
  4279. break;
  4280. }
  4281. }
  4282. if (diffs[diffs.length - 1][1] === "") {
  4283. diffs.pop(); // Remove the dummy entry at the end.
  4284. }
  4285. // Second pass: look for single edits surrounded on both sides by equalities
  4286. // which can be shifted sideways to eliminate an equality.
  4287. // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
  4288. changes = false;
  4289. pointer = 1;
  4290. // Intentionally ignore the first and last element (don't need checking).
  4291. while (pointer < diffs.length - 1) {
  4292. if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
  4293. diffPointer = diffs[pointer][1];
  4294. position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length);
  4295. // This is a single edit surrounded by equalities.
  4296. if (position === diffs[pointer - 1][1]) {
  4297. // Shift the edit over the previous equality.
  4298. diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
  4299. diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
  4300. diffs.splice(pointer - 1, 1);
  4301. changes = true;
  4302. } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
  4303. // Shift the edit over the next equality.
  4304. diffs[pointer - 1][1] += diffs[pointer + 1][1];
  4305. diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
  4306. diffs.splice(pointer + 1, 1);
  4307. changes = true;
  4308. }
  4309. }
  4310. pointer++;
  4311. }
  4312. // If shifts were made, the diff needs reordering and another shift sweep.
  4313. if (changes) {
  4314. this.diffCleanupMerge(diffs);
  4315. }
  4316. };
  4317. return function (o, n) {
  4318. var diff, output, text;
  4319. diff = new DiffMatchPatch();
  4320. output = diff.DiffMain(o, n);
  4321. diff.diffCleanupEfficiency(output);
  4322. text = diff.diffPrettyHtml(output);
  4323. return text;
  4324. };
  4325. }();
  4326. }((function() { return this; }())));