Commit f5818fd5 authored by preetwinder's avatar preetwinder Committed by Romain Courteaud

WIP [indexedDB] Add database indexes support

parent 36b09ee3
...@@ -43,14 +43,15 @@ ...@@ -43,14 +43,15 @@
/*jslint nomen: true */ /*jslint nomen: true */
/*global indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest, /*global indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest,
DOMError, Event*/ DOMError, Event, Set, parseStringToObject*/
(function (indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest, (function (indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest,
DOMError) { DOMError, parseStringToObject) {
"use strict"; "use strict";
// Read only as changing it can lead to data corruption // Read only as changing it can lead to data corruption
var UNITE = 2000000; var UNITE = 2000000,
INDEX_PREFIX = 'doc.';
function IndexedDBStorage(description) { function IndexedDBStorage(description) {
if (typeof description.database !== "string" || if (typeof description.database !== "string" ||
...@@ -59,6 +60,8 @@ ...@@ -59,6 +60,8 @@
"must be a non-empty string"); "must be a non-empty string");
} }
this._database_name = "jio:" + description.database; this._database_name = "jio:" + description.database;
this._version = description.version;
this._index_key_list = description.index_key_list || [];
} }
IndexedDBStorage.prototype.hasCapacity = function (name) { IndexedDBStorage.prototype.hasCapacity = function (name) {
...@@ -69,10 +72,15 @@ ...@@ -69,10 +72,15 @@
return key_list.join("_"); return key_list.join("_");
} }
function handleUpgradeNeeded(evt) { function handleUpgradeNeeded(evt, index_key_list) {
var db = evt.target.result, var db = evt.target.result,
store; store,
current_store_list = Array.from(db.objectStoreNames),
current_index_list,
i,
index_key;
if (current_store_list.indexOf("metadata") === -1) {
store = db.createObjectStore("metadata", { store = db.createObjectStore("metadata", {
keyPath: "_id", keyPath: "_id",
autoIncrement: false autoIncrement: false
...@@ -80,13 +88,36 @@ ...@@ -80,13 +88,36 @@
// It is not possible to use openKeyCursor on keypath directly // It is not possible to use openKeyCursor on keypath directly
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=19955 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=19955
store.createIndex("_id", "_id", {unique: true}); store.createIndex("_id", "_id", {unique: true});
} else {
store = evt.target.transaction.objectStore("metadata");
}
current_index_list = new Set(store.indexNames);
current_index_list.delete("_id");
for (i = 0; i < index_key_list.length; i += 1) {
// Prefix the index name to prevent conflict with _id
index_key = INDEX_PREFIX + index_key_list[i];
if (current_index_list.has(index_key)) {
current_index_list.delete(index_key);
} else {
store.createIndex(index_key, index_key,
{unique: false});
}
}
current_index_list = Array.from(current_index_list);
for (i = 0; i < current_index_list.length; i += 1) {
store.deleteIndex(current_index_list[i]);
}
if (current_store_list.indexOf("attachment") === -1) {
store = db.createObjectStore("attachment", { store = db.createObjectStore("attachment", {
keyPath: "_key_path", keyPath: "_key_path",
autoIncrement: false autoIncrement: false
}); });
store.createIndex("_id", "_id", {unique: false}); store.createIndex("_id", "_id", {unique: false});
}
if (current_store_list.indexOf("blob") === -1) {
store = db.createObjectStore("blob", { store = db.createObjectStore("blob", {
keyPath: "_key_path", keyPath: "_key_path",
autoIncrement: false autoIncrement: false
...@@ -95,9 +126,11 @@ ...@@ -95,9 +126,11 @@
["_id", "_attachment"], {unique: false}); ["_id", "_attachment"], {unique: false});
store.createIndex("_id", "_id", {unique: false}); store.createIndex("_id", "_id", {unique: false});
} }
}
function waitForOpenIndexedDB(db_name, callback) { function waitForOpenIndexedDB(storage, callback) {
var request; var request,
db_name = storage._database_name;
function canceller() { function canceller() {
if ((request !== undefined) && (request.result !== undefined)) { if ((request !== undefined) && (request.result !== undefined)) {
...@@ -107,7 +140,7 @@ ...@@ -107,7 +140,7 @@
function resolver(resolve, reject) { function resolver(resolve, reject) {
// Open DB // // Open DB //
request = indexedDB.open(db_name); request = indexedDB.open(db_name, storage._version);
request.onerror = function (error) { request.onerror = function (error) {
canceller(); canceller();
if ((error !== undefined) && if ((error !== undefined) &&
...@@ -135,7 +168,9 @@ ...@@ -135,7 +168,9 @@
}; };
// Create DB if necessary // // Create DB if necessary //
request.onupgradeneeded = handleUpgradeNeeded; request.onupgradeneeded = function (evt) {
handleUpgradeNeeded(evt, storage._index_key_list);
};
request.onversionchange = function () { request.onversionchange = function () {
canceller(); canceller();
...@@ -235,11 +270,14 @@ ...@@ -235,11 +270,14 @@
IndexedDBStorage.prototype.buildQuery = function (options) { IndexedDBStorage.prototype.buildQuery = function (options) {
var result_list = [], var result_list = [],
context = this; context = this,
query,
key,
value;
function pushIncludedMetadata(cursor) { function pushIncludedMetadata(cursor) {
result_list.push({ result_list.push({
"id": cursor.key, "id": cursor.primaryKey,
"value": {}, "value": {},
"doc": cursor.value.doc "doc": cursor.value.doc
}); });
...@@ -247,24 +285,30 @@ ...@@ -247,24 +285,30 @@
function pushMetadata(cursor) { function pushMetadata(cursor) {
result_list.push({ result_list.push({
"id": cursor.key, "id": cursor.primaryKey,
"value": {} "value": {}
}); });
} }
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return waitForOpenIndexedDB(context._database_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["metadata"], "readonly", return waitForTransaction(db, ["metadata"], "readonly",
function (tx) { function (tx) {
key = "_id";
if (options.subquery) {
query = parseStringToObject(options.subquery);
key = INDEX_PREFIX + query.key;
value = IDBKeyRange.only(query.value);
}
if (options.include_docs === true) { if (options.include_docs === true) {
return waitForAllSynchronousCursor( return waitForAllSynchronousCursor(
tx.objectStore("metadata").index("_id").openCursor(), tx.objectStore("metadata").index(key).openCursor(value),
pushIncludedMetadata pushIncludedMetadata
); );
} }
return waitForAllSynchronousCursor( return waitForAllSynchronousCursor(
tx.objectStore("metadata").index("_id").openKeyCursor(), tx.objectStore("metadata").index(key).openKeyCursor(value),
pushMetadata pushMetadata
); );
}); });
...@@ -279,7 +323,7 @@ ...@@ -279,7 +323,7 @@
var context = this; var context = this;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return waitForOpenIndexedDB(context._database_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["metadata"], "readonly", return waitForTransaction(db, ["metadata"], "readonly",
function (tx) { function (tx) {
return waitForIDBRequest(tx.objectStore("metadata").get(id)); return waitForIDBRequest(tx.objectStore("metadata").get(id));
...@@ -307,7 +351,7 @@ ...@@ -307,7 +351,7 @@
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return waitForOpenIndexedDB(context._database_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["metadata", "attachment"], "readonly", return waitForTransaction(db, ["metadata", "attachment"], "readonly",
function (tx) { function (tx) {
return RSVP.all([ return RSVP.all([
...@@ -336,7 +380,7 @@ ...@@ -336,7 +380,7 @@
}; };
IndexedDBStorage.prototype.put = function (id, metadata) { IndexedDBStorage.prototype.put = function (id, metadata) {
return waitForOpenIndexedDB(this._database_name, function (db) { return waitForOpenIndexedDB(this, function (db) {
return waitForTransaction(db, ["metadata"], "readwrite", return waitForTransaction(db, ["metadata"], "readwrite",
function (tx) { function (tx) {
return waitForIDBRequest(tx.objectStore("metadata").put({ return waitForIDBRequest(tx.objectStore("metadata").put({
...@@ -348,7 +392,7 @@ ...@@ -348,7 +392,7 @@
}; };
IndexedDBStorage.prototype.remove = function (id) { IndexedDBStorage.prototype.remove = function (id) {
return waitForOpenIndexedDB(this._database_name, function (db) { return waitForOpenIndexedDB(this, function (db) {
return waitForTransaction(db, ["metadata", "attachment", "blob"], return waitForTransaction(db, ["metadata", "attachment", "blob"],
"readwrite", function (tx) { "readwrite", function (tx) {
...@@ -392,10 +436,10 @@ ...@@ -392,10 +436,10 @@
if (options === undefined) { if (options === undefined) {
options = {}; options = {};
} }
var db_name = this._database_name, var start,
start,
end, end,
array_buffer_list = []; array_buffer_list = [],
context = this;
start = options.start || 0; start = options.start || 0;
end = options.end; end = options.end;
...@@ -416,7 +460,7 @@ ...@@ -416,7 +460,7 @@
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return waitForOpenIndexedDB(db_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["blob"], "readonly", return waitForTransaction(db, ["blob"], "readonly",
function (tx) { function (tx) {
var key_path = buildKeyPath([id, name]), var key_path = buildKeyPath([id, name]),
...@@ -501,7 +545,7 @@ ...@@ -501,7 +545,7 @@
// Request the full blob // Request the full blob
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return waitForOpenIndexedDB(db_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["attachment", "blob"], "readonly", return waitForTransaction(db, ["attachment", "blob"], "readonly",
function (tx) { function (tx) {
var key_path = buildKeyPath([id, name]), var key_path = buildKeyPath([id, name]),
...@@ -573,7 +617,7 @@ ...@@ -573,7 +617,7 @@
}; };
IndexedDBStorage.prototype.putAttachment = function (id, name, blob) { IndexedDBStorage.prototype.putAttachment = function (id, name, blob) {
var db_name = this._database_name; var context = this;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
// Split the blob first // Split the blob first
...@@ -591,7 +635,7 @@ ...@@ -591,7 +635,7 @@
handled_size += UNITE; handled_size += UNITE;
} }
return waitForOpenIndexedDB(db_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["attachment", "blob"], "readwrite", return waitForTransaction(db, ["attachment", "blob"], "readwrite",
function (tx) { function (tx) {
var blob_store, var blob_store,
...@@ -658,7 +702,7 @@ ...@@ -658,7 +702,7 @@
}; };
IndexedDBStorage.prototype.removeAttachment = function (id, name) { IndexedDBStorage.prototype.removeAttachment = function (id, name) {
return waitForOpenIndexedDB(this._database_name, function (db) { return waitForOpenIndexedDB(this, function (db) {
return waitForTransaction(db, ["attachment", "blob"], "readwrite", return waitForTransaction(db, ["attachment", "blob"], "readwrite",
function (tx) { function (tx) {
var promise_list = [], var promise_list = [],
...@@ -690,4 +734,5 @@ ...@@ -690,4 +734,5 @@
}; };
jIO.addStorage("indexeddb", IndexedDBStorage); jIO.addStorage("indexeddb", IndexedDBStorage);
}(indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest, DOMError)); }(indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest, DOMError,
parseStringToObject));
...@@ -19,9 +19,11 @@ ...@@ -19,9 +19,11 @@
*/ */
/*jslint nomen: true */ /*jslint nomen: true */
/*global indexedDB, Blob, sinon, IDBDatabase, /*global indexedDB, Blob, sinon, IDBDatabase,
IDBTransaction, IDBIndex, IDBObjectStore, IDBCursor, IDBKeyRange*/ IDBTransaction, IDBIndex, IDBObjectStore, IDBCursor, IDBKeyRange,
DOMException*/
(function (jIO, QUnit, indexedDB, Blob, sinon, IDBDatabase, (function (jIO, QUnit, indexedDB, Blob, sinon, IDBDatabase,
IDBTransaction, IDBIndex, IDBObjectStore, IDBCursor, IDBKeyRange) { IDBTransaction, IDBIndex, IDBObjectStore, IDBCursor, IDBKeyRange,
DOMException) {
"use strict"; "use strict";
var test = QUnit.test, var test = QUnit.test,
stop = QUnit.stop, stop = QUnit.stop,
...@@ -31,6 +33,7 @@ ...@@ -31,6 +33,7 @@
deepEqual = QUnit.deepEqual, deepEqual = QUnit.deepEqual,
equal = QUnit.equal, equal = QUnit.equal,
module = QUnit.module, module = QUnit.module,
throws = QUnit.throws,
big_string = "", big_string = "",
j; j;
...@@ -49,11 +52,188 @@ ...@@ -49,11 +52,188 @@
}); });
} }
function idCompare(value1, value2) {
if (value1.id > value2.id) {
return 1;
}
if (value1.id < value2.id) {
return -1;
}
return 0;
}
/////////////////////////////////////////////////////////////////
// indexedDBStorage.buildQuery
/////////////////////////////////////////////////////////////////
module("indexedDBStorage.buildQuery", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("Simple query matching single object", function () {
var context = this;
context.jio = jIO.createJIO({
type: "indexeddb",
database: "index2_test",
index_key_list: ["a", "b"],
});
stop();
expect(2);
context.jio.put("32", {"a": "3", "b": "2"})
.then(function () {
return context.jio.allDocs({subquery: 'a: "3"'});
})
.then(function (result) {
equal(result.data.total_rows, 1);
deepEqual(result.data.rows, [{"id": "32", "value": {}}]);
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Simple query matching multiple objects", function () {
var context = this;
context.jio = jIO.createJIO({
type: "indexeddb",
database: "index2_test",
index_key_list: ["a", "b"],
});
stop();
expect(2);
RSVP.all([
context.jio.put("32", {a: "3", b: "1"}),
context.jio.put("21", {a: "8", b: "1"}),
context.jio.put("3", {a: "5", b: "1"})
])
.then(function () {
return context.jio.allDocs({subquery: 'b: "1"'});
})
.then(function (result) {
equal(result.data.total_rows, 3);
deepEqual(result.data.rows.sort(idCompare),
[
{"id": "32", "value": {}},
{"id": "21", "value": {}},
{"id": "3", "value": {}}
].sort(idCompare));
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Index keys are modified", function () {
var context = this;
context.jio = jIO.createJIO({
type: "indexeddb",
database: "index2_test",
version: 1,
index_key_list: ["a"]
});
stop();
expect(7);
RSVP.all([
context.jio.put("32", {"a": "3", "b": "2", "c": "inverse"}),
context.jio.put("5", {"a": "6", "b": "2", "c": "strong"}),
context.jio.put("14", {"a": "67", "b": "3", "c": "disolve"})
])
.then(function () {
return context.jio.allDocs({subquery: 'a: "67"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "14", "value": {}}]);
})
.then(function () {
context.jio = jIO.createJIO({
type: "indexeddb",
database: "index2_test",
version: 2,
index_key_list: ["a", "b", "c"],
});
})
.then(function () {
return RSVP.all([
context.jio.put("18", {"a": "2", "b": "3", "c": "align"}),
context.jio.put("62", {"a": "3", "b": "2", "c": "disolve"})
]);
})
.then(function () {
return context.jio.allDocs({subquery: 'b: "3"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "14", "value": {}},
{"id": "18", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({subquery: 'c: "disolve"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "14", "value": {}},
{"id": "62", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({subquery: 'a: "3"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "32", "value": {}},
{"id": "62", "value": {}}]);
})
.then(function () {
context.jio = jIO.createJIO({
type: "indexeddb",
database: "index2_test",
index_key_list: ["a", "c"],
version: 3
});
})
.then(function () {
return context.jio.put("192", {"a": "3", "b": "3", "c": "disolve"});
})
.then(function () {
return context.jio.allDocs({subquery: 'a: "3"'});
})
.then(function (result) {
deepEqual(result.data.rows.sort(idCompare), [{"id": "192", "value": {}},
{"id": "32", "value": {}}, {"id": "62", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({query: 'c: "disolve"'});
})
.then(function (result) {
deepEqual(result.data.rows.sort(idCompare), [{"id": "14", "value": {}},
{"id": "192", "value": {}}, {"id": "62", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({subquery: 'b: "3"'});
})
.fail(function (error) {
equal(error.status_code, 501);
equal(error.message,
"Capacity 'query' is not implemented on 'indexeddb'");
})
.always(function () {
start();
});
});
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// indexeddbStorage.constructor // indexeddbStorage.constructor
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
module("indexeddbStorage.constructor"); module("indexeddbStorage.constructor");
test("default unite value", function () { test("default unite value", function () {
expect(3);
var jio = jIO.createJIO({ var jio = jIO.createJIO({
type: "indexeddb", type: "indexeddb",
database: "qunit" database: "qunit"
...@@ -61,6 +241,255 @@ ...@@ -61,6 +241,255 @@
equal(jio.__type, "indexeddb"); equal(jio.__type, "indexeddb");
deepEqual(jio.__storage._database_name, "jio:qunit"); deepEqual(jio.__storage._database_name, "jio:qunit");
deepEqual(jio.__storage._index_key_list, []);
});
/////////////////////////////////////////////////////////////////
// indexeddbStorage DB migration
/////////////////////////////////////////////////////////////////
module("indexeddbStorage.upgradeDB");
function setupDBMigrationTest(test, old_jio_kw, new_jio_kw, check_callback) {
// Create a IDB with one document
// Migrate it to a new version
// Spy IDB behaviour while getting the previous document
// Check that doument is still there
stop();
old_jio_kw.type = "indexeddb";
old_jio_kw.database = "qunit";
new_jio_kw.type = "indexeddb";
new_jio_kw.database = "qunit";
test.jio = jIO.createJIO(old_jio_kw);
return deleteIndexedDB(test.jio)
.then(function () {
return test.jio.put('foo', {'a': 1});
})
.then(function () {
test.jio = jIO.createJIO(new_jio_kw);
test.spy_open = sinon.spy(indexedDB, "open");
test.spy_create_store = sinon.spy(IDBDatabase.prototype,
"createObjectStore");
test.spy_transaction = sinon.spy(IDBDatabase.prototype, "transaction");
test.spy_store = sinon.spy(IDBTransaction.prototype, "objectStore");
test.spy_index = sinon.spy(IDBObjectStore.prototype, "index");
test.spy_create_index = sinon.spy(IDBObjectStore.prototype,
"createIndex");
test.spy_delete_index = sinon.spy(IDBObjectStore.prototype,
"deleteIndex");
return test.jio.get('foo');
})
.then(function (result) {
deepEqual(result, {'a': 1});
ok(test.spy_transaction.calledOnce, "transaction count " +
test.spy_transaction.callCount);
deepEqual(test.spy_transaction.firstCall.args[0], ["metadata"],
"transaction first argument");
equal(test.spy_transaction.firstCall.args[1], "readonly",
"transaction second argument");
})
.always(function (param) {
return check_callback(param);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
test.spy_open.restore();
delete test.spy_open;
test.spy_create_store.restore();
delete test.spy_create_store;
test.spy_transaction.restore();
delete test.spy_transaction;
test.spy_store.restore();
delete test.spy_store;
test.spy_index.restore();
delete test.spy_index;
test.spy_create_index.restore();
delete test.spy_create_index;
test.spy_delete_index.restore();
delete test.spy_delete_index;
start();
});
}
test("no change", function () {
var context = this;
expect(10);
return setupDBMigrationTest(context, {}, {}, function () {
ok(context.spy_open.calledOnce, "open count " +
context.spy_open.callCount);
equal(context.spy_open.firstCall.args[0], "jio:qunit",
"open first argument");
equal(context.spy_create_store.callCount, 0,
"createObjectStore count");
equal(context.spy_store.callCount, 1,
"objectStore count");
equal(context.spy_create_index.callCount, 0, "createIndex count");
equal(context.spy_delete_index.callCount, 0, "deleteIndex count");
});
});
test("version update, no key change", function () {
var context = this;
expect(10);
return setupDBMigrationTest(context, {}, {version: 2}, function () {
ok(context.spy_open.calledOnce, "open count " +
context.spy_open.callCount);
equal(context.spy_open.firstCall.args[0], "jio:qunit",
"open first argument");
equal(context.spy_create_store.callCount, 0,
"createObjectStore count");
equal(context.spy_store.callCount, 2,
"objectStore count");
equal(context.spy_create_index.callCount, 0, "createIndex count");
equal(context.spy_delete_index.callCount, 0, "deleteIndex count");
});
});
test("version decrease", function () {
var context = this;
expect(8);
return setupDBMigrationTest(context, {version: 3},
{version: 2}, function (evt) {
ok(evt.target.error instanceof DOMException);
equal(evt.target.error.name, 'VersionError');
ok(context.spy_open.calledOnce, "open count " +
context.spy_open.callCount);
equal(context.spy_open.firstCall.args[0], "jio:qunit",
"open first argument");
equal(context.spy_create_store.callCount, 0,
"createObjectStore count");
equal(context.spy_store.callCount, 0,
"objectStore count");
equal(context.spy_create_index.callCount, 0, "createIndex count");
equal(context.spy_delete_index.callCount, 0, "deleteIndex count");
});
});
test("version increase, key added", function () {
var context = this;
expect(13);
return setupDBMigrationTest(context, {version: 1, index_key_list: ['a']},
{version: 2, index_key_list: ['a', 'b']},
function () {
ok(context.spy_open.calledOnce, "open count " +
context.spy_open.callCount);
equal(context.spy_open.firstCall.args[0], "jio:qunit",
"open first argument");
equal(context.spy_create_store.callCount, 0,
"createObjectStore count");
equal(context.spy_store.callCount, 2,
"objectStore count");
equal(context.spy_create_index.callCount, 1, "createIndex count");
equal(context.spy_create_index.firstCall.args[0], "doc.b",
"first createIndex first argument");
equal(context.spy_create_index.firstCall.args[1], "doc.b",
"first createIndex second argument");
deepEqual(context.spy_create_index.firstCall.args[2], {unique: false},
"first createIndex third argument");
equal(context.spy_delete_index.callCount, 0, "deleteIndex count");
});
});
test("version increase, key removed", function () {
var context = this;
expect(11);
return setupDBMigrationTest(context,
{version: 1, index_key_list: ['a', 'b']},
{version: 2, index_key_list: ['b']},
function () {
ok(context.spy_open.calledOnce, "open count " +
context.spy_open.callCount);
equal(context.spy_open.firstCall.args[0], "jio:qunit",
"open first argument");
equal(context.spy_create_store.callCount, 0,
"createObjectStore count");
equal(context.spy_store.callCount, 2,
"objectStore count");
equal(context.spy_create_index.callCount, 0, "createIndex count");
equal(context.spy_delete_index.callCount, 1, "deleteIndex count");
equal(context.spy_delete_index.firstCall.args[0], "doc.a",
"first deleteIndex first argument");
});
});
test("version increase, keys added and removed", function () {
var context = this;
expect(18);
return setupDBMigrationTest(context,
{version: 1, index_key_list: ['a', 'b', 'c']},
{version: 2, index_key_list: ['e', 'b', 'f']},
function () {
ok(context.spy_open.calledOnce, "open count " +
context.spy_open.callCount);
equal(context.spy_open.firstCall.args[0], "jio:qunit",
"open first argument");
equal(context.spy_create_store.callCount, 0,
"createObjectStore count");
equal(context.spy_store.callCount, 2,
"objectStore count");
equal(context.spy_create_index.callCount, 2, "createIndex count");
equal(context.spy_create_index.firstCall.args[0], "doc.e",
"first createIndex first argument");
equal(context.spy_create_index.firstCall.args[1], "doc.e",
"first createIndex second argument");
deepEqual(context.spy_create_index.firstCall.args[2], {unique: false},
"first createIndex third argument");
equal(context.spy_create_index.secondCall.args[0], "doc.f",
"second createIndex first argument");
equal(context.spy_create_index.secondCall.args[1], "doc.f",
"second createIndex second argument");
deepEqual(context.spy_create_index.secondCall.args[2], {unique: false},
"second createIndex third argument");
equal(context.spy_delete_index.callCount, 2, "deleteIndex count");
equal(context.spy_delete_index.firstCall.args[0], "doc.a",
"first deleteIndex first argument");
equal(context.spy_delete_index.secondCall.args[0], "doc.c",
"second deleteIndex first argument");
});
});
test("version idem, keys added and removed", function () {
var context = this;
expect(10);
return setupDBMigrationTest(context,
{version: 1, index_key_list: ['a', 'b', 'c']},
{version: 1, index_key_list: ['e', 'b', 'f']},
function () {
ok(context.spy_open.calledOnce, "open count " +
context.spy_open.callCount);
equal(context.spy_open.firstCall.args[0], "jio:qunit",
"open first argument");
equal(context.spy_create_store.callCount, 0,
"createObjectStore count");
equal(context.spy_store.callCount, 1,
"objectStore count");
equal(context.spy_create_index.callCount, 0, "createIndex count");
equal(context.spy_delete_index.callCount, 0, "deleteIndex count");
});
}); });
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
...@@ -68,12 +497,35 @@ ...@@ -68,12 +497,35 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
module("indexeddbStorage.hasCapacity"); module("indexeddbStorage.hasCapacity");
test("can list documents", function () { test("can list documents", function () {
expect(2);
var jio = jIO.createJIO({ var jio = jIO.createJIO({
type: "indexeddb", type: "indexeddb",
database: "qunit" database: "qunit"
}); });
ok(jio.hasCapacity("list")); ok(jio.hasCapacity("list"));
ok(jio.hasCapacity("include"));
});
test("can not search documents", function () {
expect(4);
var jio = jIO.createJIO({
type: "indexeddb",
database: "qunit"
});
throws(
function () {
jio.hasCapacity("query");
},
function (error) {
ok(error instanceof jIO.util.jIOError);
equal(error.status_code, 501);
equal(error.message,
"Capacity 'query' is not implemented on 'indexeddb'");
return true;
}
);
}); });
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
...@@ -1724,4 +2176,5 @@ ...@@ -1724,4 +2176,5 @@
}); });
}(jIO, QUnit, indexedDB, Blob, sinon, IDBDatabase, }(jIO, QUnit, indexedDB, Blob, sinon, IDBDatabase,
IDBTransaction, IDBIndex, IDBObjectStore, IDBCursor, IDBKeyRange)); IDBTransaction, IDBIndex, IDBObjectStore, IDBCursor, IDBKeyRange,
DOMException));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment