349 lines
16 KiB
JavaScript
349 lines
16 KiB
JavaScript
|
/// <reference path="../Scripts/References.js" />
|
|||
|
|
|||
|
(function (upshot, ko, undefined) {
|
|||
|
|
|||
|
module("ChangeTracking");
|
|||
|
|
|||
|
var observability = upshot.observability;
|
|||
|
|
|||
|
test("Explicit commit multiple property edits", 5, function () {
|
|||
|
var submitCount = 0;
|
|||
|
var propertyChangedCount = 0;
|
|||
|
|
|||
|
var dc = createTestContext(function () {
|
|||
|
submitCount++;
|
|||
|
}, true);
|
|||
|
|
|||
|
var entitySet = dc.getEntitySet("Product");
|
|||
|
var prod = entitySet.getEntities()[0];
|
|||
|
var state = entitySet.getEntityState(prod);
|
|||
|
equal(state, upshot.EntityState.Unmodified);
|
|||
|
|
|||
|
entitySet.bind("propertyChanged", function () {
|
|||
|
propertyChangedCount++;
|
|||
|
});
|
|||
|
|
|||
|
$.observable(prod).property("Name", "xyz");
|
|||
|
$.observable(prod).property("Name", "foo");
|
|||
|
$.observable(prod).property("Price", 9.99);
|
|||
|
|
|||
|
equal(propertyChangedCount, 3);
|
|||
|
|
|||
|
state = entitySet.getEntityState(prod);
|
|||
|
equal(state, upshot.EntityState.ClientUpdated);
|
|||
|
|
|||
|
equal(submitCount, 0);
|
|||
|
|
|||
|
dc.commitChanges();
|
|||
|
|
|||
|
equal(submitCount, 1);
|
|||
|
});
|
|||
|
|
|||
|
asyncTest("Implicit commit multiple property edits", 5, function () {
|
|||
|
var submitCount = 0;
|
|||
|
var propertyChangedCount = 0;
|
|||
|
|
|||
|
var dc = createTestContext(function () {
|
|||
|
submitCount++;
|
|||
|
}, false);
|
|||
|
|
|||
|
var entitySet = dc.getEntitySet("Product");
|
|||
|
var prod = entitySet.getEntities()[0];
|
|||
|
var state = entitySet.getEntityState(prod);
|
|||
|
equal(state, upshot.EntityState.Unmodified);
|
|||
|
|
|||
|
entitySet.bind("propertyChanged", function () {
|
|||
|
propertyChangedCount++;
|
|||
|
});
|
|||
|
|
|||
|
$.observable(prod).property("Name", "xyz");
|
|||
|
$.observable(prod).property("Name", "foo");
|
|||
|
$.observable(prod).property("Price", 9.99);
|
|||
|
|
|||
|
// before the timeout has expired we shouldn't have committed anything
|
|||
|
equal(submitCount, 0);
|
|||
|
|
|||
|
equal(propertyChangedCount, 3);
|
|||
|
|
|||
|
state = entitySet.getEntityState(prod);
|
|||
|
equal(state, upshot.EntityState.ClientUpdated);
|
|||
|
|
|||
|
// Here we queue the test verification and start so that it runs
|
|||
|
// AFTER the queued commit
|
|||
|
setTimeout(function () {
|
|||
|
equal(submitCount, 1);
|
|||
|
start();
|
|||
|
}, 0);
|
|||
|
|
|||
|
});
|
|||
|
|
|||
|
asyncTest("Implicit commit multiple array operations", 5, function () {
|
|||
|
var submitCount = 0;
|
|||
|
|
|||
|
var dc = createTestContext(function (options, editedEntities) {
|
|||
|
submitCount++;
|
|||
|
equal(editedEntities.length, 2);
|
|||
|
}, false);
|
|||
|
|
|||
|
var entitySet = dc.getEntitySet("Product");
|
|||
|
var prod = entitySet.getEntities()[0];
|
|||
|
|
|||
|
// do an insert
|
|||
|
var newProd = {
|
|||
|
ID: 2,
|
|||
|
Name: "Frish Gnarbles",
|
|||
|
Category: "Snacks",
|
|||
|
Price: 7.99
|
|||
|
};
|
|||
|
$.observable(entitySet.getEntities()).insert(newProd);
|
|||
|
var state = entitySet.getEntityState(newProd);
|
|||
|
equal(state, upshot.EntityState.ClientAdded);
|
|||
|
|
|||
|
// do a delete
|
|||
|
entitySet.deleteEntity(prod);
|
|||
|
state = entitySet.getEntityState(prod);
|
|||
|
equal(state, upshot.EntityState.ClientDeleted);
|
|||
|
|
|||
|
// before the timeout has expired we shouldn't have committed anything
|
|||
|
equal(submitCount, 0);
|
|||
|
|
|||
|
// Here we queue the test verification and start so that it runs
|
|||
|
// AFTER the queued commit
|
|||
|
setTimeout(function () {
|
|||
|
equal(submitCount, 1);
|
|||
|
start();
|
|||
|
}, 0);
|
|||
|
});
|
|||
|
|
|||
|
/* TODO: We forced managed associations on for Dev11 beta, since unmanaged associations are broken.
|
|||
|
test("Nested entities can be added to an entity, and navigation properties are untracked", 16, function () {
|
|||
|
try {
|
|||
|
upshot.observability.configuration = observability.knockout;
|
|||
|
|
|||
|
var manageAssociations = false;
|
|||
|
var dc = createKoTestContext(function () { }, false, manageAssociations);
|
|||
|
|
|||
|
var orders = dc.getEntitySet("Order"),
|
|||
|
orderDetails = dc.getEntitySet("OrderDetail"),
|
|||
|
order = orders.getEntities()()[0],
|
|||
|
order2 = orders.getEntities()()[1],
|
|||
|
orderDetail = orderDetails.getEntities()()[0];
|
|||
|
|
|||
|
order.OrderDetails.remove(orderDetail);
|
|||
|
equal(1, order.OrderDetails().length, "There should only be a single order detail");
|
|||
|
equal(upshot.EntityState.Unmodified, orders.getEntityState(order), "The order should not be modified");
|
|||
|
equal(upshot.EntityState.Unmodified, orderDetails.getEntityState(orderDetail), "The order detail should not be modified");
|
|||
|
|
|||
|
order.OrderDetails.push(orderDetail);
|
|||
|
equal(2, order.OrderDetails().length, "There should be two order details");
|
|||
|
equal(upshot.EntityState.Unmodified, orders.getEntityState(order), "The order should not be modified");
|
|||
|
equal(upshot.EntityState.Unmodified, orderDetails.getEntityState(orderDetail), "The order detail should not be modified");
|
|||
|
|
|||
|
orderDetail.Order(order2);
|
|||
|
equal(orderDetail.OrderId(), order2.Id(), "Ids should be equal");
|
|||
|
equal(upshot.EntityState.Unmodified, orders.getEntityState(order2), "The order should not be modified");
|
|||
|
equal(upshot.EntityState.ClientUpdated, orderDetails.getEntityState(orderDetail), "The order detail should not be modified");
|
|||
|
equal(true, orderDetails.isUpdated(orderDetail, "OrderId"), "The OrderId should be modified");
|
|||
|
equal(false, orderDetails.isUpdated(orderDetail, "Order"), "The Order should not be tracked");
|
|||
|
|
|||
|
var properties = [];
|
|||
|
$.each(observability.knockout.unmap(orderDetail, "OrderDetail"), function (key, value) {
|
|||
|
properties.push(key);
|
|||
|
equal(ko.utils.unwrapObservable(orderDetail[key]), value, "Properties should be equal");
|
|||
|
});
|
|||
|
equal(properties.length, 4, "The should be 4 serialized properties");
|
|||
|
|
|||
|
} finally {
|
|||
|
upshot.observability.configuration = observability.jquery;
|
|||
|
}
|
|||
|
});
|
|||
|
*/
|
|||
|
|
|||
|
test("Nested entities can be added to an entity, and navigation properties are computed", 39, function () {
|
|||
|
try {
|
|||
|
upshot.observability.configuration = observability.knockout;
|
|||
|
|
|||
|
var dc = createKoTestContext(function () { }, false);
|
|||
|
|
|||
|
var orders = dc.getEntitySet("Order"),
|
|||
|
orderDetails = dc.getEntitySet("OrderDetail"),
|
|||
|
order = orders.getEntities()()[0],
|
|||
|
order2 = orders.getEntities()()[1],
|
|||
|
orderDetail = orderDetails.getEntities()()[0];
|
|||
|
|
|||
|
ok(order.OrderDetails.indexOf(orderDetail) >= 0 && order2.OrderDetails.indexOf(orderDetail) < 0 && orderDetail.Order() === order, "The order detail is a child of order1");
|
|||
|
equal(orders.getEntityState(order), upshot.EntityState.Unmodified, "order1 should not be modified");
|
|||
|
equal(orders.getEntityState(order2), upshot.EntityState.Unmodified, "order2 should not be modified");
|
|||
|
equal(orderDetails.getEntityState(orderDetail), upshot.EntityState.Unmodified, "The order detail should not be modified");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "OrderId"), false, "The OrderId should not be modified");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "Order"), false, "The Order should not be tracked");
|
|||
|
|
|||
|
orderDetail.OrderId(order2.Id());
|
|||
|
ok(order2.OrderDetails.indexOf(orderDetail) >= 0 && order.OrderDetails.indexOf(orderDetail) < 0 && orderDetail.Order() === order2, "The order detail is a child of order2");
|
|||
|
equal(orders.getEntityState(order), upshot.EntityState.Unmodified, "order1 should not be modified");
|
|||
|
equal(orders.getEntityState(order2), upshot.EntityState.Unmodified, "order2 should not be modified");
|
|||
|
equal(orderDetails.getEntityState(orderDetail), upshot.EntityState.ClientUpdated, "The order detail should be ClientUpdated");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "OrderId"), true, "The OrderId should be modified");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "Order"), false, "The Order should not be tracked");
|
|||
|
|
|||
|
orderDetails.revertUpdates(orderDetail);
|
|||
|
ok(order.OrderDetails.indexOf(orderDetail) >= 0 && order2.OrderDetails.indexOf(orderDetail) < 0 && orderDetail.Order() === order, "The order detail is a child of order1");
|
|||
|
equal(orders.getEntityState(order), upshot.EntityState.Unmodified, "order1 should not be modified");
|
|||
|
equal(orders.getEntityState(order2), upshot.EntityState.Unmodified, "order2 should not be modified");
|
|||
|
equal(orderDetails.getEntityState(orderDetail), upshot.EntityState.Unmodified, "The order detail should not be modified");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "OrderId"), false, "The OrderId should not be modified");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "Order"), false, "The Order should not be tracked");
|
|||
|
|
|||
|
orderDetail.Order(order2);
|
|||
|
ok(orderDetail.OrderId() === order2.Id(), "Ids should be equal");
|
|||
|
equal(orders.getEntityState(order2), upshot.EntityState.Unmodified, "The order should not be modified");
|
|||
|
equal(orderDetails.getEntityState(orderDetail), upshot.EntityState.ClientUpdated, "The order detail should be ClientUpdated");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "OrderId"), true, "The OrderId should be modified");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "Order"), false, "The Order should not be tracked");
|
|||
|
|
|||
|
orderDetails.revertUpdates(orderDetail);
|
|||
|
ok(order.OrderDetails.indexOf(orderDetail) >= 0 && order2.OrderDetails.indexOf(orderDetail) < 0 && orderDetail.Order() === order, "The order detail is a child of order1");
|
|||
|
equal(orders.getEntityState(order), upshot.EntityState.Unmodified, "order1 should not be modified");
|
|||
|
equal(orders.getEntityState(order2), upshot.EntityState.Unmodified, "order2 should not be modified");
|
|||
|
equal(orderDetails.getEntityState(orderDetail), upshot.EntityState.Unmodified, "The order detail should not be modified");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "OrderId"), false, "The OrderId should not be modified");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "Order"), false, "The Order should not be tracked");
|
|||
|
|
|||
|
orderDetail.Order(null);
|
|||
|
equal(orderDetail.OrderId(), null, "FK should be null");
|
|||
|
equal(orders.getEntityState(order), upshot.EntityState.Unmodified, "The old order should not be modified");
|
|||
|
equal(orderDetails.getEntityState(orderDetail), upshot.EntityState.ClientUpdated, "The order detail should be ClientUpdated");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "OrderId"), true, "The OrderId should be modified");
|
|||
|
equal(orderDetails.isUpdated(orderDetail, "Order"), false, "The Order should not be tracked");
|
|||
|
|
|||
|
var properties = [];
|
|||
|
$.each(observability.knockout.unmap(orderDetail, "OrderDetail"), function (key, value) {
|
|||
|
properties.push(key);
|
|||
|
equal(ko.utils.unwrapObservable(orderDetail[key]), value, "Properties should be equal");
|
|||
|
});
|
|||
|
equal(properties.length, 4, "The should be 4 serialized properties");
|
|||
|
|
|||
|
} finally {
|
|||
|
upshot.observability.configuration = observability.jquery;
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
test("Nested entities can be added to an entity, and property changes do not bubble to the parent", 2, function () {
|
|||
|
try {
|
|||
|
upshot.observability.configuration = observability.knockout;
|
|||
|
|
|||
|
var dc = createKoTestContext(function () { }, false);
|
|||
|
|
|||
|
var orders = dc.getEntitySet("Order"),
|
|||
|
orderDetails = dc.getEntitySet("OrderDetail"),
|
|||
|
order = orders.getEntities()()[0],
|
|||
|
orderDetail = orderDetails.getEntities()()[0];
|
|||
|
|
|||
|
orderDetail.Name("asdf");
|
|||
|
equal(orders.getEntityState(order), upshot.EntityState.Unmodified, "The order should not be modified");
|
|||
|
equal(orderDetails.getEntityState(orderDetail), upshot.EntityState.ClientUpdated, "The order detail should not be modified");
|
|||
|
} finally {
|
|||
|
upshot.observability.configuration = observability.jquery;
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// Create and return a context using the specified submitChanges mock
|
|||
|
function createTestContext(submitChangesMock, bufferChanges) {
|
|||
|
var dataProvider = new upshot.DataProvider();
|
|||
|
var implicitCommitHandler;
|
|||
|
if (!bufferChanges) {
|
|||
|
implicitCommitHandler = function () {
|
|||
|
dc._commitChanges({ providerParameters: {} });
|
|||
|
}
|
|||
|
}
|
|||
|
var dc = new upshot.DataContext(dataProvider, implicitCommitHandler);
|
|||
|
dc._submitChanges = submitChangesMock;
|
|||
|
|
|||
|
// add a single product to the context
|
|||
|
var type = "Product";
|
|||
|
var products = [];
|
|||
|
products.push({
|
|||
|
ID: 1,
|
|||
|
Name: "Crispy Snarfs",
|
|||
|
Category: "Snacks",
|
|||
|
Price: 12.99
|
|||
|
});
|
|||
|
products.push({
|
|||
|
ID: 2,
|
|||
|
Name: "Cheezy Snax",
|
|||
|
Category: "Snacks",
|
|||
|
Price: 1.99
|
|||
|
});
|
|||
|
|
|||
|
// mock out enough metadata to do the attach
|
|||
|
upshot.metadata(type, { key: ["ID"] });
|
|||
|
|
|||
|
dc.merge(products, type, null);
|
|||
|
|
|||
|
return dc;
|
|||
|
}
|
|||
|
|
|||
|
function createKoTestContext(submitChangesMock, bufferChanges) {
|
|||
|
var dataProvider = new upshot.DataProvider();
|
|||
|
var implicitCommitHandler;
|
|||
|
if (!bufferChanges) {
|
|||
|
implicitCommitHandler = function () {
|
|||
|
dc._commitChanges({ providerParameters: {} });
|
|||
|
}
|
|||
|
}
|
|||
|
var dc = new upshot.DataContext(dataProvider, implicitCommitHandler);
|
|||
|
dc._submitChanges = submitChangesMock;
|
|||
|
|
|||
|
// add a single product to the context
|
|||
|
var manageAssociations = true; // TODO: Lift this to a createKoTestContext parameter when unmanaged associations is supported.
|
|||
|
var OrderDetail = function (data) {
|
|||
|
this.Id = ko.observable(data.Id);
|
|||
|
this.Name = ko.observable(data.Name);
|
|||
|
this.Order = ko.observable();
|
|||
|
this.OrderId = manageAssociations ? ko.observable(data.OrderId) : ko.computed(function () {
|
|||
|
return this.Order() ? this.Order().Id() : data.OrderId;
|
|||
|
}, this);
|
|||
|
this.Extra = ko.observable("extra");
|
|||
|
};
|
|||
|
var Order = function (data) {
|
|||
|
this.Id = ko.observable(data.Id);
|
|||
|
this.Name = ko.observable(data.Name);
|
|||
|
this.OrderDetails = ko.observableArray(ko.utils.arrayMap(data.OrderDetails, function (od) { return new OrderDetail(od); }));
|
|||
|
this.Extra = ko.observable("extra");
|
|||
|
};
|
|||
|
|
|||
|
var orders = [],
|
|||
|
order = {
|
|||
|
Id: 1,
|
|||
|
Name: "Order 1",
|
|||
|
OrderDetails: []
|
|||
|
};
|
|||
|
orders.push(new Order(order));
|
|||
|
orders.push(new Order({
|
|||
|
Id: 2,
|
|||
|
Name: "Order 2",
|
|||
|
OrderDetails: []
|
|||
|
}));
|
|||
|
var orderDetails = [];
|
|||
|
orderDetails.push(new OrderDetail({
|
|||
|
Id: 1,
|
|||
|
Name: "Order Detail 1",
|
|||
|
OrderId: order.Id
|
|||
|
}));
|
|||
|
orderDetails.push(new OrderDetail({
|
|||
|
Id: 2,
|
|||
|
Name: "Order Detail 2",
|
|||
|
OrderId: order.Id
|
|||
|
}));
|
|||
|
orders[0].OrderDetails(orderDetails);
|
|||
|
|
|||
|
// mock out enough metadata to do the attach
|
|||
|
upshot.metadata("Order", { key: ["Id"], fields: { Id: { type: "Int32:#System" }, Name: { type: "String:#System" }, OrderDetails: { type: "OrderDetail", association: { Name: "O_OD", isForeignKey: false, thisKey: ["Id"], otherKey: ["OrderId"] }, array: true } } });
|
|||
|
upshot.metadata("OrderDetail", { key: ["Id"], fields: { Id: { type: "Int32:#System" }, Name: { type: "String:#System" }, Order: { type: "Order", association: { Name: "O_OD", isForeignKey: true, thisKey: ["OrderId"], otherKey: ["Id"]} }, OrderId: { type: "Int32:#System"}} });
|
|||
|
|
|||
|
dc.merge(orders, "Order", null);
|
|||
|
|
|||
|
return dc;
|
|||
|
}
|
|||
|
})(upshot, ko);
|