<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[32343] trunk/tests/qunit/vendor: Update QUnit to v1.18.0</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/32343">32343</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/32343","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>jorbin</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2015-05-04 19:43:38 +0000 (Mon, 04 May 2015)</dd>
</dl>
<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Update QUnit to v1.18.0
See https://github.com/jquery/qunit/blob/1.18.0/History.md for changes from 1.12.0 to now. Mostly bug fixes and internal changes in preparation for QUnit v2.
See <a href="https://core.trac.wordpress.org/ticket/30824">#30824</a></pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunktestsqunitvendorqunitcss">trunk/tests/qunit/vendor/qunit.css</a></li>
<li><a href="#trunktestsqunitvendorqunitjs">trunk/tests/qunit/vendor/qunit.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunktestsqunitvendorqunitcss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/qunit/vendor/qunit.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/vendor/qunit.css 2015-05-04 13:09:14 UTC (rev 32342)
+++ trunk/tests/qunit/vendor/qunit.css 2015-05-04 19:43:38 UTC (rev 32343)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,11 +1,12 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * QUnit v1.12.0 - A JavaScript Unit Testing Framework
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/*!
+ * QUnit 1.18.0
+ * http://qunitjs.com/
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * http://qunitjs.com
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Copyright 2012 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Date: 2015-04-03T10:23Z
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /** Font Family and Sizes */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -31,32 +32,29 @@
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-header {
</span><span class="cx" style="display: block; padding: 0 10px"> padding: 0.5em 0 0.5em 1em;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- color: #8699a4;
- background-color: #0d3349;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ color: #8699A4;
+ background-color: #0D3349;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> font-size: 1.5em;
</span><span class="cx" style="display: block; padding: 0 10px"> line-height: 1em;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- font-weight: normal;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ font-weight: 400;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> border-radius: 5px 5px 0 0;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- -moz-border-radius: 5px 5px 0 0;
- -webkit-border-top-right-radius: 5px;
- -webkit-border-top-left-radius: 5px;
</del><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-header a {
</span><span class="cx" style="display: block; padding: 0 10px"> text-decoration: none;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- color: #c2ccd1;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ color: #C2CCD1;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-header a:hover,
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-header a:focus {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- color: #fff;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ color: #FFF;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-testrunner-toolbar label {
</span><span class="cx" style="display: block; padding: 0 10px"> display: inline-block;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- padding: 0 .5em 0 .1em;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ padding: 0 0.5em 0 0.1em;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-banner {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -64,23 +62,35 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-testrunner-toolbar {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- padding: 0.5em 0 0.5em 2em;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ padding: 0.5em 1em 0.5em 1em;
</ins><span class="cx" style="display: block; padding: 0 10px"> color: #5E740B;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- background-color: #eee;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ background-color: #EEE;
</ins><span class="cx" style="display: block; padding: 0 10px"> overflow: hidden;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-userAgent {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- padding: 0.5em 0 0.5em 2.5em;
- background-color: #2b81af;
- color: #fff;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ padding: 0.5em 1em 0.5em 1em;
+ background-color: #2B81AF;
+ color: #FFF;
</ins><span class="cx" style="display: block; padding: 0 10px"> text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-modulefilter-container {
</span><span class="cx" style="display: block; padding: 0 10px"> float: right;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ padding: 0.2em;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.qunit-url-config {
+ display: inline-block;
+ padding: 0.1em;
+}
+
+.qunit-filter {
+ display: block;
+ float: right;
+ margin-left: 1em;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /** Tests: Pass/Fail */
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -88,24 +98,51 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests li {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- padding: 0.4em 0.5em 0.4em 2.5em;
- border-bottom: 1px solid #fff;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ padding: 0.4em 1em 0.4em 1em;
+ border-bottom: 1px solid #FFF;
</ins><span class="cx" style="display: block; padding: 0 10px"> list-style-position: inside;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#qunit-tests > li {
</ins><span class="cx" style="display: block; padding: 0 10px"> display: none;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#qunit-tests li.running,
+#qunit-tests li.pass,
+#qunit-tests li.fail,
+#qunit-tests li.skipped {
+ display: list-item;
+}
+
+#qunit-tests.hidepass li.running,
+#qunit-tests.hidepass li.pass {
+ visibility: hidden;
+ position: absolute;
+ width: 0px;
+ height: 0px;
+ padding: 0;
+ border: 0;
+ margin: 0;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests li strong {
</span><span class="cx" style="display: block; padding: 0 10px"> cursor: pointer;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#qunit-tests li.skipped strong {
+ cursor: default;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests li a {
</span><span class="cx" style="display: block; padding: 0 10px"> padding: 0.5em;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- color: #c2ccd1;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ color: #C2CCD1;
</ins><span class="cx" style="display: block; padding: 0 10px"> text-decoration: none;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+#qunit-tests li p a {
+ padding: 0.25em;
+ color: #6B6464;
+}
</ins><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests li a:hover,
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests li a:focus {
</span><span class="cx" style="display: block; padding: 0 10px"> color: #000;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -120,11 +157,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> margin-top: 0.5em;
</span><span class="cx" style="display: block; padding: 0 10px"> padding: 0.5em;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- background-color: #fff;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ background-color: #FFF;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> border-radius: 5px;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- -moz-border-radius: 5px;
- -webkit-border-radius: 5px;
</del><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> .qunit-collapsed {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -133,13 +168,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests table {
</span><span class="cx" style="display: block; padding: 0 10px"> border-collapse: collapse;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- margin-top: .2em;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ margin-top: 0.2em;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests th {
</span><span class="cx" style="display: block; padding: 0 10px"> text-align: right;
</span><span class="cx" style="display: block; padding: 0 10px"> vertical-align: top;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- padding: 0 .5em 0 0;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ padding: 0 0.5em 0 0;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests td {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -153,26 +188,26 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests del {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- background-color: #e0f2be;
- color: #374e0c;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ background-color: #E0F2BE;
+ color: #374E0C;
</ins><span class="cx" style="display: block; padding: 0 10px"> text-decoration: none;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests ins {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- background-color: #ffcaca;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ background-color: #FFCACA;
</ins><span class="cx" style="display: block; padding: 0 10px"> color: #500;
</span><span class="cx" style="display: block; padding: 0 10px"> text-decoration: none;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /*** Test Counts */
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#qunit-tests b.counts { color: black; }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#qunit-tests b.counts { color: #000; }
</ins><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests b.passed { color: #5E740B; }
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests b.failed { color: #710909; }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests li li {
</span><span class="cx" style="display: block; padding: 0 10px"> padding: 5px;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- background-color: #fff;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ background-color: #FFF;
</ins><span class="cx" style="display: block; padding: 0 10px"> border-bottom: none;
</span><span class="cx" style="display: block; padding: 0 10px"> list-style-position: inside;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -180,8 +215,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /*** Passing Styles */
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests li li.pass {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- color: #3c510c;
- background-color: #fff;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ color: #3C510C;
+ background-color: #FFF;
</ins><span class="cx" style="display: block; padding: 0 10px"> border-left: 10px solid #C6E746;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -189,7 +224,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests .pass .test-name { color: #366097; }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests .pass .test-actual,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#qunit-tests .pass .test-expected { color: #999999; }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#qunit-tests .pass .test-expected { color: #999; }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-banner.qunit-pass { background-color: #C6E746; }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -197,40 +232,52 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests li li.fail {
</span><span class="cx" style="display: block; padding: 0 10px"> color: #710909;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- background-color: #fff;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ background-color: #FFF;
</ins><span class="cx" style="display: block; padding: 0 10px"> border-left: 10px solid #EE5757;
</span><span class="cx" style="display: block; padding: 0 10px"> white-space: pre;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests > li:last-child {
</span><span class="cx" style="display: block; padding: 0 10px"> border-radius: 0 0 5px 5px;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- -moz-border-radius: 0 0 5px 5px;
- -webkit-border-bottom-right-radius: 5px;
- -webkit-border-bottom-left-radius: 5px;
</del><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#qunit-tests .fail { color: #000000; background-color: #EE5757; }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#qunit-tests .fail { color: #000; background-color: #EE5757; }
</ins><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests .fail .test-name,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#qunit-tests .fail .module-name { color: #000000; }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#qunit-tests .fail .module-name { color: #000; }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-tests .fail .test-actual { color: #EE5757; }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#qunit-tests .fail .test-expected { color: green; }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#qunit-tests .fail .test-expected { color: #008000; }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-banner.qunit-fail { background-color: #EE5757; }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/*** Skipped tests */
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#qunit-tests .skipped {
+ background-color: #EBECE9;
+}
+
+#qunit-tests .qunit-skipped-label {
+ background-color: #F4FF77;
+ display: inline-block;
+ font-style: normal;
+ color: #366097;
+ line-height: 1.8em;
+ padding: 0 0.5em;
+ margin: -0.4em 0.4em -0.4em 0;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /** Result */
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-testresult {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- padding: 0.5em 0.5em 0.5em 2.5em;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ padding: 0.5em 1em 0.5em 1em;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- color: #2b81af;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ color: #2B81AF;
</ins><span class="cx" style="display: block; padding: 0 10px"> background-color: #D2E0E6;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- border-bottom: 1px solid white;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ border-bottom: 1px solid #FFF;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> #qunit-testresult .module-name {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- font-weight: bold;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ font-weight: 700;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /** Fixture */
</span></span></pre></div>
<a id="trunktestsqunitvendorqunitjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/qunit/vendor/qunit.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/vendor/qunit.js 2015-05-04 13:09:14 UTC (rev 32342)
+++ trunk/tests/qunit/vendor/qunit.js 2015-05-04 19:43:38 UTC (rev 32343)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,35 +1,42 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * QUnit v1.12.0 - A JavaScript Unit Testing Framework
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/*!
+ * QUnit 1.18.0
+ * http://qunitjs.com/
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * http://qunitjs.com
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * https://jquery.org/license/
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Date: 2015-04-03T10:23Z
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> (function( window ) {
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> var QUnit,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- assert,
</del><span class="cx" style="display: block; padding: 0 10px"> config,
</span><span class="cx" style="display: block; padding: 0 10px"> onErrorFnPrev,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- testId = 0,
- fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ loggingCallbacks = {},
+ fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ),
</ins><span class="cx" style="display: block; padding: 0 10px"> toString = Object.prototype.toString,
</span><span class="cx" style="display: block; padding: 0 10px"> hasOwn = Object.prototype.hasOwnProperty,
</span><span class="cx" style="display: block; padding: 0 10px"> // Keep a local reference to Date (GH-283)
</span><span class="cx" style="display: block; padding: 0 10px"> Date = window.Date,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ now = Date.now || function() {
+ return new Date().getTime();
+ },
+ globalStartCalled = false,
+ runStarted = false,
</ins><span class="cx" style="display: block; padding: 0 10px"> setTimeout = window.setTimeout,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ clearTimeout = window.clearTimeout,
</ins><span class="cx" style="display: block; padding: 0 10px"> defined = {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- setTimeout: typeof window.setTimeout !== "undefined",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ document: window.document !== undefined,
+ setTimeout: window.setTimeout !== undefined,
</ins><span class="cx" style="display: block; padding: 0 10px"> sessionStorage: (function() {
</span><span class="cx" style="display: block; padding: 0 10px"> var x = "qunit-test-string";
</span><span class="cx" style="display: block; padding: 0 10px"> try {
</span><span class="cx" style="display: block; padding: 0 10px"> sessionStorage.setItem( x, x );
</span><span class="cx" style="display: block; padding: 0 10px"> sessionStorage.removeItem( x );
</span><span class="cx" style="display: block; padding: 0 10px"> return true;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- } catch( e ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ } catch ( e ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> return false;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }())
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -71,629 +78,20 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * @return {Object} New object with only the own properties (recursively).
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> objectValues = function( obj ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.
- /*jshint newcap: false */
</del><span class="cx" style="display: block; padding: 0 10px"> var key, val,
</span><span class="cx" style="display: block; padding: 0 10px"> vals = QUnit.is( "array", obj ) ? [] : {};
</span><span class="cx" style="display: block; padding: 0 10px"> for ( key in obj ) {
</span><span class="cx" style="display: block; padding: 0 10px"> if ( hasOwn.call( obj, key ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- val = obj[key];
- vals[key] = val === Object(val) ? objectValues(val) : val;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ val = obj[ key ];
+ vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> return vals;
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function Test( settings ) {
- extend( this, settings );
- this.assertions = [];
- this.testNumber = ++Test.count;
-}
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+QUnit = {};
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-Test.count = 0;
-
-Test.prototype = {
- init: function() {
- var a, b, li,
- tests = id( "qunit-tests" );
-
- if ( tests ) {
- b = document.createElement( "strong" );
- b.innerHTML = this.nameHtml;
-
- // `a` initialized at top of scope
- a = document.createElement( "a" );
- a.innerHTML = "Rerun";
- a.href = QUnit.url({ testNumber: this.testNumber });
-
- li = document.createElement( "li" );
- li.appendChild( b );
- li.appendChild( a );
- li.className = "running";
- li.id = this.id = "qunit-test-output" + testId++;
-
- tests.appendChild( li );
- }
- },
- setup: function() {
- if (
- // Emit moduleStart when we're switching from one module to another
- this.module !== config.previousModule ||
- // They could be equal (both undefined) but if the previousModule property doesn't
- // yet exist it means this is the first test in a suite that isn't wrapped in a
- // module, in which case we'll just emit a moduleStart event for 'undefined'.
- // Without this, reporters can get testStart before moduleStart which is a problem.
- !hasOwn.call( config, "previousModule" )
- ) {
- if ( hasOwn.call( config, "previousModule" ) ) {
- runLoggingCallbacks( "moduleDone", QUnit, {
- name: config.previousModule,
- failed: config.moduleStats.bad,
- passed: config.moduleStats.all - config.moduleStats.bad,
- total: config.moduleStats.all
- });
- }
- config.previousModule = this.module;
- config.moduleStats = { all: 0, bad: 0 };
- runLoggingCallbacks( "moduleStart", QUnit, {
- name: this.module
- });
- }
-
- config.current = this;
-
- this.testEnvironment = extend({
- setup: function() {},
- teardown: function() {}
- }, this.moduleTestEnvironment );
-
- this.started = +new Date();
- runLoggingCallbacks( "testStart", QUnit, {
- name: this.testName,
- module: this.module
- });
-
- /*jshint camelcase:false */
-
-
- /**
- * Expose the current test environment.
- *
- * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead.
- */
- QUnit.current_testEnvironment = this.testEnvironment;
-
- /*jshint camelcase:true */
-
- if ( !config.pollution ) {
- saveGlobal();
- }
- if ( config.notrycatch ) {
- this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
- return;
- }
- try {
- this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
- } catch( e ) {
- QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
- }
- },
- run: function() {
- config.current = this;
-
- var running = id( "qunit-testresult" );
-
- if ( running ) {
- running.innerHTML = "Running: <br/>" + this.nameHtml;
- }
-
- if ( this.async ) {
- QUnit.stop();
- }
-
- this.callbackStarted = +new Date();
-
- if ( config.notrycatch ) {
- this.callback.call( this.testEnvironment, QUnit.assert );
- this.callbackRuntime = +new Date() - this.callbackStarted;
- return;
- }
-
- try {
- this.callback.call( this.testEnvironment, QUnit.assert );
- this.callbackRuntime = +new Date() - this.callbackStarted;
- } catch( e ) {
- this.callbackRuntime = +new Date() - this.callbackStarted;
-
- QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
- // else next test will carry the responsibility
- saveGlobal();
-
- // Restart the tests if they're blocking
- if ( config.blocking ) {
- QUnit.start();
- }
- }
- },
- teardown: function() {
- config.current = this;
- if ( config.notrycatch ) {
- if ( typeof this.callbackRuntime === "undefined" ) {
- this.callbackRuntime = +new Date() - this.callbackStarted;
- }
- this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
- return;
- } else {
- try {
- this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
- } catch( e ) {
- QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
- }
- }
- checkPollution();
- },
- finish: function() {
- config.current = this;
- if ( config.requireExpects && this.expected === null ) {
- QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
- } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
- QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
- } else if ( this.expected === null && !this.assertions.length ) {
- QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
- }
-
- var i, assertion, a, b, time, li, ol,
- test = this,
- good = 0,
- bad = 0,
- tests = id( "qunit-tests" );
-
- this.runtime = +new Date() - this.started;
- config.stats.all += this.assertions.length;
- config.moduleStats.all += this.assertions.length;
-
- if ( tests ) {
- ol = document.createElement( "ol" );
- ol.className = "qunit-assert-list";
-
- for ( i = 0; i < this.assertions.length; i++ ) {
- assertion = this.assertions[i];
-
- li = document.createElement( "li" );
- li.className = assertion.result ? "pass" : "fail";
- li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
- ol.appendChild( li );
-
- if ( assertion.result ) {
- good++;
- } else {
- bad++;
- config.stats.bad++;
- config.moduleStats.bad++;
- }
- }
-
- // store result when possible
- if ( QUnit.config.reorder && defined.sessionStorage ) {
- if ( bad ) {
- sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
- } else {
- sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
- }
- }
-
- if ( bad === 0 ) {
- addClass( ol, "qunit-collapsed" );
- }
-
- // `b` initialized at top of scope
- b = document.createElement( "strong" );
- b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
-
- addEvent(b, "click", function() {
- var next = b.parentNode.lastChild,
- collapsed = hasClass( next, "qunit-collapsed" );
- ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );
- });
-
- addEvent(b, "dblclick", function( e ) {
- var target = e && e.target ? e.target : window.event.srcElement;
- if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
- target = target.parentNode;
- }
- if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
- window.location = QUnit.url({ testNumber: test.testNumber });
- }
- });
-
- // `time` initialized at top of scope
- time = document.createElement( "span" );
- time.className = "runtime";
- time.innerHTML = this.runtime + " ms";
-
- // `li` initialized at top of scope
- li = id( this.id );
- li.className = bad ? "fail" : "pass";
- li.removeChild( li.firstChild );
- a = li.firstChild;
- li.appendChild( b );
- li.appendChild( a );
- li.appendChild( time );
- li.appendChild( ol );
-
- } else {
- for ( i = 0; i < this.assertions.length; i++ ) {
- if ( !this.assertions[i].result ) {
- bad++;
- config.stats.bad++;
- config.moduleStats.bad++;
- }
- }
- }
-
- runLoggingCallbacks( "testDone", QUnit, {
- name: this.testName,
- module: this.module,
- failed: bad,
- passed: this.assertions.length - bad,
- total: this.assertions.length,
- duration: this.runtime
- });
-
- QUnit.reset();
-
- config.current = undefined;
- },
-
- queue: function() {
- var bad,
- test = this;
-
- synchronize(function() {
- test.init();
- });
- function run() {
- // each of these can by async
- synchronize(function() {
- test.setup();
- });
- synchronize(function() {
- test.run();
- });
- synchronize(function() {
- test.teardown();
- });
- synchronize(function() {
- test.finish();
- });
- }
-
- // `bad` initialized at top of scope
- // defer when previous test run passed, if storage is available
- bad = QUnit.config.reorder && defined.sessionStorage &&
- +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
-
- if ( bad ) {
- run();
- } else {
- synchronize( run, true );
- }
- }
-};
-
-// Root QUnit object.
-// `QUnit` initialized at top of scope
-QUnit = {
-
- // call on start of module test to prepend name to all tests
- module: function( name, testEnvironment ) {
- config.currentModule = name;
- config.currentModuleTestEnvironment = testEnvironment;
- config.modules[name] = true;
- },
-
- asyncTest: function( testName, expected, callback ) {
- if ( arguments.length === 2 ) {
- callback = expected;
- expected = null;
- }
-
- QUnit.test( testName, expected, callback, true );
- },
-
- test: function( testName, expected, callback, async ) {
- var test,
- nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>";
-
- if ( arguments.length === 2 ) {
- callback = expected;
- expected = null;
- }
-
- if ( config.currentModule ) {
- nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml;
- }
-
- test = new Test({
- nameHtml: nameHtml,
- testName: testName,
- expected: expected,
- async: async,
- callback: callback,
- module: config.currentModule,
- moduleTestEnvironment: config.currentModuleTestEnvironment,
- stack: sourceFromStacktrace( 2 )
- });
-
- if ( !validTest( test ) ) {
- return;
- }
-
- test.queue();
- },
-
- // Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
- expect: function( asserts ) {
- if (arguments.length === 1) {
- config.current.expected = asserts;
- } else {
- return config.current.expected;
- }
- },
-
- start: function( count ) {
- // QUnit hasn't been initialized yet.
- // Note: RequireJS (et al) may delay onLoad
- if ( config.semaphore === undefined ) {
- QUnit.begin(function() {
- // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
- setTimeout(function() {
- QUnit.start( count );
- });
- });
- return;
- }
-
- config.semaphore -= count || 1;
- // don't start until equal number of stop-calls
- if ( config.semaphore > 0 ) {
- return;
- }
- // ignore if start is called more often then stop
- if ( config.semaphore < 0 ) {
- config.semaphore = 0;
- QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );
- return;
- }
- // A slight delay, to avoid any current callbacks
- if ( defined.setTimeout ) {
- setTimeout(function() {
- if ( config.semaphore > 0 ) {
- return;
- }
- if ( config.timeout ) {
- clearTimeout( config.timeout );
- }
-
- config.blocking = false;
- process( true );
- }, 13);
- } else {
- config.blocking = false;
- process( true );
- }
- },
-
- stop: function( count ) {
- config.semaphore += count || 1;
- config.blocking = true;
-
- if ( config.testTimeout && defined.setTimeout ) {
- clearTimeout( config.timeout );
- config.timeout = setTimeout(function() {
- QUnit.ok( false, "Test timed out" );
- config.semaphore = 1;
- QUnit.start();
- }, config.testTimeout );
- }
- }
-};
-
-// `assert` initialized at top of scope
-// Assert helpers
-// All of these must either call QUnit.push() or manually do:
-// - runLoggingCallbacks( "log", .. );
-// - config.current.assertions.push({ .. });
-// We attach it to the QUnit object *after* we expose the public API,
-// otherwise `assert` will become a global variable in browsers (#341).
-assert = {
- /**
- * Asserts rough true-ish result.
- * @name ok
- * @function
- * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
- */
- ok: function( result, msg ) {
- if ( !config.current ) {
- throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
- }
- result = !!result;
- msg = msg || (result ? "okay" : "failed" );
-
- var source,
- details = {
- module: config.current.module,
- name: config.current.testName,
- result: result,
- message: msg
- };
-
- msg = "<span class='test-message'>" + escapeText( msg ) + "</span>";
-
- if ( !result ) {
- source = sourceFromStacktrace( 2 );
- if ( source ) {
- details.source = source;
- msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr></table>";
- }
- }
- runLoggingCallbacks( "log", QUnit, details );
- config.current.assertions.push({
- result: result,
- message: msg
- });
- },
-
- /**
- * Assert that the first two arguments are equal, with an optional message.
- * Prints out both actual and expected values.
- * @name equal
- * @function
- * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
- */
- equal: function( actual, expected, message ) {
- /*jshint eqeqeq:false */
- QUnit.push( expected == actual, actual, expected, message );
- },
-
- /**
- * @name notEqual
- * @function
- */
- notEqual: function( actual, expected, message ) {
- /*jshint eqeqeq:false */
- QUnit.push( expected != actual, actual, expected, message );
- },
-
- /**
- * @name propEqual
- * @function
- */
- propEqual: function( actual, expected, message ) {
- actual = objectValues(actual);
- expected = objectValues(expected);
- QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
- },
-
- /**
- * @name notPropEqual
- * @function
- */
- notPropEqual: function( actual, expected, message ) {
- actual = objectValues(actual);
- expected = objectValues(expected);
- QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
- },
-
- /**
- * @name deepEqual
- * @function
- */
- deepEqual: function( actual, expected, message ) {
- QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
- },
-
- /**
- * @name notDeepEqual
- * @function
- */
- notDeepEqual: function( actual, expected, message ) {
- QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
- },
-
- /**
- * @name strictEqual
- * @function
- */
- strictEqual: function( actual, expected, message ) {
- QUnit.push( expected === actual, actual, expected, message );
- },
-
- /**
- * @name notStrictEqual
- * @function
- */
- notStrictEqual: function( actual, expected, message ) {
- QUnit.push( expected !== actual, actual, expected, message );
- },
-
- "throws": function( block, expected, message ) {
- var actual,
- expectedOutput = expected,
- ok = false;
-
- // 'expected' is optional
- if ( typeof expected === "string" ) {
- message = expected;
- expected = null;
- }
-
- config.current.ignoreGlobalErrors = true;
- try {
- block.call( config.current.testEnvironment );
- } catch (e) {
- actual = e;
- }
- config.current.ignoreGlobalErrors = false;
-
- if ( actual ) {
- // we don't want to validate thrown error
- if ( !expected ) {
- ok = true;
- expectedOutput = null;
- // expected is a regexp
- } else if ( QUnit.objectType( expected ) === "regexp" ) {
- ok = expected.test( errorString( actual ) );
- // expected is a constructor
- } else if ( actual instanceof expected ) {
- ok = true;
- // expected is a validation function which returns true is validation passed
- } else if ( expected.call( {}, actual ) === true ) {
- expectedOutput = null;
- ok = true;
- }
-
- QUnit.push( ok, actual, expectedOutput, message );
- } else {
- QUnit.pushFailure( message, null, "No exception was thrown." );
- }
- }
-};
-
</del><span class="cx" style="display: block; padding: 0 10px"> /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @deprecated since 1.8.0
- * Kept assertion helpers in root for backwards compatibility.
- */
-extend( QUnit, assert );
-
-/**
- * @deprecated since 1.9.0
- * Kept root "raises()" for backwards compatibility.
- * (Note that we don't introduce assert.raises).
- */
-QUnit.raises = assert[ "throws" ];
-
-/**
- * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
- * Kept to avoid TypeErrors for undefined methods.
- */
-QUnit.equals = function() {
- QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
-};
-QUnit.same = function() {
- QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
-};
-
-// We want access to the constructor's prototype
-(function() {
- function F() {}
- F.prototype = QUnit;
- QUnit = new F();
- // Make F QUnit's constructor so that we can add to the prototype later
- QUnit.constructor = F;
-}());
-
-/**
</del><span class="cx" style="display: block; padding: 0 10px"> * Config object: Maintain internal state
</span><span class="cx" style="display: block; padding: 0 10px"> * Later exposed as QUnit.config
</span><span class="cx" style="display: block; padding: 0 10px"> * `config` initialized at top of scope
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -705,10 +103,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> // block until document ready
</span><span class="cx" style="display: block; padding: 0 10px"> blocking: true,
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // when enabled, show only failing tests
- // gets persisted through sessionStorage and can be changed in UI via checkbox
- hidepassed: false,
-
</del><span class="cx" style="display: block; padding: 0 10px"> // by default, run previously failed tests first
</span><span class="cx" style="display: block; padding: 0 10px"> // very useful in combination with "Hide passed tests" checked
</span><span class="cx" style="display: block; padding: 0 10px"> reorder: true,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -716,165 +110,233 @@
</span><span class="cx" style="display: block; padding: 0 10px"> // by default, modify document.title when suite is done
</span><span class="cx" style="display: block; padding: 0 10px"> altertitle: true,
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // by default, scroll to top of the page when suite is done
+ scrolltop: true,
+
</ins><span class="cx" style="display: block; padding: 0 10px"> // when enabled, all tests must call expect()
</span><span class="cx" style="display: block; padding: 0 10px"> requireExpects: false,
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // depth up-to which object will be dumped
+ maxDepth: 5,
+
</ins><span class="cx" style="display: block; padding: 0 10px"> // add checkboxes that are persisted in the query-string
</span><span class="cx" style="display: block; padding: 0 10px"> // when enabled, the id is set to `true` as a `QUnit.config` property
</span><span class="cx" style="display: block; padding: 0 10px"> urlConfig: [
</span><span class="cx" style="display: block; padding: 0 10px"> {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ id: "hidepassed",
+ label: "Hide passed tests",
+ tooltip: "Only show tests and assertions that fail. Stored as query-strings."
+ },
+ {
</ins><span class="cx" style="display: block; padding: 0 10px"> id: "noglobals",
</span><span class="cx" style="display: block; padding: 0 10px"> label: "Check for Globals",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ tooltip: "Enabling this will test if any test introduces new properties on the " +
+ "`window` object. Stored as query-strings."
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> {
</span><span class="cx" style="display: block; padding: 0 10px"> id: "notrycatch",
</span><span class="cx" style="display: block; padding: 0 10px"> label: "No try-catch",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
+ "exceptions in IE reasonable. Stored as query-strings."
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> ],
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // Set of all modules.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- modules: {},
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ modules: [],
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // logging callback queues
- begin: [],
- done: [],
- log: [],
- testStart: [],
- testDone: [],
- moduleStart: [],
- moduleDone: []
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // The first unnamed module
+ currentModule: {
+ name: "",
+ tests: []
+ },
+
+ callbacks: {}
</ins><span class="cx" style="display: block; padding: 0 10px"> };
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// Export global variables, unless an 'exports' object exists,
-// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
-if ( typeof exports === "undefined" ) {
- extend( window, QUnit.constructor.prototype );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Push a loose unnamed module to the modules collection
+config.modules.push( config.currentModule );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // Expose QUnit object
- window.QUnit = QUnit;
-}
-
</del><span class="cx" style="display: block; padding: 0 10px"> // Initialize more QUnit.config and QUnit.urlParams
</span><span class="cx" style="display: block; padding: 0 10px"> (function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- var i,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var i, current,
</ins><span class="cx" style="display: block; padding: 0 10px"> location = window.location || { search: "", protocol: "file:" },
</span><span class="cx" style="display: block; padding: 0 10px"> params = location.search.slice( 1 ).split( "&" ),
</span><span class="cx" style="display: block; padding: 0 10px"> length = params.length,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- urlParams = {},
- current;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ urlParams = {};
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> if ( params[ 0 ] ) {
</span><span class="cx" style="display: block; padding: 0 10px"> for ( i = 0; i < length; i++ ) {
</span><span class="cx" style="display: block; padding: 0 10px"> current = params[ i ].split( "=" );
</span><span class="cx" style="display: block; padding: 0 10px"> current[ 0 ] = decodeURIComponent( current[ 0 ] );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // allow just a key to turn on a flag, e.g., test.html?noglobals
</span><span class="cx" style="display: block; padding: 0 10px"> current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- urlParams[ current[ 0 ] ] = current[ 1 ];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( urlParams[ current[ 0 ] ] ) {
+ urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
+ } else {
+ urlParams[ current[ 0 ] ] = current[ 1 ];
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( urlParams.filter === true ) {
+ delete urlParams.filter;
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> QUnit.urlParams = urlParams;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // String search anywhere in moduleName+testName
</span><span class="cx" style="display: block; padding: 0 10px"> config.filter = urlParams.filter;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // Exact match of the module name
- config.module = urlParams.module;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( urlParams.maxDepth ) {
+ config.maxDepth = parseInt( urlParams.maxDepth, 10 ) === -1 ?
+ Number.POSITIVE_INFINITY :
+ urlParams.maxDepth;
+ }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ config.testId = [];
+ if ( urlParams.testId ) {
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Ensure that urlParams.testId is an array
+ urlParams.testId = decodeURIComponent( urlParams.testId ).split( "," );
+ for ( i = 0; i < urlParams.testId.length; i++ ) {
+ config.testId.push( urlParams.testId[ i ] );
+ }
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> // Figure out if we're running the tests from a server or not
</span><span class="cx" style="display: block; padding: 0 10px"> QUnit.isLocal = location.protocol === "file:";
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ // Expose the current QUnit version
+ QUnit.version = "1.18.0";
</ins><span class="cx" style="display: block; padding: 0 10px"> }());
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// Extend QUnit object,
-// these after set here because they should not be exposed as global functions
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Root QUnit object.
+// `QUnit` initialized at top of scope
</ins><span class="cx" style="display: block; padding: 0 10px"> extend( QUnit, {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- assert: assert,
</del><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- config: config,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // call on start of module test to prepend name to all tests
+ module: function( name, testEnvironment ) {
+ var currentModule = {
+ name: name,
+ testEnvironment: testEnvironment,
+ tests: []
+ };
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // Initialize the configuration options
- init: function() {
- extend( config, {
- stats: { all: 0, bad: 0 },
- moduleStats: { all: 0, bad: 0 },
- started: +new Date(),
- updateRate: 1000,
- blocking: false,
- autostart: true,
- autorun: false,
- filter: "",
- queue: [],
- semaphore: 1
- });
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // DEPRECATED: handles setup/teardown functions,
+ // beforeEach and afterEach should be used instead
+ if ( testEnvironment && testEnvironment.setup ) {
+ testEnvironment.beforeEach = testEnvironment.setup;
+ delete testEnvironment.setup;
+ }
+ if ( testEnvironment && testEnvironment.teardown ) {
+ testEnvironment.afterEach = testEnvironment.teardown;
+ delete testEnvironment.teardown;
+ }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- var tests, banner, result,
- qunit = id( "qunit" );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ config.modules.push( currentModule );
+ config.currentModule = currentModule;
+ },
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( qunit ) {
- qunit.innerHTML =
- "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
- "<h2 id='qunit-banner'></h2>" +
- "<div id='qunit-testrunner-toolbar'></div>" +
- "<h2 id='qunit-userAgent'></h2>" +
- "<ol id='qunit-tests'></ol>";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
+ asyncTest: function( testName, expected, callback ) {
+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- tests = id( "qunit-tests" );
- banner = id( "qunit-banner" );
- result = id( "qunit-testresult" );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ QUnit.test( testName, expected, callback, true );
+ },
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( tests ) {
- tests.innerHTML = "";
- }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ test: function( testName, expected, callback, async ) {
+ var test;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( banner ) {
- banner.className = "";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( arguments.length === 2 ) {
+ callback = expected;
+ expected = null;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( result ) {
- result.parentNode.removeChild( result );
- }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ test = new Test({
+ testName: testName,
+ expected: expected,
+ async: async,
+ callback: callback
+ });
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( tests ) {
- result = document.createElement( "p" );
- result.id = "qunit-testresult";
- result.className = "result";
- tests.parentNode.insertBefore( result, tests );
- result.innerHTML = "Running...<br/> ";
- }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ test.queue();
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // Resets the test setup. Useful for tests that modify the DOM.
- /*
- DEPRECATED: Use multiple tests instead of resetting inside a test.
- Use testStart or testDone for custom cleanup.
- This method will throw an error in 2.0, and will be removed in 2.1
- */
- reset: function() {
- var fixture = id( "qunit-fixture" );
- if ( fixture ) {
- fixture.innerHTML = config.fixture;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ skip: function( testName ) {
+ var test = new Test({
+ testName: testName,
+ skip: true
+ });
+
+ test.queue();
+ },
+
+ // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
+ // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
+ start: function( count ) {
+ var globalStartAlreadyCalled = globalStartCalled;
+
+ if ( !config.current ) {
+ globalStartCalled = true;
+
+ if ( runStarted ) {
+ throw new Error( "Called start() outside of a test context while already started" );
+ } else if ( globalStartAlreadyCalled || count > 1 ) {
+ throw new Error( "Called start() outside of a test context too many times" );
+ } else if ( config.autostart ) {
+ throw new Error( "Called start() outside of a test context when " +
+ "QUnit.config.autostart was true" );
+ } else if ( !config.pageLoaded ) {
+
+ // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
+ config.autostart = true;
+ return;
+ }
+ } else {
+
+ // If a test is running, adjust its semaphore
+ config.current.semaphore -= count || 1;
+
+ // Don't start until equal number of stop-calls
+ if ( config.current.semaphore > 0 ) {
+ return;
+ }
+
+ // throw an Error if start is called more often than stop
+ if ( config.current.semaphore < 0 ) {
+ config.current.semaphore = 0;
+
+ QUnit.pushFailure(
+ "Called start() while already started (test's semaphore was 0 already)",
+ sourceFromStacktrace( 2 )
+ );
+ return;
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ resumeProcessing();
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // Trigger an event on an element.
- // @example triggerEvent( document.body, "click" );
- triggerEvent: function( elem, type, event ) {
- if ( document.createEvent ) {
- event = document.createEvent( "MouseEvents" );
- event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
- 0, 0, 0, 0, 0, false, false, false, false, 0, null);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.
+ stop: function( count ) {
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- elem.dispatchEvent( event );
- } else if ( elem.fireEvent ) {
- elem.fireEvent( "on" + type );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // If there isn't a test running, don't allow QUnit.stop() to be called
+ if ( !config.current ) {
+ throw new Error( "Called stop() outside of a test context" );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ // If a test is running, adjust its semaphore
+ config.current.semaphore += count || 1;
+
+ pauseProcessing();
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ config: config,
+
</ins><span class="cx" style="display: block; padding: 0 10px"> // Safe object type checking
</span><span class="cx" style="display: block; padding: 0 10px"> is: function( type, obj ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return QUnit.objectType( obj ) === type;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -882,19 +344,20 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> objectType: function( obj ) {
</span><span class="cx" style="display: block; padding: 0 10px"> if ( typeof obj === "undefined" ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return "undefined";
- // consider: typeof null === object
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return "undefined";
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ // Consider: typeof null === object
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( obj === null ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return "null";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return "null";
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),
- type = match && match[1] || "";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
+ type = match && match[ 1 ] || "";
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> switch ( type ) {
</span><span class="cx" style="display: block; padding: 0 10px"> case "Number":
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( isNaN(obj) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( isNaN( obj ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> return "nan";
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> return "number";
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -912,309 +375,66 @@
</span><span class="cx" style="display: block; padding: 0 10px"> return undefined;
</span><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- push: function( result, actual, expected, message ) {
- if ( !config.current ) {
- throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
- }
-
- var output, source,
- details = {
- module: config.current.module,
- name: config.current.testName,
- result: result,
- message: message,
- actual: actual,
- expected: expected
- };
-
- message = escapeText( message ) || ( result ? "okay" : "failed" );
- message = "<span class='test-message'>" + message + "</span>";
- output = message;
-
- if ( !result ) {
- expected = escapeText( QUnit.jsDump.parse(expected) );
- actual = escapeText( QUnit.jsDump.parse(actual) );
- output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
-
- if ( actual !== expected ) {
- output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
- output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
- }
-
- source = sourceFromStacktrace();
-
- if ( source ) {
- details.source = source;
- output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
- }
-
- output += "</table>";
- }
-
- runLoggingCallbacks( "log", QUnit, details );
-
- config.current.assertions.push({
- result: !!result,
- message: output
- });
- },
-
- pushFailure: function( message, source, actual ) {
- if ( !config.current ) {
- throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
- }
-
- var output,
- details = {
- module: config.current.module,
- name: config.current.testName,
- result: false,
- message: message
- };
-
- message = escapeText( message ) || "error";
- message = "<span class='test-message'>" + message + "</span>";
- output = message;
-
- output += "<table>";
-
- if ( actual ) {
- output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>";
- }
-
- if ( source ) {
- details.source = source;
- output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
- }
-
- output += "</table>";
-
- runLoggingCallbacks( "log", QUnit, details );
-
- config.current.assertions.push({
- result: false,
- message: output
- });
- },
-
- url: function( params ) {
- params = extend( extend( {}, QUnit.urlParams ), params );
- var key,
- querystring = "?";
-
- for ( key in params ) {
- if ( hasOwn.call( params, key ) ) {
- querystring += encodeURIComponent( key ) + "=" +
- encodeURIComponent( params[ key ] ) + "&";
- }
- }
- return window.location.protocol + "//" + window.location.host +
- window.location.pathname + querystring.slice( 0, -1 );
- },
-
</del><span class="cx" style="display: block; padding: 0 10px"> extend: extend,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- id: id,
- addEvent: addEvent,
- addClass: addClass,
- hasClass: hasClass,
- removeClass: removeClass
- // load, equiv, jsDump, diff: Attached later
-});
</del><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * @deprecated: Created for backwards compatibility with test runner that set the hook function
- * into QUnit.{hook}, instead of invoking it and passing the hook function.
- * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
- * Doing this allows us to tell if the following methods have been overwritten on the actual
- * QUnit object.
- */
-extend( QUnit.constructor.prototype, {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ load: function() {
+ config.pageLoaded = true;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // Logging callbacks; all receive a single argument with the listed properties
- // run test/logs.html for any related changes
- begin: registerLoggingCallback( "begin" ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Initialize the configuration options
+ extend( config, {
+ stats: { all: 0, bad: 0 },
+ moduleStats: { all: 0, bad: 0 },
+ started: 0,
+ updateRate: 1000,
+ autostart: true,
+ filter: ""
+ }, true );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // done: { failed, passed, total, runtime }
- done: registerLoggingCallback( "done" ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ config.blocking = false;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // log: { result, actual, expected, message }
- log: registerLoggingCallback( "log" ),
-
- // testStart: { name }
- testStart: registerLoggingCallback( "testStart" ),
-
- // testDone: { name, failed, passed, total, duration }
- testDone: registerLoggingCallback( "testDone" ),
-
- // moduleStart: { name }
- moduleStart: registerLoggingCallback( "moduleStart" ),
-
- // moduleDone: { name, failed, passed, total }
- moduleDone: registerLoggingCallback( "moduleDone" )
-});
-
-if ( typeof document === "undefined" || document.readyState === "complete" ) {
- config.autorun = true;
-}
-
-QUnit.load = function() {
- runLoggingCallbacks( "begin", QUnit, {} );
-
- // Initialize the config, saving the execution queue
- var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,
- urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter,
- numModules = 0,
- moduleNames = [],
- moduleFilterHtml = "",
- urlConfigHtml = "",
- oldconfig = extend( {}, config );
-
- QUnit.init();
- extend(config, oldconfig);
-
- config.blocking = false;
-
- len = config.urlConfig.length;
-
- for ( i = 0; i < len; i++ ) {
- val = config.urlConfig[i];
- if ( typeof val === "string" ) {
- val = {
- id: val,
- label: val,
- tooltip: "[no tooltip available]"
- };
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( config.autostart ) {
+ resumeProcessing();
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- config[ val.id ] = QUnit.urlParams[ val.id ];
- urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) +
- "' name='" + escapeText( val.id ) +
- "' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) +
- " title='" + escapeText( val.tooltip ) +
- "'><label for='qunit-urlconfig-" + escapeText( val.id ) +
- "' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>";
</del><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( i in config.modules ) {
- if ( config.modules.hasOwnProperty( i ) ) {
- moduleNames.push(i);
- }
- }
- numModules = moduleNames.length;
- moduleNames.sort( function( a, b ) {
- return a.localeCompare( b );
- });
- moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " +
- ( config.module === undefined ? "selected='selected'" : "" ) +
- ">< All Modules ></option>";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+});
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Register logging callbacks
+(function() {
+ var i, l, key,
+ callbacks = [ "begin", "done", "log", "testStart", "testDone",
+ "moduleStart", "moduleDone" ];
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( i = 0; i < numModules; i++) {
- moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(moduleNames[i]) ) + "' " +
- ( config.module === moduleNames[i] ? "selected='selected'" : "" ) +
- ">" + escapeText(moduleNames[i]) + "</option>";
- }
- moduleFilterHtml += "</select>";
-
- // `userAgent` initialized at top of scope
- userAgent = id( "qunit-userAgent" );
- if ( userAgent ) {
- userAgent.innerHTML = navigator.userAgent;
- }
-
- // `banner` initialized at top of scope
- banner = id( "qunit-header" );
- if ( banner ) {
- banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
- }
-
- // `toolbar` initialized at top of scope
- toolbar = id( "qunit-testrunner-toolbar" );
- if ( toolbar ) {
- // `filter` initialized at top of scope
- filter = document.createElement( "input" );
- filter.type = "checkbox";
- filter.id = "qunit-filter-pass";
-
- addEvent( filter, "click", function() {
- var tmp,
- ol = document.getElementById( "qunit-tests" );
-
- if ( filter.checked ) {
- ol.className = ol.className + " hidepass";
- } else {
- tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
- ol.className = tmp.replace( / hidepass /, " " );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ function registerLoggingCallback( key ) {
+ var loggingCallback = function( callback ) {
+ if ( QUnit.objectType( callback ) !== "function" ) {
+ throw new Error(
+ "QUnit logging methods require a callback function as their first parameters."
+ );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( defined.sessionStorage ) {
- if (filter.checked) {
- sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
- } else {
- sessionStorage.removeItem( "qunit-filter-passed-tests" );
- }
- }
- });
</del><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
- filter.checked = true;
- // `ol` initialized at top of scope
- ol = document.getElementById( "qunit-tests" );
- ol.className = ol.className + " hidepass";
- }
- toolbar.appendChild( filter );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ config.callbacks[ key ].push( callback );
+ };
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // `label` initialized at top of scope
- label = document.createElement( "label" );
- label.setAttribute( "for", "qunit-filter-pass" );
- label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
- label.innerHTML = "Hide passed tests";
- toolbar.appendChild( label );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // DEPRECATED: This will be removed on QUnit 2.0.0+
+ // Stores the registered functions allowing restoring
+ // at verifyLoggingCallbacks() if modified
+ loggingCallbacks[ key ] = loggingCallback;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- urlConfigCheckboxesContainer = document.createElement("span");
- urlConfigCheckboxesContainer.innerHTML = urlConfigHtml;
- urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input");
- // For oldIE support:
- // * Add handlers to the individual elements instead of the container
- // * Use "click" instead of "change"
- // * Fallback from event.target to event.srcElement
- addEvents( urlConfigCheckboxes, "click", function( event ) {
- var params = {},
- target = event.target || event.srcElement;
- params[ target.name ] = target.checked ? true : undefined;
- window.location = QUnit.url( params );
- });
- toolbar.appendChild( urlConfigCheckboxesContainer );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return loggingCallback;
+ }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if (numModules > 1) {
- moduleFilter = document.createElement( "span" );
- moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
- moduleFilter.innerHTML = moduleFilterHtml;
- addEvent( moduleFilter.lastChild, "change", function() {
- var selectBox = moduleFilter.getElementsByTagName("select")[0],
- selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ for ( i = 0, l = callbacks.length; i < l; i++ ) {
+ key = callbacks[ i ];
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- window.location = QUnit.url({
- module: ( selectedModule === "" ) ? undefined : selectedModule,
- // Remove any existing filters
- filter: undefined,
- testNumber: undefined
- });
- });
- toolbar.appendChild(moduleFilter);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Initialize key collection of logging callback
+ if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) {
+ config.callbacks[ key ] = [];
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- }
</del><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // `main` initialized at top of scope
- main = id( "qunit-fixture" );
- if ( main ) {
- config.fixture = main.innerHTML;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ QUnit[ key ] = registerLoggingCallback( key );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+})();
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( config.autostart ) {
- QUnit.start();
- }
-};
-
-addEvent( window, "load", QUnit.load );
-
</del><span class="cx" style="display: block; padding: 0 10px"> // `onErrorFnPrev` initialized at top of scope
</span><span class="cx" style="display: block; padding: 0 10px"> // Preserve other handlers
</span><span class="cx" style="display: block; padding: 0 10px"> onErrorFnPrev = window.onerror;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1222,7 +442,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> // Cover uncaught exceptions
</span><span class="cx" style="display: block; padding: 0 10px"> // Returning true will suppress the default browser handler,
</span><span class="cx" style="display: block; padding: 0 10px"> // returning false will let it run.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-window.onerror = function ( error, filePath, linerNr ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+window.onerror = function( error, filePath, linerNr ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> var ret = false;
</span><span class="cx" style="display: block; padding: 0 10px"> if ( onErrorFnPrev ) {
</span><span class="cx" style="display: block; padding: 0 10px"> ret = onErrorFnPrev( error, filePath, linerNr );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1237,9 +457,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> QUnit.pushFailure( error, filePath + ":" + linerNr );
</span><span class="cx" style="display: block; padding: 0 10px"> } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- QUnit.test( "global failure", extend( function() {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ QUnit.test( "global failure", extend(function() {
</ins><span class="cx" style="display: block; padding: 0 10px"> QUnit.pushFailure( error, filePath + ":" + linerNr );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- }, { validTest: validTest } ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ }, { validTest: true } ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> return false;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1248,71 +468,27 @@
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> function done() {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var runtime, passed;
+
</ins><span class="cx" style="display: block; padding: 0 10px"> config.autorun = true;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // Log the last module results
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( config.currentModule ) {
- runLoggingCallbacks( "moduleDone", QUnit, {
- name: config.currentModule,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( config.previousModule ) {
+ runLoggingCallbacks( "moduleDone", {
+ name: config.previousModule.name,
+ tests: config.previousModule.tests,
</ins><span class="cx" style="display: block; padding: 0 10px"> failed: config.moduleStats.bad,
</span><span class="cx" style="display: block; padding: 0 10px"> passed: config.moduleStats.all - config.moduleStats.bad,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- total: config.moduleStats.all
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ total: config.moduleStats.all,
+ runtime: now() - config.moduleStats.started
</ins><span class="cx" style="display: block; padding: 0 10px"> });
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> delete config.previousModule;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- var i, key,
- banner = id( "qunit-banner" ),
- tests = id( "qunit-tests" ),
- runtime = +new Date() - config.started,
- passed = config.stats.all - config.stats.bad,
- html = [
- "Tests completed in ",
- runtime,
- " milliseconds.<br/>",
- "<span class='passed'>",
- passed,
- "</span> assertions of <span class='total'>",
- config.stats.all,
- "</span> passed, <span class='failed'>",
- config.stats.bad,
- "</span> failed."
- ].join( "" );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ runtime = now() - config.started;
+ passed = config.stats.all - config.stats.bad;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( banner ) {
- banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
- }
-
- if ( tests ) {
- id( "qunit-testresult" ).innerHTML = html;
- }
-
- if ( config.altertitle && typeof document !== "undefined" && document.title ) {
- // show ✖ for good, ✔ for bad suite result in title
- // use escape sequences in case file gets loaded with non-utf-8-charset
- document.title = [
- ( config.stats.bad ? "\u2716" : "\u2714" ),
- document.title.replace( /^[\u2714\u2716] /i, "" )
- ].join( " " );
- }
-
- // clear own sessionStorage items if all tests passed
- if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
- // `key` & `i` initialized at top of scope
- for ( i = 0; i < sessionStorage.length; i++ ) {
- key = sessionStorage.key( i++ );
- if ( key.indexOf( "qunit-test-" ) === 0 ) {
- sessionStorage.removeItem( key );
- }
- }
- }
-
- // scroll back to top to show results
- if ( window.scrollTo ) {
- window.scrollTo(0, 0);
- }
-
- runLoggingCallbacks( "done", QUnit, {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ runLoggingCallbacks( "done", {
</ins><span class="cx" style="display: block; padding: 0 10px"> failed: config.stats.bad,
</span><span class="cx" style="display: block; padding: 0 10px"> passed: passed,
</span><span class="cx" style="display: block; padding: 0 10px"> total: config.stats.all,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1320,60 +496,16 @@
</span><span class="cx" style="display: block; padding: 0 10px"> });
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/** @return Boolean: true if this test should be ran */
-function validTest( test ) {
- var include,
- filter = config.filter && config.filter.toLowerCase(),
- module = config.module && config.module.toLowerCase(),
- fullName = (test.module + ": " + test.testName).toLowerCase();
-
- // Internally-generated tests are always valid
- if ( test.callback && test.callback.validTest === validTest ) {
- delete test.callback.validTest;
- return true;
- }
-
- if ( config.testNumber ) {
- return test.testNumber === config.testNumber;
- }
-
- if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
- return false;
- }
-
- if ( !filter ) {
- return true;
- }
-
- include = filter.charAt( 0 ) !== "!";
- if ( !include ) {
- filter = filter.slice( 1 );
- }
-
- // If the filter matches, we need to honour include
- if ( fullName.indexOf( filter ) !== -1 ) {
- return include;
- }
-
- // Otherwise, do the opposite
- return !include;
-}
-
-// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
-// Later Safari and IE10 are supposed to support error.stack as well
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Doesn't support IE6 to IE9, it will return undefined on these browsers
</ins><span class="cx" style="display: block; padding: 0 10px"> // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
</span><span class="cx" style="display: block; padding: 0 10px"> function extractStacktrace( e, offset ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- offset = offset === undefined ? 3 : offset;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ offset = offset === undefined ? 4 : offset;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> var stack, include, i;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( e.stacktrace ) {
- // Opera
- return e.stacktrace.split( "\n" )[ offset + 3 ];
- } else if ( e.stack ) {
- // Firefox, Chrome
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( e.stack ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> stack = e.stack.split( "\n" );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if (/^error$/i.test( stack[0] ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( /^error$/i.test( stack[ 0 ] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> stack.shift();
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> if ( fileName ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1389,51 +521,43 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> return stack[ offset ];
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ // Support: Safari <=6 only
</ins><span class="cx" style="display: block; padding: 0 10px"> } else if ( e.sourceURL ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // Safari, PhantomJS
- // hopefully one day Safari provides actual stacktraces
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // exclude useless self-reference for generated Error objects
</span><span class="cx" style="display: block; padding: 0 10px"> if ( /qunit.js$/.test( e.sourceURL ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // for actual exceptions, this is useful
</span><span class="cx" style="display: block; padding: 0 10px"> return e.sourceURL + ":" + e.line;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> function sourceFromStacktrace( offset ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- try {
- throw new Error();
- } catch ( e ) {
- return extractStacktrace( e, offset );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var error = new Error();
+
+ // Support: Safari <=7 only, IE <=10 - 11 only
+ // Not all browsers generate the `stack` property for `new Error()`, see also #636
+ if ( !error.stack ) {
+ try {
+ throw error;
+ } catch ( err ) {
+ error = err;
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-}
</del><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * Escape text for attribute or text content.
- */
-function escapeText( s ) {
- if ( !s ) {
- return "";
- }
- s = s + "";
- // Both single quotes and double quotes (for attributes)
- return s.replace( /['"<>&]/g, function( s ) {
- switch( s ) {
- case "'":
- return "'";
- case "\"":
- return """;
- case "<":
- return "<";
- case ">":
- return ">";
- case "&":
- return "&";
- }
- });
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return extractStacktrace( error, offset );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> function synchronize( callback, last ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( QUnit.objectType( callback ) === "array" ) {
+ while ( callback.length ) {
+ synchronize( callback.shift() );
+ }
+ return;
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> config.queue.push( callback );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> if ( config.autorun && !config.blocking ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1445,11 +569,17 @@
</span><span class="cx" style="display: block; padding: 0 10px"> function next() {
</span><span class="cx" style="display: block; padding: 0 10px"> process( last );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- var start = new Date().getTime();
- config.depth = config.depth ? config.depth + 1 : 1;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var start = now();
+ config.depth = ( config.depth || 0 ) + 1;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> while ( config.queue.length && !config.blocking ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( !defined.setTimeout || config.updateRate <= 0 ||
+ ( ( now() - start ) < config.updateRate ) ) {
+ if ( config.current ) {
+
+ // Reset async tracking for each phase of the Test lifecycle
+ config.current.usedAsync = false;
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> config.queue.shift()();
</span><span class="cx" style="display: block; padding: 0 10px"> } else {
</span><span class="cx" style="display: block; padding: 0 10px"> setTimeout( next, 13 );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1462,6 +592,79 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function begin() {
+ var i, l,
+ modulesLog = [];
+
+ // If the test run hasn't officially begun yet
+ if ( !config.started ) {
+
+ // Record the time of the test run's beginning
+ config.started = now();
+
+ verifyLoggingCallbacks();
+
+ // Delete the loose unnamed module if unused.
+ if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
+ config.modules.shift();
+ }
+
+ // Avoid unnecessary information by not logging modules' test environments
+ for ( i = 0, l = config.modules.length; i < l; i++ ) {
+ modulesLog.push({
+ name: config.modules[ i ].name,
+ tests: config.modules[ i ].tests
+ });
+ }
+
+ // The test run is officially beginning now
+ runLoggingCallbacks( "begin", {
+ totalTests: Test.count,
+ modules: modulesLog
+ });
+ }
+
+ config.blocking = false;
+ process( true );
+}
+
+function resumeProcessing() {
+ runStarted = true;
+
+ // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
+ if ( defined.setTimeout ) {
+ setTimeout(function() {
+ if ( config.current && config.current.semaphore > 0 ) {
+ return;
+ }
+ if ( config.timeout ) {
+ clearTimeout( config.timeout );
+ }
+
+ begin();
+ }, 13 );
+ } else {
+ begin();
+ }
+}
+
+function pauseProcessing() {
+ config.blocking = true;
+
+ if ( config.testTimeout && defined.setTimeout ) {
+ clearTimeout( config.timeout );
+ config.timeout = setTimeout(function() {
+ if ( config.current ) {
+ config.current.semaphore = 0;
+ QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
+ } else {
+ throw new Error( "Test timed out" );
+ }
+ resumeProcessing();
+ }, config.testTimeout );
+ }
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> function saveGlobal() {
</span><span class="cx" style="display: block; padding: 0 10px"> config.pollution = [];
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1487,12 +690,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> newGlobals = diff( config.pollution, old );
</span><span class="cx" style="display: block; padding: 0 10px"> if ( newGlobals.length > 0 ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> deletedGlobals = diff( old, config.pollution );
</span><span class="cx" style="display: block; padding: 0 10px"> if ( deletedGlobals.length > 0 ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1503,7 +706,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> for ( i = 0; i < result.length; i++ ) {
</span><span class="cx" style="display: block; padding: 0 10px"> for ( j = 0; j < b.length; j++ ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( result[i] === b[j] ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( result[ i ] === b[ j ] ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> result.splice( i, 1 );
</span><span class="cx" style="display: block; padding: 0 10px"> i--;
</span><span class="cx" style="display: block; padding: 0 10px"> break;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1513,14 +716,15 @@
</span><span class="cx" style="display: block; padding: 0 10px"> return result;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function extend( a, b ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function extend( a, b, undefOnly ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> for ( var prop in b ) {
</span><span class="cx" style="display: block; padding: 0 10px"> if ( hasOwn.call( b, prop ) ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // Avoid "Member not found" error in IE8 caused by messing with window.constructor
</span><span class="cx" style="display: block; padding: 0 10px"> if ( !( prop === "constructor" && a === window ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> if ( b[ prop ] === undefined ) {
</span><span class="cx" style="display: block; padding: 0 10px"> delete a[ prop ];
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- } else {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> a[ prop ] = b[ prop ];
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1530,77 +734,680 @@
</span><span class="cx" style="display: block; padding: 0 10px"> return a;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * @param {HTMLElement} elem
- * @param {string} type
- * @param {Function} fn
- */
-function addEvent( elem, type, fn ) {
- // Standards-based browsers
- if ( elem.addEventListener ) {
- elem.addEventListener( type, fn, false );
- // IE
- } else {
- elem.attachEvent( "on" + type, fn );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function runLoggingCallbacks( key, args ) {
+ var i, l, callbacks;
+
+ callbacks = config.callbacks[ key ];
+ for ( i = 0, l = callbacks.length; i < l; i++ ) {
+ callbacks[ i ]( args );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * @param {Array|NodeList} elems
- * @param {string} type
- * @param {Function} fn
- */
-function addEvents( elems, type, fn ) {
- var i = elems.length;
- while ( i-- ) {
- addEvent( elems[i], type, fn );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// DEPRECATED: This will be removed on 2.0.0+
+// This function verifies if the loggingCallbacks were modified by the user
+// If so, it will restore it, assign the given callback and print a console warning
+function verifyLoggingCallbacks() {
+ var loggingCallback, userCallback;
+
+ for ( loggingCallback in loggingCallbacks ) {
+ if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
+
+ userCallback = QUnit[ loggingCallback ];
+
+ // Restore the callback function
+ QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
+
+ // Assign the deprecated given callback
+ QUnit[ loggingCallback ]( userCallback );
+
+ if ( window.console && window.console.warn ) {
+ window.console.warn(
+ "QUnit." + loggingCallback + " was replaced with a new value.\n" +
+ "Please, check out the documentation on how to apply logging callbacks.\n" +
+ "Reference: http://api.qunitjs.com/category/callbacks/"
+ );
+ }
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function hasClass( elem, name ) {
- return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;
-}
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// from jquery.js
+function inArray( elem, array ) {
+ if ( array.indexOf ) {
+ return array.indexOf( elem );
+ }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function addClass( elem, name ) {
- if ( !hasClass( elem, name ) ) {
- elem.className += (elem.className ? " " : "") + name;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ for ( var i = 0, length = array.length; i < length; i++ ) {
+ if ( array[ i ] === elem ) {
+ return i;
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ return -1;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function removeClass( elem, name ) {
- var set = " " + elem.className + " ";
- // Class name may appear multiple times
- while ( set.indexOf(" " + name + " ") > -1 ) {
- set = set.replace(" " + name + " " , " ");
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function Test( settings ) {
+ var i, l;
+
+ ++Test.count;
+
+ extend( this, settings );
+ this.assertions = [];
+ this.semaphore = 0;
+ this.usedAsync = false;
+ this.module = config.currentModule;
+ this.stack = sourceFromStacktrace( 3 );
+
+ // Register unique strings
+ for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
+ if ( this.module.tests[ i ].name === this.testName ) {
+ this.testName += " ";
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // If possible, trim it for prettiness, but not necessarily
- elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ this.testId = generateHash( this.module.name, this.testName );
+
+ this.module.tests.push({
+ name: this.testName,
+ testId: this.testId
+ });
+
+ if ( settings.skip ) {
+
+ // Skipped tests will fully ignore any sent callback
+ this.callback = function() {};
+ this.async = false;
+ this.expected = 0;
+ } else {
+ this.assert = new Assert( this );
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function id( name ) {
- return !!( typeof document !== "undefined" && document && document.getElementById ) &&
- document.getElementById( name );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+Test.count = 0;
+
+Test.prototype = {
+ before: function() {
+ if (
+
+ // Emit moduleStart when we're switching from one module to another
+ this.module !== config.previousModule ||
+
+ // They could be equal (both undefined) but if the previousModule property doesn't
+ // yet exist it means this is the first test in a suite that isn't wrapped in a
+ // module, in which case we'll just emit a moduleStart event for 'undefined'.
+ // Without this, reporters can get testStart before moduleStart which is a problem.
+ !hasOwn.call( config, "previousModule" )
+ ) {
+ if ( hasOwn.call( config, "previousModule" ) ) {
+ runLoggingCallbacks( "moduleDone", {
+ name: config.previousModule.name,
+ tests: config.previousModule.tests,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all,
+ runtime: now() - config.moduleStats.started
+ });
+ }
+ config.previousModule = this.module;
+ config.moduleStats = { all: 0, bad: 0, started: now() };
+ runLoggingCallbacks( "moduleStart", {
+ name: this.module.name,
+ tests: this.module.tests
+ });
+ }
+
+ config.current = this;
+
+ this.testEnvironment = extend( {}, this.module.testEnvironment );
+ delete this.testEnvironment.beforeEach;
+ delete this.testEnvironment.afterEach;
+
+ this.started = now();
+ runLoggingCallbacks( "testStart", {
+ name: this.testName,
+ module: this.module.name,
+ testId: this.testId
+ });
+
+ if ( !config.pollution ) {
+ saveGlobal();
+ }
+ },
+
+ run: function() {
+ var promise;
+
+ config.current = this;
+
+ if ( this.async ) {
+ QUnit.stop();
+ }
+
+ this.callbackStarted = now();
+
+ if ( config.notrycatch ) {
+ promise = this.callback.call( this.testEnvironment, this.assert );
+ this.resolvePromise( promise );
+ return;
+ }
+
+ try {
+ promise = this.callback.call( this.testEnvironment, this.assert );
+ this.resolvePromise( promise );
+ } catch ( e ) {
+ this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
+ this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
+
+ // else next test will carry the responsibility
+ saveGlobal();
+
+ // Restart the tests if they're blocking
+ if ( config.blocking ) {
+ QUnit.start();
+ }
+ }
+ },
+
+ after: function() {
+ checkPollution();
+ },
+
+ queueHook: function( hook, hookName ) {
+ var promise,
+ test = this;
+ return function runHook() {
+ config.current = test;
+ if ( config.notrycatch ) {
+ promise = hook.call( test.testEnvironment, test.assert );
+ test.resolvePromise( promise, hookName );
+ return;
+ }
+ try {
+ promise = hook.call( test.testEnvironment, test.assert );
+ test.resolvePromise( promise, hookName );
+ } catch ( error ) {
+ test.pushFailure( hookName + " failed on " + test.testName + ": " +
+ ( error.message || error ), extractStacktrace( error, 0 ) );
+ }
+ };
+ },
+
+ // Currently only used for module level hooks, can be used to add global level ones
+ hooks: function( handler ) {
+ var hooks = [];
+
+ // Hooks are ignored on skipped tests
+ if ( this.skip ) {
+ return hooks;
+ }
+
+ if ( this.module.testEnvironment &&
+ QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) {
+ hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) );
+ }
+
+ return hooks;
+ },
+
+ finish: function() {
+ config.current = this;
+ if ( config.requireExpects && this.expected === null ) {
+ this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
+ "not called.", this.stack );
+ } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
+ this.pushFailure( "Expected " + this.expected + " assertions, but " +
+ this.assertions.length + " were run", this.stack );
+ } else if ( this.expected === null && !this.assertions.length ) {
+ this.pushFailure( "Expected at least one assertion, but none were run - call " +
+ "expect(0) to accept zero assertions.", this.stack );
+ }
+
+ var i,
+ bad = 0;
+
+ this.runtime = now() - this.started;
+ config.stats.all += this.assertions.length;
+ config.moduleStats.all += this.assertions.length;
+
+ for ( i = 0; i < this.assertions.length; i++ ) {
+ if ( !this.assertions[ i ].result ) {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+
+ runLoggingCallbacks( "testDone", {
+ name: this.testName,
+ module: this.module.name,
+ skipped: !!this.skip,
+ failed: bad,
+ passed: this.assertions.length - bad,
+ total: this.assertions.length,
+ runtime: this.runtime,
+
+ // HTML Reporter use
+ assertions: this.assertions,
+ testId: this.testId,
+
+ // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
+ duration: this.runtime
+ });
+
+ // QUnit.reset() is deprecated and will be replaced for a new
+ // fixture reset function on QUnit 2.0/2.1.
+ // It's still called here for backwards compatibility handling
+ QUnit.reset();
+
+ config.current = undefined;
+ },
+
+ queue: function() {
+ var bad,
+ test = this;
+
+ if ( !this.valid() ) {
+ return;
+ }
+
+ function run() {
+
+ // each of these can by async
+ synchronize([
+ function() {
+ test.before();
+ },
+
+ test.hooks( "beforeEach" ),
+
+ function() {
+ test.run();
+ },
+
+ test.hooks( "afterEach" ).reverse(),
+
+ function() {
+ test.after();
+ },
+ function() {
+ test.finish();
+ }
+ ]);
+ }
+
+ // `bad` initialized at top of scope
+ // defer when previous test run passed, if storage is available
+ bad = QUnit.config.reorder && defined.sessionStorage &&
+ +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
+
+ if ( bad ) {
+ run();
+ } else {
+ synchronize( run, true );
+ }
+ },
+
+ push: function( result, actual, expected, message ) {
+ var source,
+ details = {
+ module: this.module.name,
+ name: this.testName,
+ result: result,
+ message: message,
+ actual: actual,
+ expected: expected,
+ testId: this.testId,
+ runtime: now() - this.started
+ };
+
+ if ( !result ) {
+ source = sourceFromStacktrace();
+
+ if ( source ) {
+ details.source = source;
+ }
+ }
+
+ runLoggingCallbacks( "log", details );
+
+ this.assertions.push({
+ result: !!result,
+ message: message
+ });
+ },
+
+ pushFailure: function( message, source, actual ) {
+ if ( !this instanceof Test ) {
+ throw new Error( "pushFailure() assertion outside test context, was " +
+ sourceFromStacktrace( 2 ) );
+ }
+
+ var details = {
+ module: this.module.name,
+ name: this.testName,
+ result: false,
+ message: message || "error",
+ actual: actual || null,
+ testId: this.testId,
+ runtime: now() - this.started
+ };
+
+ if ( source ) {
+ details.source = source;
+ }
+
+ runLoggingCallbacks( "log", details );
+
+ this.assertions.push({
+ result: false,
+ message: message
+ });
+ },
+
+ resolvePromise: function( promise, phase ) {
+ var then, message,
+ test = this;
+ if ( promise != null ) {
+ then = promise.then;
+ if ( QUnit.objectType( then ) === "function" ) {
+ QUnit.stop();
+ then.call(
+ promise,
+ QUnit.start,
+ function( error ) {
+ message = "Promise rejected " +
+ ( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
+ " " + test.testName + ": " + ( error.message || error );
+ test.pushFailure( message, extractStacktrace( error, 0 ) );
+
+ // else next test will carry the responsibility
+ saveGlobal();
+
+ // Unblock
+ QUnit.start();
+ }
+ );
+ }
+ }
+ },
+
+ valid: function() {
+ var include,
+ filter = config.filter && config.filter.toLowerCase(),
+ module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(),
+ fullName = ( this.module.name + ": " + this.testName ).toLowerCase();
+
+ // Internally-generated tests are always valid
+ if ( this.callback && this.callback.validTest ) {
+ return true;
+ }
+
+ if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) {
+ return false;
+ }
+
+ if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) {
+ return false;
+ }
+
+ if ( !filter ) {
+ return true;
+ }
+
+ include = filter.charAt( 0 ) !== "!";
+ if ( !include ) {
+ filter = filter.slice( 1 );
+ }
+
+ // If the filter matches, we need to honour include
+ if ( fullName.indexOf( filter ) !== -1 ) {
+ return include;
+ }
+
+ // Otherwise, do the opposite
+ return !include;
+ }
+
+};
+
+// Resets the test setup. Useful for tests that modify the DOM.
+/*
+DEPRECATED: Use multiple tests instead of resetting inside a test.
+Use testStart or testDone for custom cleanup.
+This method will throw an error in 2.0, and will be removed in 2.1
+*/
+QUnit.reset = function() {
+
+ // Return on non-browser environments
+ // This is necessary to not break on node tests
+ if ( typeof window === "undefined" ) {
+ return;
+ }
+
+ var fixture = defined.document && document.getElementById &&
+ document.getElementById( "qunit-fixture" );
+
+ if ( fixture ) {
+ fixture.innerHTML = config.fixture;
+ }
+};
+
+QUnit.pushFailure = function() {
+ if ( !QUnit.config.current ) {
+ throw new Error( "pushFailure() assertion outside test context, in " +
+ sourceFromStacktrace( 2 ) );
+ }
+
+ // Gets current test obj
+ var currentTest = QUnit.config.current;
+
+ return currentTest.pushFailure.apply( currentTest, arguments );
+};
+
+// Based on Java's String.hashCode, a simple but not
+// rigorously collision resistant hashing function
+function generateHash( module, testName ) {
+ var hex,
+ i = 0,
+ hash = 0,
+ str = module + "\x1C" + testName,
+ len = str.length;
+
+ for ( ; i < len; i++ ) {
+ hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );
+ hash |= 0;
+ }
+
+ // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
+ // strictly necessary but increases user understanding that the id is a SHA-like hash
+ hex = ( 0x100000000 + hash ).toString( 16 );
+ if ( hex.length < 8 ) {
+ hex = "0000000" + hex;
+ }
+
+ return hex.slice( -8 );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-function registerLoggingCallback( key ) {
- return function( callback ) {
- config[key].push( callback );
- };
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function Assert( testContext ) {
+ this.test = testContext;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// Supports deprecated method of completely overwriting logging callbacks
-function runLoggingCallbacks( key, scope, args ) {
- var i, callbacks;
- if ( QUnit.hasOwnProperty( key ) ) {
- QUnit[ key ].call(scope, args );
- } else {
- callbacks = config[ key ];
- for ( i = 0; i < callbacks.length; i++ ) {
- callbacks[ i ].call( scope, args );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Assert helpers
+QUnit.assert = Assert.prototype = {
+
+ // Specify the number of expected assertions to guarantee that failed test
+ // (no assertions are run at all) don't slip through.
+ expect: function( asserts ) {
+ if ( arguments.length === 1 ) {
+ this.test.expected = asserts;
+ } else {
+ return this.test.expected;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ },
+
+ // Increment this Test's semaphore counter, then return a single-use function that
+ // decrements that counter a maximum of once.
+ async: function() {
+ var test = this.test,
+ popped = false;
+
+ test.semaphore += 1;
+ test.usedAsync = true;
+ pauseProcessing();
+
+ return function done() {
+ if ( !popped ) {
+ test.semaphore -= 1;
+ popped = true;
+ resumeProcessing();
+ } else {
+ test.pushFailure( "Called the callback returned from `assert.async` more than once",
+ sourceFromStacktrace( 2 ) );
+ }
+ };
+ },
+
+ // Exports test.push() to the user API
+ push: function( /* result, actual, expected, message */ ) {
+ var assert = this,
+ currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
+
+ // Backwards compatibility fix.
+ // Allows the direct use of global exported assertions and QUnit.assert.*
+ // Although, it's use is not recommended as it can leak assertions
+ // to other tests from async tests, because we only get a reference to the current test,
+ // not exactly the test where assertion were intended to be called.
+ if ( !currentTest ) {
+ throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
+ }
+
+ if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {
+ currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",
+ sourceFromStacktrace( 2 ) );
+
+ // Allow this assertion to continue running anyway...
+ }
+
+ if ( !( assert instanceof Assert ) ) {
+ assert = currentTest.assert;
+ }
+ return assert.test.push.apply( assert.test, arguments );
+ },
+
+ ok: function( result, message ) {
+ message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
+ QUnit.dump.parse( result ) );
+ this.push( !!result, result, true, message );
+ },
+
+ notOk: function( result, message ) {
+ message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " +
+ QUnit.dump.parse( result ) );
+ this.push( !result, result, false, message );
+ },
+
+ equal: function( actual, expected, message ) {
+ /*jshint eqeqeq:false */
+ this.push( expected == actual, actual, expected, message );
+ },
+
+ notEqual: function( actual, expected, message ) {
+ /*jshint eqeqeq:false */
+ this.push( expected != actual, actual, expected, message );
+ },
+
+ propEqual: function( actual, expected, message ) {
+ actual = objectValues( actual );
+ expected = objectValues( expected );
+ this.push( QUnit.equiv( actual, expected ), actual, expected, message );
+ },
+
+ notPropEqual: function( actual, expected, message ) {
+ actual = objectValues( actual );
+ expected = objectValues( expected );
+ this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
+ },
+
+ deepEqual: function( actual, expected, message ) {
+ this.push( QUnit.equiv( actual, expected ), actual, expected, message );
+ },
+
+ notDeepEqual: function( actual, expected, message ) {
+ this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
+ },
+
+ strictEqual: function( actual, expected, message ) {
+ this.push( expected === actual, actual, expected, message );
+ },
+
+ notStrictEqual: function( actual, expected, message ) {
+ this.push( expected !== actual, actual, expected, message );
+ },
+
+ "throws": function( block, expected, message ) {
+ var actual, expectedType,
+ expectedOutput = expected,
+ ok = false,
+ currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current;
+
+ // 'expected' is optional unless doing string comparison
+ if ( message == null && typeof expected === "string" ) {
+ message = expected;
+ expected = null;
+ }
+
+ currentTest.ignoreGlobalErrors = true;
+ try {
+ block.call( currentTest.testEnvironment );
+ } catch (e) {
+ actual = e;
+ }
+ currentTest.ignoreGlobalErrors = false;
+
+ if ( actual ) {
+ expectedType = QUnit.objectType( expected );
+
+ // we don't want to validate thrown error
+ if ( !expected ) {
+ ok = true;
+ expectedOutput = null;
+
+ // expected is a regexp
+ } else if ( expectedType === "regexp" ) {
+ ok = expected.test( errorString( actual ) );
+
+ // expected is a string
+ } else if ( expectedType === "string" ) {
+ ok = expected === errorString( actual );
+
+ // expected is a constructor, maybe an Error constructor
+ } else if ( expectedType === "function" && actual instanceof expected ) {
+ ok = true;
+
+ // expected is an Error object
+ } else if ( expectedType === "object" ) {
+ ok = actual instanceof expected.constructor &&
+ actual.name === expected.name &&
+ actual.message === expected.message;
+
+ // expected is a validation function which returns true if validation passed
+ } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
+ expectedOutput = null;
+ ok = true;
+ }
+ }
+
+ currentTest.assert.push( ok, actual, expectedOutput, message );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-}
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+};
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word
+// Known to us are: Closure Compiler, Narwhal
+(function() {
+ /*jshint sub:true */
+ Assert.prototype.raises = Assert.prototype[ "throws" ];
+}());
+
</ins><span class="cx" style="display: block; padding: 0 10px"> // Test for equality any JavaScript type.
</span><span class="cx" style="display: block; padding: 0 10px"> // Author: Philippe Rathé <prathe@gmail.com>
</span><span class="cx" style="display: block; padding: 0 10px"> QUnit.equiv = (function() {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1619,22 +1426,26 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // the real equiv function
</span><span class="cx" style="display: block; padding: 0 10px"> var innerEquiv,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // stack to decide between skip/abort functions
</span><span class="cx" style="display: block; padding: 0 10px"> callers = [],
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // stack to avoiding loops from circular referencing
</span><span class="cx" style="display: block; padding: 0 10px"> parents = [],
</span><span class="cx" style="display: block; padding: 0 10px"> parentsB = [],
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- getProto = Object.getPrototypeOf || function ( obj ) {
- /*jshint camelcase:false */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ getProto = Object.getPrototypeOf || function( obj ) {
+ /* jshint camelcase: false, proto: true */
</ins><span class="cx" style="display: block; padding: 0 10px"> return obj.__proto__;
</span><span class="cx" style="display: block; padding: 0 10px"> },
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- callbacks = (function () {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ callbacks = (function() {
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // for string, boolean, number and null
</span><span class="cx" style="display: block; padding: 0 10px"> function useStrictEquality( b, a ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> /*jshint eqeqeq:false */
</span><span class="cx" style="display: block; padding: 0 10px"> if ( b instanceof a.constructor || a instanceof b.constructor ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // to catch short annotation VS 'new' annotation of a
</span><span class="cx" style="display: block; padding: 0 10px"> // declaration
</span><span class="cx" style="display: block; padding: 0 10px"> // e.g. var i = 1;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1662,10 +1473,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> "regexp": function( b, a ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return QUnit.objectType( b ) === "regexp" &&
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // the regex itself
</span><span class="cx" style="display: block; padding: 0 10px"> a.source === b.source &&
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // and its modifiers
</span><span class="cx" style="display: block; padding: 0 10px"> a.global === b.global &&
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // (gmi) ...
</span><span class="cx" style="display: block; padding: 0 10px"> a.ignoreCase === b.ignoreCase &&
</span><span class="cx" style="display: block; padding: 0 10px"> a.multiline === b.multiline &&
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1676,7 +1490,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> // - abort otherwise,
</span><span class="cx" style="display: block; padding: 0 10px"> // initial === would have catch identical references anyway
</span><span class="cx" style="display: block; padding: 0 10px"> "function": function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- var caller = callers[callers.length - 1];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var caller = callers[ callers.length - 1 ];
</ins><span class="cx" style="display: block; padding: 0 10px"> return caller !== Object && typeof caller !== "undefined";
</span><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1700,10 +1514,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> for ( i = 0; i < len; i++ ) {
</span><span class="cx" style="display: block; padding: 0 10px"> loop = false;
</span><span class="cx" style="display: block; padding: 0 10px"> for ( j = 0; j < parents.length; j++ ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- aCircular = parents[j] === a[i];
- bCircular = parentsB[j] === b[i];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ aCircular = parents[ j ] === a[ i ];
+ bCircular = parentsB[ j ] === b[ i ];
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( aCircular || bCircular ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( a[i] === b[i] || aCircular && bCircular ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> loop = true;
</span><span class="cx" style="display: block; padding: 0 10px"> } else {
</span><span class="cx" style="display: block; padding: 0 10px"> parents.pop();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1712,7 +1526,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( !loop && !innerEquiv(a[i], b[i]) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> parents.pop();
</span><span class="cx" style="display: block; padding: 0 10px"> parentsB.pop();
</span><span class="cx" style="display: block; padding: 0 10px"> return false;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1724,6 +1538,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> "object": function( b, a ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> /*jshint forin:false */
</span><span class="cx" style="display: block; padding: 0 10px"> var i, j, loop, aCircular, bCircular,
</span><span class="cx" style="display: block; padding: 0 10px"> // Default to true
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1734,11 +1549,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> // comparing constructors is more strict than using
</span><span class="cx" style="display: block; padding: 0 10px"> // instanceof
</span><span class="cx" style="display: block; padding: 0 10px"> if ( a.constructor !== b.constructor ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // Allow objects with no prototype to be equivalent to
</span><span class="cx" style="display: block; padding: 0 10px"> // objects with Object as their constructor.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
- ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
- return false;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) ||
+ ( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) {
+ return false;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1753,10 +1569,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> for ( i in a ) {
</span><span class="cx" style="display: block; padding: 0 10px"> loop = false;
</span><span class="cx" style="display: block; padding: 0 10px"> for ( j = 0; j < parents.length; j++ ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- aCircular = parents[j] === a[i];
- bCircular = parentsB[j] === b[i];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ aCircular = parents[ j ] === a[ i ];
+ bCircular = parentsB[ j ] === b[ i ];
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( aCircular || bCircular ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( a[i] === b[i] || aCircular && bCircular ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> loop = true;
</span><span class="cx" style="display: block; padding: 0 10px"> } else {
</span><span class="cx" style="display: block; padding: 0 10px"> eq = false;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1764,8 +1580,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- aProperties.push(i);
- if ( !loop && !innerEquiv(a[i], b[i]) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ aProperties.push( i );
+ if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> eq = false;
</span><span class="cx" style="display: block; padding: 0 10px"> break;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1791,35 +1607,30 @@
</span><span class="cx" style="display: block; padding: 0 10px"> return true; // end transition
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return (function( a, b ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return ( (function( a, b ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( a === b ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return true; // catch the most you can
</span><span class="cx" style="display: block; padding: 0 10px"> } else if ( a === null || b === null || typeof a === "undefined" ||
</span><span class="cx" style="display: block; padding: 0 10px"> typeof b === "undefined" ||
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- QUnit.objectType(a) !== QUnit.objectType(b) ) {
- return false; // don't lose time with error prone cases
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ QUnit.objectType( a ) !== QUnit.objectType( b ) ) {
+
+ // don't lose time with error prone cases
+ return false;
</ins><span class="cx" style="display: block; padding: 0 10px"> } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return bindCallbacks(a, callbacks, [ b, a ]);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return bindCallbacks( a, callbacks, [ b, a ] );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // apply transition with (1..n) arguments
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- }( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ }( args[ 0 ], args[ 1 ] ) ) &&
+ innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> };
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> return innerEquiv;
</span><span class="cx" style="display: block; padding: 0 10px"> }());
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/**
- * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
- * http://flesler.blogspot.com Licensed under BSD
- * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
- *
- * @projectDescription Advanced and extensible data dumping for Javascript.
- * @version 1.0.0
- * @author Ariel Flesler
- * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
- */
-QUnit.jsDump = (function() {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Based on jsDump by Ariel Flesler
+// http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
+QUnit.dump = (function() {
</ins><span class="cx" style="display: block; padding: 0 10px"> function quote( str ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1827,48 +1638,57 @@
</span><span class="cx" style="display: block; padding: 0 10px"> return o + "";
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> function join( pre, arr, post ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- var s = jsDump.separator(),
- base = jsDump.indent(),
- inner = jsDump.indent(1);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var s = dump.separator(),
+ base = dump.indent(),
+ inner = dump.indent( 1 );
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( arr.join ) {
</span><span class="cx" style="display: block; padding: 0 10px"> arr = arr.join( "," + s + inner );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> if ( !arr ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return pre + post;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return [ pre, inner + arr, base + post ].join(s);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return [ pre, inner + arr, base + post ].join( s );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> function array( arr, stack ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- var i = arr.length, ret = new Array(i);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var i = arr.length,
+ ret = new Array( i );
+
+ if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
+ return "[object Array]";
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> this.up();
</span><span class="cx" style="display: block; padding: 0 10px"> while ( i-- ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- ret[i] = this.parse( arr[i] , undefined , stack);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ ret[ i ] = this.parse( arr[ i ], undefined, stack );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> this.down();
</span><span class="cx" style="display: block; padding: 0 10px"> return join( "[", ret, "]" );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> var reName = /^function (\w+)/,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- jsDump = {
- // type is used mostly internally, you can fix a (custom)type in advance
- parse: function( obj, type, stack ) {
- stack = stack || [ ];
- var inStack, res,
- parser = this.parsers[ type || this.typeOf(obj) ];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ dump = {
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- type = typeof parser;
- inStack = inArray( obj, stack );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // objType is used mostly internally, you can fix a (custom) type in advance
+ parse: function( obj, objType, stack ) {
+ stack = stack || [];
+ var res, parser, parserType,
+ inStack = inArray( obj, stack );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> if ( inStack !== -1 ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return "recursion(" + (inStack - stack.length) + ")";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return "recursion(" + ( inStack - stack.length ) + ")";
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( type === "function" ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ objType = objType || this.typeOf( obj );
+ parser = this.parsers[ objType ];
+ parserType = typeof parser;
+
+ if ( parserType === "function" ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> stack.push( obj );
</span><span class="cx" style="display: block; padding: 0 10px"> res = parser.call( this, obj, stack );
</span><span class="cx" style="display: block; padding: 0 10px"> stack.pop();
</span><span class="cx" style="display: block; padding: 0 10px"> return res;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return ( type === "string" ) ? parser : this.parsers.error;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return ( parserType === "string" ) ? parser : this.parsers.error;
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> typeOf: function( obj ) {
</span><span class="cx" style="display: block; padding: 0 10px"> var type;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1876,23 +1696,29 @@
</span><span class="cx" style="display: block; padding: 0 10px"> type = "null";
</span><span class="cx" style="display: block; padding: 0 10px"> } else if ( typeof obj === "undefined" ) {
</span><span class="cx" style="display: block; padding: 0 10px"> type = "undefined";
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- } else if ( QUnit.is( "regexp", obj) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ } else if ( QUnit.is( "regexp", obj ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> type = "regexp";
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- } else if ( QUnit.is( "date", obj) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ } else if ( QUnit.is( "date", obj ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> type = "date";
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- } else if ( QUnit.is( "function", obj) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ } else if ( QUnit.is( "function", obj ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> type = "function";
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ } else if ( obj.setInterval !== undefined &&
+ obj.document !== undefined &&
+ obj.nodeType === undefined ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> type = "window";
</span><span class="cx" style="display: block; padding: 0 10px"> } else if ( obj.nodeType === 9 ) {
</span><span class="cx" style="display: block; padding: 0 10px"> type = "document";
</span><span class="cx" style="display: block; padding: 0 10px"> } else if ( obj.nodeType ) {
</span><span class="cx" style="display: block; padding: 0 10px"> type = "node";
</span><span class="cx" style="display: block; padding: 0 10px"> } else if (
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // native arrays
</span><span class="cx" style="display: block; padding: 0 10px"> toString.call( obj ) === "[object Array]" ||
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // NodeList objects
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ ( typeof obj.length === "number" && obj.item !== undefined &&
+ ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&
+ obj[ 0 ] === undefined ) ) )
</ins><span class="cx" style="display: block; padding: 0 10px"> ) {
</span><span class="cx" style="display: block; padding: 0 10px"> type = "array";
</span><span class="cx" style="display: block; padding: 0 10px"> } else if ( obj.constructor === Error.prototype.constructor ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1903,7 +1729,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> return type;
</span><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> separator: function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? " " : " ";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? " " : " ";
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> // extra can be a number, shortcut for increasing-calling-decreasing
</span><span class="cx" style="display: block; padding: 0 10px"> indent: function( extra ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1912,9 +1738,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> var chr = this.indentChar;
</span><span class="cx" style="display: block; padding: 0 10px"> if ( this.HTML ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return new Array( this.depth + ( extra || 0 ) ).join(chr);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return new Array( this.depth + ( extra || 0 ) ).join( chr );
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> up: function( a ) {
</span><span class="cx" style="display: block; padding: 0 10px"> this.depth += a || 1;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1923,7 +1749,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> this.depth -= a || 1;
</span><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> setParser: function( name, parser ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- this.parsers[name] = parser;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ this.parsers[ name ] = parser;
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> // The next 3 are exposed so you can use them
</span><span class="cx" style="display: block; padding: 0 10px"> quote: quote,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1931,11 +1757,13 @@
</span><span class="cx" style="display: block; padding: 0 10px"> join: join,
</span><span class="cx" style="display: block; padding: 0 10px"> //
</span><span class="cx" style="display: block; padding: 0 10px"> depth: 1,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- // This is the list of parsers, to modify them, use jsDump.setParser
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ maxDepth: QUnit.config.maxDepth,
+
+ // This is the list of parsers, to modify them, use dump.setParser
</ins><span class="cx" style="display: block; padding: 0 10px"> parsers: {
</span><span class="cx" style="display: block; padding: 0 10px"> window: "[Window]",
</span><span class="cx" style="display: block; padding: 0 10px"> document: "[Document]",
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- error: function(error) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ error: function( error ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> return "Error(\"" + error.message + "\")";
</span><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> unknown: "[Unknown]",
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1943,52 +1771,71 @@
</span><span class="cx" style="display: block; padding: 0 10px"> "undefined": "undefined",
</span><span class="cx" style="display: block; padding: 0 10px"> "function": function( fn ) {
</span><span class="cx" style="display: block; padding: 0 10px"> var ret = "function",
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // functions never have name in IE
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> if ( name ) {
</span><span class="cx" style="display: block; padding: 0 10px"> ret += " " + name;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> ret += "( ";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
- return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
+ return join( ret, dump.parse( fn, "functionCode" ), "}" );
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> array: array,
</span><span class="cx" style="display: block; padding: 0 10px"> nodelist: array,
</span><span class="cx" style="display: block; padding: 0 10px"> "arguments": array,
</span><span class="cx" style="display: block; padding: 0 10px"> object: function( map, stack ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- /*jshint forin:false */
- var ret = [ ], keys, key, val, i;
- QUnit.jsDump.up();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var keys, key, val, i, nonEnumerableProperties,
+ ret = [];
+
+ if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
+ return "[object Object]";
+ }
+
+ dump.up();
</ins><span class="cx" style="display: block; padding: 0 10px"> keys = [];
</span><span class="cx" style="display: block; padding: 0 10px"> for ( key in map ) {
</span><span class="cx" style="display: block; padding: 0 10px"> keys.push( key );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ // Some properties are not always enumerable on Error objects.
+ nonEnumerableProperties = [ "message", "name" ];
+ for ( i in nonEnumerableProperties ) {
+ key = nonEnumerableProperties[ i ];
+ if ( key in map && inArray( key, keys ) < 0 ) {
+ keys.push( key );
+ }
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> keys.sort();
</span><span class="cx" style="display: block; padding: 0 10px"> for ( i = 0; i < keys.length; i++ ) {
</span><span class="cx" style="display: block; padding: 0 10px"> key = keys[ i ];
</span><span class="cx" style="display: block; padding: 0 10px"> val = map[ key ];
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ ret.push( dump.parse( key, "key" ) + ": " +
+ dump.parse( val, undefined, stack ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- QUnit.jsDump.down();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ dump.down();
</ins><span class="cx" style="display: block; padding: 0 10px"> return join( "{", ret, "}" );
</span><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> node: function( node ) {
</span><span class="cx" style="display: block; padding: 0 10px"> var len, i, val,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- open = QUnit.jsDump.HTML ? "<" : "<",
- close = QUnit.jsDump.HTML ? ">" : ">",
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ open = dump.HTML ? "<" : "<",
+ close = dump.HTML ? ">" : ">",
</ins><span class="cx" style="display: block; padding: 0 10px"> tag = node.nodeName.toLowerCase(),
</span><span class="cx" style="display: block; padding: 0 10px"> ret = open + tag,
</span><span class="cx" style="display: block; padding: 0 10px"> attrs = node.attributes;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> if ( attrs ) {
</span><span class="cx" style="display: block; padding: 0 10px"> for ( i = 0, len = attrs.length; i < len; i++ ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- val = attrs[i].nodeValue;
- // IE6 includes all attributes in .attributes, even ones not explicitly set.
- // Those have values like undefined, null, 0, false, "" or "inherit".
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ val = attrs[ i ].nodeValue;
+
+ // IE6 includes all attributes in .attributes, even ones not explicitly
+ // set. Those have values like undefined, null, 0, false, "" or
+ // "inherit".
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( val && val !== "inherit" ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ ret += " " + attrs[ i ].nodeName + "=" +
+ dump.parse( val, "attribute" );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2001,6 +1848,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> return ret + open + "/" + tag + close;
</span><span class="cx" style="display: block; padding: 0 10px"> },
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // function calls it internally, it's the arguments part of the function
</span><span class="cx" style="display: block; padding: 0 10px"> functionArgs: function( fn ) {
</span><span class="cx" style="display: block; padding: 0 10px"> var args,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2010,10 +1858,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> return "";
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- args = new Array(l);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ args = new Array( l );
</ins><span class="cx" style="display: block; padding: 0 10px"> while ( l-- ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> // 97 is 'a'
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- args[l] = String.fromCharCode(97+l);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ args[ l ] = String.fromCharCode( 97 + l );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> return " " + args.join( ", " ) + " ";
</span><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2037,176 +1886,1943 @@
</span><span class="cx" style="display: block; padding: 0 10px"> multiline: true
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return jsDump;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return dump;
</ins><span class="cx" style="display: block; padding: 0 10px"> }());
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// from jquery.js
-function inArray( elem, array ) {
- if ( array.indexOf ) {
- return array.indexOf( elem );
- }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// back compat
+QUnit.jsDump = QUnit.dump;
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( var i = 0, length = array.length; i < length; i++ ) {
- if ( array[ i ] === elem ) {
- return i;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// For browser, export only select globals
+if ( typeof window !== "undefined" ) {
+
+ // Deprecated
+ // Extend assert methods to QUnit and Global scope through Backwards compatibility
+ (function() {
+ var i,
+ assertions = Assert.prototype;
+
+ function applyCurrent( current ) {
+ return function() {
+ var assert = new Assert( QUnit.config.current );
+ current.apply( assert, arguments );
+ };
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- }
</del><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return -1;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ for ( i in assertions ) {
+ QUnit[ i ] = applyCurrent( assertions[ i ] );
+ }
+ })();
+
+ (function() {
+ var i, l,
+ keys = [
+ "test",
+ "module",
+ "expect",
+ "asyncTest",
+ "start",
+ "stop",
+ "ok",
+ "notOk",
+ "equal",
+ "notEqual",
+ "propEqual",
+ "notPropEqual",
+ "deepEqual",
+ "notDeepEqual",
+ "strictEqual",
+ "notStrictEqual",
+ "throws"
+ ];
+
+ for ( i = 0, l = keys.length; i < l; i++ ) {
+ window[ keys[ i ] ] = QUnit[ keys[ i ] ];
+ }
+ })();
+
+ window.QUnit = QUnit;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// For nodejs
+if ( typeof module !== "undefined" && module && module.exports ) {
+ module.exports = QUnit;
+
+ // For consistency with CommonJS environments' exports
+ module.exports.QUnit = QUnit;
+}
+
+// For CommonJS with exports, but without module.exports, like Rhino
+if ( typeof exports !== "undefined" && exports ) {
+ exports.QUnit = QUnit;
+}
+
+if ( typeof define === "function" && define.amd ) {
+ define( function() {
+ return QUnit;
+ } );
+ QUnit.config.autostart = false;
+}
+
+// Get a reference to the global object, like window in browsers
+}( (function() {
+ return this;
+})() ));
+
+/*istanbul ignore next */
+// jscs:disable maximumLineLength
</ins><span class="cx" style="display: block; padding: 0 10px"> /*
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Javascript Diff Algorithm
- * By John Resig (http://ejohn.org/)
- * Modified by Chu Alan "sprite"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * This file is a modified version of google-diff-match-patch's JavaScript implementation
+ * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
+ * modifications are licensed as more fully set forth in LICENSE.txt.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * Released under the MIT license.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * The original source of google-diff-match-patch is attributable and licensed as follows:
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Copyright 2006 Google Inc.
+ * http://code.google.com/p/google-diff-match-patch/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
</ins><span class="cx" style="display: block; padding: 0 10px"> * More Info:
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * http://ejohn.org/projects/javascript-diff-algorithm/
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * https://code.google.com/p/google-diff-match-patch/
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * Usage: QUnit.diff(expected, actual)
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) === "the quick <del>brown </del> fox jump<ins>s</ins><del>ed</del over"
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> QUnit.diff = (function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- /*jshint eqeqeq:false, eqnull:true */
- function diff( o, n ) {
- var i,
- ns = {},
- os = {};
</del><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( i = 0; i < n.length; i++ ) {
- if ( !hasOwn.call( ns, n[i] ) ) {
- ns[ n[i] ] = {
- rows: [],
- o: null
- };
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ function DiffMatchPatch() {
+
+ // Defaults.
+ // Redefine these in your program to override the defaults.
+
+ // Number of seconds to map a diff before giving up (0 for infinity).
+ this.DiffTimeout = 1.0;
+ // Cost of an empty edit operation in terms of edit characters.
+ this.DiffEditCost = 4;
+ }
+
+ // DIFF FUNCTIONS
+
+ /**
+ * The data structure representing a diff is an array of tuples:
+ * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
+ * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
+ */
+ var DIFF_DELETE = -1,
+ DIFF_INSERT = 1,
+ DIFF_EQUAL = 0;
+
+ /**
+ * Find the differences between two texts. Simplifies the problem by stripping
+ * any common prefix or suffix off the texts before diffing.
+ * @param {string} text1 Old string to be diffed.
+ * @param {string} text2 New string to be diffed.
+ * @param {boolean=} optChecklines Optional speedup flag. If present and false,
+ * then don't run a line-level diff first to identify the changed areas.
+ * Defaults to true, which does a faster, slightly less optimal diff.
+ * @param {number} optDeadline Optional time when the diff should be complete
+ * by. Used internally for recursive calls. Users should set DiffTimeout
+ * instead.
+ * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+ */
+ DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines, optDeadline ) {
+ var deadline, checklines, commonlength,
+ commonprefix, commonsuffix, diffs;
+ // Set a deadline by which time the diff must be complete.
+ if ( typeof optDeadline === "undefined" ) {
+ if ( this.DiffTimeout <= 0 ) {
+ optDeadline = Number.MAX_VALUE;
+ } else {
+ optDeadline = ( new Date() ).getTime() + this.DiffTimeout * 1000;
+ }
+ }
+ deadline = optDeadline;
+
+ // Check for null inputs.
+ if ( text1 === null || text2 === null ) {
+ throw new Error( "Null input. (DiffMain)" );
+ }
+
+ // Check for equality (speedup).
+ if ( text1 === text2 ) {
+ if ( text1 ) {
+ return [
+ [ DIFF_EQUAL, text1 ]
+ ];
+ }
+ return [];
+ }
+
+ if ( typeof optChecklines === "undefined" ) {
+ optChecklines = true;
+ }
+
+ checklines = optChecklines;
+
+ // Trim off common prefix (speedup).
+ commonlength = this.diffCommonPrefix( text1, text2 );
+ commonprefix = text1.substring( 0, commonlength );
+ text1 = text1.substring( commonlength );
+ text2 = text2.substring( commonlength );
+
+ // Trim off common suffix (speedup).
+ /////////
+ commonlength = this.diffCommonSuffix( text1, text2 );
+ commonsuffix = text1.substring( text1.length - commonlength );
+ text1 = text1.substring( 0, text1.length - commonlength );
+ text2 = text2.substring( 0, text2.length - commonlength );
+
+ // Compute the diff on the middle block.
+ diffs = this.diffCompute( text1, text2, checklines, deadline );
+
+ // Restore the prefix and suffix.
+ if ( commonprefix ) {
+ diffs.unshift( [ DIFF_EQUAL, commonprefix ] );
+ }
+ if ( commonsuffix ) {
+ diffs.push( [ DIFF_EQUAL, commonsuffix ] );
+ }
+ this.diffCleanupMerge( diffs );
+ return diffs;
+ };
+
+ /**
+ * Reduce the number of edits by eliminating operationally trivial equalities.
+ * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+ */
+ DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) {
+ var changes, equalities, equalitiesLength, lastequality,
+ pointer, preIns, preDel, postIns, postDel;
+ changes = false;
+ equalities = []; // Stack of indices where equalities are found.
+ equalitiesLength = 0; // Keeping our own length var is faster in JS.
+ /** @type {?string} */
+ lastequality = null;
+ // Always equal to diffs[equalities[equalitiesLength - 1]][1]
+ pointer = 0; // Index of current position.
+ // Is there an insertion operation before the last equality.
+ preIns = false;
+ // Is there a deletion operation before the last equality.
+ preDel = false;
+ // Is there an insertion operation after the last equality.
+ postIns = false;
+ // Is there a deletion operation after the last equality.
+ postDel = false;
+ while ( pointer < diffs.length ) {
+ if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found.
+ if ( diffs[ pointer ][ 1 ].length < this.DiffEditCost && ( postIns || postDel ) ) {
+ // Candidate found.
+ equalities[ equalitiesLength++ ] = pointer;
+ preIns = postIns;
+ preDel = postDel;
+ lastequality = diffs[ pointer ][ 1 ];
+ } else {
+ // Not a candidate, and can never become one.
+ equalitiesLength = 0;
+ lastequality = null;
+ }
+ postIns = postDel = false;
+ } else { // An insertion or deletion.
+ if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) {
+ postDel = true;
+ } else {
+ postIns = true;
+ }
+ /*
+ * Five types to be split:
+ * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
+ * <ins>A</ins>X<ins>C</ins><del>D</del>
+ * <ins>A</ins><del>B</del>X<ins>C</ins>
+ * <ins>A</del>X<ins>C</ins><del>D</del>
+ * <ins>A</ins><del>B</del>X<del>C</del>
+ */
+ if ( lastequality && ( ( preIns && preDel && postIns && postDel ) ||
+ ( ( lastequality.length < this.DiffEditCost / 2 ) &&
+ ( preIns + preDel + postIns + postDel ) === 3 ) ) ) {
+ // Duplicate record.
+ diffs.splice( equalities[equalitiesLength - 1], 0, [ DIFF_DELETE, lastequality ] );
+ // Change second copy to insert.
+ diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;
+ equalitiesLength--; // Throw away the equality we just deleted;
+ lastequality = null;
+ if (preIns && preDel) {
+ // No changes made which could affect previous entry, keep going.
+ postIns = postDel = true;
+ equalitiesLength = 0;
+ } else {
+ equalitiesLength--; // Throw away the previous equality.
+ pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;
+ postIns = postDel = false;
+ }
+ changes = true;
+ }
+ }
+ pointer++;
+ }
+
+ if ( changes ) {
+ this.diffCleanupMerge( diffs );
+ }
+ };
+
+ /**
+ * Convert a diff array into a pretty HTML report.
+ * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+ * @param {integer} string to be beautified.
+ * @return {string} HTML representation.
+ */
+ DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) {
+ var op, data, x, html = [];
+ for ( x = 0; x < diffs.length; x++ ) {
+ op = diffs[x][0]; // Operation (insert, delete, equal)
+ data = diffs[x][1]; // Text of change.
+ switch ( op ) {
+ case DIFF_INSERT:
+ html[x] = "<ins>" + data + "</ins>";
+ break;
+ case DIFF_DELETE:
+ html[x] = "<del>" + data + "</del>";
+ break;
+ case DIFF_EQUAL:
+ html[x] = "<span>" + data + "</span>";
+ break;
+ }
+ }
+ return html.join("");
+ };
+
+ /**
+ * Determine the common prefix of two strings.
+ * @param {string} text1 First string.
+ * @param {string} text2 Second string.
+ * @return {number} The number of characters common to the start of each
+ * string.
+ */
+ DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) {
+ var pointermid, pointermax, pointermin, pointerstart;
+ // Quick check for common null cases.
+ if ( !text1 || !text2 || text1.charAt(0) !== text2.charAt(0) ) {
+ return 0;
+ }
+ // Binary search.
+ // Performance analysis: http://neil.fraser.name/news/2007/10/09/
+ pointermin = 0;
+ pointermax = Math.min( text1.length, text2.length );
+ pointermid = pointermax;
+ pointerstart = 0;
+ while ( pointermin < pointermid ) {
+ if ( text1.substring( pointerstart, pointermid ) === text2.substring( pointerstart, pointermid ) ) {
+ pointermin = pointermid;
+ pointerstart = pointermin;
+ } else {
+ pointermax = pointermid;
+ }
+ pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
+ }
+ return pointermid;
+ };
+
+ /**
+ * Determine the common suffix of two strings.
+ * @param {string} text1 First string.
+ * @param {string} text2 Second string.
+ * @return {number} The number of characters common to the end of each string.
+ */
+ DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) {
+ var pointermid, pointermax, pointermin, pointerend;
+ // Quick check for common null cases.
+ if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
+ return 0;
+ }
+ // Binary search.
+ // Performance analysis: http://neil.fraser.name/news/2007/10/09/
+ pointermin = 0;
+ pointermax = Math.min(text1.length, text2.length);
+ pointermid = pointermax;
+ pointerend = 0;
+ while ( pointermin < pointermid ) {
+ if (text1.substring( text1.length - pointermid, text1.length - pointerend ) ===
+ text2.substring( text2.length - pointermid, text2.length - pointerend ) ) {
+ pointermin = pointermid;
+ pointerend = pointermin;
+ } else {
+ pointermax = pointermid;
+ }
+ pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
+ }
+ return pointermid;
+ };
+
+ /**
+ * Find the differences between two texts. Assumes that the texts do not
+ * have any common prefix or suffix.
+ * @param {string} text1 Old string to be diffed.
+ * @param {string} text2 New string to be diffed.
+ * @param {boolean} checklines Speedup flag. If false, then don't run a
+ * line-level diff first to identify the changed areas.
+ * If true, then run a faster, slightly less optimal diff.
+ * @param {number} deadline Time when the diff should be complete by.
+ * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+ * @private
+ */
+ DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) {
+ var diffs, longtext, shorttext, i, hm,
+ text1A, text2A, text1B, text2B,
+ midCommon, diffsA, diffsB;
+
+ if ( !text1 ) {
+ // Just add some text (speedup).
+ return [
+ [ DIFF_INSERT, text2 ]
+ ];
+ }
+
+ if (!text2) {
+ // Just delete some text (speedup).
+ return [
+ [ DIFF_DELETE, text1 ]
+ ];
+ }
+
+ longtext = text1.length > text2.length ? text1 : text2;
+ shorttext = text1.length > text2.length ? text2 : text1;
+ i = longtext.indexOf( shorttext );
+ if ( i !== -1 ) {
+ // Shorter text is inside the longer text (speedup).
+ diffs = [
+ [ DIFF_INSERT, longtext.substring( 0, i ) ],
+ [ DIFF_EQUAL, shorttext ],
+ [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ]
+ ];
+ // Swap insertions for deletions if diff is reversed.
+ if ( text1.length > text2.length ) {
+ diffs[0][0] = diffs[2][0] = DIFF_DELETE;
+ }
+ return diffs;
+ }
+
+ if ( shorttext.length === 1 ) {
+ // Single character string.
+ // After the previous speedup, the character can't be an equality.
+ return [
+ [ DIFF_DELETE, text1 ],
+ [ DIFF_INSERT, text2 ]
+ ];
+ }
+
+ // Check to see if the problem can be split in two.
+ hm = this.diffHalfMatch(text1, text2);
+ if (hm) {
+ // A half-match was found, sort out the return data.
+ text1A = hm[0];
+ text1B = hm[1];
+ text2A = hm[2];
+ text2B = hm[3];
+ midCommon = hm[4];
+ // Send both pairs off for separate processing.
+ diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
+ diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
+ // Merge the results.
+ return diffsA.concat([
+ [ DIFF_EQUAL, midCommon ]
+ ], diffsB);
+ }
+
+ if (checklines && text1.length > 100 && text2.length > 100) {
+ return this.diffLineMode(text1, text2, deadline);
+ }
+
+ return this.diffBisect(text1, text2, deadline);
+ };
+
+ /**
+ * Do the two texts share a substring which is at least half the length of the
+ * longer text?
+ * This speedup can produce non-minimal diffs.
+ * @param {string} text1 First string.
+ * @param {string} text2 Second string.
+ * @return {Array.<string>} Five element Array, containing the prefix of
+ * text1, the suffix of text1, the prefix of text2, the suffix of
+ * text2 and the common middle. Or null if there was no match.
+ * @private
+ */
+ DiffMatchPatch.prototype.diffHalfMatch = function(text1, text2) {
+ var longtext, shorttext, dmp,
+ text1A, text2B, text2A, text1B, midCommon,
+ hm1, hm2, hm;
+ if (this.DiffTimeout <= 0) {
+ // Don't risk returning a non-optimal diff if we have unlimited time.
+ return null;
+ }
+ longtext = text1.length > text2.length ? text1 : text2;
+ shorttext = text1.length > text2.length ? text2 : text1;
+ if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
+ return null; // Pointless.
+ }
+ dmp = this; // 'this' becomes 'window' in a closure.
+
+ /**
+ * Does a substring of shorttext exist within longtext such that the substring
+ * is at least half the length of longtext?
+ * Closure, but does not reference any external variables.
+ * @param {string} longtext Longer string.
+ * @param {string} shorttext Shorter string.
+ * @param {number} i Start index of quarter length substring within longtext.
+ * @return {Array.<string>} Five element Array, containing the prefix of
+ * longtext, the suffix of longtext, the prefix of shorttext, the suffix
+ * of shorttext and the common middle. Or null if there was no match.
+ * @private
+ */
+ function diffHalfMatchI(longtext, shorttext, i) {
+ var seed, j, bestCommon, prefixLength, suffixLength,
+ bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
+ // Start with a 1/4 length substring at position i as a seed.
+ seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
+ j = -1;
+ bestCommon = "";
+ while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
+ prefixLength = dmp.diffCommonPrefix(longtext.substring(i),
+ shorttext.substring(j));
+ suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i),
+ shorttext.substring(0, j));
+ if (bestCommon.length < suffixLength + prefixLength) {
+ bestCommon = shorttext.substring(j - suffixLength, j) +
+ shorttext.substring(j, j + prefixLength);
+ bestLongtextA = longtext.substring(0, i - suffixLength);
+ bestLongtextB = longtext.substring(i + prefixLength);
+ bestShorttextA = shorttext.substring(0, j - suffixLength);
+ bestShorttextB = shorttext.substring(j + prefixLength);
+ }
+ }
+ if (bestCommon.length * 2 >= longtext.length) {
+ return [ bestLongtextA, bestLongtextB,
+ bestShorttextA, bestShorttextB, bestCommon
+ ];
+ } else {
+ return null;
+ }
+ }
+
+ // First check if the second quarter is the seed for a half-match.
+ hm1 = diffHalfMatchI(longtext, shorttext,
+ Math.ceil(longtext.length / 4));
+ // Check again based on the third quarter.
+ hm2 = diffHalfMatchI(longtext, shorttext,
+ Math.ceil(longtext.length / 2));
+ if (!hm1 && !hm2) {
+ return null;
+ } else if (!hm2) {
+ hm = hm1;
+ } else if (!hm1) {
+ hm = hm2;
+ } else {
+ // Both matched. Select the longest.
+ hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
+ }
+
+ // A half-match was found, sort out the return data.
+ text1A, text1B, text2A, text2B;
+ if (text1.length > text2.length) {
+ text1A = hm[0];
+ text1B = hm[1];
+ text2A = hm[2];
+ text2B = hm[3];
+ } else {
+ text2A = hm[0];
+ text2B = hm[1];
+ text1A = hm[2];
+ text1B = hm[3];
+ }
+ midCommon = hm[4];
+ return [ text1A, text1B, text2A, text2B, midCommon ];
+ };
+
+ /**
+ * Do a quick line-level diff on both strings, then rediff the parts for
+ * greater accuracy.
+ * This speedup can produce non-minimal diffs.
+ * @param {string} text1 Old string to be diffed.
+ * @param {string} text2 New string to be diffed.
+ * @param {number} deadline Time when the diff should be complete by.
+ * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+ * @private
+ */
+ DiffMatchPatch.prototype.diffLineMode = function(text1, text2, deadline) {
+ var a, diffs, linearray, pointer, countInsert,
+ countDelete, textInsert, textDelete, j;
+ // Scan the text on a line-by-line basis first.
+ a = this.diffLinesToChars(text1, text2);
+ text1 = a.chars1;
+ text2 = a.chars2;
+ linearray = a.lineArray;
+
+ diffs = this.DiffMain(text1, text2, false, deadline);
+
+ // Convert the diff back to original text.
+ this.diffCharsToLines(diffs, linearray);
+ // Eliminate freak matches (e.g. blank lines)
+ this.diffCleanupSemantic(diffs);
+
+ // Rediff any replacement blocks, this time character-by-character.
+ // Add a dummy entry at the end.
+ diffs.push( [ DIFF_EQUAL, "" ] );
+ pointer = 0;
+ countDelete = 0;
+ countInsert = 0;
+ textDelete = "";
+ textInsert = "";
+ while (pointer < diffs.length) {
+ switch ( diffs[pointer][0] ) {
+ case DIFF_INSERT:
+ countInsert++;
+ textInsert += diffs[pointer][1];
+ break;
+ case DIFF_DELETE:
+ countDelete++;
+ textDelete += diffs[pointer][1];
+ break;
+ case DIFF_EQUAL:
+ // Upon reaching an equality, check for prior redundancies.
+ if (countDelete >= 1 && countInsert >= 1) {
+ // Delete the offending records and add the merged ones.
+ diffs.splice(pointer - countDelete - countInsert,
+ countDelete + countInsert);
+ pointer = pointer - countDelete - countInsert;
+ a = this.DiffMain(textDelete, textInsert, false, deadline);
+ for (j = a.length - 1; j >= 0; j--) {
+ diffs.splice( pointer, 0, a[j] );
+ }
+ pointer = pointer + a.length;
+ }
+ countInsert = 0;
+ countDelete = 0;
+ textDelete = "";
+ textInsert = "";
+ break;
+ }
+ pointer++;
+ }
+ diffs.pop(); // Remove the dummy entry at the end.
+
+ return diffs;
+ };
+
+ /**
+ * Find the 'middle snake' of a diff, split the problem in two
+ * and return the recursively constructed diff.
+ * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+ * @param {string} text1 Old string to be diffed.
+ * @param {string} text2 New string to be diffed.
+ * @param {number} deadline Time at which to bail if not yet complete.
+ * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+ * @private
+ */
+ DiffMatchPatch.prototype.diffBisect = function(text1, text2, deadline) {
+ var text1Length, text2Length, maxD, vOffset, vLength,
+ v1, v2, x, delta, front, k1start, k1end, k2start,
+ k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
+ // Cache the text lengths to prevent multiple calls.
+ text1Length = text1.length;
+ text2Length = text2.length;
+ maxD = Math.ceil((text1Length + text2Length) / 2);
+ vOffset = maxD;
+ vLength = 2 * maxD;
+ v1 = new Array(vLength);
+ v2 = new Array(vLength);
+ // Setting all elements to -1 is faster in Chrome & Firefox than mixing
+ // integers and undefined.
+ for (x = 0; x < vLength; x++) {
+ v1[x] = -1;
+ v2[x] = -1;
+ }
+ v1[vOffset + 1] = 0;
+ v2[vOffset + 1] = 0;
+ delta = text1Length - text2Length;
+ // If the total number of characters is odd, then the front path will collide
+ // with the reverse path.
+ front = (delta % 2 !== 0);
+ // Offsets for start and end of k loop.
+ // Prevents mapping of space beyond the grid.
+ k1start = 0;
+ k1end = 0;
+ k2start = 0;
+ k2end = 0;
+ for (d = 0; d < maxD; d++) {
+ // Bail out if deadline is reached.
+ if ((new Date()).getTime() > deadline) {
+ break;
+ }
+
+ // Walk the front path one step.
+ for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
+ k1Offset = vOffset + k1;
+ if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) {
+ x1 = v1[k1Offset + 1];
+ } else {
+ x1 = v1[k1Offset - 1] + 1;
+ }
+ y1 = x1 - k1;
+ while (x1 < text1Length && y1 < text2Length &&
+ text1.charAt(x1) === text2.charAt(y1)) {
+ x1++;
+ y1++;
+ }
+ v1[k1Offset] = x1;
+ if (x1 > text1Length) {
+ // Ran off the right of the graph.
+ k1end += 2;
+ } else if (y1 > text2Length) {
+ // Ran off the bottom of the graph.
+ k1start += 2;
+ } else if (front) {
+ k2Offset = vOffset + delta - k1;
+ if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
+ // Mirror x2 onto top-left coordinate system.
+ x2 = text1Length - v2[k2Offset];
+ if (x1 >= x2) {
+ // Overlap detected.
+ return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+ }
+ }
+ }
+ }
+
+ // Walk the reverse path one step.
+ for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
+ k2Offset = vOffset + k2;
+ if ( k2 === -d || (k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) {
+ x2 = v2[k2Offset + 1];
+ } else {
+ x2 = v2[k2Offset - 1] + 1;
+ }
+ y2 = x2 - k2;
+ while (x2 < text1Length && y2 < text2Length &&
+ text1.charAt(text1Length - x2 - 1) ===
+ text2.charAt(text2Length - y2 - 1)) {
+ x2++;
+ y2++;
+ }
+ v2[k2Offset] = x2;
+ if (x2 > text1Length) {
+ // Ran off the left of the graph.
+ k2end += 2;
+ } else if (y2 > text2Length) {
+ // Ran off the top of the graph.
+ k2start += 2;
+ } else if (!front) {
+ k1Offset = vOffset + delta - k2;
+ if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
+ x1 = v1[k1Offset];
+ y1 = vOffset + x1 - k1Offset;
+ // Mirror x2 onto top-left coordinate system.
+ x2 = text1Length - x2;
+ if (x1 >= x2) {
+ // Overlap detected.
+ return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+ }
+ }
+ }
+ }
+ }
+ // Diff took too long and hit the deadline or
+ // number of diffs equals number of characters, no commonality at all.
+ return [
+ [ DIFF_DELETE, text1 ],
+ [ DIFF_INSERT, text2 ]
+ ];
+ };
+
+ /**
+ * Given the location of the 'middle snake', split the diff in two parts
+ * and recurse.
+ * @param {string} text1 Old string to be diffed.
+ * @param {string} text2 New string to be diffed.
+ * @param {number} x Index of split point in text1.
+ * @param {number} y Index of split point in text2.
+ * @param {number} deadline Time at which to bail if not yet complete.
+ * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+ * @private
+ */
+ DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) {
+ var text1a, text1b, text2a, text2b, diffs, diffsb;
+ text1a = text1.substring(0, x);
+ text2a = text2.substring(0, y);
+ text1b = text1.substring(x);
+ text2b = text2.substring(y);
+
+ // Compute both diffs serially.
+ diffs = this.DiffMain(text1a, text2a, false, deadline);
+ diffsb = this.DiffMain(text1b, text2b, false, deadline);
+
+ return diffs.concat(diffsb);
+ };
+
+ /**
+ * Reduce the number of edits by eliminating semantically trivial equalities.
+ * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+ */
+ DiffMatchPatch.prototype.diffCleanupSemantic = function(diffs) {
+ var changes, equalities, equalitiesLength, lastequality,
+ pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1,
+ lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
+ changes = false;
+ equalities = []; // Stack of indices where equalities are found.
+ equalitiesLength = 0; // Keeping our own length var is faster in JS.
+ /** @type {?string} */
+ lastequality = null;
+ // Always equal to diffs[equalities[equalitiesLength - 1]][1]
+ pointer = 0; // Index of current position.
+ // Number of characters that changed prior to the equality.
+ lengthInsertions1 = 0;
+ lengthDeletions1 = 0;
+ // Number of characters that changed after the equality.
+ lengthInsertions2 = 0;
+ lengthDeletions2 = 0;
+ while (pointer < diffs.length) {
+ if (diffs[pointer][0] === DIFF_EQUAL) { // Equality found.
+ equalities[equalitiesLength++] = pointer;
+ lengthInsertions1 = lengthInsertions2;
+ lengthDeletions1 = lengthDeletions2;
+ lengthInsertions2 = 0;
+ lengthDeletions2 = 0;
+ lastequality = diffs[pointer][1];
+ } else { // An insertion or deletion.
+ if (diffs[pointer][0] === DIFF_INSERT) {
+ lengthInsertions2 += diffs[pointer][1].length;
+ } else {
+ lengthDeletions2 += diffs[pointer][1].length;
+ }
+ // Eliminate an equality that is smaller or equal to the edits on both
+ // sides of it.
+ if (lastequality && (lastequality.length <=
+ Math.max(lengthInsertions1, lengthDeletions1)) &&
+ (lastequality.length <= Math.max(lengthInsertions2,
+ lengthDeletions2))) {
+ // Duplicate record.
+ diffs.splice( equalities[ equalitiesLength - 1 ], 0, [ DIFF_DELETE, lastequality ] );
+ // Change second copy to insert.
+ diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
+ // Throw away the equality we just deleted.
+ equalitiesLength--;
+ // Throw away the previous equality (it needs to be reevaluated).
+ equalitiesLength--;
+ pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
+ lengthInsertions1 = 0; // Reset the counters.
+ lengthDeletions1 = 0;
+ lengthInsertions2 = 0;
+ lengthDeletions2 = 0;
+ lastequality = null;
+ changes = true;
+ }
+ }
+ pointer++;
+ }
+
+ // Normalize the diff.
+ if (changes) {
+ this.diffCleanupMerge(diffs);
+ }
+
+ // Find any overlaps between deletions and insertions.
+ // e.g: <del>abcxxx</del><ins>xxxdef</ins>
+ // -> <del>abc</del>xxx<ins>def</ins>
+ // e.g: <del>xxxabc</del><ins>defxxx</ins>
+ // -> <ins>def</ins>xxx<del>abc</del>
+ // Only extract an overlap if it is as big as the edit ahead or behind it.
+ pointer = 1;
+ while (pointer < diffs.length) {
+ if (diffs[pointer - 1][0] === DIFF_DELETE &&
+ diffs[pointer][0] === DIFF_INSERT) {
+ deletion = diffs[pointer - 1][1];
+ insertion = diffs[pointer][1];
+ overlapLength1 = this.diffCommonOverlap(deletion, insertion);
+ overlapLength2 = this.diffCommonOverlap(insertion, deletion);
+ if (overlapLength1 >= overlapLength2) {
+ if (overlapLength1 >= deletion.length / 2 ||
+ overlapLength1 >= insertion.length / 2) {
+ // Overlap found. Insert an equality and trim the surrounding edits.
+ diffs.splice( pointer, 0, [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] );
+ diffs[pointer - 1][1] =
+ deletion.substring(0, deletion.length - overlapLength1);
+ diffs[pointer + 1][1] = insertion.substring(overlapLength1);
+ pointer++;
+ }
+ } else {
+ if (overlapLength2 >= deletion.length / 2 ||
+ overlapLength2 >= insertion.length / 2) {
+ // Reverse overlap found.
+ // Insert an equality and swap and trim the surrounding edits.
+ diffs.splice( pointer, 0, [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ] );
+ diffs[pointer - 1][0] = DIFF_INSERT;
+ diffs[pointer - 1][1] =
+ insertion.substring(0, insertion.length - overlapLength2);
+ diffs[pointer + 1][0] = DIFF_DELETE;
+ diffs[pointer + 1][1] =
+ deletion.substring(overlapLength2);
+ pointer++;
+ }
+ }
+ pointer++;
+ }
+ pointer++;
+ }
+ };
+
+ /**
+ * Determine if the suffix of one string is the prefix of another.
+ * @param {string} text1 First string.
+ * @param {string} text2 Second string.
+ * @return {number} The number of characters common to the end of the first
+ * string and the start of the second string.
+ * @private
+ */
+ DiffMatchPatch.prototype.diffCommonOverlap = function(text1, text2) {
+ var text1Length, text2Length, textLength,
+ best, length, pattern, found;
+ // Cache the text lengths to prevent multiple calls.
+ text1Length = text1.length;
+ text2Length = text2.length;
+ // Eliminate the null case.
+ if (text1Length === 0 || text2Length === 0) {
+ return 0;
+ }
+ // Truncate the longer string.
+ if (text1Length > text2Length) {
+ text1 = text1.substring(text1Length - text2Length);
+ } else if (text1Length < text2Length) {
+ text2 = text2.substring(0, text1Length);
+ }
+ textLength = Math.min(text1Length, text2Length);
+ // Quick check for the worst case.
+ if (text1 === text2) {
+ return textLength;
+ }
+
+ // Start by looking for a single character match
+ // and increase length until no match is found.
+ // Performance analysis: http://neil.fraser.name/news/2010/11/04/
+ best = 0;
+ length = 1;
+ while (true) {
+ pattern = text1.substring(textLength - length);
+ found = text2.indexOf(pattern);
+ if (found === -1) {
+ return best;
+ }
+ length += found;
+ if (found === 0 || text1.substring(textLength - length) ===
+ text2.substring(0, length)) {
+ best = length;
+ length++;
+ }
+ }
+ };
+
+ /**
+ * Split two texts into an array of strings. Reduce the texts to a string of
+ * hashes where each Unicode character represents one line.
+ * @param {string} text1 First string.
+ * @param {string} text2 Second string.
+ * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
+ * An object containing the encoded text1, the encoded text2 and
+ * the array of unique strings.
+ * The zeroth element of the array of unique strings is intentionally blank.
+ * @private
+ */
+ DiffMatchPatch.prototype.diffLinesToChars = function(text1, text2) {
+ var lineArray, lineHash, chars1, chars2;
+ lineArray = []; // e.g. lineArray[4] === 'Hello\n'
+ lineHash = {}; // e.g. lineHash['Hello\n'] === 4
+
+ // '\x00' is a valid character, but various debuggers don't like it.
+ // So we'll insert a junk entry to avoid generating a null character.
+ lineArray[0] = "";
+
+ /**
+ * Split a text into an array of strings. Reduce the texts to a string of
+ * hashes where each Unicode character represents one line.
+ * Modifies linearray and linehash through being a closure.
+ * @param {string} text String to encode.
+ * @return {string} Encoded string.
+ * @private
+ */
+ function diffLinesToCharsMunge(text) {
+ var chars, lineStart, lineEnd, lineArrayLength, line;
+ chars = "";
+ // Walk the text, pulling out a substring for each line.
+ // text.split('\n') would would temporarily double our memory footprint.
+ // Modifying text would create many large strings to garbage collect.
+ lineStart = 0;
+ lineEnd = -1;
+ // Keeping our own length variable is faster than looking it up.
+ lineArrayLength = lineArray.length;
+ while (lineEnd < text.length - 1) {
+ lineEnd = text.indexOf("\n", lineStart);
+ if (lineEnd === -1) {
+ lineEnd = text.length - 1;
+ }
+ line = text.substring(lineStart, lineEnd + 1);
+ lineStart = lineEnd + 1;
+
+ if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) :
+ (lineHash[line] !== undefined)) {
+ chars += String.fromCharCode( lineHash[ line ] );
+ } else {
+ chars += String.fromCharCode(lineArrayLength);
+ lineHash[line] = lineArrayLength;
+ lineArray[lineArrayLength++] = line;
+ }
+ }
+ return chars;
+ }
+
+ chars1 = diffLinesToCharsMunge(text1);
+ chars2 = diffLinesToCharsMunge(text2);
+ return {
+ chars1: chars1,
+ chars2: chars2,
+ lineArray: lineArray
+ };
+ };
+
+ /**
+ * Rehydrate the text in a diff from a string of line hashes to real lines of
+ * text.
+ * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+ * @param {!Array.<string>} lineArray Array of unique strings.
+ * @private
+ */
+ DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) {
+ var x, chars, text, y;
+ for ( x = 0; x < diffs.length; x++ ) {
+ chars = diffs[x][1];
+ text = [];
+ for ( y = 0; y < chars.length; y++ ) {
+ text[y] = lineArray[chars.charCodeAt(y)];
+ }
+ diffs[x][1] = text.join("");
+ }
+ };
+
+ /**
+ * Reorder and merge like edit sections. Merge equalities.
+ * Any edit section can move as long as it doesn't cross an equality.
+ * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+ */
+ DiffMatchPatch.prototype.diffCleanupMerge = function(diffs) {
+ var pointer, countDelete, countInsert, textInsert, textDelete,
+ commonlength, changes;
+ diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end.
+ pointer = 0;
+ countDelete = 0;
+ countInsert = 0;
+ textDelete = "";
+ textInsert = "";
+ commonlength;
+ while (pointer < diffs.length) {
+ switch ( diffs[ pointer ][ 0 ] ) {
+ case DIFF_INSERT:
+ countInsert++;
+ textInsert += diffs[pointer][1];
+ pointer++;
+ break;
+ case DIFF_DELETE:
+ countDelete++;
+ textDelete += diffs[pointer][1];
+ pointer++;
+ break;
+ case DIFF_EQUAL:
+ // Upon reaching an equality, check for prior redundancies.
+ if (countDelete + countInsert > 1) {
+ if (countDelete !== 0 && countInsert !== 0) {
+ // Factor out any common prefixies.
+ commonlength = this.diffCommonPrefix(textInsert, textDelete);
+ if (commonlength !== 0) {
+ if ((pointer - countDelete - countInsert) > 0 &&
+ diffs[pointer - countDelete - countInsert - 1][0] ===
+ DIFF_EQUAL) {
+ diffs[pointer - countDelete - countInsert - 1][1] +=
+ textInsert.substring(0, commonlength);
+ } else {
+ diffs.splice( 0, 0, [ DIFF_EQUAL,
+ textInsert.substring( 0, commonlength )
+ ] );
+ pointer++;
+ }
+ textInsert = textInsert.substring(commonlength);
+ textDelete = textDelete.substring(commonlength);
+ }
+ // Factor out any common suffixies.
+ commonlength = this.diffCommonSuffix(textInsert, textDelete);
+ if (commonlength !== 0) {
+ diffs[pointer][1] = textInsert.substring(textInsert.length -
+ commonlength) + diffs[pointer][1];
+ textInsert = textInsert.substring(0, textInsert.length -
+ commonlength);
+ textDelete = textDelete.substring(0, textDelete.length -
+ commonlength);
+ }
+ }
+ // Delete the offending records and add the merged ones.
+ if (countDelete === 0) {
+ diffs.splice( pointer - countInsert,
+ countDelete + countInsert, [ DIFF_INSERT, textInsert ] );
+ } else if (countInsert === 0) {
+ diffs.splice( pointer - countDelete,
+ countDelete + countInsert, [ DIFF_DELETE, textDelete ] );
+ } else {
+ diffs.splice( pointer - countDelete - countInsert,
+ countDelete + countInsert, [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] );
+ }
+ pointer = pointer - countDelete - countInsert +
+ (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
+ } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
+ // Merge this equality with the previous one.
+ diffs[pointer - 1][1] += diffs[pointer][1];
+ diffs.splice(pointer, 1);
+ } else {
+ pointer++;
+ }
+ countInsert = 0;
+ countDelete = 0;
+ textDelete = "";
+ textInsert = "";
+ break;
+ }
+ }
+ if (diffs[diffs.length - 1][1] === "") {
+ diffs.pop(); // Remove the dummy entry at the end.
+ }
+
+ // Second pass: look for single edits surrounded on both sides by equalities
+ // which can be shifted sideways to eliminate an equality.
+ // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
+ changes = false;
+ pointer = 1;
+ // Intentionally ignore the first and last element (don't need checking).
+ while (pointer < diffs.length - 1) {
+ if (diffs[pointer - 1][0] === DIFF_EQUAL &&
+ diffs[pointer + 1][0] === DIFF_EQUAL) {
+ // This is a single edit surrounded by equalities.
+ if ( diffs[ pointer ][ 1 ].substring( diffs[ pointer ][ 1 ].length -
+ diffs[ pointer - 1 ][ 1 ].length ) === diffs[ pointer - 1 ][ 1 ] ) {
+ // Shift the edit over the previous equality.
+ diffs[pointer][1] = diffs[pointer - 1][1] +
+ diffs[pointer][1].substring(0, diffs[pointer][1].length -
+ diffs[pointer - 1][1].length);
+ diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
+ diffs.splice(pointer - 1, 1);
+ changes = true;
+ } else if ( diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer + 1 ][ 1 ].length ) ===
+ diffs[ pointer + 1 ][ 1 ] ) {
+ // Shift the edit over the next equality.
+ diffs[pointer - 1][1] += diffs[pointer + 1][1];
+ diffs[pointer][1] =
+ diffs[pointer][1].substring(diffs[pointer + 1][1].length) +
+ diffs[pointer + 1][1];
+ diffs.splice(pointer + 1, 1);
+ changes = true;
+ }
+ }
+ pointer++;
+ }
+ // If shifts were made, the diff needs reordering and another shift sweep.
+ if (changes) {
+ this.diffCleanupMerge(diffs);
+ }
+ };
+
+ return function(o, n) {
+ var diff, output, text;
+ diff = new DiffMatchPatch();
+ output = diff.DiffMain(o, n);
+ //console.log(output);
+ diff.diffCleanupEfficiency(output);
+ text = diff.diffPrettyHtml(output);
+
+ return text;
+ };
+}());
+// jscs:enable
+
+(function() {
+
+// Deprecated QUnit.init - Ref #530
+// Re-initialize the configuration options
+QUnit.init = function() {
+ var tests, banner, result, qunit,
+ config = QUnit.config;
+
+ config.stats = { all: 0, bad: 0 };
+ config.moduleStats = { all: 0, bad: 0 };
+ config.started = 0;
+ config.updateRate = 1000;
+ config.blocking = false;
+ config.autostart = true;
+ config.autorun = false;
+ config.filter = "";
+ config.queue = [];
+
+ // Return on non-browser environments
+ // This is necessary to not break on node tests
+ if ( typeof window === "undefined" ) {
+ return;
+ }
+
+ qunit = id( "qunit" );
+ if ( qunit ) {
+ qunit.innerHTML =
+ "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
+ "<h2 id='qunit-banner'></h2>" +
+ "<div id='qunit-testrunner-toolbar'></div>" +
+ "<h2 id='qunit-userAgent'></h2>" +
+ "<ol id='qunit-tests'></ol>";
+ }
+
+ tests = id( "qunit-tests" );
+ banner = id( "qunit-banner" );
+ result = id( "qunit-testresult" );
+
+ if ( tests ) {
+ tests.innerHTML = "";
+ }
+
+ if ( banner ) {
+ banner.className = "";
+ }
+
+ if ( result ) {
+ result.parentNode.removeChild( result );
+ }
+
+ if ( tests ) {
+ result = document.createElement( "p" );
+ result.id = "qunit-testresult";
+ result.className = "result";
+ tests.parentNode.insertBefore( result, tests );
+ result.innerHTML = "Running...<br /> ";
+ }
+};
+
+// Don't load the HTML Reporter on non-Browser environments
+if ( typeof window === "undefined" ) {
+ return;
+}
+
+var config = QUnit.config,
+ hasOwn = Object.prototype.hasOwnProperty,
+ defined = {
+ document: window.document !== undefined,
+ sessionStorage: (function() {
+ var x = "qunit-test-string";
+ try {
+ sessionStorage.setItem( x, x );
+ sessionStorage.removeItem( x );
+ return true;
+ } catch ( e ) {
+ return false;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- ns[ n[i] ].rows.push( i );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ }())
+ },
+ modulesList = [];
+
+/**
+* Escape text for attribute or text content.
+*/
+function escapeText( s ) {
+ if ( !s ) {
+ return "";
+ }
+ s = s + "";
+
+ // Both single quotes and double quotes (for attributes)
+ return s.replace( /['"<>&]/g, function( s ) {
+ switch ( s ) {
+ case "'":
+ return "'";
+ case "\"":
+ return """;
+ case "<":
+ return "<";
+ case ">":
+ return ">";
+ case "&":
+ return "&";
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ });
+}
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( i = 0; i < o.length; i++ ) {
- if ( !hasOwn.call( os, o[i] ) ) {
- os[ o[i] ] = {
- rows: [],
- n: null
- };
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/**
+ * @param {HTMLElement} elem
+ * @param {string} type
+ * @param {Function} fn
+ */
+function addEvent( elem, type, fn ) {
+ if ( elem.addEventListener ) {
+
+ // Standards-based browsers
+ elem.addEventListener( type, fn, false );
+ } else if ( elem.attachEvent ) {
+
+ // support: IE <9
+ elem.attachEvent( "on" + type, function() {
+ var event = window.event;
+ if ( !event.target ) {
+ event.target = event.srcElement || document;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- os[ o[i] ].rows.push( i );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ fn.call( elem, event );
+ });
+ }
+}
+
+/**
+ * @param {Array|NodeList} elems
+ * @param {string} type
+ * @param {Function} fn
+ */
+function addEvents( elems, type, fn ) {
+ var i = elems.length;
+ while ( i-- ) {
+ addEvent( elems[ i ], type, fn );
+ }
+}
+
+function hasClass( elem, name ) {
+ return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
+}
+
+function addClass( elem, name ) {
+ if ( !hasClass( elem, name ) ) {
+ elem.className += ( elem.className ? " " : "" ) + name;
+ }
+}
+
+function toggleClass( elem, name ) {
+ if ( hasClass( elem, name ) ) {
+ removeClass( elem, name );
+ } else {
+ addClass( elem, name );
+ }
+}
+
+function removeClass( elem, name ) {
+ var set = " " + elem.className + " ";
+
+ // Class name may appear multiple times
+ while ( set.indexOf( " " + name + " " ) >= 0 ) {
+ set = set.replace( " " + name + " ", " " );
+ }
+
+ // trim for prettiness
+ elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
+}
+
+function id( name ) {
+ return defined.document && document.getElementById && document.getElementById( name );
+}
+
+function getUrlConfigHtml() {
+ var i, j, val,
+ escaped, escapedTooltip,
+ selection = false,
+ len = config.urlConfig.length,
+ urlConfigHtml = "";
+
+ for ( i = 0; i < len; i++ ) {
+ val = config.urlConfig[ i ];
+ if ( typeof val === "string" ) {
+ val = {
+ id: val,
+ label: val
+ };
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( i in ns ) {
- if ( hasOwn.call( ns, i ) ) {
- if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
- n[ ns[i].rows[0] ] = {
- text: n[ ns[i].rows[0] ],
- row: os[i].rows[0]
- };
- o[ os[i].rows[0] ] = {
- text: o[ os[i].rows[0] ],
- row: ns[i].rows[0]
- };
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ escaped = escapeText( val.id );
+ escapedTooltip = escapeText( val.tooltip );
+
+ if ( config[ val.id ] === undefined ) {
+ config[ val.id ] = QUnit.urlParams[ val.id ];
+ }
+
+ if ( !val.value || typeof val.value === "string" ) {
+ urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
+ "' name='" + escaped + "' type='checkbox'" +
+ ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
+ ( config[ val.id ] ? " checked='checked'" : "" ) +
+ " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
+ "' title='" + escapedTooltip + "'>" + val.label + "</label>";
+ } else {
+ urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
+ "' title='" + escapedTooltip + "'>" + val.label +
+ ": </label><select id='qunit-urlconfig-" + escaped +
+ "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
+
+ if ( QUnit.is( "array", val.value ) ) {
+ for ( j = 0; j < val.value.length; j++ ) {
+ escaped = escapeText( val.value[ j ] );
+ urlConfigHtml += "<option value='" + escaped + "'" +
+ ( config[ val.id ] === val.value[ j ] ?
+ ( selection = true ) && " selected='selected'" : "" ) +
+ ">" + escaped + "</option>";
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ } else {
+ for ( j in val.value ) {
+ if ( hasOwn.call( val.value, j ) ) {
+ urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
+ ( config[ val.id ] === j ?
+ ( selection = true ) && " selected='selected'" : "" ) +
+ ">" + escapeText( val.value[ j ] ) + "</option>";
+ }
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( config[ val.id ] && !selection ) {
+ escaped = escapeText( config[ val.id ] );
+ urlConfigHtml += "<option value='" + escaped +
+ "' selected='selected' disabled='disabled'>" + escaped + "</option>";
+ }
+ urlConfigHtml += "</select>";
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( i = 0; i < n.length - 1; i++ ) {
- if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
- n[ i + 1 ] == o[ n[i].row + 1 ] ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return urlConfigHtml;
+}
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- n[ i + 1 ] = {
- text: n[ i + 1 ],
- row: n[i].row + 1
- };
- o[ n[i].row + 1 ] = {
- text: o[ n[i].row + 1 ],
- row: i + 1
- };
- }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+// Handle "click" events on toolbar checkboxes and "change" for select menus.
+// Updates the URL with the new state of `config.urlConfig` values.
+function toolbarChanged() {
+ var updatedUrl, value,
+ field = this,
+ params = {};
+
+ // Detect if field is a select menu or a checkbox
+ if ( "selectedIndex" in field ) {
+ value = field.options[ field.selectedIndex ].value || undefined;
+ } else {
+ value = field.checked ? ( field.defaultValue || true ) : undefined;
+ }
+
+ params[ field.name ] = value;
+ updatedUrl = setUrl( params );
+
+ if ( "hidepassed" === field.name && "replaceState" in window.history ) {
+ config[ field.name ] = value || false;
+ if ( value ) {
+ addClass( id( "qunit-tests" ), "hidepass" );
+ } else {
+ removeClass( id( "qunit-tests" ), "hidepass" );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( i = n.length - 1; i > 0; i-- ) {
- if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
- n[ i - 1 ] == o[ n[i].row - 1 ]) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // It is not necessary to refresh the whole page
+ window.history.replaceState( null, "", updatedUrl );
+ } else {
+ window.location = updatedUrl;
+ }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- n[ i - 1 ] = {
- text: n[ i - 1 ],
- row: n[i].row - 1
- };
- o[ n[i].row - 1 ] = {
- text: o[ n[i].row - 1 ],
- row: i - 1
- };
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function setUrl( params ) {
+ var key,
+ querystring = "?";
+
+ params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
+
+ for ( key in params ) {
+ if ( hasOwn.call( params, key ) ) {
+ if ( params[ key ] === undefined ) {
+ continue;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ querystring += encodeURIComponent( key );
+ if ( params[ key ] !== true ) {
+ querystring += "=" + encodeURIComponent( params[ key ] );
+ }
+ querystring += "&";
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ }
+ return location.protocol + "//" + location.host +
+ location.pathname + querystring.slice( 0, -1 );
+}
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return {
- o: o,
- n: n
- };
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function applyUrlParams() {
+ var selectedModule,
+ modulesList = id( "qunit-modulefilter" ),
+ filter = id( "qunit-filter-input" ).value;
+
+ selectedModule = modulesList ?
+ decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) :
+ undefined;
+
+ window.location = setUrl({
+ module: ( selectedModule === "" ) ? undefined : selectedModule,
+ filter: ( filter === "" ) ? undefined : filter,
+
+ // Remove testId filter
+ testId: undefined
+ });
+}
+
+function toolbarUrlConfigContainer() {
+ var urlConfigContainer = document.createElement( "span" );
+
+ urlConfigContainer.innerHTML = getUrlConfigHtml();
+ addClass( urlConfigContainer, "qunit-url-config" );
+
+ // For oldIE support:
+ // * Add handlers to the individual elements instead of the container
+ // * Use "click" instead of "change" for checkboxes
+ addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
+ addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
+
+ return urlConfigContainer;
+}
+
+function toolbarLooseFilter() {
+ var filter = document.createElement( "form" ),
+ label = document.createElement( "label" ),
+ input = document.createElement( "input" ),
+ button = document.createElement( "button" );
+
+ addClass( filter, "qunit-filter" );
+
+ label.innerHTML = "Filter: ";
+
+ input.type = "text";
+ input.value = config.filter || "";
+ input.name = "filter";
+ input.id = "qunit-filter-input";
+
+ button.innerHTML = "Go";
+
+ label.appendChild( input );
+
+ filter.appendChild( label );
+ filter.appendChild( button );
+ addEvent( filter, "submit", function( ev ) {
+ applyUrlParams();
+
+ if ( ev && ev.preventDefault ) {
+ ev.preventDefault();
+ }
+
+ return false;
+ });
+
+ return filter;
+}
+
+function toolbarModuleFilterHtml() {
+ var i,
+ moduleFilterHtml = "";
+
+ if ( !modulesList.length ) {
+ return false;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return function( o, n ) {
- o = o.replace( /\s+$/, "" );
- n = n.replace( /\s+$/, "" );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ modulesList.sort(function( a, b ) {
+ return a.localeCompare( b );
+ });
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- var i, pre,
- str = "",
- out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
- oSpace = o.match(/\s+/g),
- nSpace = n.match(/\s+/g);
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
+ "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
+ ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
+ ">< All Modules ></option>";
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( oSpace == null ) {
- oSpace = [ " " ];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ for ( i = 0; i < modulesList.length; i++ ) {
+ moduleFilterHtml += "<option value='" +
+ escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
+ ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
+ ">" + escapeText( modulesList[ i ] ) + "</option>";
+ }
+ moduleFilterHtml += "</select>";
+
+ return moduleFilterHtml;
+}
+
+function toolbarModuleFilter() {
+ var toolbar = id( "qunit-testrunner-toolbar" ),
+ moduleFilter = document.createElement( "span" ),
+ moduleFilterHtml = toolbarModuleFilterHtml();
+
+ if ( !toolbar || !moduleFilterHtml ) {
+ return false;
+ }
+
+ moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
+ moduleFilter.innerHTML = moduleFilterHtml;
+
+ addEvent( moduleFilter.lastChild, "change", applyUrlParams );
+
+ toolbar.appendChild( moduleFilter );
+}
+
+function appendToolbar() {
+ var toolbar = id( "qunit-testrunner-toolbar" );
+
+ if ( toolbar ) {
+ toolbar.appendChild( toolbarUrlConfigContainer() );
+ toolbar.appendChild( toolbarLooseFilter() );
+ }
+}
+
+function appendHeader() {
+ var header = id( "qunit-header" );
+
+ if ( header ) {
+ header.innerHTML = "<a href='" +
+ setUrl({ filter: undefined, module: undefined, testId: undefined }) +
+ "'>" + header.innerHTML + "</a> ";
+ }
+}
+
+function appendBanner() {
+ var banner = id( "qunit-banner" );
+
+ if ( banner ) {
+ banner.className = "";
+ }
+}
+
+function appendTestResults() {
+ var tests = id( "qunit-tests" ),
+ result = id( "qunit-testresult" );
+
+ if ( result ) {
+ result.parentNode.removeChild( result );
+ }
+
+ if ( tests ) {
+ tests.innerHTML = "";
+ result = document.createElement( "p" );
+ result.id = "qunit-testresult";
+ result.className = "result";
+ tests.parentNode.insertBefore( result, tests );
+ result.innerHTML = "Running...<br /> ";
+ }
+}
+
+function storeFixture() {
+ var fixture = id( "qunit-fixture" );
+ if ( fixture ) {
+ config.fixture = fixture.innerHTML;
+ }
+}
+
+function appendUserAgent() {
+ var userAgent = id( "qunit-userAgent" );
+
+ if ( userAgent ) {
+ userAgent.innerHTML = "";
+ userAgent.appendChild(
+ document.createTextNode(
+ "QUnit " + QUnit.version + "; " + navigator.userAgent
+ )
+ );
+ }
+}
+
+function appendTestsList( modules ) {
+ var i, l, x, z, test, moduleObj;
+
+ for ( i = 0, l = modules.length; i < l; i++ ) {
+ moduleObj = modules[ i ];
+
+ if ( moduleObj.name ) {
+ modulesList.push( moduleObj.name );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- else {
- oSpace.push( " " );
- }
</del><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( nSpace == null ) {
- nSpace = [ " " ];
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
+ test = moduleObj.tests[ x ];
+
+ appendTest( test.name, test.testId, moduleObj.name );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- else {
- nSpace.push( " " );
- }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( out.n.length === 0 ) {
- for ( i = 0; i < out.o.length; i++ ) {
- str += "<del>" + out.o[i] + oSpace[i] + "</del>";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function appendTest( name, testId, moduleName ) {
+ var title, rerunTrigger, testBlock, assertList,
+ tests = id( "qunit-tests" );
+
+ if ( !tests ) {
+ return;
+ }
+
+ title = document.createElement( "strong" );
+ title.innerHTML = getNameHtml( name, moduleName );
+
+ rerunTrigger = document.createElement( "a" );
+ rerunTrigger.innerHTML = "Rerun";
+ rerunTrigger.href = setUrl({ testId: testId });
+
+ testBlock = document.createElement( "li" );
+ testBlock.appendChild( title );
+ testBlock.appendChild( rerunTrigger );
+ testBlock.id = "qunit-test-output-" + testId;
+
+ assertList = document.createElement( "ol" );
+ assertList.className = "qunit-assert-list";
+
+ testBlock.appendChild( assertList );
+
+ tests.appendChild( testBlock );
+}
+
+// HTML Reporter initialization and load
+QUnit.begin(function( details ) {
+ var qunit = id( "qunit" );
+
+ // Fixture is the only one necessary to run without the #qunit element
+ storeFixture();
+
+ if ( qunit ) {
+ qunit.innerHTML =
+ "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
+ "<h2 id='qunit-banner'></h2>" +
+ "<div id='qunit-testrunner-toolbar'></div>" +
+ "<h2 id='qunit-userAgent'></h2>" +
+ "<ol id='qunit-tests'></ol>";
+ }
+
+ appendHeader();
+ appendBanner();
+ appendTestResults();
+ appendUserAgent();
+ appendToolbar();
+ appendTestsList( details.modules );
+ toolbarModuleFilter();
+
+ if ( qunit && config.hidepassed ) {
+ addClass( qunit.lastChild, "hidepass" );
+ }
+});
+
+QUnit.done(function( details ) {
+ var i, key,
+ banner = id( "qunit-banner" ),
+ tests = id( "qunit-tests" ),
+ html = [
+ "Tests completed in ",
+ details.runtime,
+ " milliseconds.<br />",
+ "<span class='passed'>",
+ details.passed,
+ "</span> assertions of <span class='total'>",
+ details.total,
+ "</span> passed, <span class='failed'>",
+ details.failed,
+ "</span> failed."
+ ].join( "" );
+
+ if ( banner ) {
+ banner.className = details.failed ? "qunit-fail" : "qunit-pass";
+ }
+
+ if ( tests ) {
+ id( "qunit-testresult" ).innerHTML = html;
+ }
+
+ if ( config.altertitle && defined.document && document.title ) {
+
+ // show ✖ for good, ✔ for bad suite result in title
+ // use escape sequences in case file gets loaded with non-utf-8-charset
+ document.title = [
+ ( details.failed ? "\u2716" : "\u2714" ),
+ document.title.replace( /^[\u2714\u2716] /i, "" )
+ ].join( " " );
+ }
+
+ // clear own sessionStorage items if all tests passed
+ if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
+ for ( i = 0; i < sessionStorage.length; i++ ) {
+ key = sessionStorage.key( i++ );
+ if ( key.indexOf( "qunit-test-" ) === 0 ) {
+ sessionStorage.removeItem( key );
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- else {
- if ( out.n[0].text == null ) {
- for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
- str += "<del>" + out.o[n] + oSpace[n] + "</del>";
- }
- }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( i = 0; i < out.n.length; i++ ) {
- if (out.n[i].text == null) {
- str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
- }
- else {
- // `pre` initialized at top of scope
- pre = "";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // scroll back to top to show results
+ if ( config.scrolltop && window.scrollTo ) {
+ window.scrollTo( 0, 0 );
+ }
+});
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
- pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
- }
- str += " " + out.n[i].text + nSpace[i] + pre;
- }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+function getNameHtml( name, module ) {
+ var nameHtml = "";
+
+ if ( module ) {
+ nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
+ }
+
+ nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
+
+ return nameHtml;
+}
+
+QUnit.testStart(function( details ) {
+ var running, testBlock, bad;
+
+ testBlock = id( "qunit-test-output-" + details.testId );
+ if ( testBlock ) {
+ testBlock.className = "running";
+ } else {
+
+ // Report later registered tests
+ appendTest( details.name, details.testId, details.module );
+ }
+
+ running = id( "qunit-testresult" );
+ if ( running ) {
+ bad = QUnit.config.reorder && defined.sessionStorage &&
+ +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name );
+
+ running.innerHTML = ( bad ?
+ "Rerunning previously failed test: <br />" :
+ "Running: <br />" ) +
+ getNameHtml( details.name, details.module );
+ }
+
+});
+
+QUnit.log(function( details ) {
+ var assertList, assertLi,
+ message, expected, actual,
+ testItem = id( "qunit-test-output-" + details.testId );
+
+ if ( !testItem ) {
+ return;
+ }
+
+ message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
+ message = "<span class='test-message'>" + message + "</span>";
+ message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
+
+ // pushFailure doesn't provide details.expected
+ // when it calls, it's implicit to also not show expected and diff stuff
+ // Also, we need to check details.expected existence, as it can exist and be undefined
+ if ( !details.result && hasOwn.call( details, "expected" ) ) {
+ expected = escapeText( QUnit.dump.parse( details.expected ) );
+ actual = escapeText( QUnit.dump.parse( details.actual ) );
+ message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
+ expected +
+ "</pre></td></tr>";
+
+ if ( actual !== expected ) {
+ message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
+ actual + "</pre></td></tr>" +
+ "<tr class='test-diff'><th>Diff: </th><td><pre>" +
+ QUnit.diff( expected, actual ) + "</pre></td></tr>";
+ } else {
+ if ( expected.indexOf( "[object Array]" ) !== -1 ||
+ expected.indexOf( "[object Object]" ) !== -1 ) {
+ 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='" + setUrl({ maxDepth: -1 }) + "'>" +
+ "Rerun</a> without max depth.</p></td></tr>";
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return str;
- };
-}());
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( details.source ) {
+ message += "<tr class='test-source'><th>Source: </th><td><pre>" +
+ escapeText( details.source ) + "</pre></td></tr>";
+ }
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// for CommonJS environments, export everything
-if ( typeof exports !== "undefined" ) {
- extend( exports, QUnit.constructor.prototype );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ message += "</table>";
+
+ // this occours when pushFailure is set and we have an extracted stack trace
+ } else if ( !details.result && details.source ) {
+ message += "<table>" +
+ "<tr class='test-source'><th>Source: </th><td><pre>" +
+ escapeText( details.source ) + "</pre></td></tr>" +
+ "</table>";
+ }
+
+ assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
+
+ assertLi = document.createElement( "li" );
+ assertLi.className = details.result ? "pass" : "fail";
+ assertLi.innerHTML = message;
+ assertList.appendChild( assertLi );
+});
+
+QUnit.testDone(function( details ) {
+ var testTitle, time, testItem, assertList,
+ good, bad, testCounts, skipped,
+ tests = id( "qunit-tests" );
+
+ if ( !tests ) {
+ return;
+ }
+
+ testItem = id( "qunit-test-output-" + details.testId );
+
+ assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
+
+ good = details.passed;
+ bad = details.failed;
+
+ // store result when possible
+ if ( config.reorder && defined.sessionStorage ) {
+ if ( bad ) {
+ sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
+ } else {
+ sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
+ }
+ }
+
+ if ( bad === 0 ) {
+ addClass( assertList, "qunit-collapsed" );
+ }
+
+ // testItem.firstChild is the test name
+ testTitle = testItem.firstChild;
+
+ testCounts = bad ?
+ "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
+ "";
+
+ testTitle.innerHTML += " <b class='counts'>(" + testCounts +
+ details.assertions.length + ")</b>";
+
+ if ( details.skipped ) {
+ testItem.className = "skipped";
+ skipped = document.createElement( "em" );
+ skipped.className = "qunit-skipped-label";
+ skipped.innerHTML = "skipped";
+ testItem.insertBefore( skipped, testTitle );
+ } else {
+ addEvent( testTitle, "click", function() {
+ toggleClass( assertList, "qunit-collapsed" );
+ });
+
+ testItem.className = bad ? "fail" : "pass";
+
+ time = document.createElement( "span" );
+ time.className = "runtime";
+ time.innerHTML = details.runtime + " ms";
+ testItem.insertBefore( time, assertList );
+ }
+});
+
+if ( defined.document ) {
+ if ( document.readyState === "complete" ) {
+ QUnit.load();
+ } else {
+ addEvent( window, "load", QUnit.load );
+ }
+} else {
+ config.pageLoaded = true;
+ config.autorun = true;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-// get at whatever the global object is, like window in browsers
-}( (function() {return this;}.call()) ));
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+})();
</ins></span></pre>
</div>
</div>
</body>
</html>