Permalink
Browse files

Clean up async tests, "extensions", and runner code for Jasmine.

  • Loading branch information...
1 parent c236572 commit d63b4ba4eddef5a2545c8733edf4697c0d7ef6c5 @mbest mbest committed Nov 17, 2012
Showing with 153 additions and 181 deletions.
  1. +92 −103 spec/asyncBehaviors.js
  2. +57 −60 spec/lib/jasmine.extensions.js
  3. +4 −18 spec/runner.html
View
@@ -1,116 +1,105 @@
describe("Throttled observables", function() {
- it("Should notify subscribers asynchronously after writes stop for the specified timeout duration", function() {
- var observable = ko.observable('A').extend({ throttle: 50 });
- var notifiedValues = [];
- observable.subscribe(function(value) {
- notifiedValues.push(value);
+ it("Should notify subscribers asynchronously after writes stop for the specified timeout duration", function() {
+ var observable = ko.observable('A').extend({ throttle: 50 });
+ var notifiedValues = [];
+ observable.subscribe(function(value) {
+ notifiedValues.push(value);
+ });
+
+ // Mutate a few times
+ observable('B');
+ observable('C');
+ observable('D');
+ expect(notifiedValues.length).toEqual(0); // Should not notify synchronously
+
+ // Wait
+ waits(20);
+ runs(function() {
+ // Mutate more
+ observable('E');
+ observable('F');
+ expect(notifiedValues.length).toEqual(0); // Should not notify until end of throttle timeout
+ });
+
+ // Wait until after timeout
+ waitsFor(function() {
+ return notifiedValues.length > 0;
+ }, 60);
+ runs(function() {
+ expect(notifiedValues.length).toEqual(1);
+ expect(notifiedValues[0]).toEqual("F");
+ });
});
-
- runs(function() {
-
- // Mutate a few times
- observable('B');
- observable('C');
- observable('D');
- expect(notifiedValues.length).toEqual(0); // Should not notify synchronously
-
- // Wait
- setTimeout(function() {
- // Mutate more
- observable('E');
- observable('F');
- expect(notifiedValues.length).toEqual(0); // Should not notify until end of throttle timeout
- }, 20);
- });
-
- waitsFor(function() {
- // Wait until after timeout
- return notifiedValues.length > 0;
- }, 80);
-
- runs(function() {
- expect(notifiedValues.length).toEqual(1);
- expect(notifiedValues[0]).toEqual("F");
- });
-
- });
});
describe("Throttled dependent observables", function() {
- it("Should notify subscribers asynchronously after dependencies stop updating for the specified timeout duration", function() {
- var underlying = ko.observable();
- var asyncDepObs = ko.dependentObservable(function() {
- return underlying();
- }).extend({ throttle: 100 });
- var notifiedValues = [];
- asyncDepObs.subscribe(function(value) {
- notifiedValues.push(value);
- });
-
-
- runs(function() {
- // Check initial state
- expect(asyncDepObs()).toBeUndefined();
-
- // Mutate
- underlying('New value');
- expect(asyncDepObs()).toBeUndefined(); // Should not update synchronously
- expect(notifiedValues.length).toEqual(0);
-
- // Wait
- setTimeout(function() {
- // After 50ms, still shouldn't have evaluated
- expect(asyncDepObs()).toBeUndefined(); // Should not update until throttle timeout
+ it("Should notify subscribers asynchronously after dependencies stop updating for the specified timeout duration", function() {
+ var underlying = ko.observable();
+ var asyncDepObs = ko.dependentObservable(function() {
+ return underlying();
+ }).extend({ throttle: 100 });
+ var notifiedValues = [];
+ asyncDepObs.subscribe(function(value) {
+ notifiedValues.push(value);
+ });
+
+ // Check initial state
+ expect(asyncDepObs()).toBeUndefined();
+
+ // Mutate
+ underlying('New value');
+ expect(asyncDepObs()).toBeUndefined(); // Should not update synchronously
expect(notifiedValues.length).toEqual(0);
- }, 50);
- });
-
- waitsFor(function() {
- // Now wait for throttle timeout
- return notifiedValues.length > 0;
- }, 110);
-
- runs(function() {
- expect(asyncDepObs()).toEqual('New value');
- expect(notifiedValues.length).toEqual(1);
- expect(notifiedValues[0]).toEqual('New value');
- });
- });
-
- it("Should run evaluator only once when dependencies stop updating for the specified timeout duration", function() {
- var evaluationCount = 0;
- var someDependency = ko.observable();
- var asyncDepObs = ko.dependentObservable(function() {
- evaluationCount++;
- return someDependency();
- }).extend({ throttle: 100 });
-
- runs(function() {
- // Mutate a few times synchronously
- expect(evaluationCount).toEqual(1); // Evaluates synchronously when first created, like all dependent observables
- someDependency("A");
- someDependency("B");
- someDependency("C");
- expect(evaluationCount).toEqual(1); // Should not re-evaluate synchronously when dependencies update
-
- // Also mutate async
- setTimeout(function() {
- someDependency("D");
- expect(evaluationCount).toEqual(1);
- }, 10);
+ // After 50ms, still shouldn't have evaluated
+ waits(50);
+ runs(function() {
+ expect(asyncDepObs()).toBeUndefined(); // Should not update until throttle timeout
+ expect(notifiedValues.length).toEqual(0);
+ });
+
+ // Now wait for throttle timeout
+ waitsFor(function() {
+ return notifiedValues.length > 0;
+ }, 60);
+ runs(function() {
+ expect(asyncDepObs()).toEqual('New value');
+ expect(notifiedValues.length).toEqual(1);
+ expect(notifiedValues[0]).toEqual('New value');
+ });
});
- waitsFor(function() {
- // Now wait for throttle timeout
- return evaluationCount > 1;
- }, 120);
-
- runs(function() {
- expect(evaluationCount).toEqual(2); // Finally, it's evaluated
- expect(asyncDepObs()).toEqual("D");
+ it("Should run evaluator only once when dependencies stop updating for the specified timeout duration", function() {
+ var evaluationCount = 0;
+ var someDependency = ko.observable();
+ var asyncDepObs = ko.dependentObservable(function() {
+ evaluationCount++;
+ return someDependency();
+ }).extend({ throttle: 100 });
+
+ // Mutate a few times synchronously
+ expect(evaluationCount).toEqual(1); // Evaluates synchronously when first created, like all dependent observables
+ someDependency("A");
+ someDependency("B");
+ someDependency("C");
+ expect(evaluationCount).toEqual(1); // Should not re-evaluate synchronously when dependencies update
+
+ // Also mutate async
+ waits(10);
+ runs(function() {
+ someDependency("D");
+ expect(evaluationCount).toEqual(1);
+ });
+
+ // Now wait for throttle timeout
+ waitsFor(function() {
+ return evaluationCount > 1;
+ }, 110);
+ runs(function() {
+ expect(evaluationCount).toEqual(2); // Finally, it's evaluated
+ expect(asyncDepObs()).toEqual("D");
+ });
});
- });
});
@@ -1,77 +1,74 @@
-beforeEach(function() {
- this.addMatchers({
- toEqualOneOf: function (expectedPossibilities) {
- for (var i = 0; i < expectedPossibilities.length; i++) {
+jasmine.Matchers.prototype.toEqualOneOf = function (expectedPossibilities) {
+ for (var i = 0; i < expectedPossibilities.length; i++) {
if (this.env.equals_(this.actual, expectedPossibilities[i])) {
- return true;
+ return true;
}
- }
- return false;
- },
- toContainHtml: function (expectedHtml) {
- var cleanedHtml = this.actual.innerHTML.toLowerCase().replace(/\r\n/g, "");
- // IE < 9 strips whitespace immediately following comment nodes. Normalize by doing the same on all browsers.
- cleanedHtml = cleanedHtml.replace(/(<!--.*?-->)\s*/g, "$1");
- expectedHtml = expectedHtml.replace(/(<!--.*?-->)\s*/g, "$1");
- // Also remove __ko__ expando properties (for DOM data) - most browsers hide these anyway but IE < 9 includes them in innerHTML
- cleanedHtml = cleanedHtml.replace(/ __ko__\d+=\"(ko\d+|null)\"/g, "");
- // Fix explanatory message
- this.actual = cleanedHtml;
- return cleanedHtml === expectedHtml;
- },
- toContainText: function (expectedText) {
- var actualText = 'textContent' in this.actual ? this.actual.textContent : this.actual.innerText;
- var cleanedActualText = actualText.replace(/\r\n/g, "\n");
- // Fix explanatory message
- this.actual = cleanedActualText;
- return cleanedActualText === expectedText;
- },
- toHaveOwnProperties: function (expectedProperties) {
- var ownProperties = [];
- for (var prop in this.actual) {
+ }
+ return false;
+};
+
+jasmine.Matchers.prototype.toContainHtml = function (expectedHtml) {
+ var cleanedHtml = this.actual.innerHTML.toLowerCase().replace(/\r\n/g, "");
+ // IE < 9 strips whitespace immediately following comment nodes. Normalize by doing the same on all browsers.
+ cleanedHtml = cleanedHtml.replace(/(<!--.*?-->)\s*/g, "$1");
+ expectedHtml = expectedHtml.replace(/(<!--.*?-->)\s*/g, "$1");
+ // Also remove __ko__ expando properties (for DOM data) - most browsers hide these anyway but IE < 9 includes them in innerHTML
+ cleanedHtml = cleanedHtml.replace(/ __ko__\d+=\"(ko\d+|null)\"/g, "");
+ this.actual = cleanedHtml; // Fix explanatory message
+ return cleanedHtml === expectedHtml;
+};
+
+jasmine.Matchers.prototype.toContainText = function (expectedText) {
+ var actualText = 'textContent' in this.actual ? this.actual.textContent : this.actual.innerText;
+ var cleanedActualText = actualText.replace(/\r\n/g, "\n");
+ this.actual = cleanedActualText; // Fix explanatory message
+ return cleanedActualText === expectedText;
+};
+
+jasmine.Matchers.prototype.toHaveOwnProperties = function (expectedProperties) {
+ var ownProperties = [];
+ for (var prop in this.actual) {
if (this.actual.hasOwnProperty(prop)) {
- ownProperties.push(prop);
+ ownProperties.push(prop);
}
- }
- return this.env.equals_(ownProperties, expectedProperties);
- },
- toHaveSelectedValues: function (expectedValues) {
- var selectedNodes = ko.utils.arrayFilter(this.actual.childNodes, function (node) { return node.selected; }),
- selectedValues = ko.utils.arrayMap(selectedNodes, function (node) { return ko.selectExtensions.readValue(node); });
- // Fix explanatory message
- this.actual = selectedValues;
- return this.env.equals_(selectedValues, expectedValues);
}
- });
-});
+ return this.env.equals_(ownProperties, expectedProperties);
+};
+
+jasmine.Matchers.prototype.toHaveSelectedValues = function (expectedValues) {
+ var selectedNodes = ko.utils.arrayFilter(this.actual.childNodes, function (node) { return node.selected; }),
+ selectedValues = ko.utils.arrayMap(selectedNodes, function (node) { return ko.selectExtensions.readValue(node); });
+ this.actual = selectedValues; // Fix explanatory message
+ return this.env.equals_(selectedValues, expectedValues);
+};
jasmine.addScriptReference = function(scriptUrl) {
- if (window.console)
- console.log("Loading " + scriptUrl + "...");
- document.write("<scr" + "ipt type='text/javascript' src='" + scriptUrl + "'></sc" + "ript>");
+ if (window.console)
+ console.log("Loading " + scriptUrl + "...");
+ document.write("<scr" + "ipt type='text/javascript' src='" + scriptUrl + "'></sc" + "ript>");
};
jasmine.prepareTestNode = function() {
- // The bindings specs make frequent use of this utility function to set up
- // a clean new DOM node they can execute code against
- var existingNode = document.getElementById("testNode");
- if (existingNode != null)
- existingNode.parentNode.removeChild(existingNode);
- testNode = document.createElement("div");
- testNode.id = "testNode";
- document.body.appendChild(testNode);
+ // The bindings specs make frequent use of this utility function to set up
+ // a clean new DOM node they can execute code against
+ var existingNode = document.getElementById("testNode");
+ if (existingNode != null)
+ existingNode.parentNode.removeChild(existingNode);
+ testNode = document.createElement("div");
+ testNode.id = "testNode";
+ document.body.appendChild(testNode);
};
// Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
// Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
// If there is a future need to detect specific versions of IE10+, we will amend this.
jasmine.ieVersion = (function() {
- var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
+ var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
- // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
- while (
- div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
- iElems[0]
- );
- return version > 4 ? version : undefined;
-}());
+ // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
+ while (
+ div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
+ iElems[0]
+ );
+ return version > 4 ? version : undefined;
+}());
View
@@ -14,6 +14,7 @@
<script type="text/javascript" src="lib/jasmine-1.2.0/jasmine-html.js"></script>
<!-- our jasmine extensions -->
+ <link rel="stylesheet" type="text/css" href="lib/jasmine.extensions.css" />
@SteveSanderson

SteveSanderson Nov 20, 2012

Contributor

Did you mean to add this file? It doesn't appear to be in the repo.

@mbest

mbest Nov 20, 2012

Owner

I've added the file now.

<script type="text/javascript" src="lib/jasmine.extensions.js"></script>
<!-- knockout polyfills -->
@@ -69,28 +70,13 @@
<script type="text/javascript">
(function() {
var jasmineEnv = jasmine.getEnv();
- jasmineEnv.updateInterval = 1000;
+ jasmineEnv.updateInterval = 100;
var htmlReporter = new jasmine.HtmlReporter();
-
jasmineEnv.addReporter(htmlReporter);
+ jasmineEnv.specFilter = htmlReporter.specFilter;
- jasmineEnv.specFilter = function(spec) {
- return htmlReporter.specFilter(spec);
- };
-
- var currentWindowOnload = window.onload;
-
- window.onload = function() {
- if (currentWindowOnload) {
- currentWindowOnload();
- }
- execJasmine();
- };
-
- function execJasmine() {
- jasmineEnv.execute();
- }
+ jasmineEnv.execute();
})();
</script>

0 comments on commit d63b4ba

Please sign in to comment.