diff --git a/test/asset/qunit-extras.js b/test/asset/qunit-extras.js new file mode 100644 index 000000000..89d8221db --- /dev/null +++ b/test/asset/qunit-extras.js @@ -0,0 +1,164 @@ +;(function(root, undefined) { + 'use strict'; + + /** Native method shortcut */ + var unshift = Array.prototype.unshift; + + /** Used to match HTML entities */ + var reEscapedHtml = /(&|<|>|"|')/g; + + /** Used to match parts of the assert message */ + var reDied = /^Died on test #\d+/, + reExpected = /Expected: *<\/th>
([\s\S]*?)<\/pre>/,
+      reMessage = /^([\s\S]*?)<\/span>/;
+
+  /** Used to convert HTML entities to characters */
+  var htmlUnescapes = {
+    '&': '&',
+    '<': '<',
+    '>': '>',
+    '"': '"',
+    ''': "'"
+  };
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Checks if a given value is present in an array using strict equality
+   * for comparisons, i.e. `===`.
+   *
+   * @oruvate
+   * @param {Array} array The array to iterate over.
+   * @param {*} target The value to check for.
+   * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
+   */
+  function contains(array, value) {
+    var index = -1,
+        length = array ? array.length : 0;
+
+    while (++index < length) {
+      if (array[index] === value) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Resolves the value of `property` on `object`. If `object` is falsey then
+   * `undefined` is returned.
+   *
+   * @private
+   * @param {Object} object The object to inspect.
+   * @param {string} property The property to get the value of.
+   * @returns {*} Returns the resolved value.
+   */
+  function result(object, property) {
+    return object ? object[property] : undefined;
+  }
+
+  /**
+   * Converts the HTML entities `&`, `<`, `>`, `"`, and `'`
+   * in `string` to their corresponding characters.
+   *
+   * @private
+   * @param {string} string The string to unescape.
+   * @returns {string} Returns the unescaped string.
+   */
+  function unescape(string) {
+    return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
+  }
+
+  /**
+   * Used by `unescape` to convert HTML entities to characters.
+   *
+   * @private
+   * @param {string} match The matched character to unescape.
+   * @returns {string} Returns the unescaped character.
+   */
+  function unescapeHtmlChar(match) {
+    return htmlUnescapes[match];
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /** The number of retries async tests have to succeed */
+  QUnit.config.asyncRetries = 0;
+
+  /** An object of excused tests and assertions */
+  QUnit.config.excused = {};
+
+  /**
+   * A callback triggered at the start of every test.
+   *
+   * @memberOf QUnit
+   * @param {Object} details An object with `module` and `name` properties.
+   */
+  QUnit.testStart(function(details) {
+    var excused = QUnit.config.excused || {},
+        excusedTests = excused[details.module],
+        excusedAsserts = excusedTests && excusedTests[details.name];
+
+    var test = QUnit.config.current,
+        finish = test.finish;
+
+    // allow async tests to retry
+    if (test.async && !test.retries) {
+      test.retries = 0;
+      test.finish = function() {
+        var asserts = this.assertions,
+            index = -1,
+            length = asserts.length,
+            queue = QUnit.config.queue;
+
+        while (++index < length) {
+          var assert = asserts[index];
+          if (!assert.result && this.retries < QUnit.config.asyncRetries) {
+            this.retries++;
+            asserts.length = 0;
+
+            var oldLength = queue.length;
+            this.queue();
+            unshift.apply(queue, queue.splice(oldLength, queue.length - oldLength));
+            return;
+          }
+        }
+        finish.call(this);
+      };
+    }
+    // nothing to excuse
+    if (!excusedAsserts) {
+      return;
+    }
+    // excuse the entire test
+    if (excusedAsserts === true) {
+      test.async = false;
+      test.callback = function() {};
+      test.expected = 0;
+      return;
+    }
+    // excuse specific assertions
+    test.finish = function() {
+      var asserts = this.assertions,
+          index = -1,
+          length = asserts.length;
+
+      while (++index < length) {
+        var assert = asserts[index],
+            message = unescape(result(reMessage.exec(assert.message), 1)),
+            died = result(reDied.exec(message), 0),
+            expected = unescape(result(reExpected.exec(assert.message), 1));
+
+        if ((message && contains(excusedAsserts, message)) ||
+            (died && contains(excusedAsserts, died)) ||
+            (expected && (
+              contains(excusedAsserts, expected) ||
+              contains(excusedAsserts, expected.replace(/\s+/g, ''))
+            ))) {
+          assert.result = true;
+        }
+      }
+      finish.call(this);
+    };
+  });
+}(this));
diff --git a/test/backbone.html b/test/backbone.html
index 8b9fa6be6..c9c56e7db 100644
--- a/test/backbone.html
+++ b/test/backbone.html
@@ -22,75 +22,37 @@
 		
 		
 		
+		
 		
 		
 		
diff --git a/test/index.html b/test/index.html
index 70c74330d..963e4c684 100644
--- a/test/index.html
+++ b/test/index.html
@@ -13,6 +13,7 @@
 	
 		
 		
+		
 		
 		
@@ -72,6 +73,7 @@ delete Object._defineProperty; delete Object._keys; + QUnit.config.asyncRetries = 3; QUnit.config.hidepassed = true; // assign results to `global_test_results` for Sauce Labs diff --git a/test/underscore.html b/test/underscore.html index d74021998..35d5e6eda 100644 --- a/test/underscore.html +++ b/test/underscore.html @@ -22,150 +22,94 @@ +