Skip to content

Commit

Permalink
Split default bindings specs into separate files
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveSanderson committed Aug 21, 2012
1 parent 48c911b commit 8664530
Show file tree
Hide file tree
Showing 24 changed files with 1,904 additions and 1,897 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ desktop.ini
.eprj
perf/*
*.orig

.DS_Store
59 changes: 59 additions & 0 deletions spec/defaultBindings/attrBehaviors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
describe('Binding: Attr', {
before_each: JSSpec.prepareTestNode,

'Should be able to set arbitrary attribute values': function() {
var model = { myValue: "first value" };
testNode.innerHTML = "<div data-bind='attr: {firstAttribute: myValue, \"second-attribute\": true}'></div>";
ko.applyBindings(model, testNode);
value_of(testNode.childNodes[0].getAttribute("firstAttribute")).should_be("first value");
value_of(testNode.childNodes[0].getAttribute("second-attribute")).should_be("true");
},

'Should be able to set \"name\" attribute, even on IE6-7': function() {
var myValue = ko.observable("myName");
testNode.innerHTML = "<input data-bind='attr: { name: myValue }' />";
ko.applyBindings({ myValue: myValue }, testNode);
value_of(testNode.childNodes[0].name).should_be("myName");
value_of(testNode.childNodes[0].outerHTML).should_match('name="?myName"?');

// Also check we can remove it (which, for a name attribute, means setting it to an empty string)
myValue(false);
value_of(testNode.childNodes[0].name).should_be("");
value_of(testNode.childNodes[0].outerHTML).should_not_match('name="?([^">]+)');
},

'Should respond to changes in an observable value': function() {
var model = { myprop : ko.observable("initial value") };
testNode.innerHTML = "<div data-bind='attr: { someAttrib: myprop }'></div>";
ko.applyBindings(model, testNode);
value_of(testNode.childNodes[0].getAttribute("someAttrib")).should_be("initial value");

// Change the observable; observe it reflected in the DOM
model.myprop("new value");
value_of(testNode.childNodes[0].getAttribute("someAttrib")).should_be("new value");
},

'Should remove the attribute if the value is strictly false, null, or undefined': function() {
var model = { myprop : ko.observable() };
testNode.innerHTML = "<div data-bind='attr: { someAttrib: myprop }'></div>";
ko.applyBindings(model, testNode);
ko.utils.arrayForEach([false, null, undefined], function(testValue) {
model.myprop("nonempty value");
value_of(testNode.childNodes[0].getAttribute("someAttrib")).should_be("nonempty value");
model.myprop(testValue);
value_of(testNode.childNodes[0].getAttribute("someAttrib")).should_be(null);
});
},

'Should be able to set class attribute and access it using className property': function() {
var model = { myprop : ko.observable("newClass") };
testNode.innerHTML = "<div class='oldClass' data-bind=\"attr: {'class': myprop}\"></div>";
value_of(testNode.childNodes[0].className).should_be("oldClass");
ko.applyBindings(model, testNode);
value_of(testNode.childNodes[0].className).should_be("newClass");
// Should be able to clear class also
model.myprop(undefined);
value_of(testNode.childNodes[0].className).should_be("");
value_of(testNode.childNodes[0].getAttribute("class")).should_be(null);
}
});
176 changes: 176 additions & 0 deletions spec/defaultBindings/checkedBehaviors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
describe('Binding: Checked', {
before_each: JSSpec.prepareTestNode,

'Triggering a click should toggle a checkbox\'s checked state before the event handler fires': function() {
// This isn't strictly to do with the checked binding, but if this doesn't work, the rest of the specs aren't meaningful
testNode.innerHTML = "<input type='checkbox' />";
var clickHandlerFireCount = 0, expectedCheckedStateInHandler;
ko.utils.registerEventHandler(testNode.childNodes[0], "click", function() {
clickHandlerFireCount++;
value_of(testNode.childNodes[0].checked).should_be(expectedCheckedStateInHandler);
})
value_of(testNode.childNodes[0].checked).should_be(false);
expectedCheckedStateInHandler = true;
ko.utils.triggerEvent(testNode.childNodes[0], "click");
value_of(testNode.childNodes[0].checked).should_be(true);
value_of(clickHandlerFireCount).should_be(1);

expectedCheckedStateInHandler = false;
ko.utils.triggerEvent(testNode.childNodes[0], "click");
value_of(testNode.childNodes[0].checked).should_be(false);
value_of(clickHandlerFireCount).should_be(2);
},

'Should be able to control a checkbox\'s checked state': function () {
var myobservable = new ko.observable(true);
testNode.innerHTML = "<input type='checkbox' data-bind='checked:someProp' />";

ko.applyBindings({ someProp: myobservable }, testNode);
value_of(testNode.childNodes[0].checked).should_be(true);

myobservable(false);
value_of(testNode.childNodes[0].checked).should_be(false);
},

'Should update observable properties on the underlying model when the checkbox click event fires': function () {
var myobservable = new ko.observable(false);
testNode.innerHTML = "<input type='checkbox' data-bind='checked:someProp' />";
ko.applyBindings({ someProp: myobservable }, testNode);

ko.utils.triggerEvent(testNode.childNodes[0], "click");
value_of(myobservable()).should_be(true);
},

'Should only notify observable properties on the underlying model *once* even if the checkbox change events fire multiple times': function () {
var myobservable = new ko.observable();
var timesNotified = 0;
myobservable.subscribe(function() { timesNotified++ });
testNode.innerHTML = "<input type='checkbox' data-bind='checked:someProp' />";
ko.applyBindings({ someProp: myobservable }, testNode);

// Multiple events only cause one notification...
ko.utils.triggerEvent(testNode.childNodes[0], "click");
ko.utils.triggerEvent(testNode.childNodes[0], "change");
ko.utils.triggerEvent(testNode.childNodes[0], "change");
value_of(timesNotified).should_be(1);

// ... until the checkbox value actually changes
ko.utils.triggerEvent(testNode.childNodes[0], "click");
ko.utils.triggerEvent(testNode.childNodes[0], "change");
value_of(timesNotified).should_be(2);
},

'Should update non-observable properties on the underlying model when the checkbox click event fires': function () {
var model = { someProp: false };
testNode.innerHTML = "<input type='checkbox' data-bind='checked:someProp' />";
ko.applyBindings(model, testNode);

ko.utils.triggerEvent(testNode.childNodes[0], "click");
value_of(model.someProp).should_be(true);
},

'Should update observable properties on the underlying model when the checkbox is clicked': function () {
var myobservable = new ko.observable(false);
testNode.innerHTML = "<input type='checkbox' data-bind='checked:someProp' />";
ko.applyBindings({ someProp: myobservable }, testNode);

ko.utils.triggerEvent(testNode.childNodes[0], "click");
value_of(myobservable()).should_be(true);
},

'Should update non-observable properties on the underlying model when the checkbox is clicked': function () {
var model = { someProp: false };
testNode.innerHTML = "<input type='checkbox' data-bind='checked:someProp' />";
ko.applyBindings(model, testNode);

ko.utils.triggerEvent(testNode.childNodes[0], "click");
value_of(model.someProp).should_be(true);
},

'Should make a radio button checked if and only if its value matches the bound model property': function () {
var myobservable = new ko.observable("another value");
testNode.innerHTML = "<input type='radio' value='This Radio Button Value' data-bind='checked:someProp' />";

ko.applyBindings({ someProp: myobservable }, testNode);
value_of(testNode.childNodes[0].checked).should_be(false);

myobservable("This Radio Button Value");
value_of(testNode.childNodes[0].checked).should_be(true);
},

'Should set an observable model property to this radio button\'s value when checked': function () {
var myobservable = new ko.observable("another value");
testNode.innerHTML = "<input type='radio' value='this radio button value' data-bind='checked:someProp' />";
ko.applyBindings({ someProp: myobservable }, testNode);

value_of(myobservable()).should_be("another value");
testNode.childNodes[0].click();
value_of(myobservable()).should_be("this radio button value");
},

'Should only notify observable properties on the underlying model *once* even if the radio button change/click events fire multiple times': function () {
var myobservable = new ko.observable("original value");
var timesNotified = 0;
myobservable.subscribe(function() { timesNotified++ });
testNode.innerHTML = "<input type='radio' value='this radio button value' data-bind='checked:someProp' /><input type='radio' value='different value' data-bind='checked:someProp' />";
ko.applyBindings({ someProp: myobservable }, testNode);

// Multiple events only cause one notification...
ko.utils.triggerEvent(testNode.childNodes[0], "click");
ko.utils.triggerEvent(testNode.childNodes[0], "change");
ko.utils.triggerEvent(testNode.childNodes[0], "click");
ko.utils.triggerEvent(testNode.childNodes[0], "change");
value_of(timesNotified).should_be(1);

// ... until you click something with a different value
ko.utils.triggerEvent(testNode.childNodes[1], "click");
ko.utils.triggerEvent(testNode.childNodes[1], "change");
value_of(timesNotified).should_be(2);
},

'Should set a non-observable model property to this radio button\'s value when checked': function () {
var model = { someProp: "another value" };
testNode.innerHTML = "<input type='radio' value='this radio button value' data-bind='checked:someProp' />";
ko.applyBindings(model, testNode);

ko.utils.triggerEvent(testNode.childNodes[0], "click");
value_of(model.someProp).should_be("this radio button value");
},

'When a checkbox is bound to an array, the checkbox should control whether its value is in that array': function() {
var model = { myArray: ["Existing value", "Unrelated value"] };
testNode.innerHTML = "<input type='checkbox' value='Existing value' data-bind='checked:myArray' />"
+ "<input type='checkbox' value='New value' data-bind='checked:myArray' />";
ko.applyBindings(model, testNode);

value_of(model.myArray).should_be(["Existing value", "Unrelated value"]);

// Checkbox initial state is determined by whether the value is in the array
value_of(testNode.childNodes[0].checked).should_be(true);
value_of(testNode.childNodes[1].checked).should_be(false);
// Checking the checkbox puts it in the array
ko.utils.triggerEvent(testNode.childNodes[1], "click");
value_of(testNode.childNodes[1].checked).should_be(true);
value_of(model.myArray).should_be(["Existing value", "Unrelated value", "New value"]);
// Unchecking the checkbox removes it from the array
ko.utils.triggerEvent(testNode.childNodes[1], "click");
value_of(testNode.childNodes[1].checked).should_be(false);
value_of(model.myArray).should_be(["Existing value", "Unrelated value"]);
},

'When a checkbox is bound to an observable array, the checkbox checked state responds to changes in the array': function() {
var model = { myObservableArray: ko.observableArray(["Unrelated value"]) };
testNode.innerHTML = "<input type='checkbox' value='My value' data-bind='checked:myObservableArray' />";
ko.applyBindings(model, testNode);

value_of(testNode.childNodes[0].checked).should_be(false);

// Put the value in the array; observe the checkbox reflect this
model.myObservableArray.push("My value");
value_of(testNode.childNodes[0].checked).should_be(true);

// Remove the value from the array; observe the checkbox reflect this
model.myObservableArray.remove("My value");
value_of(testNode.childNodes[0].checked).should_be(false);
}
});
19 changes: 19 additions & 0 deletions spec/defaultBindings/clickBehaviors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
describe('Binding: Click', {
// This is just a special case of the "event" binding, so not necessary to respecify all its behaviours
before_each: JSSpec.prepareTestNode,

'Should invoke the supplied function on click, using model as \'this\' param and first arg, and event as second arg': function () {
var model = {
wasCalled: false,
doCall: function (arg1, arg2) {
this.wasCalled = true;
value_of(arg1).should_be(model);
value_of(arg2.type).should_be("click");
}
};
testNode.innerHTML = "<button data-bind='click:doCall'>hey</button>";
ko.applyBindings(model, testNode);
ko.utils.triggerEvent(testNode.childNodes[0], "click");
value_of(model.wasCalled).should_be(true);
}
});
52 changes: 52 additions & 0 deletions spec/defaultBindings/cssBehaviors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
describe('Binding: CSS class name', {
before_each: JSSpec.prepareTestNode,

'Should give the element the specific CSS class only when the specified value is true': function () {
var observable1 = new ko.observable();
var observable2 = new ko.observable(true);
testNode.innerHTML = "<div class='unrelatedClass1 unrelatedClass2' data-bind='css: { myRule: someModelProperty, anotherRule: anotherModelProperty }'>Hallo</div>";
ko.applyBindings({ someModelProperty: observable1, anotherModelProperty: observable2 }, testNode);

value_of(testNode.childNodes[0].className).should_be("unrelatedClass1 unrelatedClass2 anotherRule");
observable1(true);
value_of(testNode.childNodes[0].className).should_be("unrelatedClass1 unrelatedClass2 anotherRule myRule");
observable2(false);
value_of(testNode.childNodes[0].className).should_be("unrelatedClass1 unrelatedClass2 myRule");
},

'Should give the element a single CSS class without a leading space when the specified value is true': function() {
var observable1 = new ko.observable();
testNode.innerHTML = "<div data-bind='css: { myRule: someModelProperty }'>Hallo</div>";
ko.applyBindings({ someModelProperty: observable1 }, testNode);

value_of(testNode.childNodes[0].className).should_be("");
observable1(true);
value_of(testNode.childNodes[0].className).should_be("myRule");
},

'Should toggle multiple CSS classes if specified as a single string separated by spaces': function() {
var observable1 = new ko.observable();
testNode.innerHTML = "<div class='unrelatedClass1' data-bind='css: { \"myRule _another-Rule123\": someModelProperty }'>Hallo</div>";
ko.applyBindings({ someModelProperty: observable1 }, testNode);

value_of(testNode.childNodes[0].className).should_be("unrelatedClass1");
observable1(true);
value_of(testNode.childNodes[0].className).should_be("unrelatedClass1 myRule _another-Rule123");
observable1(false);
value_of(testNode.childNodes[0].className).should_be("unrelatedClass1");
},

'Should set/change dynamic CSS class(es) if string is specified': function() {
var observable1 = new ko.observable("");
testNode.innerHTML = "<div class='unrelatedClass1' data-bind='css: someModelProperty'>Hallo</div>";
ko.applyBindings({ someModelProperty: observable1 }, testNode);

value_of(testNode.childNodes[0].className).should_be("unrelatedClass1");
observable1("my-Rule");
value_of(testNode.childNodes[0].className).should_be("unrelatedClass1 my-Rule");
observable1("another_Rule my-Rule");
value_of(testNode.childNodes[0].className).should_be("unrelatedClass1 another_Rule my-Rule");
observable1(undefined);
value_of(testNode.childNodes[0].className).should_be("unrelatedClass1");
}
});
37 changes: 37 additions & 0 deletions spec/defaultBindings/enableDisableBehaviors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
describe('Binding: Enable/Disable', {
before_each: JSSpec.prepareTestNode,

'Enable means the node is enabled only when the value is true': function () {
var observable = new ko.observable();
testNode.innerHTML = "<input data-bind='enable:myModelProperty()' />";
ko.applyBindings({ myModelProperty: observable }, testNode);

value_of(testNode.childNodes[0].disabled).should_be(true);
observable(1);
value_of(testNode.childNodes[0].disabled).should_be(false);
},

'Disable means the node is enabled only when the value is false': function () {
var observable = new ko.observable();
testNode.innerHTML = "<input data-bind='disable:myModelProperty()' />";
ko.applyBindings({ myModelProperty: observable }, testNode);

value_of(testNode.childNodes[0].disabled).should_be(false);
observable(1);
value_of(testNode.childNodes[0].disabled).should_be(true);
},

'Enable should unwrap observables implicitly': function () {
var observable = new ko.observable(false);
testNode.innerHTML = "<input data-bind='enable:myModelProperty' />";
ko.applyBindings({ myModelProperty: observable }, testNode);
value_of(testNode.childNodes[0].disabled).should_be(true);
},

'Disable should unwrap observables implicitly': function () {
var observable = new ko.observable(false);
testNode.innerHTML = "<input data-bind='disable:myModelProperty' />";
ko.applyBindings({ myModelProperty: observable }, testNode);
value_of(testNode.childNodes[0].disabled).should_be(false);
}
});
Loading

0 comments on commit 8664530

Please sign in to comment.