Skip to content

Commit

Permalink
expand afterRender tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mbest committed Oct 15, 2017
1 parent 4740bb3 commit bf48f00
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 208 deletions.
38 changes: 38 additions & 0 deletions spec/bindingAttributeBehaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -583,4 +583,42 @@ describe('Binding attribute syntax', function() {
expect(testNode).toContainHtml('<p>replaced</p><template>test</template><p>replaced</p>');
});
});

it('Should call an afterRender callback function after descendent elements are bound', function () {
var callbacks = 0,
callback = function (nodes, data) {
expect(nodes.length).toEqual(1);
expect(nodes[0]).toEqual(testNode.childNodes[0].childNodes[0]);
expect(data).toEqual(vm);
callbacks++;
},
vm = { callback: callback };

testNode.innerHTML = "<div data-bind='afterRender: callback'><span data-bind='text: \"Some Text\"'></span></div>";
ko.applyBindings(vm, testNode);
expect(callbacks).toEqual(1);
});

it('Should call an afterRender callback function when bound to a virtual element', function () {
var callbacks = 0,
callback = function (nodes, data) {
expect(nodes.length).toEqual(1);
expect(nodes[0]).toEqual(testNode.childNodes[1]);
expect(data).toEqual(vm);
callbacks++;
},
vm = { callback: callback };

testNode.innerHTML = "<!-- ko afterRender: callback --><span data-bind='text: \"Some Text\"'></span><!-- /ko -->";
ko.applyBindings(vm, testNode);
expect(callbacks).toEqual(1);
});

it('Should not call an afterRender callback function when there are no descendant nodes', function () {
var callbacks = 0;

testNode.innerHTML = "<div data-bind='afterRender: callback'></span></div>";
ko.applyBindings({ callback: function () { callbacks++; } }, testNode);
expect(callbacks).toEqual(0);
});
});
39 changes: 39 additions & 0 deletions spec/bindingDependencyBehaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,45 @@ describe('Binding dependencies', function() {
expect(testNode).toContainText('new value');
});

it('Should not cause updates if an observable accessed in an afterRender callback is changed', function () {
ko.bindingHandlers.test = {
init: function() {
return { controlsDescendantBindings: true };
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
ko.utils.unwrapObservable(valueAccessor());
element.innerHTML = "<span data-bind='text: childprop'></span>";
ko.applyBindingsToDescendants(bindingContext, element);
}
};

var callbackObservable = ko.observable(1),
bindingObservable = ko.observable(1),
callbacks = 0,
vm = {
childprop: 'child',
bindingObservable: bindingObservable,
callback: function () { callbackObservable(); callbacks++; }
};

testNode.innerHTML = "<div data-bind='test: bindingObservable, afterRender: callback'></div>";
ko.applyBindings(vm, testNode);
expect(callbacks).toEqual(1);

// Change the childprop which is not an observable so should not change the bound element
vm.childprop = 'new child';
expect(testNode.childNodes[0]).toContainText('child');

// Update callback observable and check that the binding wasn't updated
callbackObservable(2);
expect(testNode.childNodes[0]).toContainText('child');

// Update the bound observable and verify that the binding is now updated
bindingObservable(2);
expect(testNode.childNodes[0]).toContainText('new child');
expect(callbacks).toEqual(2);
});

