mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-14 07:02:34 +01:00
1815 lines
42 KiB
JavaScript
1815 lines
42 KiB
JavaScript
module('Mapping');
|
|
|
|
test('ko.mapping.toJS should unwrap observable values', function () {
|
|
var atomicValues = ["hello", 123, true, null, undefined,
|
|
{
|
|
a: 1
|
|
}];
|
|
for (var i = 0; i < atomicValues.length; i++) {
|
|
var data = ko.observable(atomicValues[i]);
|
|
var result = ko.mapping.toJS(data);
|
|
equal(ko.isObservable(result), false);
|
|
deepEqual(result, atomicValues[i]);
|
|
}
|
|
});
|
|
|
|
test('ko.mapping.toJS should unwrap observable properties, including nested ones', function () {
|
|
var data = {
|
|
a: ko.observable(123),
|
|
b: {
|
|
b1: ko.observable(456),
|
|
b2: 789
|
|
}
|
|
};
|
|
var result = ko.mapping.toJS(data);
|
|
equal(result.a, 123);
|
|
equal(result.b.b1, 456);
|
|
equal(result.b.b2, 789);
|
|
});
|
|
|
|
test('ko.mapping.toJS should unwrap observable arrays and things inside them', function () {
|
|
var data = ko.observableArray(['a', 1,
|
|
{
|
|
someProp: ko.observable('Hey')
|
|
}]);
|
|
var result = ko.mapping.toJS(data);
|
|
equal(result.length, 3);
|
|
equal(result[0], 'a');
|
|
equal(result[1], 1);
|
|
equal(result[2].someProp, 'Hey');
|
|
});
|
|
|
|
test('ko.mapping.toJS should ignore specified single property', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b"
|
|
};
|
|
|
|
var result = ko.mapping.toJS(data, { ignore: "b" });
|
|
equal(result.a, "a");
|
|
equal(result.b, undefined);
|
|
});
|
|
|
|
test('ko.mapping.toJS should ignore specified single property on update', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b",
|
|
c: "c"
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data);
|
|
equal(result.a(), "a");
|
|
equal(result.b(), "b");
|
|
equal(result.c(), "c");
|
|
ko.mapping.fromJS({ a: "a2", b: "b2", c: "c2" }, { ignore: ["b", "c"] }, result);
|
|
equal(result.a(), "a2");
|
|
equal(result.b(), "b");
|
|
equal(result.c(), "c");
|
|
});
|
|
|
|
test('ko.mapping.toJS should ignore specified multiple properties', function() {
|
|
var data = {
|
|
a: { a1: "a1", a2: "a2" },
|
|
b: "b"
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { ignore: ["a.a1", "b"] });
|
|
equal(result.a.a1, undefined);
|
|
equal(result.a.a2(), "a2");
|
|
equal(result.b, undefined);
|
|
|
|
data.a.a1 = "a11";
|
|
data.a.a2 = "a22";
|
|
ko.mapping.fromJS(data, {}, result);
|
|
equal(result.a.a1, undefined);
|
|
equal(result.a.a2(), "a22");
|
|
equal(result.b, undefined);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should ignore specified single property', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b"
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { ignore: "b" });
|
|
equal(result.a(), "a");
|
|
equal(result.b, undefined);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should ignore specified array item', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: [{ b1: "v1" }, { b2: "v2" }]
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { ignore: "b[1].b2" });
|
|
equal(result.a(), "a");
|
|
equal(result.b()[0].b1(), "v1");
|
|
equal(result.b()[1].b2, undefined);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should ignore specified single property, also when going back .toJS', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b"
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { ignore: "b" });
|
|
var js = ko.mapping.toJS(result);
|
|
equal(js.a, "a");
|
|
equal(js.b, undefined);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should copy specified single property', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b"
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { copy: "b" });
|
|
equal(result.a(), "a");
|
|
equal(result.b, "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should copy specified array', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: ["b1", "b2"]
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { copy: "b" });
|
|
equal(result.a(), "a");
|
|
deepEqual(result.b, ["b1", "b2"]);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should copy specified array item', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: [{ b1: "v1" }, { b2: "v2" }]
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { copy: "b[0].b1" });
|
|
equal(result.a(), "a");
|
|
equal(result.b()[0].b1, "v1");
|
|
equal(result.b()[1].b2(), "v2");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should copy specified single property, also when going back .toJS', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b"
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { copy: "b" });
|
|
var js = ko.mapping.toJS(result);
|
|
equal(js.a, "a");
|
|
equal(js.b, "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should copy specified single property, also when going back .toJS, except when overridden', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b"
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { copy: "b" });
|
|
var js = ko.mapping.toJS(result, { ignore: "b" });
|
|
equal(js.a, "a");
|
|
equal(js.b, undefined);
|
|
});
|
|
|
|
test('ko.mapping.toJS should include specified single property', function() {
|
|
var data = {
|
|
a: "a"
|
|
};
|
|
|
|
var mapped = ko.mapping.fromJS(data);
|
|
mapped.c = 1;
|
|
mapped.d = 2;
|
|
var result = ko.mapping.toJS(mapped, { include: "c" });
|
|
equal(result.a, "a");
|
|
equal(result.c, 1);
|
|
equal(result.d, undefined);
|
|
});
|
|
|
|
test('ko.mapping.toJS should by default ignore the mapping property', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b"
|
|
};
|
|
|
|
var fromJS = ko.mapping.fromJS(data);
|
|
var result = ko.mapping.toJS(fromJS);
|
|
equal(result.a, "a");
|
|
equal(result.b, "b");
|
|
equal(result.__ko_mapping__, undefined);
|
|
});
|
|
|
|
test('ko.mapping.toJS should by default include the _destroy property', function() {
|
|
var data = {
|
|
a: "a"
|
|
};
|
|
|
|
var fromJS = ko.mapping.fromJS(data);
|
|
fromJS._destroy = true;
|
|
var result = ko.mapping.toJS(fromJS);
|
|
equal(result.a, "a");
|
|
equal(result._destroy, true);
|
|
});
|
|
|
|
test('ko.mapping.toJS should merge the default includes', function() {
|
|
var data = {
|
|
a: "a"
|
|
};
|
|
|
|
var fromJS = ko.mapping.fromJS(data);
|
|
fromJS.b = "b";
|
|
fromJS._destroy = true;
|
|
var result = ko.mapping.toJS(fromJS, { include: "b" });
|
|
equal(result.a, "a");
|
|
equal(result.b, "b");
|
|
equal(result._destroy, true);
|
|
});
|
|
|
|
test('ko.mapping.toJS should merge the default ignores', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b",
|
|
c: "c"
|
|
};
|
|
|
|
ko.mapping.defaultOptions().ignore = ["a"];
|
|
var fromJS = ko.mapping.fromJS(data);
|
|
var result = ko.mapping.toJS(fromJS, { ignore: "b" });
|
|
equal(result.a, undefined);
|
|
equal(result.b, undefined);
|
|
equal(result.c, "c");
|
|
});
|
|
|
|
test('ko.mapping.defaultOptions should by default include the _destroy property', function() {
|
|
notEqual(ko.utils.arrayIndexOf(ko.mapping.defaultOptions().include, "_destroy"), -1);
|
|
});
|
|
|
|
test('ko.mapping.defaultOptions.include should be an array', function() {
|
|
var didThrow = false;
|
|
try {
|
|
ko.mapping.defaultOptions().include = {};
|
|
ko.mapping.toJS({});
|
|
}
|
|
catch (ex) {
|
|
didThrow = true
|
|
}
|
|
equal(didThrow, true);
|
|
});
|
|
|
|
test('ko.mapping.defaultOptions.ignore should be an array', function() {
|
|
var didThrow = false;
|
|
try {
|
|
ko.mapping.defaultOptions().ignore = {};
|
|
ko.mapping.toJS({});
|
|
}
|
|
catch (ex) {
|
|
didThrow = true
|
|
}
|
|
equal(didThrow, true);
|
|
});
|
|
|
|
test('ko.mapping.defaultOptions can be set', function() {
|
|
var oldOptions = ko.mapping.defaultOptions();
|
|
ko.mapping.defaultOptions({ a: "a" });
|
|
var newOptions = ko.mapping.defaultOptions();
|
|
ko.mapping.defaultOptions(oldOptions);
|
|
equal(newOptions.a, "a");
|
|
});
|
|
|
|
test('recognized root-level options should be moved into a root namespace, leaving other options in place', function() {
|
|
var recognizedRootProperties = ['create', 'update', 'key', 'arrayChanged'];
|
|
|
|
// Zero out the default options so they don't interfere with this test
|
|
ko.mapping.defaultOptions({});
|
|
|
|
// Set up a mapping with root and child mappings
|
|
var mapping = {
|
|
ignore: ['a'],
|
|
copy: ['b'],
|
|
include: ['c'],
|
|
create: function(opts) { return opts.data; },
|
|
update: function(opts) { return opts.data; },
|
|
key: function(item) { return ko.utils.unwrapObservable(item.id); },
|
|
arrayChanged: function(event, item) { },
|
|
children: {
|
|
ignore: ['a1'],
|
|
copy: ['b1'],
|
|
include: ['c1'],
|
|
create: function(opts) { return opts.data; },
|
|
update: function(opts) { return opts.data; },
|
|
key: function(item) { return ko.utils.unwrapObservable(item.id); },
|
|
arrayChanged: function(event, item) { }
|
|
}
|
|
};
|
|
|
|
// Run the mapping through ko.mapping.fromJS
|
|
var resultantMapping = ko.mapping.fromJS({}, mapping).__ko_mapping__;
|
|
|
|
// Test that the recognized root-level mappings were moved into a root-level namespace
|
|
for(var i=recognizedRootProperties.length-1; i>=0; i--) {
|
|
notDeepEqual(resultantMapping[recognizedRootProperties[i]], mapping[[recognizedRootProperties[i]]]);
|
|
deepEqual(resultantMapping[''][recognizedRootProperties[i]], mapping[[recognizedRootProperties[i]]]);
|
|
};
|
|
|
|
// Test that the non-recognized root-level and descendant mappings were left in place
|
|
for(property in mapping) {
|
|
window[recognizedRootProperties.indexOf(property) == -1 ? 'deepEqual' : 'notDeepEqual'](resultantMapping[property], mapping[property]);
|
|
};
|
|
});
|
|
|
|
test('ko.mapping.toJS should ignore properties that were not part of the original model', function () {
|
|
var data = {
|
|
a: 123,
|
|
b: {
|
|
b1: 456,
|
|
b2: [
|
|
"b21", "b22"
|
|
],
|
|
}
|
|
};
|
|
|
|
var mapped = ko.mapping.fromJS(data);
|
|
mapped.extraProperty = ko.observable(333);
|
|
mapped.extraFunction = function() {};
|
|
|
|
var unmapped = ko.mapping.toJS(mapped);
|
|
equal(unmapped.a, 123);
|
|
equal(unmapped.b.b1, 456);
|
|
equal(unmapped.b.b2[0], "b21");
|
|
equal(unmapped.b.b2[1], "b22");
|
|
equal(unmapped.extraProperty, undefined);
|
|
equal(unmapped.extraFunction, undefined);
|
|
equal(unmapped.__ko_mapping__, undefined);
|
|
});
|
|
|
|
test('ko.mapping.toJS should ignore properties that were not part of the original model when there are no nested create callbacks', function () {
|
|
var data = [
|
|
{
|
|
a: [{ id: "a1.1" }, { id: "a1.2" }]
|
|
}
|
|
];
|
|
|
|
var mapped = ko.mapping.fromJS(data, {
|
|
create: function(options) {
|
|
return ko.mapping.fromJS(options.data);
|
|
}
|
|
});
|
|
mapped.extraProperty = ko.observable(333);
|
|
mapped.extraFunction = function() {};
|
|
|
|
var unmapped = ko.mapping.toJS(mapped);
|
|
equal(unmapped[0].a[0].id, "a1.1");
|
|
equal(unmapped[0].a[1].id, "a1.2");
|
|
equal(unmapped.extraProperty, undefined);
|
|
equal(unmapped.extraFunction, undefined);
|
|
equal(unmapped.__ko_mapping__, undefined);
|
|
});
|
|
|
|
test('ko.mapping.toJS should ignore properties that were not part of the original model when there are nested create callbacks', function () {
|
|
var data = [
|
|
{
|
|
a: [{ id: "a1.1" }, { id: "a1.2" }]
|
|
}
|
|
];
|
|
|
|
var nestedMappingOptions = {
|
|
a: {
|
|
create: function(options) {
|
|
return ko.mapping.fromJS(options.data);
|
|
}
|
|
}
|
|
};
|
|
|
|
var mapped = ko.mapping.fromJS(data, {
|
|
create: function(options) {
|
|
return ko.mapping.fromJS(options.data, nestedMappingOptions);
|
|
}
|
|
});
|
|
mapped.extraProperty = ko.observable(333);
|
|
mapped.extraFunction = function() {};
|
|
|
|
var unmapped = ko.mapping.toJS(mapped);
|
|
equal(unmapped[0].a[0].id, "a1.1");
|
|
equal(unmapped[0].a[1].id, "a1.2");
|
|
equal(unmapped.extraProperty, undefined);
|
|
equal(unmapped.extraFunction, undefined);
|
|
equal(unmapped.__ko_mapping__, undefined);
|
|
});
|
|
|
|
test('ko.mapping.toJS should ignore specified properties', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b",
|
|
c: "c"
|
|
};
|
|
|
|
var result = ko.mapping.toJS(data, { ignore: ["b", "c"] });
|
|
equal(result.a, "a");
|
|
equal(result.b, undefined);
|
|
equal(result.c, undefined);
|
|
});
|
|
|
|
test('ko.mapping.toJSON should ignore specified properties', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b",
|
|
c: "c"
|
|
};
|
|
|
|
var result = ko.mapping.toJSON(data, { ignore: ["b", "c"] });
|
|
equal(result, "{\"a\":\"a\"}");
|
|
});
|
|
|
|
test('ko.mapping.toJSON should unwrap everything and then stringify', function () {
|
|
var data = ko.observableArray(['a', 1,
|
|
{
|
|
someProp: ko.observable('Hey')
|
|
}]);
|
|
var result = ko.mapping.toJSON(data);
|
|
|
|
// Check via parsing so the specs are independent of browser-specific JSON string formatting
|
|
equal(typeof result, 'string');
|
|
var parsedResult = ko.utils.parseJson(result);
|
|
equal(parsedResult.length, 3);
|
|
equal(parsedResult[0], 'a');
|
|
equal(parsedResult[1], 1);
|
|
equal(parsedResult[2].someProp, 'Hey');
|
|
});
|
|
|
|
test('ko.mapping.fromJS should require a parameter', function () {
|
|
var didThrow = false;
|
|
try {
|
|
ko.mapping.fromJS()
|
|
}
|
|
catch (ex) {
|
|
didThrow = true
|
|
}
|
|
equal(didThrow, true);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should return an observable if you supply an atomic value', function () {
|
|
var atomicValues = ["hello", 123, true, null, undefined];
|
|
for (var i = 0; i < atomicValues.length; i++) {
|
|
var result = ko.mapping.fromJS(atomicValues[i]);
|
|
equal(ko.isObservable(result), true);
|
|
equal(result(), atomicValues[i]);
|
|
}
|
|
});
|
|
|
|
test('ko.mapping.fromJS should be able to map into an existing object', function () {
|
|
var existingObj = {
|
|
a: "a"
|
|
};
|
|
|
|
var obj = {
|
|
b: "b"
|
|
};
|
|
|
|
ko.mapping.fromJS(obj, {}, existingObj);
|
|
|
|
equal(ko.isObservable(existingObj.a), false);
|
|
equal(ko.isObservable(existingObj.b), true);
|
|
equal(existingObj.a, "a");
|
|
equal(existingObj.b(), "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should return an observableArray if you supply an array, but should not wrap its entries in further observables', function () {
|
|
var sampleArray = ["a", "b"];
|
|
var result = ko.mapping.fromJS(sampleArray);
|
|
equal(typeof result.destroyAll, 'function'); // Just an example of a function on ko.observableArray but not on Array
|
|
equal(result().length, 2);
|
|
equal(result()[0], "a");
|
|
equal(result()[1], "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should return an observableArray if you supply an array, and leave entries as observables if there is a create mapping that does that', function () {
|
|
var sampleArray = {array: ["a", "b"]};
|
|
var result = ko.mapping.fromJS(sampleArray, {
|
|
array: {
|
|
create: function(options) {
|
|
return new ko.observable(options.data);
|
|
}
|
|
}
|
|
});
|
|
equal(result.array().length, 2);
|
|
equal(ko.isObservable(result.array()[0]),true);
|
|
equal(ko.isObservable(result.array()[1]),true);
|
|
equal(result.array()[0](), "a");
|
|
equal(result.array()[1](), "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should not return an observable if you supply an object that could have properties', function () {
|
|
equal(ko.isObservable(ko.mapping.fromJS({})), false);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should not wrap functions in an observable', function () {
|
|
var result = ko.mapping.fromJS({}, {
|
|
create: function(model) {
|
|
return {
|
|
myFunc: function() {
|
|
return 123;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
equal(result.myFunc(), 123);
|
|
});
|
|
|
|
test('ko.mapping.fromJS update callbacks should pass in a non-observable', function () {
|
|
var result = ko.mapping.fromJS({
|
|
obj: { a: "a" }
|
|
}, {
|
|
obj: {
|
|
update: function(options) {
|
|
equal(options.observable, undefined);
|
|
return { b: "b" };
|
|
}
|
|
}
|
|
});
|
|
equal(result.obj.b, "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS update callbacks should pass in an observable, when original is also observable', function () {
|
|
var result = ko.mapping.fromJS({
|
|
obj: ko.observable("a")
|
|
}, {
|
|
obj: {
|
|
update: function(options) {
|
|
return options.observable() + "ab";
|
|
}
|
|
}
|
|
});
|
|
equal(result.obj(), "aab");
|
|
});
|
|
|
|
test('ko.mapping.fromJS update callbacks should pass in an observable, when original is not observable', function () {
|
|
var result = ko.mapping.fromJS({
|
|
obj: "a"
|
|
}, {
|
|
obj: {
|
|
update: function(options) {
|
|
return options.observable() + "ab";
|
|
}
|
|
}
|
|
});
|
|
equal(result.obj(), "aab");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should map the top-level atomic properties on the supplied object as observables', function () {
|
|
var result = ko.mapping.fromJS({
|
|
a: 123,
|
|
b: 'Hello',
|
|
c: true
|
|
});
|
|
equal(ko.isObservable(result.a), true);
|
|
equal(ko.isObservable(result.b), true);
|
|
equal(ko.isObservable(result.c), true);
|
|
equal(result.a(), 123);
|
|
equal(result.b(), 'Hello');
|
|
equal(result.c(), true);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should not map the top-level non-atomic properties on the supplied object as observables', function () {
|
|
var result = ko.mapping.fromJS({
|
|
a: {
|
|
a1: "Hello"
|
|
}
|
|
});
|
|
equal(ko.isObservable(result.a), false);
|
|
equal(ko.isObservable(result.a.a1), true);
|
|
equal(result.a.a1(), 'Hello');
|
|
});
|
|
|
|
test('ko.mapping.fromJS should not map the top-level non-atomic properties on the supplied overriden model as observables', function () {
|
|
var result = ko.mapping.fromJS({
|
|
a: {
|
|
a2: "a2"
|
|
}
|
|
}, {
|
|
create: function(model) {
|
|
return {
|
|
a: {
|
|
a1: "a1"
|
|
}
|
|
};
|
|
}
|
|
});
|
|
equal(ko.isObservable(result.a), false);
|
|
equal(ko.isObservable(result.a.a1), false);
|
|
equal(result.a.a2, undefined);
|
|
equal(result.a.a1, 'a1');
|
|
});
|
|
|
|
test('ko.mapping.fromJS should not map top-level objects on the supplied overriden model as observables', function () {
|
|
var dummyObject = function (options) {
|
|
this.a1 = options.a1;
|
|
return this;
|
|
}
|
|
|
|
var result = ko.mapping.fromJS({}, {
|
|
create: function(model) {
|
|
return {
|
|
a: new dummyObject({
|
|
a1: "Hello"
|
|
})
|
|
};
|
|
}
|
|
});
|
|
equal(ko.isObservable(result.a), false);
|
|
equal(ko.isObservable(result.a.a1), false);
|
|
equal(result.a.a1, 'Hello');
|
|
});
|
|
|
|
test('ko.mapping.fromJS should allow non-unique atomic properties', function () {
|
|
var vm = ko.mapping.fromJS({
|
|
a: [1, 2, 1]
|
|
});
|
|
|
|
deepEqual(vm.a(), [1, 2, 1]);
|
|
});
|
|
/* speed optimizations don't allow this anymore...
|
|
test('ko.mapping.fromJS should not allow non-unique non-atomic properties', function () {
|
|
var options = {
|
|
key: function(item) { return ko.utils.unwrapObservable(item.id); }
|
|
};
|
|
|
|
var didThrow = false;
|
|
try {
|
|
ko.mapping.fromJS([
|
|
{ id: "a1" },
|
|
{ id: "a2" },
|
|
{ id: "a1" }
|
|
], options);
|
|
}
|
|
catch (ex) {
|
|
didThrow = true
|
|
}
|
|
equal(didThrow, true);
|
|
});
|
|
*/
|
|
test('ko.mapping.fromJS should map descendant properties on the supplied object as observables', function () {
|
|
var result = ko.mapping.fromJS({
|
|
a: {
|
|
a1: 'a1value',
|
|
a2: {
|
|
a21: 'a21value',
|
|
a22: 'a22value'
|
|
}
|
|
},
|
|
b: {
|
|
b1: null,
|
|
b2: undefined
|
|
}
|
|
});
|
|
equal(result.a.a1(), 'a1value');
|
|
equal(result.a.a2.a21(), 'a21value');
|
|
equal(result.a.a2.a22(), 'a22value');
|
|
equal(result.b.b1(), null);
|
|
equal(result.b.b2(), undefined);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should map observable properties, but without adding a further observable wrapper', function () {
|
|
var result = ko.mapping.fromJS({
|
|
a: ko.observable('Hey')
|
|
});
|
|
equal(result.a(), 'Hey');
|
|
});
|
|
|
|
test('ko.mapping.fromJS should escape from reference cycles', function () {
|
|
var obj = {};
|
|
obj.someProp = {
|
|
owner: obj
|
|
};
|
|
var result = ko.mapping.fromJS(obj);
|
|
equal(result.someProp.owner === result, true);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send relevant create callbacks', function () {
|
|
var items = [];
|
|
var index = 0;
|
|
var result = ko.mapping.fromJS({
|
|
a: "hello"
|
|
}, {
|
|
create: function (model) {
|
|
index++;
|
|
return model;
|
|
}
|
|
});
|
|
equal(index, 1);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send relevant create callbacks when mapping arrays', function () {
|
|
var items = [];
|
|
var index = 0;
|
|
var result = ko.mapping.fromJS([
|
|
"hello"
|
|
], {
|
|
create: function (model) {
|
|
index++;
|
|
return model;
|
|
}
|
|
});
|
|
equal(index, 1);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send parent along to create callback when creating an object', function() {
|
|
var obj = {
|
|
a: "a",
|
|
b: {
|
|
b1: "b1"
|
|
}
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(obj, {
|
|
"b": {
|
|
create: function(options) {
|
|
equal(ko.isObservable(options.parent.a), true);
|
|
equal(options.parent.a(), "a");
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send parent along to create callback when creating an array item inside an object', function() {
|
|
var obj = {
|
|
a: "a",
|
|
b: [
|
|
{ id: 1 },
|
|
{ id: 2 }
|
|
]
|
|
};
|
|
|
|
var target = {};
|
|
var numCreated = 0;
|
|
var result = ko.mapping.fromJS(obj, {
|
|
"b": {
|
|
create: function(options) {
|
|
equal(ko.isObservable(options.parent), false);
|
|
equal(options.parent, target);
|
|
numCreated++;
|
|
}
|
|
}
|
|
}, target);
|
|
|
|
equal(numCreated, 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send parent along to create callback when creating an array item inside an array', function() {
|
|
// parent is the array
|
|
|
|
var obj = [
|
|
{ id: 1 },
|
|
{ id: 2 }
|
|
];
|
|
|
|
var target = [];
|
|
var numCreated = 0;
|
|
var result = ko.mapping.fromJS(obj, {
|
|
create: function(options) {
|
|
equal(ko.isObservable(options.parent), true);
|
|
numCreated++;
|
|
}
|
|
}, target);
|
|
|
|
equal(numCreated, 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should update objects in arrays that were specified in the overriden model in the create callback', function () {
|
|
var options = {
|
|
create: function(options) {
|
|
return ko.mapping.fromJS(options.data);
|
|
}
|
|
}
|
|
|
|
var result = ko.mapping.fromJS([], options);
|
|
ko.mapping.fromJS([{
|
|
a: "a",
|
|
b: "b"
|
|
}], {}, result);
|
|
|
|
equal(ko.isObservable(result), true);
|
|
equal(ko.isObservable(result()[0].a), true);
|
|
equal(result()[0].a(), "a");
|
|
equal(ko.isObservable(result()[0].b), true);
|
|
equal(result()[0].b(), "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should use the create callback to update objects in arrays', function () {
|
|
var created = [];
|
|
var arrayEvents = 0;
|
|
|
|
var options = {
|
|
key: function(item) { return ko.utils.unwrapObservable(item.id); },
|
|
create: function(options) {
|
|
created.push(options.data.id);
|
|
return ko.mapping.fromJS(options.data);
|
|
},
|
|
arrayChanged: function(event, item) {
|
|
arrayEvents++;
|
|
}
|
|
}
|
|
|
|
var result = ko.mapping.fromJS([
|
|
{ id: "a" }
|
|
], options);
|
|
|
|
ko.mapping.fromJS([
|
|
{ id: "a" },
|
|
{ id: "b" }
|
|
], {}, result);
|
|
|
|
equal(created[0], "a");
|
|
equal(created[1], "b");
|
|
equal(result()[0].id(), "a");
|
|
equal(result()[1].id(), "b");
|
|
equal(arrayEvents, 3); // added, retained, added
|
|
});
|
|
|
|
test('ko.mapping.fromJS should not call the create callback for existing objects', function () {
|
|
var numCreate = 0;
|
|
var options = {
|
|
create: function (model) {
|
|
numCreate++;
|
|
var overridenModel = {};
|
|
return overridenModel;
|
|
}
|
|
};
|
|
|
|
var items = [];
|
|
var index = 0;
|
|
var result = ko.mapping.fromJS({
|
|
a: "hello"
|
|
}, options);
|
|
|
|
ko.mapping.fromJS({
|
|
a: "bye"
|
|
}, {}, result);
|
|
|
|
equal(numCreate, 1);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should not overwrite the existing observable array', function () {
|
|
var result = ko.mapping.fromJS({
|
|
a: [1]
|
|
});
|
|
|
|
var resultA = result.a;
|
|
|
|
ko.mapping.fromJS({
|
|
a: [1]
|
|
}, result);
|
|
|
|
equal(resultA, result.a);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send an added callback for every array item that is added to a previously non-existent array', function () {
|
|
var added = [];
|
|
|
|
var options = {
|
|
"a" : {
|
|
arrayChanged: function (event, newValue) {
|
|
if (event === "added") added.push(newValue);
|
|
}
|
|
}
|
|
};
|
|
var result = ko.mapping.fromJS({}, options);
|
|
ko.mapping.fromJS({
|
|
a: [1, 2]
|
|
}, {}, result);
|
|
equal(added.length, 2);
|
|
equal(added[0], 1);
|
|
equal(added[1], 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send an added callback for every array item that is added to a previously empty array', function () {
|
|
var added = [];
|
|
|
|
var options = {
|
|
"a": {
|
|
arrayChanged: function (event, newValue) {
|
|
if (event === "added") added.push(newValue);
|
|
}
|
|
}
|
|
};
|
|
var result = ko.mapping.fromJS({ a: [] }, options);
|
|
ko.mapping.fromJS({
|
|
a: [1, 2]
|
|
}, {}, result);
|
|
equal(added.length, 2);
|
|
equal(added[0], 1);
|
|
equal(added[1], 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should not make observable anything that is not in the js object', function () {
|
|
var result = ko.mapping.fromJS({});
|
|
result.a = "a";
|
|
equal(ko.isObservable(result.a), false);
|
|
|
|
ko.mapping.fromJS({
|
|
b: "b"
|
|
}, {}, result);
|
|
|
|
equal(ko.isObservable(result.a), false);
|
|
equal(ko.isObservable(result.b), true);
|
|
equal(result.a, "a");
|
|
equal(result.b(), "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should not make observable anything that is not in the js object when overriding the model', function () {
|
|
var options = {
|
|
create: function(model) {
|
|
return {
|
|
a: "a"
|
|
}
|
|
}
|
|
};
|
|
|
|
var result = ko.mapping.fromJS({}, options);
|
|
ko.mapping.fromJS({
|
|
b: "b"
|
|
}, {}, result);
|
|
|
|
equal(ko.isObservable(result.a), false);
|
|
equal(ko.isObservable(result.b), true);
|
|
equal(result.a, "a");
|
|
equal(result.b(), "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send an added callback for every array item that is added', function () {
|
|
var added = [];
|
|
|
|
var options = {
|
|
"a": {
|
|
arrayChanged: function (event, newValue) {
|
|
if (event === "added") added.push(newValue);
|
|
}
|
|
}
|
|
};
|
|
var result = ko.mapping.fromJS({
|
|
a: []
|
|
}, options);
|
|
ko.mapping.fromJS({
|
|
a: [1, 2]
|
|
}, {}, result);
|
|
equal(added.length, 2);
|
|
equal(added[0], 1);
|
|
equal(added[1], 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send an added callback for every array item that is added', function () {
|
|
var added = [];
|
|
|
|
var result = ko.mapping.fromJS({
|
|
a: [1, 2]
|
|
}, {
|
|
"a": {
|
|
arrayChanged: function (event, newValue) {
|
|
if (event === "added") added.push(newValue);
|
|
}
|
|
}
|
|
});
|
|
equal(added.length, 2);
|
|
equal(added[0], 1);
|
|
equal(added[1], 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJSON should parse and then map in the same way', function () {
|
|
var jsonString = ko.utils.stringifyJson({ // Note that "undefined" property values are omitted by the stringifier, so not testing those
|
|
a: {
|
|
a1: 'a1value',
|
|
a2: {
|
|
a21: 'a21value',
|
|
a22: 'a22value'
|
|
}
|
|
},
|
|
b: {
|
|
b1: null
|
|
}
|
|
});
|
|
var result = ko.mapping.fromJSON(jsonString);
|
|
equal(result.a.a1(), 'a1value');
|
|
equal(result.a.a2.a21(), 'a21value');
|
|
equal(result.a.a2.a22(), 'a22value');
|
|
equal(result.b.b1(), null);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should be able to map empty object structures', function () {
|
|
var obj = {
|
|
someProp: undefined,
|
|
a: []
|
|
};
|
|
var result = ko.mapping.fromJS(obj);
|
|
equal(ko.isObservable(result.someProp), true);
|
|
equal(ko.isObservable(result.a), true);
|
|
equal(ko.isObservable(result.unknownProperty), false);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send create callbacks when atomic items are constructed', function () {
|
|
var atomicValues = ["hello", 123, true, null, undefined];
|
|
var callbacksReceived = 0;
|
|
for (var i = 0; i < atomicValues.length; i++) {
|
|
var result = ko.mapping.fromJS(atomicValues[i], {
|
|
create: function (item) {
|
|
callbacksReceived++;
|
|
return item;
|
|
}
|
|
});
|
|
}
|
|
equal(callbacksReceived, 5);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send callbacks when atomic array elements are constructed', function () {
|
|
var oldItems = {
|
|
array: []
|
|
};
|
|
var newItems = {
|
|
array: [{
|
|
id: 1
|
|
},
|
|
{
|
|
id: 2
|
|
}]
|
|
};
|
|
|
|
var items = [];
|
|
var result = ko.mapping.fromJS(oldItems, {
|
|
"array": {
|
|
arrayChanged: function (event, item) {
|
|
if (event == "added")
|
|
items.push(item);
|
|
}
|
|
}
|
|
});
|
|
ko.mapping.fromJS(newItems, {}, result);
|
|
equal(items.length, 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should not send callbacks containing parent names when descendant objects are constructed', function () {
|
|
var obj = {
|
|
a: {
|
|
a1: "hello",
|
|
a2: 234,
|
|
a3: {
|
|
a31: null
|
|
}
|
|
}
|
|
};
|
|
var parents = [];
|
|
var pushParent = function (item, parent) {
|
|
parents.push(parent);
|
|
return item;
|
|
};
|
|
var result = ko.mapping.fromJS(obj, {
|
|
create: pushParent
|
|
});
|
|
equal(parents.length, 1);
|
|
equal(parents[0], undefined);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should create instead of update, on empty objects', function () {
|
|
var obj = {
|
|
a: ["a1", "a2"]
|
|
};
|
|
|
|
var result;
|
|
result = ko.mapping.fromJS({});
|
|
ko.mapping.fromJS(obj, {}, result);
|
|
equal(result.a().length, 2);
|
|
equal(result.a()[0], "a1");
|
|
equal(result.a()[1], "a2");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should update atomic observables', function () {
|
|
var atomicValues = ["hello", 123, true, null, undefined];
|
|
var atomicValues2 = ["hello2", 124, false, "not null", "defined"];
|
|
|
|
for (var i = 0; i < atomicValues.length; i++) {
|
|
var result = ko.mapping.fromJS(atomicValues[i]);
|
|
ko.mapping.fromJS(atomicValues2[i], {}, result);
|
|
equal(ko.isObservable(result), true);
|
|
equal(result(), atomicValues2[i]);
|
|
}
|
|
});
|
|
|
|
test('ko.mapping.fromJS should update objects', function () {
|
|
var obj = {
|
|
a: "prop",
|
|
b: {
|
|
b1: null,
|
|
b2: "b2"
|
|
}
|
|
}
|
|
|
|
var obj2 = {
|
|
a: "prop2",
|
|
b: {
|
|
b1: 124,
|
|
b2: "b22"
|
|
}
|
|
}
|
|
|
|
var result = ko.mapping.fromJS(obj);
|
|
ko.mapping.fromJS(obj2, {}, result);
|
|
equal(result.a(), "prop2");
|
|
equal(result.b.b1(), 124);
|
|
equal(result.b.b2(), "b22");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should update initially empty objects', function () {
|
|
var obj = {
|
|
a: undefined,
|
|
b: []
|
|
}
|
|
|
|
var obj2 = {
|
|
a: "prop2",
|
|
b: ["b1", "b2"]
|
|
}
|
|
|
|
var result = ko.mapping.fromJS(obj);
|
|
ko.mapping.fromJS(obj2, {}, result);
|
|
equal(result.a(), "prop2");
|
|
equal(result.b()[0], "b1");
|
|
equal(result.b()[1], "b2");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should update arrays containing atomic types', function () {
|
|
var obj = ["a1", "a2", 6];
|
|
var obj2 = ["a3", "a4", 7];
|
|
|
|
var result = ko.mapping.fromJS(obj);
|
|
|
|
ko.mapping.fromJS(obj2, {}, result);
|
|
equal(result().length, 3);
|
|
equal(result()[0], "a3");
|
|
equal(result()[1], "a4");
|
|
equal(result()[2], 7);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should update arrays containing objects', function () {
|
|
var obj = {
|
|
a: [{
|
|
id: 1,
|
|
value: "a1"
|
|
},
|
|
{
|
|
id: 2,
|
|
value: "a2"
|
|
}]
|
|
}
|
|
|
|
var obj2 = {
|
|
a: [{
|
|
id: 1,
|
|
value: "a1"
|
|
},
|
|
{
|
|
id: 3,
|
|
value: "a3"
|
|
}]
|
|
}
|
|
|
|
var options = {
|
|
"a": {
|
|
key: function (item) {
|
|
return item.id;
|
|
}
|
|
}
|
|
};
|
|
var result = ko.mapping.fromJS(obj, options);
|
|
|
|
ko.mapping.fromJS(obj2, {}, result);
|
|
equal(result.a().length, 2);
|
|
equal(result.a()[0].value(), "a1");
|
|
equal(result.a()[1].value(), "a3");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send a callback when adding new objects to an array', function () {
|
|
var obj = [{
|
|
id: 1
|
|
}];
|
|
var obj2 = [{
|
|
id: 1
|
|
},
|
|
{
|
|
id: 2
|
|
}];
|
|
|
|
var mappedItems = [];
|
|
|
|
var options = {
|
|
key: function(item) {
|
|
return item.id;
|
|
},
|
|
arrayChanged: function (event, item) {
|
|
if (event == "added") mappedItems.push(item);
|
|
}
|
|
};
|
|
var result = ko.mapping.fromJS(obj, options);
|
|
ko.mapping.fromJS(obj2, {}, result);
|
|
equal(mappedItems.length, 2);
|
|
equal(mappedItems[0].id(), 1);
|
|
equal(mappedItems[1].id(), 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should be able to update from an observable source', function () {
|
|
var obj = [{
|
|
id: 1
|
|
}];
|
|
var obj2 = ko.mapping.fromJS([{
|
|
id: 1
|
|
},
|
|
{
|
|
id: 2
|
|
}]);
|
|
|
|
var result = ko.mapping.fromJS(obj);
|
|
ko.mapping.fromJS(obj2, {}, result);
|
|
equal(result().length, 2);
|
|
equal(result()[0].id(), 1);
|
|
equal(result()[1].id(), 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should send a deleted callback when an item was deleted from an array', function () {
|
|
var obj = [1, 2];
|
|
var obj2 = [1];
|
|
|
|
var items = [];
|
|
|
|
var options = {
|
|
arrayChanged: function (event, item) {
|
|
if (event == "deleted") items.push(item);
|
|
}
|
|
};
|
|
var result = ko.mapping.fromJS(obj, options);
|
|
ko.mapping.fromJS(obj2, {}, result);
|
|
equal(items.length, 1);
|
|
equal(items[0], 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should reuse options that were added in ko.mapping.fromJS', function() {
|
|
var viewModelMapping = {
|
|
key: function(data) {
|
|
return ko.utils.unwrapObservable(data.id);
|
|
},
|
|
create: function(options) {
|
|
return new viewModel(options);
|
|
}
|
|
};
|
|
|
|
var viewModel = function(options) {
|
|
var mapping = {
|
|
entries: viewModelMapping
|
|
};
|
|
|
|
ko.mapping.fromJS(options.data, mapping, this);
|
|
|
|
this.func = function() { return true; };
|
|
};
|
|
|
|
var model = ko.mapping.fromJS([], viewModelMapping);
|
|
|
|
var data = [{
|
|
"id": 1,
|
|
"entries": [{
|
|
"id": 2,
|
|
"entries": [{
|
|
"id": 3,
|
|
"entries": []
|
|
}]
|
|
}]
|
|
}];
|
|
|
|
ko.mapping.fromJS(data, {}, model);
|
|
ko.mapping.fromJS(data, {}, model);
|
|
|
|
equal(model()[0].func(), true);
|
|
equal(model()[0].entries()[0].func(), true);
|
|
equal(model()[0].entries()[0].entries()[0].func(), true);
|
|
});
|
|
|
|
test('ko.mapping.toJS should not change the mapped object', function() {
|
|
var obj = {
|
|
a: "a"
|
|
}
|
|
|
|
var result = ko.mapping.fromJS(obj);
|
|
result.b = ko.observable(123);
|
|
var toJS = ko.mapping.toJS(result);
|
|
|
|
equal(ko.isObservable(result.b), true);
|
|
equal(result.b(), 123);
|
|
equal(toJS.b, undefined);
|
|
});
|
|
|
|
test('ko.mapping.toJS should not change the mapped array', function() {
|
|
var obj = [{
|
|
a: 50
|
|
}]
|
|
|
|
var result = ko.mapping.fromJS(obj);
|
|
result()[0].b = ko.observable(123);
|
|
var toJS = ko.mapping.toJS(result);
|
|
|
|
equal(ko.isObservable(result()[0].b), true);
|
|
equal(result()[0].b(), 123);
|
|
});
|
|
|
|
test('observableArray.mappedRemove should use key callback if available', function() {
|
|
var obj = [
|
|
{ id : 1 },
|
|
{ id : 2 }
|
|
]
|
|
|
|
var result = ko.mapping.fromJS(obj, {
|
|
key: function(item) {
|
|
return ko.utils.unwrapObservable(item.id);
|
|
}
|
|
});
|
|
result.mappedRemove({ id : 2 });
|
|
equal(result().length, 1);
|
|
});
|
|
|
|
test('observableArray.mappedRemove with predicate should use key callback if available', function() {
|
|
var obj = [
|
|
{ id : 1 },
|
|
{ id : 2 }
|
|
]
|
|
|
|
var result = ko.mapping.fromJS(obj, {
|
|
key: function(item) {
|
|
return ko.utils.unwrapObservable(item.id);
|
|
}
|
|
});
|
|
result.mappedRemove(function(key) {
|
|
return key == 2;
|
|
});
|
|
equal(result().length, 1);
|
|
});
|
|
|
|
test('observableArray.mappedRemoveAll should use key callback if available', function() {
|
|
var obj = [
|
|
{ id : 1 },
|
|
{ id : 2 }
|
|
]
|
|
|
|
var result = ko.mapping.fromJS(obj, {
|
|
key: function(item) {
|
|
return ko.utils.unwrapObservable(item.id);
|
|
}
|
|
});
|
|
result.mappedRemoveAll([{ id : 2 }]);
|
|
equal(result().length, 1);
|
|
});
|
|
|
|
test('observableArray.mappedDestroy should use key callback if available', function() {
|
|
var obj = [
|
|
{ id : 1 },
|
|
{ id : 2 }
|
|
]
|
|
|
|
var result = ko.mapping.fromJS(obj, {
|
|
key: function(item) {
|
|
return ko.utils.unwrapObservable(item.id);
|
|
}
|
|
});
|
|
result.mappedDestroy({ id : 2 });
|
|
equal(result()[0]._destroy, undefined);
|
|
equal(result()[1]._destroy, true);
|
|
});
|
|
|
|
test('observableArray.mappedDestroy with predicate should use key callback if available', function() {
|
|
var obj = [
|
|
{ id : 1 },
|
|
{ id : 2 }
|
|
]
|
|
|
|
var result = ko.mapping.fromJS(obj, {
|
|
key: function(item) {
|
|
return ko.utils.unwrapObservable(item.id);
|
|
}
|
|
});
|
|
result.mappedDestroy(function(key) {
|
|
return key == 2;
|
|
});
|
|
equal(result()[0]._destroy, undefined);
|
|
equal(result()[1]._destroy, true);
|
|
});
|
|
|
|
test('observableArray.mappedDestroyAll should use key callback if available', function() {
|
|
var obj = [
|
|
{ id : 1 },
|
|
{ id : 2 }
|
|
]
|
|
|
|
var result = ko.mapping.fromJS(obj, {
|
|
key: function(item) {
|
|
return ko.utils.unwrapObservable(item.id);
|
|
}
|
|
});
|
|
result.mappedDestroyAll([{ id : 2 }]);
|
|
equal(result()[0]._destroy, undefined);
|
|
equal(result()[1]._destroy, true);
|
|
});
|
|
|
|
test('observableArray.mappedIndexOf should use key callback if available', function() {
|
|
var obj = [
|
|
{ id : 1 },
|
|
{ id : 2 }
|
|
]
|
|
|
|
var result = ko.mapping.fromJS(obj, {
|
|
key: function(item) {
|
|
return ko.utils.unwrapObservable(item.id);
|
|
}
|
|
});
|
|
equal(result.mappedIndexOf({ id : 1 }), 0);
|
|
equal(result.mappedIndexOf({ id : 2 }), 1);
|
|
equal(result.mappedIndexOf({ id : 3 }), -1);
|
|
});
|
|
|
|
test('observableArray.mappedCreate should use key callback if available and not allow duplicates', function() {
|
|
var obj = [
|
|
{ id : 1 },
|
|
{ id : 2 }
|
|
]
|
|
|
|
var result = ko.mapping.fromJS(obj, {
|
|
key: function(item) {
|
|
return ko.utils.unwrapObservable(item.id);
|
|
}
|
|
});
|
|
|
|
var caught = false;
|
|
try {
|
|
result.mappedCreate({ id : 1 });
|
|
}
|
|
catch(e) {
|
|
caught = true;
|
|
}
|
|
|
|
equal(caught, true);
|
|
equal(result().length, 2);
|
|
});
|
|
|
|
test('observableArray.mappedCreate should use create callback if available', function() {
|
|
var obj = [
|
|
{ id : 1 },
|
|
{ id : 2 }
|
|
]
|
|
|
|
var childModel = function(data){
|
|
ko.mapping.fromJS(data, {}, this);
|
|
this.Hello = ko.observable("hello");
|
|
}
|
|
|
|
var result = ko.mapping.fromJS(obj, {
|
|
key: function(item) {
|
|
return ko.utils.unwrapObservable(item.id);
|
|
},
|
|
create: function(options){
|
|
return new childModel(options.data);
|
|
}
|
|
});
|
|
|
|
result.mappedCreate({ id: 3 });
|
|
var index = result.mappedIndexOf({ id : 3 });
|
|
equal(index, 2);
|
|
equal(result()[index].Hello(), "hello");
|
|
});
|
|
|
|
test('observableArray.mappedCreate should use update callback if available', function() {
|
|
var obj = [
|
|
{ id : 1 },
|
|
{ id : 2 }
|
|
]
|
|
|
|
var childModel = function(data){
|
|
ko.mapping.fromJS(data, {}, this);
|
|
}
|
|
|
|
var result = ko.mapping.fromJS(obj, {
|
|
key: function(item) {
|
|
return ko.utils.unwrapObservable(item.id);
|
|
},
|
|
create: function(options){
|
|
return new childModel(options.data);
|
|
},
|
|
update: function(options){
|
|
return {
|
|
bla: options.data.id * 10
|
|
};
|
|
}
|
|
});
|
|
|
|
result.mappedCreate({ id: 3 });
|
|
equal(result()[0].bla, 10);
|
|
equal(result()[2].bla, 30);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should merge options from subsequent calls', function() {
|
|
var obj = ['a'];
|
|
|
|
var result = ko.mapping.fromJS(obj, { dummyOption1: 1 });
|
|
ko.mapping.fromJS({}, { dummyOption2: 2 }, result);
|
|
|
|
equal(result.__ko_mapping__.dummyOption1, 1);
|
|
equal(result.__ko_mapping__.dummyOption2, 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should correctly handle falsey values', function () {
|
|
var obj = [false, ""];
|
|
|
|
var result = ko.mapping.fromJS(obj);
|
|
|
|
equal(result()[0] === false, true);
|
|
equal(result()[1] === "", true);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should correctly handle falsey values in keys', function () {
|
|
var created = [];
|
|
var gotDeletedEvent = false;
|
|
|
|
var options = {
|
|
key: function(item) { return ko.utils.unwrapObservable(item.id); },
|
|
arrayChanged: function(event, item) {
|
|
if (event === "deleted") gotDeletedEvent = true;
|
|
}
|
|
}
|
|
|
|
var result = ko.mapping.fromJS([
|
|
{ id: 0 }
|
|
], options);
|
|
|
|
ko.mapping.fromJS([
|
|
{ id: 0 },
|
|
{ id: 1 }
|
|
], {}, result);
|
|
|
|
equal(gotDeletedEvent, false);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should allow duplicate atomic items in arrays', function () {
|
|
var result = ko.mapping.fromJS([
|
|
"1", "1", "2"
|
|
]);
|
|
|
|
equal(result().length, 3);
|
|
equal(result()[0], "1");
|
|
equal(result()[1], "1");
|
|
equal(result()[2], "2");
|
|
|
|
ko.mapping.fromJS([
|
|
"1", "1", "1", "2"
|
|
], {}, result);
|
|
|
|
equal(result().length, 4);
|
|
equal(result()[0], "1");
|
|
equal(result()[1], "1");
|
|
equal(result()[2], "1");
|
|
equal(result()[3], "2");
|
|
});
|
|
|
|
test('when doing ko.mapping.fromJS on an already mapped object, the new options should combine with the old', function() {
|
|
var dataA = {
|
|
a: "a"
|
|
};
|
|
var dataB = {
|
|
b: "b"
|
|
};
|
|
|
|
var mapped = {};
|
|
ko.mapping.fromJS(dataA, {}, mapped);
|
|
ko.mapping.fromJS(dataB, {}, mapped);
|
|
equal(mapped.__ko_mapping__.mappedProperties.a, true);
|
|
equal(mapped.__ko_mapping__.mappedProperties.b, true);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should merge options from subsequent calls', function() {
|
|
var obj = ['a'];
|
|
|
|
var result = ko.mapping.fromJS(obj, { dummyOption1: 1 });
|
|
ko.mapping.fromJS(['b'], { dummyOption2: 2 }, result);
|
|
|
|
equal(result.__ko_mapping__.dummyOption1, 1);
|
|
equal(result.__ko_mapping__.dummyOption2, 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should work on unmapped objects', function() {
|
|
var obj = ko.observableArray(['a']);
|
|
|
|
ko.mapping.fromJS(['b'], {}, obj);
|
|
|
|
equal(obj()[0], 'b');
|
|
});
|
|
|
|
test('ko.mapping.fromJS should update an array only once', function() {
|
|
var obj = {
|
|
a: ko.observableArray()
|
|
};
|
|
|
|
var updateCount = 0;
|
|
obj.a.subscribe(function() {
|
|
updateCount++;
|
|
});
|
|
|
|
ko.mapping.fromJS({ a: [1, 2, 3] }, {}, obj);
|
|
|
|
equal(updateCount, 1);
|
|
});
|
|
|
|
test('ko.mapping.fromJSON should merge options from subsequent calls', function() {
|
|
var obj = ['a'];
|
|
|
|
var result = ko.mapping.fromJS(obj, { dummyOption1: 1 });
|
|
ko.mapping.fromJSON('["b"]', { dummyOption2: 2 }, result);
|
|
|
|
equal(result.__ko_mapping__.dummyOption1, 1);
|
|
equal(result.__ko_mapping__.dummyOption2, 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should be able to update observables not created by fromJS', function() {
|
|
var existing = {
|
|
a: ko.observable(),
|
|
d: ko.observableArray()
|
|
};
|
|
|
|
ko.mapping.fromJS({
|
|
a: {
|
|
b: "b!"
|
|
},
|
|
d: [2]
|
|
}, {}, existing);
|
|
|
|
equal(existing.a().b(), "b!");
|
|
equal(existing.d().length, 1);
|
|
equal(existing.d()[0], 2);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should accept an already mapped object as the second parameter', function() {
|
|
var mapped = ko.mapping.fromJS({ a: "a" });
|
|
ko.mapping.fromJS({ a: "b" }, mapped);
|
|
equal(mapped.a(), "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should properly map objects that appear in multiple places', function() {
|
|
var obj = { title: "Lorem ipsum" }, obj2 = { title: "Lorem ipsum 2" };
|
|
var x = [obj,obj2];
|
|
var y = { o: obj, x: x };
|
|
|
|
var z = ko.mapping.fromJS(y);
|
|
|
|
equal(y.x[0].title, "Lorem ipsum");
|
|
equal(z.x()[0].title(), "Lorem ipsum");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should properly update arrays containing a NULL key', function() {
|
|
var data = [1,2,3,null];
|
|
var model=ko.mapping.fromJS(data);
|
|
|
|
deepEqual(model(), [1,2,3,null]);
|
|
|
|
data = [null,1,2,3];
|
|
ko.mapping.fromJS(data, {}, model);
|
|
|
|
deepEqual(model(), [null,1,2,3]);
|
|
});
|
|
|
|
test('ko.mapping.visitModel will pass in correct parent names', function() {
|
|
var data = { a: { a2: "a2value" } };
|
|
var parents = [];
|
|
ko.mapping.visitModel(data, function(obj, parent) {
|
|
parents.push(parent);
|
|
});
|
|
equal(parents.length, 3);
|
|
equal(parents[0], undefined);
|
|
equal(parents[1], "a");
|
|
equal(parents[2], "a.a2");
|
|
});
|
|
|
|
test('ko.mapping.toJS should merge the default observe', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b",
|
|
c: "c"
|
|
};
|
|
|
|
ko.mapping.defaultOptions().observe = ["a"];
|
|
var result = ko.mapping.fromJS(data, { observe: "b" });
|
|
equal(ko.isObservable(result.a), true);
|
|
equal(ko.isObservable(result.b), true);
|
|
equal(ko.isObservable(result.c), false);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should observe specified single property', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b"
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { observe: "a" });
|
|
equal(result.a(), "a");
|
|
equal(result.b, "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should observe specified array', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: ["b1", "b2"]
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { observe: "b" });
|
|
equal(result.a, "a");
|
|
equal(ko.isObservable(result.b), true);
|
|
});
|
|
|
|
test('ko.mapping.fromJS should observe specified array item', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: [{ b1: "v1" }, { b2: "v2" }]
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { observe: "b[0].b1" });
|
|
equal(result.a, "a");
|
|
equal(result.b[0].b1(), "v1");
|
|
equal(result.b[1].b2, "v2");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should observe specified array but not the children', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: [{ b1: "v1" }, { b2: "v2" }]
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { observe: "b" });
|
|
equal(result.a, "a");
|
|
equal(result.b()[0].b1, "v1");
|
|
equal(result.b()[1].b2, "v2");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should observe specified single property, also when going back .toJS', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b"
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { observe: "b" });
|
|
var js = ko.mapping.toJS(result);
|
|
equal(js.a, "a");
|
|
equal(js.b, "b");
|
|
});
|
|
|
|
test('ko.mapping.fromJS should copy specified single property, also when going back .toJS, except when overridden', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b"
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, { observe: "b" });
|
|
var js = ko.mapping.toJS(result, { ignore: "b" });
|
|
equal(js.a, "a");
|
|
equal(js.b, undefined);
|
|
});
|
|
|
|
test('ko.mapping.fromJS explicit declared none observable members should not be mapped to an observable', function() {
|
|
var data = {
|
|
a: "a",
|
|
b: "b",
|
|
c: "c"
|
|
};
|
|
|
|
var ViewModel = function() {
|
|
this.a = ko.observable();
|
|
this.b = null;
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, {}, new ViewModel());
|
|
equal(ko.isObservable(result.a), true);
|
|
equal(ko.isObservable(result.b), false);
|
|
equal(ko.isObservable(result.c), true);
|
|
equal(result.b, data.b);
|
|
});
|
|
|
|
test('ko.mapping.toJS explicit declared none observable members should be mapped toJS correctly', function() {
|
|
var data = {
|
|
a: "a",
|
|
};
|
|
|
|
var ViewModel = function() {
|
|
this.a = null;
|
|
};
|
|
|
|
var result = ko.mapping.fromJS(data, {}, new ViewModel());
|
|
var js = ko.mapping.toJS(result);
|
|
|
|
equal(js.b, data.b);
|
|
});
|
|
|