dojo.provide("tests.data.ItemFileWriteStore"); dojo.require("tests.data.readOnlyItemFileTestTemplates"); dojo.require("dojo.data.ItemFileWriteStore"); dojo.require("dojo.data.api.Read"); dojo.require("dojo.data.api.Identity"); dojo.require("dojo.data.api.Write"); dojo.require("dojo.data.api.Notification"); // First, make sure ItemFileWriteStore can still pass all the same unit tests // that we use for its superclass, ItemFileReadStore: tests.data.readOnlyItemFileTestTemplates.registerTestsForDatastore("dojo.data.ItemFileWriteStore"); tests.data.ItemFileWriteStore.getTestData = function(name){ var data = {}; if(name === "reference_integrity"){ if(dojo.isBrowser){ data = {url: dojo.moduleUrl("tests", "data/reference_integrity.json").toString() }; }else{ data = { data: { "identifier": "id", "label": "name", "items": [ {"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}, {"id": 3, "name": "Item 3"}, {"id": 4, "name": "Item 4"}, {"id": 5, "name": "Item 5"}, {"id": 6, "name": "Item 6"}, {"id": 7, "name": "Item 7"}, {"id": 8, "name": "Item 8"}, {"id": 9, "name": "Item 9"}, {"id": 10, "name": "Item 10", "friends": [{"_reference": 1},{"_reference": 3},{"_reference": 5}]}, {"id": 11, "name": "Item 11", "friends": [{"_reference": 10}], "siblings": [{"_reference": 10}]}, {"id": 12, "name": "Item 12", "friends": [{"_reference": 3},{"_reference": 7}], "enemies": [{"_reference": 10}]}, {"id": 13, "name": "Item 13", "friends": [{"_reference": 10}]}, {"id": 14, "name": "Item 14", "friends": [{"_reference": 11}]}, {"id": 15, "name": "item 15", "friends": [{"id": 16, "name": "Item 16"}]} ] } }; } } return data; }; // Now run some tests that are specific to the write-access features: doh.register("tests.data.ItemFileWriteStore", [ function test_getFeatures(){ // summary: // Simple test of the getFeatures function of the store // description: // Simple test of the getFeatures function of the store var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var features = store.getFeatures(); // make sure we have the expected features: doh.assertTrue(features["dojo.data.api.Read"] !== null); doh.assertTrue(features["dojo.data.api.Identity"] !== null); doh.assertTrue(features["dojo.data.api.Write"] !== null); doh.assertTrue(features["dojo.data.api.Notification"] !== null); doh.assertFalse(features["iggy"]); // and only the expected features: var count = 0; for(var i in features){ doh.assertTrue((i === "dojo.data.api.Read" || i === "dojo.data.api.Identity" || i === "dojo.data.api.Write" || i === "dojo.data.api.Notification")); count++; } doh.assertEqual(count, 4); }, function testWriteAPI_setValue(){ // summary: // Simple test of the setValue API // description: // Simple test of the setValue API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); var onComplete = function(items, request){ doh.assertEqual(1, items.length); var item = items[0]; doh.assertTrue(store.containsValue(item, "capital", "Cairo")); // FIXME: // Okay, so this seems very odd. Maybe I'm just being dense. // These tests works: doh.assertEqual(store.isDirty(item), false); doh.assertTrue(store.isDirty(item) === false); // But these seemingly equivalent tests will not work: // doh.assertFalse(store.isDirty(item)); // doh.assertTrue(!(store.isDirty(item))); // // All of which seems especially weird, given that this *does* work: doh.assertFalse(store.isDirty()); doh.assertTrue(store.isDirty(item) === false); doh.assertTrue(!store.isDirty()); store.setValue(item, "capital", "New Cairo"); doh.assertTrue(store.isDirty(item)); doh.assertTrue(store.isDirty()); doh.assertEqual(store.getValue(item, "capital").toString(), "New Cairo"); deferred.callback(true); }; var onError = function(error, request){ deferred.errback(error); }; store.fetch({query:{name:"Egypt"}, onComplete: onComplete, onError: onError}); return deferred; //Object }, function testWriteAPI_setValues(){ // summary: // Simple test of the setValues API // description: // Simple test of the setValues API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); function onComplete(items, request){ doh.assertEqual(1, items.length); var item = items[0]; doh.assertTrue(store.containsValue(item, "name", "Egypt")); doh.assertTrue(store.isDirty(item) === false); doh.assertTrue(!store.isDirty()); store.setValues(item, "name", ["Egypt 1", "Egypt 2"]); doh.assertTrue(store.isDirty(item)); doh.assertTrue(store.isDirty()); var values = store.getValues(item, "name"); doh.assertTrue(values[0] == "Egypt 1"); doh.assertTrue(values[1] == "Egypt 2"); deferred.callback(true); } function onError(error, request){ deferred.errback(error); } store.fetch({query:{name:"Egypt"}, onComplete: onComplete, onError: onError}); return deferred; //Object }, function testWriteAPI_unsetAttribute(){ // summary: // Simple test of the unsetAttribute API // description: // Simple test of the unsetAttribute API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); function onComplete(items, request) { doh.assertEqual(1, items.length); var item = items[0]; doh.assertTrue(store.containsValue(item, "name", "Egypt")); doh.assertTrue(store.isDirty(item) === false); doh.assertTrue(!store.isDirty()); store.unsetAttribute(item, "name"); doh.assertTrue(store.isDirty(item)); doh.assertTrue(store.isDirty()); doh.assertTrue(!store.hasAttribute(item, "name")); deferred.callback(true); } function onError(error, request) { deferred.errback(error); } store.fetch({query:{name:"Egypt"}, onComplete: onComplete, onError: onError}); return deferred; //Object }, function testWriteAPI_newItem(){ // summary: // Simple test of the newItem API // description: // Simple test of the newItem API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); doh.assertTrue(!store.isDirty()); var onNewInvoked = false; store.onNew = function(newItem, parentInfo){ doh.assertTrue(newItem !== null); doh.assertTrue(parentInfo === null); doh.assertTrue(store.isItem(newItem)); onNewInvoked = true; }; var canada = store.newItem({name: "Canada", abbr:"ca", capital:"Ottawa"}); doh.assertTrue(onNewInvoked); doh.assertTrue(store.isDirty(canada)); doh.assertTrue(store.isDirty()); doh.assertTrue(store.getValues(canada, "name") == "Canada"); function onComplete(items, request){ doh.assertEqual(1, items.length); var item = items[0]; doh.assertTrue(store.containsValue(item, "name", "Canada")); deferred.callback(true); } function onError(error, request){ deferred.errback(error); } store.fetch({query:{name:"Canada"}, onComplete: onComplete, onError: onError}); return deferred; //Object }, function testWriteAPI_newItem_withParent(){ // summary: // Simple test of the newItem API with a parent assignment // description: // Simple test of the newItem API with a parent assignment var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); doh.assertTrue(!store.isDirty()); var onError = function(error, request){ deferred.errback(error); }; var onComplete = function(items, request){ doh.assertEqual(1, items.length); var item = items[0]; doh.assertTrue(store.containsValue(item, "name", "Egypt")); //Attach an onNew to validate we get expected values. var onNewInvoked = false; store.onNew = function(newItem, parentInfo){ doh.assertEqual(item, parentInfo.item); doh.assertEqual("cities", parentInfo.attribute); doh.assertTrue(parentInfo.oldValue === undefined); doh.assertTrue(parentInfo.newValue === newItem); onNewInvoked = true; }; //Attach an onSet and verify onSet is NOT called in this case. store.onSet = function(item, attribute, oldValue, newValue){ doh.assertTrue(false); }; //See if we can add in a new item representing the city of Cairo. //This should also call the onNew set above.... var newItem = store.newItem({name: "Cairo", abbr: "Cairo"}, {parent: item, attribute: "cities"}); doh.assertTrue(onNewInvoked); function onCompleteNewItemShallow(items, request){ doh.assertEqual(0, items.length); function onCompleteNewItemDeep(items, request){ doh.assertEqual(1, items.length); var item = items[0]; doh.assertEqual("Cairo", store.getValue(item, "name")); deferred.callback(true); } //Do a deep search now, should find the new item of the city with name attribute Cairo. store.fetch({query:{name:"Cairo"}, onComplete: onCompleteNewItemDeep, onError: onError, queryOptions: {deep:true}}); } //Do a shallow search first, should find nothing. store.fetch({query:{name:"Cairo"}, onComplete: onCompleteNewItemShallow, onError: onError}); }; store.fetch({query:{name:"Egypt"}, onComplete: onComplete, onError: onError}); return deferred; //Object }, function testWriteAPI_newItem_multiple_withParent(){ // summary: // Simple test of the newItem API with a parent assignment multiple times. // description: // Simple test of the newItem API with a parent assignment multiple times. var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); doh.assertTrue(!store.isDirty()); function onComplete(items, request){ doh.assertEqual(1, items.length); var item = items[0]; doh.assertTrue(store.containsValue(item, "name", "Egypt")); //Attach an onNew to validate we get expected values. store.onNew = function(newItem, parentInfo){ doh.assertEqual(item, parentInfo.item); doh.assertEqual("cities", parentInfo.attribute); doh.assertTrue(parentInfo.oldValue === undefined); doh.assertTrue(parentInfo.newValue === newItem); }; //See if we can add in a new item representing the city of Cairo. //This should also call the onNew set above.... var newItem1 = store.newItem({name: "Cairo", abbr: "Cairo"}, {parent: item, attribute: "cities"}); //Attach a new onNew to validate we get expected values. store.onNew = function(newItem, parentInfo){ doh.assertEqual(item, parentInfo.item); doh.assertEqual("cities", parentInfo.attribute); console.log(parentInfo.oldValue); doh.assertTrue(parentInfo.oldValue == newItem1); doh.assertTrue(parentInfo.newValue[0] == newItem1); doh.assertTrue(parentInfo.newValue[1] == newItem); }; var newItem2 = store.newItem({name: "Banha", abbr: "Banha"}, {parent: item, attribute: "cities"}); //Attach a new onNew to validate we get expected values. store.onNew = function(newItem, parentInfo){ doh.assertEqual(item, parentInfo.item); doh.assertEqual("cities", parentInfo.attribute); doh.assertTrue(parentInfo.oldValue[0] == newItem1); doh.assertTrue(parentInfo.oldValue[1] == newItem2); doh.assertTrue(parentInfo.newValue[0] == newItem1); doh.assertTrue(parentInfo.newValue[1] == newItem2); doh.assertTrue(parentInfo.newValue[2] == newItem); }; var newItem3 = store.newItem({name: "Damanhur", abbr: "Damanhur"}, {parent: item, attribute: "cities"}); deferred.callback(true); } function onError(error, request){ deferred.errback(error); } store.fetch({query:{name:"Egypt"}, onComplete: onComplete, onError: onError}); return deferred; //Object }, function testWriteAPI_deleteItem(){ // summary: // Simple test of the deleteItem API // description: // Simple test of the deleteItem API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); var onError = function(error, request){ deferred.errback(error); }; var onComplete = function(items, request){ doh.assertEqual(1, items.length); var item = items[0]; doh.assertTrue(store.containsValue(item, "name", "Egypt")); doh.assertTrue(store.isDirty(item) === false); doh.assertTrue(!store.isDirty()); store.deleteItem(item); doh.assertTrue(store.isDirty(item)); doh.assertTrue(store.isDirty()); var onCompleteToo = function(itemsToo, requestToo) { doh.assertEqual(0, itemsToo.length); deferred.callback(true); }; store.fetch({query:{name:"Egypt"}, onComplete: onCompleteToo, onError: onError}); }; store.fetch({query:{name:"Egypt"}, onComplete: onComplete, onError: onError}); return deferred; //Object }, function testWriteAPI_isDirty(){ // summary: // Simple test of the isDirty API // description: // Simple test of the isDirty API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); function onComplete(items, request) { doh.assertEqual(1, items.length); var item = items[0]; doh.assertTrue(store.containsValue(item, "name", "Egypt")); store.setValue(item, "name", "Egypt 2"); doh.assertTrue(store.getValue(item, "name") == "Egypt 2"); doh.assertTrue(store.isDirty(item)); deferred.callback(true); } function onError(error, request) { deferred.errback(error); } store.fetch({query:{name:"Egypt"}, onComplete: onComplete, onError: onError}); return deferred; //Object }, function testWriteAPI_revert(){ // summary: // Simple test of the revert API // description: // Simple test of the revert API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); var onError = function(error, request){ deferred.errback(error); }; var onComplete = function(items, request) { doh.assertEqual(1, items.length); var item = items[0]; doh.assertTrue(store.containsValue(item, "name", "Egypt")); doh.assertTrue(store.isDirty(item) === false); doh.assertTrue(!store.isDirty()); store.setValue(item, "name", "Egypt 2"); doh.assertTrue(store.getValue(item, "name") == "Egypt 2"); doh.assertTrue(store.isDirty(item)); doh.assertTrue(store.isDirty()); store.revert(); //Fetch again to see if it reset the state. var onCompleteToo = function(itemsToo, requestToo){ doh.assertEqual(1, itemsToo.length); var itemToo = itemsToo[0]; doh.assertTrue(store.containsValue(itemToo, "name", "Egypt")); deferred.callback(true); }; store.fetch({query:{name:"Egypt"}, onComplete: onCompleteToo, onError: onError}); }; store.fetch({query:{name:"Egypt"}, onComplete: onComplete, onError: onError}); return deferred; //Object }, function testWriteAPI_save(){ // summary: // Simple test of the save API // description: // Simple test of the save API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); function onError(error){ deferred.errback(error); } function onItem(item){ store.setValue(item, "capital", "New Cairo"); function onComplete() { deferred.callback(true); } store.save({onComplete:onComplete, onError:onError}); } store.fetchItemByIdentity({identity:"eg", onItem:onItem, onError:onError}); return deferred; //Object }, function testWriteAPI_saveVerifyState(){ // summary: // Simple test of the save API // description: // Simple test of the save API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); function onError(error){ deferred.errback(error); } function onItem(item){ store.setValue(item, "capital", "New Cairo"); function onComplete() { //Check internal state. Note: Users should NOT do this, this is a UT verification //of internals in this case. Ref tracker: #4394 doh.assertTrue(!store._saveInProgress); deferred.callback(true); } store.save({onComplete:onComplete, onError:onError}); } store.fetchItemByIdentity({identity:"eg", onItem:onItem, onError:onError}); return deferred; //Object }, function testWriteAPI_saveEverything(){ // summary: // Simple test of the save API // description: // Simple test of the save API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var egypt; var deferred = new doh.Deferred(); var onError = function(error){ deferred.errback(error); }; store._saveEverything = function(saveCompleteCallback, saveFailedCallback, newFileContentString){ var struct = dojo.fromJson(newFileContentString); doh.assertEqual(struct.identifier, store.getIdentityAttributes(egypt)[0]); doh.assertEqual(struct.label, store.getLabelAttributes(egypt)[0]); doh.assertEqual(struct.items.length, 7); var cloneStore = new dojo.data.ItemFileWriteStore({data:struct}); var onItemClone = function(itemClone){ var egyptClone = itemClone; doh.assertEqual(store.getIdentityAttributes(egypt)[0], cloneStore.getIdentityAttributes(egyptClone)[0]); doh.assertEqual(store.getLabelAttributes(egypt)[0], cloneStore.getLabelAttributes(egyptClone)[0]); doh.assertEqual(store.getValue(egypt, "name"), cloneStore.getValue(egyptClone, "name")); }; cloneStore.fetchItemByIdentity({identity:"eg", onItem:onItemClone, onError:onError}); saveCompleteCallback(); }; var onItem = function(item){ egypt = item; var onComplete = function() { deferred.callback(true); }; store.setValue(egypt, "capital", "New Cairo"); store.save({onComplete:onComplete, onError:onError}); }; store.fetchItemByIdentity({identity:"eg", onItem:onItem, onError:onError}); return deferred; //Object }, function testWriteAPI_saveEverything_withDateType(){ // summary: // Simple test of the save API with a non-atomic type (Date) that has a type mapping. // description: // Simple test of the save API with a non-atomic type (Date) that has a type mapping. var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); store._saveEverything = function(saveCompleteCallback, saveFailedCallback, newFileContentString){ //Now load the new data into a datastore and validate that it stored the date right. var dataset = dojo.fromJson(newFileContentString); var newStore = new dojo.data.ItemFileWriteStore({data: dataset}); function gotItem(item){ var independenceDate = newStore.getValue(item,"independence"); doh.assertTrue(independenceDate instanceof Date); doh.assertTrue(dojo.date.compare(new Date(1993,4,24), independenceDate, "date") === 0); saveCompleteCallback(); } function failed(error, request){ deferred.errback(error); saveFailedCallback(); } newStore.fetchItemByIdentity({identity:"eg", onItem:gotItem, onError:failed}); }; var onError = function(error){ deferred.errback(error); }; var onItem = function(item){ var onComplete = function() { deferred.callback(true); }; store.setValue(item, "independence", new Date(1993,4,24)); store.save({onComplete:onComplete, onError:onError}); }; store.fetchItemByIdentity({identity:"eg", onItem:onItem, onError:onError}); return deferred; //Object }, function testWriteAPI_saveEverything_withCustomColorTypeSimple(){ // summary: // Simple test of the save API with a non-atomic type (dojo.Color) that has a type mapping. // description: // Simple test of the save API with a non-atomic type (dojo.Color) that has a type mapping. //Set up the store basics: What data it has, and what to do when save is called for saveEverything //And how to map the 'Color' type in and out of the format. //(Test of saving all to a some location...) var dataset = { identifier:'name', items: [ { name:'Kermit', species:'frog', color:{_type:'Color', _value:'green'} }, { name:'Beaker', hairColor:{_type:'Color', _value:'red'} } ] }; var customTypeMap = {'Color': dojo.Color }; var store = new dojo.data.ItemFileWriteStore({ data:dataset, typeMap: customTypeMap }); var deferred = new doh.Deferred(); store._saveEverything = function(saveCompleteCallback, saveFailedCallback, newFileContentString){ //Now load the new data into a datastore and validate that it stored the Color right. var dataset = dojo.fromJson(newFileContentString); var newStore = new dojo.data.ItemFileWriteStore({data: dataset, typeMap: customTypeMap}); var deferred = new doh.Deferred(); function gotItem(item){ var hairColor = newStore.getValue(item,"hairColor"); doh.assertTrue(hairColor instanceof dojo.Color); doh.assertEqual("rgba(255, 255, 0, 1)", hairColor.toString()); saveCompleteCallback(); } function failed(error, request){ deferred.errback(error); saveFailedCallback(); } newStore.fetchItemByIdentity({identity:"Animal", onItem:gotItem, onError:failed}); }; //Add a new item with a color type, then save it. var onError = function(error){ deferred.errback(error); }; var onComplete = function() { deferred.callback(true); }; var animal = store.newItem({name: "Animal", hairColor: new dojo.Color("yellow")}); store.save({onComplete:onComplete, onError:onError}); return deferred; //Object }, function testWriteAPI_saveEverything_withCustomColorTypeGeneral(){ // summary: // Simple test of the save API with a non-atomic type (dojo.Color) that has a type mapping. // description: // Simple test of the save API with a non-atomic type (dojo.Color) that has a type mapping. //Set up the store basics: What data it has, and what to do when save is called for saveEverything //And how to map the 'Color' type in and out of the format. //(Test of saving all to a some location...) var dataset = { identifier:'name', items: [ { name:'Kermit', species:'frog', color:{_type:'Color', _value:'green'} }, { name:'Beaker', hairColor:{_type:'Color', _value:'red'} } ] }; var customTypeMap = {'Color': { type: dojo.Color, deserialize: function(value){ return new dojo.Color(value); }, serialize: function(obj){ return obj.toString(); } } }; var store = new dojo.data.ItemFileWriteStore({ data:dataset, typeMap: customTypeMap }); var deferred = new doh.Deferred(); store._saveEverything = function(saveCompleteCallback, saveFailedCallback, newFileContentString){ //Now load the new data into a datastore and validate that it stored the Color right. var dataset = dojo.fromJson(newFileContentString); var newStore = new dojo.data.ItemFileWriteStore({data: dataset, typeMap: customTypeMap}); var gotItem = function(item){ var hairColor = newStore.getValue(item,"hairColor"); doh.assertTrue(hairColor instanceof dojo.Color); doh.assertEqual("rgba(255, 255, 0, 1)", hairColor.toString()); saveCompleteCallback(); }; var failed = function(error, request){ deferred.errback(error); saveFailedCallback(); }; newStore.fetchItemByIdentity({identity:"Animal", onItem:gotItem, onError:failed}); }; //Add a new item with a color type, then save it. var onError = function(error){ deferred.errback(error); }; var onComplete = function() { deferred.callback(true); }; var animal = store.newItem({name: "Animal", hairColor: new dojo.Color("yellow")}); store.save({onComplete:onComplete, onError:onError}); return deferred; //Object }, function testWriteAPI_newItem_revert(){ // summary: // Test for bug #5357. Ensure that the revert properly nulls the identity position // for a new item after revert. var args = {data: { label:"name", items:[ {name:'Ecuador', capital:'Quito'}, {name:'Egypt', capital:'Cairo'}, {name:'El Salvador', capital:'San Salvador'}, {name:'Equatorial Guinea', capital:'Malabo'}, {name:'Eritrea', capital:'Asmara'}, {name:'Estonia', capital:'Tallinn'}, {name:'Ethiopia', capital:'Addis Ababa'} ] } }; var store = new dojo.data.ItemFileWriteStore(args); var newCountry = store.newItem({name: "Utopia", capitol: "Perfect"}); //DO NOT ACCESS THIS WAY. THESE ARE INTERNAL VARIABLES. DOING THIS FOR TEST PURPOSES. var itemEntryNum = newCountry[store._itemNumPropName]; doh.assertTrue(store._arrayOfAllItems[itemEntryNum] === newCountry); store.revert(); doh.assertTrue(store._arrayOfAllItems[itemEntryNum] === null); }, function testNotificationAPI_onSet(){ // summary: // Simple test of the onSet API // description: // Simple test of the onSet API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); function onError(error){ deferred.errback(error); } function onItem(fetchedItem){ var egypt = fetchedItem; var connectHandle = null; function setValueHandler(item, attribute, oldValue, newValue){ doh.assertTrue(store.isItem(item)); doh.assertTrue(item == egypt); doh.assertTrue(attribute == "capital"); doh.assertTrue(oldValue == "Cairo"); doh.assertTrue(newValue == "New Cairo"); deferred.callback(true); dojo.disconnect(connectHandle); } connectHandle = dojo.connect(store, "onSet", setValueHandler); store.setValue(egypt, "capital", "New Cairo"); } store.fetchItemByIdentity({identity:"eg", onItem:onItem, onError:onError}); }, function testNotificationAPI_onNew(){ // summary: // Simple test of the onNew API // description: // Simple test of the onNew API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); var connectHandle = null; function newItemHandler(item){ doh.assertTrue(store.isItem(item)); doh.assertTrue(store.getValue(item, "name") == "Canada"); deferred.callback(true); dojo.disconnect(connectHandle); } connectHandle = dojo.connect(store, "onNew", newItemHandler); var canada = store.newItem({name:"Canada", abbr:"ca", capital:"Ottawa"}); }, function testNotificationAPI_onDelete(){ // summary: // Simple test of the onDelete API // description: // Simple test of the onDelete API var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var deferred = new doh.Deferred(); function onError(error){ deferred.errback(error); } function onItem(fetchedItem){ var egypt = fetchedItem; var connectHandle = null; function deleteItemHandler(item){ doh.assertTrue(store.isItem(item) === false); doh.assertTrue(item == egypt); deferred.callback(true); dojo.disconnect(connectHandle); } connectHandle = dojo.connect(store, "onDelete", deleteItemHandler); store.deleteItem(egypt); } store.fetchItemByIdentity({identity:"eg", onItem:onItem, onError:onError}); }, function testReadAPI_functionConformanceToo(){ // summary: // Simple test read API conformance. Checks to see all declared functions are actual functions on the instances. // description: // Simple test read API conformance. Checks to see all declared functions are actual functions on the instances. var testStore = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var readApi = new dojo.data.api.Read(); var passed = true; for(var functionName in readApi){ var member = readApi[functionName]; //Check that all the 'Read' defined functions exist on the test store. if(typeof member === "function"){ var testStoreMember = testStore[functionName]; if(!(typeof testStoreMember === "function")){ passed = false; break; } } } doh.assertTrue(passed); }, function testWriteAPI_functionConformance(){ // summary: // Simple test write API conformance. Checks to see all declared functions are actual functions on the instances. // description: // Simple test write API conformance. Checks to see all declared functions are actual functions on the instances. var testStore = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var writeApi = new dojo.data.api.Write(); var passed = true; for(var functionName in writeApi){ var member = writeApi[functionName]; //Check that all the 'Write' defined functions exist on the test store. if(typeof member === "function"){ var testStoreMember = testStore[functionName]; if(!(typeof testStoreMember === "function")){ passed = false; break; } } } doh.assertTrue(passed); }, function testNotificationAPI_functionConformance(){ // summary: // Simple test Notification API conformance. Checks to see all declared functions are actual functions on the instances. // description: // Simple test Notification API conformance. Checks to see all declared functions are actual functions on the instances. var testStore = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries")); var api = new dojo.data.api.Notification(); var passed = true; for(var functionName in api){ var member = api[functionName]; //Check that all the 'Write' defined functions exist on the test store. if(typeof member === "function"){ var testStoreMember = testStore[functionName]; if(!(typeof testStoreMember === "function")){ passed = false; break; } } } doh.assertTrue(passed); }, function testIdentityAPI_noIdentifierSpecified(){ // summary: // Test for bug #3873. Given a datafile that does not specify an // identifier, make sure ItemFileWriteStore auto-creates identities // that are unique even after calls to deleteItem() and newItem() var args = {data: { label:"name", items:[ {name:'Ecuador', capital:'Quito'}, {name:'Egypt', capital:'Cairo'}, {name:'El Salvador', capital:'San Salvador'}, {name:'Equatorial Guinea', capital:'Malabo'}, {name:'Eritrea', capital:'Asmara'}, {name:'Estonia', capital:'Tallinn'}, {name:'Ethiopia', capital:'Addis Ababa'} ] } }; var store = new dojo.data.ItemFileWriteStore(args); var deferred = new doh.Deferred(); var onError = function(error, request){ deferred.errback(error); }; var onComplete = function(items, request){ doh.assertEqual(7, items.length); var lastItem = items[(items.length - 1)]; var idOfLastItem = store.getIdentity(lastItem); store.deleteItem(lastItem); store.newItem({name:'Canada', capital:'Ottawa'}); var onCompleteAgain = function(itemsAgain, requestAgain){ doh.assertEqual(7, itemsAgain.length); var identitiesInUse = {}; for(var i = 0; i < itemsAgain.length; ++i){ var item = itemsAgain[i]; var id = store.getIdentity(item); if(identitiesInUse.hasOwnProperty(id)){ // there should not already be an entry for this id doh.assertTrue(false); }else{ // we want to add the entry now identitiesInUse[id] = item; } } deferred.callback(true); }; store.fetch({onComplete:onCompleteAgain, onError:onError}); }; store.fetch({onComplete:onComplete, onError:onError}); return deferred; }, function testIdentityAPI_noIdentifierSpecified_revert(){ // summary: // Test for bug #4691 Given a datafile that does not specify an // identifier, make sure ItemFileWriteStore auto-creates identities // that are unique even after calls to deleteItem() and newItem() var args = {data: { label:"name", items:[ {name:'Ecuador', capital:'Quito'}, {name:'Egypt', capital:'Cairo'}, {name:'El Salvador', capital:'San Salvador'}, {name:'Equatorial Guinea', capital:'Malabo'}, {name:'Eritrea', capital:'Asmara'}, {name:'Estonia', capital:'Tallinn'}, {name:'Ethiopia', capital:'Addis Ababa'} ] } }; var store = new dojo.data.ItemFileWriteStore(args); var deferred = new doh.Deferred(); var onError = function(error, request){ deferred.errback(error); }; var onComplete = function(items, request){ doh.assertEqual(7, items.length); var lastItem = items[(items.length - 1)]; var idOfLastItem = store.getIdentity(lastItem); store.deleteItem(lastItem); store.newItem({name:'Canada', capital:'Ottawa'}); var onCompleteAgain = function(itemsAgain, requestAgain){ doh.assertEqual(7, itemsAgain.length); var identitiesInUse = {}; for(var i = 0; i < itemsAgain.length; ++i){ var item = itemsAgain[i]; var id = store.getIdentity(item); if(identitiesInUse.hasOwnProperty(id)){ // there should not already be an entry for this id doh.assertTrue(false); }else{ // we want to add the entry now identitiesInUse[id] = item; } } //Last test, revert everything and check item sizes. store.revert(); //Now call fetch again and verify store state. var revertComplete = function(itemsReverted, request){ doh.assertEqual(7, itemsReverted.length); deferred.callback(true); }; store.fetch({onComplete:revertComplete, onError:onError}); }; store.fetch({onComplete:onCompleteAgain, onError:onError}); }; store.fetch({onComplete:onComplete, onError:onError}); return deferred; }, function testReferenceIntegrity_checkReferences(){ // summary: // Simple test to verify the references were properly resolved. // description: // Simple test to verify the references were properly resolved. var store = new dojo.data.ItemFileWriteStore(tests.data.ItemFileWriteStore.getTestData("reference_integrity")); var deferred = new doh.Deferred(); var onError = function(error, request){ deferred.errback(error); }; var onComplete = function(items, request){ var item10 = null; var item1 = null; var item3 = null; var item5 = null; var i; for (i = 0; i < items.length; i++) { var ident = store.getIdentity(items[i]); if (ident === 10) { item10 = items[i]; }else if (ident === 1) { item1 = items[i]; }else if (ident === 3) { item3 = items[i]; }else if (ident === 5) { item5 = items[i]; } } var friends = store.getValues(item10, "friends"); doh.assertTrue(friends !== null); doh.assertTrue(friends !== undefined); doh.assertTrue(store.isItem(item10)); doh.assertTrue(store.isItem(item1)); doh.assertTrue(store.isItem(item3)); doh.assertTrue(store.isItem(item5)); var found = 0; try{ for (i = 0; i < friends.length; i++) { if (i === 0) { doh.assertTrue(store.isItem(friends[i])); doh.assertEqual(friends[i], item1); doh.assertEqual(store.getIdentity(friends[i]), 1); found++; }else if (i === 1) { doh.assertTrue(store.isItem(friends[i])); doh.assertEqual(friends[i], item3); doh.assertEqual(store.getIdentity(friends[i]), 3); found++; }else if (i === 2) { doh.assertTrue(store.isItem(friends[i])); doh.assertEqual(friends[i], item5); doh.assertEqual(store.getIdentity(friends[i]), 5); found++; } } }catch(e){ doh.errback(e); } doh.assertEqual(3, found); deferred.callback(true); }; store.fetch({onError: onError, onComplete: onComplete}); return deferred; }, function testReferenceIntegrity_deleteReferencedItem(){ // summary: // Simple test to verify the references were properly deleted. // description: // Simple test to verify the references were properly deleted. var store = new dojo.data.ItemFileWriteStore(tests.data.ItemFileWriteStore.getTestData("reference_integrity")); var deferred = new doh.Deferred(); var passed = true; function onError(error, request){ deferred.errback(error); } function onItem(item, request){ try{ console.log("Before delete map state is: " + dojo.toJson(item[store._reverseRefMap])); store.deleteItem(item); console.log("After delete map state is: " + dojo.toJson(item[store._reverseRefMap])); function verifyRefDelete(items, request){ var passed = true; for(var i = 0; i < items.length; i++){ var curItem = items[i]; var attributes = store.getAttributes(curItem); for(var j = 0; j < attributes.length; j++){ var values = store.getValues(curItem, attributes[j]); var badRef = false; for(var k = 0; k < values.length; k++){ var value = values[k]; try{ var id = store.getIdentity(value); if(id == 10){ badRef = true; break; } }catch(e){/*Not an item, even a dead one, just eat it.*/} } if(badRef){ deferred.errback(new Error("Found a reference remaining to a deleted item. Failure.")); passed = false; break; } } } if(passed){ deferred.callback(true); } } store.fetch({onComplete: verifyRefDelete, onError: onError}); }catch(error){ deferred.errback(error); } } store.fetchItemByIdentity({identity: 10, onError: onError, onItem: onItem}); return deferred; }, function testReferenceIntegrity_deleteReferencedItemThenRevert(){ // summary: // Simple test to verify the references were properly deleted. // description: // Simple test to verify the references were properly deleted. var store = new dojo.data.ItemFileWriteStore(tests.data.ItemFileWriteStore.getTestData("reference_integrity")); var deferred = new doh.Deferred(); var passed = true; function onError(error, request){ deferred.errback(error); } function onItem(item, request){ try{ //DO NOT EVER ACCESS THESE VARIABLES LIKE THIS! //THIS IS FOR TESTING INTERNAL STATE! console.log("Map before delete:"); store._dumpReferenceMap(); var beforeDelete = dojo.toJson(item[store._reverseRefMap]); store.deleteItem(item); console.log("Map after delete:"); store._dumpReferenceMap(); var afterDelete = dojo.toJson(item[store._reverseRefMap]); store.revert(); console.log("Map after revert:"); store._dumpReferenceMap(); var afterRevert = dojo.toJson(item[store._reverseRefMap]); doh.assertTrue(afterRevert === beforeDelete); }catch(e){ deferred.errback(e); passed = false; } if(passed){ deferred.callback(true); } } store.fetchItemByIdentity({identity: 10, onError: onError, onItem: onItem}); return deferred; }, function testReferenceIntegrity_deleteMultipleItemsWithReferencesAndRevert(){ // summary: // Simple test to verify that a flow of deleting items with references and reverting does not damage the internal structure. // Created for tracker bug: #5743 // description: // Simple test to verify that a flow of deleting items with references and reverting does not damage the internal structure. // Created for tracker bug: #5743 var store = new dojo.data.ItemFileWriteStore(tests.data.readOnlyItemFileTestTemplates.getTestData("countries_references")); var deferred = new doh.Deferred(); var passed = true; function onError(error, request){ deferred.errback(error); doh.assertTrue(false); } function onItem(item, request){ //Save off the located item, then locate another one (peer to Egypt) doh.assertTrue(store.isItem(item)); var egypt = item; function onItem2(item, request){ doh.assertTrue(store.isItem(item)); var nairobi = item; //Delete them store.deleteItem(egypt); store.deleteItem(nairobi); try{ //Revert, then do a fetch. If the internals have been damaged, this will generally //cause onError to fire instead of onComplete. store.revert(); function onComplete(items, request){ deferred.callback(true); } store.fetch({query: {name: "*"}, start: 0, count: 20, onComplete: onComplete, onError: onError}); }catch(e){ deferred.errback(e); } } store.fetchItemByIdentity({identity: "Nairobi", onError: onError, onItem: onItem2}); } store.fetchItemByIdentity({identity: "Egypt", onError: onError, onItem: onItem}); return deferred; }, function testReferenceIntegrity_removeReferenceFromAttribute(){ // summary: // Simple test to verify the reference removal updates the internal map. // description: // Simple test to verify the reference removal updates the internal map. var store = new dojo.data.ItemFileWriteStore(tests.data.ItemFileWriteStore.getTestData("reference_integrity")); var deferred = new doh.Deferred(); var passed = true; function onError(error, request){ deferred.errback(error); doh.assertTrue(false); } function onItem(item, request){ try{ store.setValues(item, "friends", [null]); function onItem2(item10, request){ //DO NOT EVER ACCESS THESE VARIABLES LIKE THIS! //THIS IS FOR TESTING INTERNAL STATE! var refMap = item10[store._reverseRefMap]; store._dumpReferenceMap(); console.log("MAP for Item 10 is: " + dojo.toJson(refMap)); //Assert there is no reference to item 10 in item 11's attribute 'friends'. doh.assertTrue(!refMap["11"]["friends"]); store.setValues(item, "siblings", [0, 1, 2]); //Assert there are no more references to 10 in 11. Ergo, "11" should be a 'undefined' attribute for the map of items referencing '10'.. doh.assertTrue(!refMap["11"]); deferred.callback(true); } store.fetchItemByIdentity({identity: 10, onError: onError, onItem: onItem2}); }catch(e){ console.debug(e); deferred.errback(e); doh.assertTrue(false); } } store.fetchItemByIdentity({identity: 11, onError: onError, onItem: onItem}); return deferred; }, function testReferenceIntegrity_deleteReferencedItemNonParent(){ // summary: // Simple test to verify the references to a non-parent item was properly deleted. // description: // Simple test to verify the references to a non-parent item was properly deleted. var store = new dojo.data.ItemFileWriteStore(tests.data.ItemFileWriteStore.getTestData("reference_integrity")); var deferred = new doh.Deferred(); var passed = true; function onError(error, request){ deferred.errback(error); } function onItem(item, request){ try{ console.log("Reference state for item 16 is: " + dojo.toJson(item[store._reverseRefMap])); store.deleteItem(item); function verifyRefDelete(items, request){ var passed = true; for(var i = 0; i < items.length; i++){ var curItem = items[i]; var attributes = store.getAttributes(curItem); for(var j = 0; j < attributes.length; j++){ var values = store.getValues(curItem, attributes[j]); var badRef = false; for(var k = 0; k < values.length; k++){ var value = values[k]; try{ var id = store.getIdentity(value); if(id == 16){ badRef = true; break; } }catch(e){/*Not an item, even a dead one, just eat it.*/} } if(badRef){ deferred.errback(new Error("Found a reference remaining to a deleted item. Failure.")); passed = false; break; } } } if(passed){ deferred.callback(true); } } store.fetch({onComplete: verifyRefDelete, onError: onError}); }catch(error){ deferred.errback(error); } } store.fetchItemByIdentity({identity: 16, onError: onError, onItem: onItem}); return deferred; }, function testReferenceIntegrity_addReferenceToAttribute(){ // summary: // Simple test to verify the reference additions can happen. // description: // Simple test to verify the reference additions can happen. var store = new dojo.data.ItemFileWriteStore(tests.data.ItemFileWriteStore.getTestData("reference_integrity")); var deferred = new doh.Deferred(); var passed = true; function onError(error, request){ deferred.errback(error); doh.assertTrue(false); } function onComplete(items, request){ doh.assertTrue(items.length > 2); var item1 = items[0]; var item2 = items[1]; //DO NOT EVER ACCESS THESE VARIABLES LIKE THIS! //THIS IS FOR TESTING INTERNAL STATE! console.log("Map state for Item 1 is: " + dojo.toJson(item1[store._reverseRefMap])); console.log("Map state for Item 2 is: " + dojo.toJson(item2[store._reverseRefMap])); store.setValue(item1, "siblings", item2); //Emit the current map state for inspection. console.log("Map state for Item 1 is: " + dojo.toJson(item1[store._reverseRefMap])); console.log("Map state for Item 2 is: " + dojo.toJson(item2[store._reverseRefMap])); doh.assertTrue(item2[store._reverseRefMap] !== null); //Assert there is a recorded reference to item 2 in item 1's attribute 'sibling'. doh.assertTrue(item2[store._reverseRefMap][store.getIdentity(item1)]["siblings"]); deferred.callback(true); } store.fetch({onError: onError, onComplete: onComplete}); return deferred; }, function testReferenceIntegrity_newItemWithParentReference(){ // summary: // Simple test to verify that newItems with a parent properly record the parent's reference in the map. // description: // Simple test to verify that newItems with a parent properly record the parent's reference in the map. var store = new dojo.data.ItemFileWriteStore(tests.data.ItemFileWriteStore.getTestData("reference_integrity")); var deferred = new doh.Deferred(); var passed = true; function onError(error, request){ deferred.errback(error); doh.assertTrue(false); } function onItem(item, request){ try{ //Create a new item and set its parent to item 10's uncle attribute. var newItem = store.newItem({id: 17, name: "Item 17"}, {parent: item, attribute: "uncles"}); //DO NOT EVER ACCESS THESE VARIABLES LIKE THIS! //THIS IS FOR TESTING INTERNAL STATE! //Look up the references to 17, as item 10 has one now on attribute 'uncles' var refs = newItem[store._reverseRefMap]; //Assert there is a reference from 10 to item 17, on attribute uncle doh.assertTrue(refs["10"]["uncles"]); console.log("State of map of item 17 after newItem: " + dojo.toJson(refs)); }catch(e){ console.debug(e); deferred.errback(e); doh.assertTrue(false); passed = false; } if(passed){ deferred.callback(true); } } store.fetchItemByIdentity({identity: 10, onError: onError, onItem: onItem}); return deferred; }, function testReferenceIntegrity_newItemWithReferenceToExistingItem(){ // summary: // Simple test to verify that a new item with references to existing items properly record the references in the map. // description: // Simple test to verify that a new item with references to existing items properly record the references in the map. var store = new dojo.data.ItemFileWriteStore(tests.data.ItemFileWriteStore.getTestData("reference_integrity")); var deferred = new doh.Deferred(); var passed = true; function onError(error, request){ deferred.errback(error); doh.assertTrue(false); } function onItem(item, request){ try{ //DO NOT EVER ACCESS THESE VARIABLES LIKE THIS! //THIS IS FOR TESTING INTERNAL STATE! console.log("State of reference map to item 10 before newItem: " + dojo.toJson(item[store._reverseRefMap])); //Create a new item and set its parent to item 10's uncle attribute. var newItem = store.newItem({id: 17, name: "Item 17", friends: [item]}); //DO NOT EVER ACCESS THESE VARIABLES LIKE THIS! //THIS IS FOR TESTING INTERNAL STATE! //Look up the references to 10, as item 17 has one on friends now. var refs = item[store._reverseRefMap]; //Assert there is a reference from 15 to item 10, on attribute friends doh.assertTrue(refs["17"]["friends"]); console.log("State of reference map to item 10 after newItem: " + dojo.toJson(refs)); }catch(e){ console.debug(e); deferred.errback(e); doh.assertTrue(false); passed = false; } if(passed){ deferred.callback(true); } } store.fetchItemByIdentity({identity: 10, onError: onError, onItem: onItem}); return deferred; }, function testReferenceIntegrity_disableReferenceIntegrity(){ // summary: // Simple test to verify reference integrity can be disabled. // description: // Simple test to verify reference integrity can be disabled. var params = tests.data.ItemFileWriteStore.getTestData("reference_integrity"); params.referenceIntegrity = false; var store = new dojo.data.ItemFileWriteStore(params); var deferred = new doh.Deferred(); function onError(error, request){ deferred.errback(error); doh.assertTrue(false); } function onItem(item, request){ //DO NOT EVER ACCESS THESE VARIABLES LIKE THIS! //THIS IS FOR TESTING INTERNAL STATE! if(item[store._reverseRefMap] === undefined){ deferred.callback(true); }else{ deferred.errback(new Error("Disabling of reference integrity failed.")); } } store.fetchItemByIdentity({identity: 10, onError: onError, onItem: onItem}); return deferred; }, function testReadAPI_close_dirty_failure(){ // summary: // Function to test the close api properly clears the store for reload when clearOnClose is set. if (dojo.isBrowser) { var params = tests.data.readOnlyItemFileTestTemplates.getTestData("countries"); params.clearOnClose = true; params.urlPreventCache = true; var store = new dojo.data.ItemFileWriteStore(params); var d = new doh.Deferred(); var onItem = function(item){ var error = null; try { doh.assertTrue(item !== null); var ec = item; var val = store.getValue(ec, "name"); doh.assertEqual("Ecuador", val); var newItem = store.newItem({abbr: "foo", name: "bar"}); //Should throw an error... store.close(); }catch (e){ error = e; } if (error === null) { d.errback(new Error("Store was dirty, should have thrown an error on close!")); }else{ d.callback(true); } }; var onError = function(errData){ d.errback(errData); }; store.fetchItemByIdentity({identity:"ec", onItem:onItem, onError:onError}); return d; // Deferred } } ] );