blob: 1a05849d8c7ea6607115f4db16b5e5c366a6e423 [file] [log] [blame]
/// <reference path="../src/datajs-store.js">
/// <reference path="../src/datajs-store-dom.js">
/// <reference path="common/djstest.js" />
// odata-tests.js
(function (window, undefined) {
var cleanDomStorage = function () {
/// <summary>Cleans all the data saved in the browser's DOM Storage.</summary>
if (window.localStorage) {
window.localStorage.clear();
}
};
var cleanMemoryStorage = function () {
/// <summary>Clean memory storage is a no op.</summary>
};
var cleanIndexedDbStorage = function () {
var stores = this.stores;
$.each(stores, function (_, store) {
store.close();
});
djstest.wait(function (done) {
djstest.cleanStoreOnIndexedDb(stores, done);
});
};
var canCreateMemoryStore = function () {
/// <summary>Checks whether memory storage is supported by the browser.</summary>
/// <returns type="Boolean">True.</summary>
return true;
};
var canCreateDomStore = function () {
/// <summary>Checks whether Web Storage (DOM Storage) is supported by the browser.</summary>
/// <returns type="Boolean">True if DOM Storage is supported by the browser; false otherwise.</summary>
return !!window.localStorage;
};
var canCreateIndexedDb = function () {
/// <summary>Checks whether Web Storage (DOM Storage) is supported by the browser.</summary>
/// <returns type="Boolean">True if IndexedDB is supported by the browser, false otherwise.</returns>
return !!djstest.indexedDB;
};
var canCreateStore = function (mechanism) {
/// <summary>Determines whether a particular mechanism is supported by the browser.</summary>
/// <param name="mechanism" type="String">Mechanism name.</param>
/// <returns type="Boolean">True if the mechanism is supported by the browser; otherwise false.</summary>
var implementation = mechanismImplementations[mechanism];
return implementation && implementation.canCreate();
}
var makeUnexpectedErrorHandler = function (fail) {
return function (err) {
djstest.fail("error: " + err.name + " -- message: " + err.message);
fail();
};
};
var testJobDone = function (succeeded) {
if (!succeeded) {
djstest.fail("Job completed but some of the functions it called failed");
}
djstest.done();
};
var mechanismImplementations = {
indexeddb: { factory: datajs.IndexedDBStore, canCreate: canCreateIndexedDb, cleanup: cleanIndexedDbStorage },
dom: { factory: datajs.DomStore, canCreate: canCreateDomStore, cleanup: cleanDomStorage },
memory: { factory: datajs.MemoryStore, canCreate: canCreateMemoryStore, cleanup: cleanMemoryStorage }
};
var oldWindowOnError;
for (var mechanism in mechanismImplementations) {
module("Unit", {
mechanism: mechanism,
createStore: function (name) {
var store = datajs.createStore(name + "_" + this.mechanism, this.mechanism);
this.stores.push(store);
return store;
},
setup: function () {
this.stores = [];
mechanismImplementations[this.mechanism].cleanup.call(this);
// FireFox 7.0.1 bubbles an error event when there is an IndexedDB error, even when the error has been handled graciously.
// This is a work around to keep QUnit from reporting false failures in IndexedDB negative tests.
if (this.mechanism === "indexeddb") {
oldWindowOnError = window.onerror;
window.onerror = null;
}
},
teardown: function () {
mechanismImplementations[this.mechanism].cleanup.call(this);
this.stores = [];
// Restore QUnit's onerror handler.
if (this.mechanism === "indexeddb") {
window.onerror = oldWindowOnError;
}
}
});
if (!canCreateStore(mechanism)) {
djstest.addTest(function (mechanism) {
djstest.expectException(function () {
mechanismImplemenatations[mechanism].factory.create("my horrible not working store");
});
djstest.done();
}, "Local storage mechanism " + mechanism + " not supported by this browser", mechanism);
} else {
djstest.addTest(function storeAddTest(mechanism) {
var tuples = [
{ key: "null", value: null },
{ key: "undefined", value: undefined },
{ key: "number", value: 12345.678 },
{ key: "string", value: "String value" },
{ key: "date", value: new Date() },
{ key: "object", value: { p1: 1234, nested: { p1: "a", p2: "b"}} },
{ key: "array", value: [1, 2, 3, 4, 5] },
{ key: "key1", value: "some value" },
{ key: "key1", value: "this should fail", error: true },
{ key: ["key", "key2"], value: ["value", "value2"], error: mechanism !== "indexeddb" },
{ key: ["key6", "key7", "key6"], value: ["value", "value2", "value3"], error: true }
];
var store = this.createStore("store1");
var job = new djstest.Job();
$.each(tuples, function (_, tuple) {
job.queue(function task(success, fail) {
var unexpectedError = makeUnexpectedErrorHandler(fail);
djstest.log("running task");
store.add(tuple.key, tuple.value,
function (key, value) {
djstest.assertAreEqual(key, tuple.key, "Keys match for " + mechanism + " - key = " + key.toString());
djstest.assertAreEqualDeep(value, tuple.value, "Values match for " + mechanism + " - key = " + key.toString());
job.queueNext(function (success, fail) {
store.read(tuple.key, function (key, value) {
djstest.assertAreEqualDeep(value, tuple.value, "Key: " + key + " is present in the store");
success();
}, makeUnexpectedErrorHandler(fail));
});
success();
},
function (err) {
if (!tuple.error) {
unexpectedError(err);
} else {
djstest.pass("error handler was called as expected");
success();
}
});
});
});
job.run(function (succeeded) {
store.close();
testJobDone(succeeded);
});
}, "Store Add Test with mechanism " + mechanism, mechanism);
djstest.addTest(function storeAddOrUpdateTest(mechanism) {
var tuples = [
{ key: "null", value: null },
{ key: "undefined", value: undefined },
{ key: "number", value: 12345.678 },
{ key: "string", value: "String value" },
{ key: "date", value: new Date() },
{ key: "object", value: { p1: 1234, nested: { p1: "a", p2: "b"}} },
{ key: "array", value: [1, 2, 3, 4, 5] },
{ key: "key1", value: "some value" },
{ key: "key1", value: "this should not fail" },
{ key: ["key", "key2", "key3"], value: ["value", "value2", "value3"], error: mechanism !== "indexeddb" },
{ key: ["key", "key2", "key3"], value: ["value4", "value5", "value6"], error: mechanism !== "indexeddb" },
{ key: "key1", value: 456 }
];
var store = this.createStore("store2");
var job = new djstest.Job();
$.each(tuples, function (_, tuple) {
job.queue(function (success, fail) {
var unexpectedError = makeUnexpectedErrorHandler(fail);
store.addOrUpdate(tuple.key, tuple.value,
function (key, value) {
djstest.assert(!tuple.error, "success should be called");
djstest.assertAreEqual(key, tuple.key, "Keys match");
djstest.assertAreEqualDeep(value, tuple.value, "Values match");
store.read(tuple.key, function (key, value) {
djstest.assertAreEqual(key, tuple.key, "Keys match");
djstest.assertAreEqualDeep(value, tuple.value, "Values match");
success();
}, unexpectedError);
},
function (err) {
if (!tuple.error) {
unexpectedError(err);
} else {
djstest.pass("error handler was called as expected");
success();
}
});
});
});
job.run(function (succeeded) {
store.close();
testJobDone(succeeded);
});
}, "Store Add or Update Test with mechanism " + mechanism, mechanism);
djstest.addTest(function storeContainsTest(mechanism) {
var store = this.createStore("store3");
var job = new djstest.Job();
job.queue(function (success, fail) {
store.add("Key1", "Some value", success, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.contains("Key1", function (contained) {
djstest.assert(contained, "Key is present in the store");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.contains("Key2", function (contained) {
djstest.assert(!contained, "Key is not present in the store");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.run(function (succeeded) {
store.close();
testJobDone(succeeded);
});
}, "Store Contains Test with mechanism " + mechanism, mechanism);
djstest.addTest(function storeGetAllKeysTest(mechanism) {
var store = this.createStore("store4");
var store2 = this.createStore("store4_1");
var expectedKeys = [];
var job = new djstest.Job();
var i;
for (i = 1; i <= 20; i++) {
(function (i) {
job.queue(function (success, fail) {
store.add(i.toString(), "value" + i, success, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store2.add((i + 20).toString(), "value" + (i + 20), success, makeUnexpectedErrorHandler(fail));
});
})(i);
expectedKeys.push(i.toString());
}
job.queue(function (success, fail) {
store.getAllKeys(function (keys) {
expectedKeys.sort();
keys.sort();
djstest.assertAreEqualDeep(keys, expectedKeys, "All expected keys where returned");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.run(function (succeeded) {
store.close();
store2.close();
testJobDone(succeeded);
});
}, "Store Get All Keys Test with mechanism " + mechanism, mechanism);
djstest.addTest(function storeReadTest(mechanism) {
var tuples = [
{ key: "null", value: null },
{ key: "undefined", value: undefined },
{ key: "number", value: 12345.678 },
{ key: "string", value: "String value" },
{ key: "date", value: new Date() },
{ key: "dateOffset", value: (function () {
var d = new Date();
d.__type = "Edm.DateTimeOffset";
d.__offset = "+03:30";
return d;
})()
},
{ key: "complexDate", value: (function () {
var d = new Date();
d.nestedDate = new Date();
d.nestedDate.__type = "Edm.DateTimeOffset";
d.nestedDate.__offset = "+03:30";
return d;
})()
},
{ key: "object", value: { p1: 1234, nested: { p1: "a", p2: "b", p3: new Date()}} },
{ key: "array", value: [1, 2, 3, 4, 5] }
];
var store = this.createStore("store5");
var job = new djstest.Job();
$.each(tuples, function (_, tuple) {
job.queue(function (success, fail) {
store.add(tuple.key, tuple.value,
function () {
job.queue(function (success, fail) {
store.read(tuple.key, function (key, value) {
djstest.assertAreEqual(key, tuple.key, "Keys match");
djstest.assertAreEqualDeep(value, tuple.value, "Values match");
success();
}, makeUnexpectedErrorHandler(fail));
});
success();
},
function (err) {
if (!tuple.error) {
djstest.fail(err.message);
fail();
} else {
djstest.pass("error handler was called as expected");
success();
}
});
});
});
job.queue(function (success, fail) {
store.read("Unknown key", function (key, value) {
djstest.assertAreEqual(value, undefined, "Store get returns undefined for keys that do not exist in the store");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.run(function (succeeded) {
store.close();
testJobDone(succeeded);
});
}, "Store Read Test with mechanism " + mechanism, mechanism);
djstest.addTest(function storeReadArrayTest(mechanism) {
var makeError = function (success, fail) {
return function (err) {
if (mechanism !== "indexeddb") {
djstest.pass("Error callback called as expected");
success();
} else {
djstest.fail(err.message);
fail();
}
};
};
var store = this.createStore("store6");
var job = new djstest.Job();
job.queue(function (success, fail) {
store.add(["key", "key2", "key3"], ["value", "value2", "value3"], success, makeError(success, fail));
});
job.queue(function (success, fail) {
store.read(["key", "key2", "key3"], function (keys, values) {
djstest.assertAreEqualDeep(keys, ["key", "key2", "key3"]);
djstest.assertAreEqualDeep(values, ["value", "value2", "value3"]);
success();
}, makeError(success, fail));
});
job.queue(function (success, fail) {
store.read(["key", "badkey"], function (keys, values) {
djstest.assertAreEqualDeep(keys, ["key", "badkey"]);
djstest.assertAreEqualDeep(values, ["value", undefined]);
success();
}, makeError(success, fail));
});
job.run(function (succeeded) {
store.close();
testJobDone(succeeded);
});
}, "Store Read Array Test with mechanism " + mechanism, mechanism);
djstest.addTest(function storeRemoveTest(mechanism) {
var store = this.createStore("store7");
var job = new djstest.Job();
job.queue(function (success, fail) {
store.add("Key1", "Some value", success, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.add("Key2", "Some value", success, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.remove("Key1", function () {
djstest.pass("Key1 was removed from the store")
success();
}, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.contains("Key1", function (contained) {
djstest.assert(!contained, "Key1 is not present in the store");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.remove("Key that has never been added", function () {
djstest.pass('"Key that has never been added" was removed from the store');
success();
}, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.contains("Key2", function (contained) {
djstest.assert(contained, "Key2 is present in the store");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.run(function (succeeded) {
store.close();
testJobDone(succeeded);
});
}, "Store Remove Test with mechanism " + mechanism, mechanism);
djstest.addTest(function storeUpdateTest(mechanism) {
var store = this.createStore("store8");
var startKey = "Key1";
var startValue = "start value";
var updateKey = "Key2";
var updatedValue = "updated value";
var job = new djstest.Job();
job.queue(function (success, fail) {
store.add(startKey, startValue, success, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.add(updateKey, startValue, success, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.update(updateKey, updatedValue, function (key, value) {
djstest.assertAreEqual(key, updateKey, "Updated keys match");
djstest.assertAreEqualDeep(value, updatedValue, "Updated values match");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.read(updateKey, function (key, value) {
djstest.assertAreEqual(key, updateKey, "Updated keys match after get");
djstest.assertAreEqualDeep(value, updatedValue, "Updated values match after get");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.read(startKey, function (key, value) {
djstest.assertAreEqual(key, startKey, "Non updated keys match after get");
djstest.assertAreEqualDeep(value, startValue, "Non updated values match after get");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.run(function (succeeded) {
store.close();
testJobDone(succeeded);
});
}, "Store Update Test with mechanism " + mechanism, mechanism);
djstest.addTest(function storeClearTest(mechanism) {
var store = this.createStore("store9");
var store2 = this.createStore("store9_1");
var job = new djstest.Job();
job.queue(function (success, fail) {
store.add("Key1", "value in store", success, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.add("Key2", "value in store", success, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.add("Key3", "value in store", success, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store2.add("Key1", "value in store2", success, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.clear(function () {
djstest.pass("Store was cleared");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.contains("Key1", function (contained) {
djstest.assert(!contained, "Key1 was removed from store");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store2.contains("Key1", function (contained) {
djstest.assert(contained, "Key1 still exists in store 2");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.run(function (succeeded) {
store.close();
store2.close();
testJobDone(succeeded);
});
}, "Store Clear Test with mechanism " + mechanism, mechanism);
djstest.addTest(function storeUpdateNonExistentTest(mechanism) {
var store = this.createStore("store10");
var job = new djstest.Job();
job.queue(function (success, fail) {
store.add("key", "value", success, makeUnexpectedErrorHandler(fail));
});
job.queue(function (success, fail) {
store.update("badKey", "new value",
function () {
djstest.fail("Sucess handler called when not expected");
fail();
},
function (err) {
djstest.pass("Error callback called as expexted");
success();
});
});
job.queue(function (success, fail) {
store.update(["key", "badkey"], ["value", "badvalue"],
function () {
djstest.fail("Sucess handler called when not expected");
fail();
},
function (err) {
djstest.pass("Error callback called as expected");
success();
});
});
job.queue(function (success, fail) {
store.read("key", function (key, value) {
djstest.assertAreEqual(value, "value", "value was not changed");
success();
}, makeUnexpectedErrorHandler(fail));
});
job.run(function (succeeded) {
store.close();
testJobDone(succeeded);
});
}, "Store Update Non-Existent Test with mechanism " + mechanism, mechanism);
djstest.addTest(function storeUpdateArrayTest(mechanism) {
var makeError = function (success, fail) {
return function (err) {
if (mechanism !== "indexeddb") {
djstest.pass("Error callback called as expected");
success();
} else {
djstest.fail(err.message);
fail();
}
};
};
var store = this.createStore("store11");
var job = new djstest.Job();
job.queue(function (success, fail) {
store.add(["key", "key2"], ["value1", "value2"], success, makeError(success, fail));
});
job.queue(function (success, fail) {
store.update(["key", "key2"], ["value1", "value4"], success, makeError(success, fail));
});
job.queue(function (success, fail) {
store.read(["key", "key2"], function (key, value) {
djstest.assertAreEqualDeep(value, ["value1", "value4"], "value was not changed");
success();
}, makeError(success, fail));
});
job.run(function (succeeded) {
store.close();
testJobDone(succeeded);
});
}, "Store Update Array Test with mechanism " + mechanism, mechanism);
}
}
module("Unit");
djstest.addTest(function CreateStoreTest() {
var defaultExpected = canCreateDomStore() ? "dom" : "memory";
var tests = [
{ mechanism: "dom", exception: !canCreateDomStore(), expected: "dom" },
{ mechanism: "memory", exception: false, expected: "memory" },
{ mechanism: "", exception: false, expected: defaultExpected },
{ mechanism: null, exception: false, expected: defaultExpected },
{ mechanism: "unknown", exception: true }
];
var i, len;
for (i = 0, len = tests.length; i < len; i++) {
try {
var test = tests[i];
var store = datajs.createStore("testStore" + i, tests[i].mechanism);
if (!test.exception) {
djstest.assertAreEqual(store.mechanism, test.expected, "Created store of the expected mechanism");
} else {
djstest.fail("Didn't get the expected exception");
}
}
catch (e) {
djstest.assert(test.exception, "Expected exception");
}
}
djstest.done();
});
djstest.addTest(function CreateBestStoreTest() {
var bestMechanism;
for (var name in mechanismImplementations) {
if (!bestMechanism && canCreateStore(name) && name !== "indexeddb") {
bestMechanism = name;
}
}
if (bestMechanism) {
var tests = [
"best",
undefined
];
for (var i in tests) {
var store = datajs.createStore("best store ever " + i, tests[i]);
djstest.assertAreEqual(store.mechanism, bestMechanism, "Mechanisms match");
}
} else {
djstest.pass("This browser doesn't support any of the implemented local storage mechanisms");
}
djstest.done();
});
})(this);