describe('Observable view models', function() {
it('Should update bindings (including callbacks)', function() {
var vm = ko.observable(), clickedVM;
Expand Down
21 changes: 21 additions & 0 deletions spec/components/componentBindingBehaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,27 @@ describe('Components: Component binding', function() {
expect(testNode).toContainText('Hello! Your param is 456 Goodbye.');
});

it('Should call an afterRender callback function', function () {
testNode.innerHTML = '<div data-bind="component: testComponentBindingValue, afterRender: callback"></div>';
ko.components.register(testComponentName, { template: '<div data-bind="text: myvalue"></div>' });
testComponentParams.myvalue = 'some parameter value';

var callbacks = 0;
outerViewModel.callback = function (nodes, data) {
expect(nodes.length).toEqual(1);
expect(nodes[0]).toEqual(testNode.childNodes[0].childNodes[0]);
expect(data).toEqual(testComponentParams);
callbacks++;
};

ko.applyBindings(outerViewModel, testNode);
expect(callbacks).toEqual(0);

jasmine.Clock.tick(1);
expect(testNode.childNodes[0]).toContainHtml('<div data-bind="text: myvalue">some parameter value</div>');
expect(callbacks).toEqual(1);
});

describe('Does not automatically subscribe to any observables you evaluate during createViewModel or a viewmodel constructor', function() {
// This clarifies that, if a developer wants to react when some observable parameter
// changes, then it's their responsibility to subscribe to it or use a computed.
Expand Down
63 changes: 1 addition & 62 deletions spec/defaultBindings/ifBehaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,22 +97,7 @@ describe('Binding: If', function() {
expect(testNode).toContainHtml("hello <!-- ko if: condition1 -->first is true<!-- ko if: condition2 -->both are true<!-- /ko --><!-- /ko -->");
});

it('Should call an afterRender callback function and not cause updates if an observable accessed in the callback is changed', function () {
testNode.innerHTML = "<div data-bind='if: condition, afterRender: callback'><span data-bind='text: someText'></span></div>";
var callbackObservable = ko.observable(1),
callbacks = 0;
var viewModel = { condition: ko.observable(true), someText: "hello", callback: function() { callbackObservable(); callbacks++; } };
ko.applyBindings(viewModel, testNode);
expect(callbacks).toEqual(1);
expect(testNode.childNodes[0]).toContainText('hello');

viewModel.someText = "bello";
// Update callback observable and check that the binding wasn't updated
callbackObservable(2);
expect(testNode.childNodes[0]).toContainText('hello');
});

it('Should not call an afterRender callback function when data gets cleared', function () {
it('Should call an afterRender callback function', function () {
testNode.innerHTML = "<div data-bind='if: condition, afterRender: callback'><span data-bind='text: someText'></span></div>";
var someItem = ko.observable({ childprop: 'child' }),
callbacks = 0;
Expand All @@ -129,50 +114,4 @@ describe('Binding: If', function() {
expect(callbacks).toEqual(2);
expect(testNode.childNodes[0]).toContainText('hello');
});

it('Should call an afterRender callback, passing all of the rendered nodes, accounting for node preprocessing and virtual element bindings', function () {
// Set up a binding provider that converts text nodes to expressions
var originalBindingProvider = ko.bindingProvider.instance,
preprocessingBindingProvider = function () { };
preprocessingBindingProvider.prototype = originalBindingProvider;
ko.bindingProvider.instance = new preprocessingBindingProvider();
ko.bindingProvider.instance.preprocessNode = function (node) {
if (node.nodeType === 3 && node.data.charAt(0) === "$") {
var newNodes = [
document.createComment('ko text: ' + node.data),
document.createComment('/ko')
];
for (var i = 0; i < newNodes.length; i++) {
node.parentNode.insertBefore(newNodes[i], node);
}
node.parentNode.removeChild(node);
return newNodes;
}
};

// Now perform a with binding, and see that afterRender gets the output from the preprocessor and bindings
testNode.innerHTML = "<div data-bind='if: condition, afterRender: callback'><span>[</span>$data.someText<span>]</span></div>";
var callbacks = 0;
var viewModel = {
condition: ko.observable(true),
someText: "hello",
callback: function (nodes, data) {
expect(nodes.length).toBe(5);
expect(nodes[0]).toContainText('['); // <span>[</span>
expect(nodes[1].nodeType).toBe(8); // <!-- ko text: $data.childprop -->
expect(nodes[2].nodeType).toBe(3); // text node inserted by text binding
expect(nodes[3].nodeType).toBe(8); // <!-- /ko -->
expect(nodes[4]).toContainText(']'); // <span>]</span>
expect(data).toBe(viewModel);
callbacks++;
}
};

ko.applyBindings(viewModel, testNode);

expect(testNode.childNodes[0]).toContainText('[hello]');
expect(callbacks).toBe(1);

ko.bindingProvider.instance = originalBindingProvider;
});
});
63 changes: 1 addition & 62 deletions spec/defaultBindings/ifnotBehaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,8 @@ describe('Binding: Ifnot', function() {
expect(ko.contextFor(testNode.childNodes[0].childNodes[1]).$parents.length).toEqual(0);
});

it('Should call an afterRender callback function and not cause updates if an observable accessed in the callback is changed', function () {
it('Should call an afterRender callback function', function () {
testNode.innerHTML = "<div data-bind='ifnot: condition, afterRender: callback'><span data-bind='text: someText'></span></div>";
var callbackObservable = ko.observable(1),
callbacks = 0;
var viewModel = { condition: ko.observable(false), someText: "hello", callback: function() { callbackObservable(); callbacks++; } };
ko.applyBindings(viewModel, testNode);
expect(callbacks).toEqual(1);
expect(testNode.childNodes[0]).toContainText('hello');

viewModel.someText = "bello";
// Update callback observable and check that the binding wasn't updated
callbackObservable(2);
expect(testNode.childNodes[0]).toContainText('hello');
});

it('Should not call an afterRender callback function when data gets cleared', function () {
testNode.innerHTML = "<div data-bind='ifnot: condition, afterRender: callback'><span data-bind='text: someText'></span></div>";
var someItem = ko.observable({ childprop: 'child' }),
callbacks = 0;
var viewModel = { condition: ko.observable(false), someText: "hello", callback: function () { callbacks++; } };
Expand All @@ -95,50 +80,4 @@ describe('Binding: Ifnot', function() {
expect(callbacks).toEqual(2);
expect(testNode.childNodes[0]).toContainText('hello');
});

it('Should call an afterRender callback, passing all of the rendered nodes, accounting for node preprocessing and virtual element bindings', function () {
// Set up a binding provider that converts text nodes to expressions
var originalBindingProvider = ko.bindingProvider.instance,
preprocessingBindingProvider = function () { };
preprocessingBindingProvider.prototype = originalBindingProvider;
ko.bindingProvider.instance = new preprocessingBindingProvider();
ko.bindingProvider.instance.preprocessNode = function (node) {
if (node.nodeType === 3 && node.data.charAt(0) === "$") {
var newNodes = [
document.createComment('ko text: ' + node.data),
document.createComment('/ko')
];
for (var i = 0; i < newNodes.length; i++) {
node.parentNode.insertBefore(newNodes[i], node);
}
node.parentNode.removeChild(node);
return newNodes;
}
};

// Now perform a with binding, and see that afterRender gets the output from the preprocessor and bindings
testNode.innerHTML = "<div data-bind='ifnot: condition, afterRender: callback'><span>[</span>$data.someText<span>]</span></div>";
var callbacks = 0;
var viewModel = {
condition: ko.observable(false),
someText: "hello",
callback: function (nodes, data) {
expect(nodes.length).toBe(5);
expect(nodes[0]).toContainText('['); // <span>[</span>
expect(nodes[1].nodeType).toBe(8); // <!-- ko text: $data.childprop -->
expect(nodes[2].nodeType).toBe(3); // text node inserted by text binding
expect(nodes[3].nodeType).toBe(8); // <!-- /ko -->
expect(nodes[4]).toContainText(']'); // <span>]</span>
expect(data).toBe(viewModel);
callbacks++;
}
};

ko.applyBindings(viewModel, testNode);

expect(testNode.childNodes[0]).toContainText('[hello]');
expect(callbacks).toBe(1);

ko.bindingProvider.instance = originalBindingProvider;
});
});
85 changes: 1 addition & 84 deletions spec/defaultBindings/withBehaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,46 +225,7 @@ describe('Binding: With', function() {
expect(testNode).toContainText("Total: 25");
});

it('Should call an afterRender callback function and not cause updates if an observable accessed in the callback is changed', function () {
testNode.innerHTML = "<div data-bind='with: someItem, afterRender: callback'><span data-bind='text: childprop'></span></div>";
var callbackObservable = ko.observable(1),
someItem = ko.observable({ childprop: 'child' }),
callbacks = 0;
ko.applyBindings({ someItem: someItem, callback: function () { callbackObservable(); callbacks++; } }, testNode);
expect(callbacks).toEqual(1);

// Change the childprop which is not an observable so should not change the bound element
someItem().childprop = 'hidden child';
expect(testNode.childNodes[0]).toContainText('child');
// Update callback observable and check that the binding wasn't updated
callbackObservable(2);
expect(testNode.childNodes[0]).toContainText('child');
// Update the observable and verify that the binding is now updated
someItem({ childprop: 'new child' });
expect(testNode.childNodes[0]).toContainText('new child');
expect(callbacks).toEqual(2);
});

it('Should call an afterRender callback function when bound to a virtual element', function () {
testNode.innerHTML = "<!-- ko with: someItem, afterRender: callback --><span data-bind='text: childprop'></span><!-- /ko -->";
var someItem = ko.observable({ childprop: 'child' }),
callbacks = 0;
var callback = function (nodes, data) {
expect(nodes.length).toEqual(1);
expect(nodes[0]).toEqual(testNode.childNodes[1]);
expect(data.childprop).toEqual(someItem().childprop);
callbacks++;
};
ko.applyBindings({ someItem: someItem, callback: callback }, testNode);
expect(callbacks).toEqual(1);

// Update the observable and verify that the binding is now updated
someItem({ childprop: 'new child' });
expect(testNode.childNodes[1]).toContainText('new child');
expect(callbacks).toEqual(2);
});

it('Should not call an afterRender callback function when data gets cleared', function () {
it('Should call an afterRender callback function', function () {
testNode.innerHTML = "<div data-bind='with: someItem, afterRender: callback'><span data-bind='text: childprop'></span></div>";
var someItem = ko.observable({ childprop: 'child' }),
callbacks = 0;
Expand All @@ -280,48 +241,4 @@ describe('Binding: With', function() {
expect(callbacks).toEqual(2);
expect(testNode.childNodes[0]).toContainText('new child');
});

it('Should call an afterRender callback, passing all of the rendered nodes, accounting for node preprocessing and virtual element bindings', function () {
// Set up a binding provider that converts text nodes to expressions
var originalBindingProvider = ko.bindingProvider.instance,
preprocessingBindingProvider = function () { };
preprocessingBindingProvider.prototype = originalBindingProvider;
ko.bindingProvider.instance = new preprocessingBindingProvider();
ko.bindingProvider.instance.preprocessNode = function (node) {
if (node.nodeType === 3 && node.data.charAt(0) === "$") {
var newNodes = [
document.createComment('ko text: ' + node.data),
document.createComment('/ko')
];
for (var i = 0; i < newNodes.length; i++) {
node.parentNode.insertBefore(newNodes[i], node);
}
node.parentNode.removeChild(node);
return newNodes;
}
};

// Now perform a with binding, and see that afterRender gets the output from the preprocessor and bindings
testNode.innerHTML = "<div data-bind='with: someItem, afterRender: callback'><span>[</span>$data.childprop<span>]</span></div>";
var someItem = ko.observable({ childprop: 'child property' }),
callbacks = 0;
ko.applyBindings({
someItem: someItem,
callback: function (nodes, data) {
expect(nodes.length).toBe(5);
expect(nodes[0]).toContainText('['); // <span>[</span>
expect(nodes[1].nodeType).toBe(8); // <!-- ko text: $data.childprop -->
expect(nodes[2].nodeType).toBe(3); // text node inserted by text binding
expect(nodes[3].nodeType).toBe(8); // <!-- /ko -->
expect(nodes[4]).toContainText(']'); // <span>]</span>
expect(data).toBe(someItem());
callbacks++;
}
}, testNode);

expect(testNode.childNodes[0]).toContainText('[child property]');
expect(callbacks).toBe(1);

ko.bindingProvider.instance = originalBindingProvider;
});
});
Loading

0 comments on commit bf48f00

Please sign in to comment.