Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

// META: title=IDBObjectStore.createIndex()
// META: global=window,worker
// META: script=resources/support.js
'use strict';
async_test(t => {
let db;
let open_rq = createdb(t);
open_rq.onupgradeneeded = function (e) {
db = e.target.result;
let objStore = db.createObjectStore("store");
let index = objStore.createIndex("index", "indexedProperty", { unique: true });
assert_true(index instanceof IDBIndex, "IDBIndex");
assert_equals(index.name, "index", "name");
assert_equals(index.objectStore, objStore, "objectStore");
assert_equals(index.keyPath, "indexedProperty", "keyPath");
assert_true(index.unique, "unique");
assert_false(index.multiEntry, "multiEntry");
t.done();
};
}, "Returns an IDBIndex and the properties are set correctly");
async_test(t => {
let db, aborted,
record = { indexedProperty: "bar" };
let open_rq = createdb(t);
open_rq.onupgradeneeded = function (e) {
db = e.target.result;
let txn = e.target.transaction,
objStore = db.createObjectStore("store");
objStore.add(record, 1);
objStore.add(record, 2);
let index = objStore.createIndex("index", "indexedProperty", { unique: true });
assert_true(index instanceof IDBIndex, "IDBIndex");
e.target.transaction.onabort = t.step_func(function (e) {
aborted = true;
assert_equals(e.type, "abort", "event type");
});
db.onabort = function (e) {
assert_true(aborted, "transaction.abort event has fired");
t.done();
};
e.target.transaction.oncomplete = fail(t, "got complete, expected abort");
};
}, "Attempt to create an index that requires unique values on an object store already contains duplicates");
async_test(t => {
let db, aborted;
let open_rq = createdb(t);
open_rq.onupgradeneeded = function (e) {
db = e.target.result;
let txn = e.target.transaction,
objStore = db.createObjectStore("store", { keyPath: 'key' });
for (let i = 0; i < 100; i++)
objStore.add({ key: "key_" + i, indexedProperty: "indexed_" + i });
let idx = objStore.createIndex("index", "indexedProperty")
idx.get('indexed_99').onsuccess = t.step_func(function (e) {
assert_equals(e.target.result.key, 'key_99', 'key');
});
idx.get('indexed_9').onsuccess = t.step_func(function (e) {
assert_equals(e.target.result.key, 'key_9', 'key');
});
}
open_rq.onsuccess = function () {
t.done();
}
}, "The index is usable right after being made");
async_test(t => {
let db,
events = [];
let open_rq = createdb(t);
open_rq.onupgradeneeded = function (e) {
db = e.target.result;
e.target.transaction.oncomplete = log("transaction.complete");
let txn = e.target.transaction,
objStore = db.createObjectStore("store");
let rq_add1 = objStore.add({ animal: "Unicorn" }, 1);
rq_add1.onsuccess = log("rq_add1.success");
rq_add1.onerror = log("rq_add1.error");
objStore.createIndex("index", "animal", { unique: true });
let rq_add2 = objStore.add({ animal: "Unicorn" }, 2);
rq_add2.onsuccess = log("rq_add2.success");
rq_add2.onerror = function (e) {
log("rq_add2.error")(e);
e.preventDefault();
e.stopPropagation();
}
objStore.deleteIndex("index");
let rq_add3 = objStore.add({ animal: "Unicorn" }, 3);
rq_add3.onsuccess = log("rq_add3.success");
rq_add3.onerror = log("rq_add3.error");
}
open_rq.onsuccess = function (e) {
log("open_rq.success")(e);
assert_array_equals(events, ["rq_add1.success",
"rq_add2.error: ConstraintError",
"rq_add3.success",
"transaction.complete",
"open_rq.success"],
"events");
t.done();
}
function log(msg) {
return function (e) {
if (e && e.target && e.target.error)
events.push(msg + ": " + e.target.error.name);
else
events.push(msg);
};
}
}, "Event ordering for a later deleted index");
async_test(t => {
let db, aborted;
let open_rq = createdb(t);
open_rq.onupgradeneeded = function (e) {
db = e.target.result;
let txn = e.target.transaction,
objStore = db.createObjectStore("store");
for (let i = 0; i < 5; i++)
objStore.add("object_" + i, i);
let rq = objStore.createIndex("index", "")
rq.onerror = function () { assert_unreached("error: " + rq.error.name); }
rq.onsuccess = function () { }
objStore.index("index")
.get('object_4')
.onsuccess = t.step_func(function (e) {
assert_equals(e.target.result, 'object_4', 'result');
});
}
open_rq.onsuccess = function () {
t.done();
}
}, "Empty keyPath");
async_test(t => {
// Transaction may fire window.onerror in some implementations.
setup({ allow_uncaught_exception: true });
let db,
events = [];
let open_rq = createdb(t);
open_rq.onupgradeneeded = function (e) {
db = e.target.result;
db.onerror = log("db.error");
db.onabort = log("db.abort");
e.target.transaction.onabort = log("transaction.abort")
e.target.transaction.onerror = log("transaction.error")
e.target.transaction.oncomplete = log("transaction.complete")
let txn = e.target.transaction,
objStore = db.createObjectStore("store");
let rq_add1 = objStore.add({ animal: "Unicorn" }, 1);
rq_add1.onsuccess = log("rq_add1.success");
rq_add1.onerror = log("rq_add1.error");
let rq_add2 = objStore.add({ animal: "Unicorn" }, 2);
rq_add2.onsuccess = log("rq_add2.success");
rq_add2.onerror = log("rq_add2.error");
objStore.createIndex("index", "animal", { unique: true })
let rq_add3 = objStore.add({ animal: "Unicorn" }, 3);
rq_add3.onsuccess = log("rq_add3.success");
rq_add3.onerror = log("rq_add3.error");
}
open_rq.onerror = function (e) {
log("open_rq.error")(e);
assert_array_equals(events, ["rq_add1.success",
"rq_add2.success",
"rq_add3.error: AbortError",
"transaction.error: AbortError",
"db.error: AbortError",
"transaction.abort: ConstraintError",
"db.abort: ConstraintError",
"open_rq.error: AbortError"],
"events");
t.done();
}
function log(msg) {
return function (e) {
if (e && e.target && e.target.error)
events.push(msg + ": " + e.target.error.name);
else
events.push(msg);
};
}
}, "Event order when unique constraint is triggered");
async_test(t => {
setup({ allow_uncaught_exception: true });
let db,
events = [];
const open_rq = createdb(t);
open_rq.onupgradeneeded = function (e) {
db = e.target.result;
let txn = e.target.transaction;
db.onerror = log("db.error");
db.onabort = log("db.abort");
txn.onabort = log("transaction.abort")
txn.onerror = log("transaction.error")
txn.oncomplete = log("transaction.complete")
let objStore = db.createObjectStore("store");
let rq_add1 = objStore.add({ animal: "Unicorn" }, 1);
rq_add1.onsuccess = log("rq_add1.success");
rq_add1.onerror = log("rq_add1.error");
objStore.createIndex("index", "animal", { unique: true })
let rq_add2 = objStore.add({ animal: "Unicorn" }, 2);
rq_add2.onsuccess = log("rq_add2.success");
rq_add2.onerror = log("rq_add2.error");
let rq_add3 = objStore.add({ animal: "Horse" }, 3);
rq_add3.onsuccess = log("rq_add3.success");
rq_add3.onerror = log("rq_add3.error");
}
open_rq.onerror = function (e) {
log("open_rq.error")(e);
assert_array_equals(events, ["rq_add1.success",
"rq_add2.error: ConstraintError",
"transaction.error: ConstraintError",
"db.error: ConstraintError",
"rq_add3.error: AbortError",
"transaction.error: AbortError",
"db.error: AbortError",
"transaction.abort: ConstraintError",
"db.abort: ConstraintError",
"open_rq.error: AbortError"],
"events");
t.done();
}
function log(msg) {
return function (e) {
if (e && e.target && e.target.error)
events.push(msg + ": " + e.target.error.name);
else
events.push(msg);
};
}
}, "Event ordering for ConstraintError on request");
async_test(t => {
let db,
now = new Date(),
mar18 = new Date(1111111111111),
ar = ["Yay", 2, -Infinity],
num = 1337;
const open_rq = createdb(t);
open_rq.onupgradeneeded = function (e) {
db = e.target.result;
let txn = e.target.transaction,
objStore = db.createObjectStore("store", { keyPath: 'key' });
objStore.add({ key: "now", i: now });
objStore.add({ key: "mar18", i: mar18 });
objStore.add({ key: "array", i: ar });
objStore.add({ key: "number", i: num });
let idx = objStore.createIndex("index", "i")
idx.get(now).onsuccess = t.step_func(function (e) {
assert_equals(e.target.result.key, 'now', 'key');
assert_equals(e.target.result.i.getTime(), now.getTime(), 'getTime');
});
idx.get(mar18).onsuccess = t.step_func(function (e) {
assert_equals(e.target.result.key, 'mar18', 'key');
assert_equals(e.target.result.i.getTime(), mar18.getTime(), 'getTime');
});
idx.get(ar).onsuccess = t.step_func(function (e) {
assert_equals(e.target.result.key, 'array', 'key');
assert_array_equals(e.target.result.i, ar, 'array is the same');
});
idx.get(num).onsuccess = t.step_func(function (e) {
assert_equals(e.target.result.key, 'number', 'key');
assert_equals(e.target.result.i, num, 'number is the same');
});
}
open_rq.onsuccess = function () {
t.done();
}
}, "Index can be valid keys");
async_test(t => {
let db;
const open_rq = createdb(t);
open_rq.onupgradeneeded = function (e) {
db = e.target.result
let store = db.createObjectStore("store")
for (let i = 0; i < 5; i++)
store.add({ idx: "object_" + i }, i)
store.createIndex("", "idx")
store.index("")
.get('object_4')
.onsuccess = t.step_func(function (e) {
assert_equals(e.target.result.idx, 'object_4', 'result')
})
assert_equals(store.indexNames[0], "", "indexNames[0]")
assert_equals(store.indexNames.length, 1, "indexNames.length")
}
open_rq.onsuccess = function () {
let store = db.transaction("store", "readonly", { durability: 'relaxed' }).objectStore("store")
assert_equals(store.indexNames[0], "", "indexNames[0]")
assert_equals(store.indexNames.length, 1, "indexNames.length")
t.done()
}
}, "IDBObjectStore.createIndex() - empty name");
async_test(t => {
const open_rq = createdb(t);
open_rq.onupgradeneeded = function (e) {
let db = e.target.result;
let ostore = db.createObjectStore("store");
ostore.createIndex("a", "a");
assert_throws_dom("ConstraintError", function () {
ostore.createIndex("a", "a");
});
t.done();
}
}, "If an index with the name name already exists in this object store, the implementation must throw a DOMException of type ConstraintError");
async_test(t => {
const open_rq = createdb(t);
open_rq.onupgradeneeded = function (e) {
let db = e.target.result;
let ostore = db.createObjectStore("store");
assert_throws_dom("SyntaxError", function () {
ostore.createIndex("ab", ".");
});
t.done();
}
}, "If keyPath is not a valid key path, the implementation must throw a DOMException of type SyntaxError");
async_test(t => {
let db, ostore;
let open_rq = createdb(t);
open_rq.onupgradeneeded = function (event) {
db = event.target.result;
ostore = db.createObjectStore("store");
db.deleteObjectStore("store");
}
open_rq.onsuccess = function (event) {
t.step(function () {
assert_throws_dom("InvalidStateError", function () {
ostore.createIndex("index", "indexedProperty");
});
});
t.done();
}
}, "If the object store has been deleted, the implementation must throw a DOMException of type InvalidStateError");
async_test(t => {
let db;
const open_rq = createdb(t);
open_rq.onupgradeneeded = function (event) {
db = event.target.result;
db.createObjectStore("store");
}
open_rq.onsuccess = function (event) {
let txn = db.transaction("store", "readwrite", { durability: 'relaxed' });
let ostore = txn.objectStore("store");
t.step(function () {
assert_throws_dom("InvalidStateError", function () {
ostore.createIndex("index", "indexedProperty");
});
});
t.done();
}
}, "Operate out versionchange throw InvalidStateError");
/* IndexedDB: Exception Order of IDBObjectStore.createIndex() */
indexeddb_test(
function (t, db, txn) {
let store = db.createObjectStore("s");
},
function (t, db) {
let txn = db.transaction("s", "readonly", { durability: 'relaxed' });
let store = txn.objectStore("s");
txn.oncomplete = function () {
assert_throws_dom("InvalidStateError", function () {
store.createIndex("index", "foo");
});
t.done();
};
},
"InvalidStateError(Incorrect mode) vs. TransactionInactiveError. Mode check should precede state check of the transaction."
);
let gDeletedObjectStore;
indexeddb_test(
function (t, db, txn) {
gDeletedObjectStore = db.createObjectStore("s");
db.deleteObjectStore("s");
txn.oncomplete = function () {
assert_throws_dom("InvalidStateError", function () {
gDeletedObjectStore.createIndex("index", "foo");
});
t.done();
};
},
null,
"InvalidStateError(Deleted ObjectStore) vs. TransactionInactiveError. Deletion check should precede transaction-state check."
);
indexeddb_test(
function (t, db, txn) {
let store = db.createObjectStore("s");
store.createIndex("index", "foo");
txn.oncomplete = function () {
assert_throws_dom("TransactionInactiveError", function () {
store.createIndex("index", "foo");
});
t.done();
};
},
null,
"TransactionInactiveError vs. ConstraintError. Transaction-state check should precede index name check."
);
indexeddb_test(
function (t, db) {
let store = db.createObjectStore("s");
store.createIndex("index", "foo");
assert_throws_dom("ConstraintError", function () {
store.createIndex("index", "invalid key path");
});
assert_throws_dom("ConstraintError", function () {
store.createIndex("index",
["invalid key path 1", "invalid key path 2"]);
});
t.done();
},
null,
"ConstraintError vs. SyntaxError. Index name check should precede syntax check of the key path"
);
indexeddb_test(
function (t, db) {
let store = db.createObjectStore("s");
assert_throws_dom("SyntaxError", function () {
store.createIndex("index",
["invalid key path 1", "invalid key path 2"],
{ multiEntry: true });
});
t.done();
},
null,
"SyntaxError vs. InvalidAccessError. Syntax check should precede multiEntry check of the key path."
);
/* AutoIncrement in Compound Index */
indexeddb_test(
function (t, db, txn) {
// No auto-increment
let store = db.createObjectStore("Store1", { keyPath: "id" });
store.createIndex("CompoundKey", ["num", "id"]);
// Add data
store.put({ id: 1, num: 100 });
},
function (t, db) {
let store = db.transaction("Store1", "readwrite", { durability: 'relaxed' }).objectStore("Store1");
store.openCursor().onsuccess = t.step_func(function (e) {
let item = e.target.result.value;
store.index("CompoundKey").get([item.num, item.id]).onsuccess = t.step_func(function (e) {
assert_equals(e.target.result ? e.target.result.num : null, 100, 'Expected 100.');
t.done();
});
});
},
"Explicit Primary Key"
);
indexeddb_test(
function (t, db, txn) {
// Auto-increment
let store = db.createObjectStore("Store2", { keyPath: "id", autoIncrement: true });
store.createIndex("CompoundKey", ["num", "id"]);
// Add data
store.put({ num: 100 });
},
function (t, db) {
let store = db.transaction("Store2", "readwrite", { durability: 'relaxed' }).objectStore("Store2");
store.openCursor().onsuccess = t.step_func(function (e) {
let item = e.target.result.value;
store.index("CompoundKey").get([item.num, item.id]).onsuccess = t.step_func(function (e) {
assert_equals(e.target.result ? e.target.result.num : null, 100, 'Expected 100.');
t.done();
});
});
},
"Auto-Increment Primary Key"
);
indexeddb_test(
function (t, db, txn) {
// Auto-increment
let store = db.createObjectStore("Store3", { keyPath: "id", autoIncrement: true });
store.createIndex("CompoundKey", ["num", "id", "other"]);
let num = 100;
// Add data to Store3 - valid keys
// Objects will be stored in Store3 and keys will get added
// to the CompoundKeys index.
store.put({ num: num++, other: 0 });
store.put({ num: num++, other: [0] });
// Add data - missing key
// Objects will be stored in Store3 but keys won't get added to
// the CompoundKeys index because the 'other' keypath doesn't
// resolve to a value.
store.put({ num: num++ });
// Add data to Store3 - invalid keys
// Objects will be stored in Store3 but keys won't get added to
// the CompoundKeys index because the 'other' property values
// aren't valid keys.
store.put({ num: num++, other: null });
store.put({ num: num++, other: {} });
store.put({ num: num++, other: [null] });
store.put({ num: num++, other: [{}] });
},
function (t, db) {
let store = db.transaction("Store3", "readwrite", { durability: 'relaxed' }).objectStore("Store3");
const keys = [];
let count;
store.count().onsuccess = t.step_func(e => { count = e.target.result; });
store.index("CompoundKey").openCursor().onsuccess = t.step_func(function (e) {
const cursor = e.target.result;
if (cursor !== null) {
keys.push(cursor.key);
cursor.continue();
return;
}
// Done iteration, check results.
assert_equals(count, 7, 'Expected all 7 records to be stored.');
assert_equals(keys.length, 2, 'Expected exactly two index entries.');
assert_array_equals(keys[0], [100, 1, 0]);
assert_object_equals(keys[1], [101, 2, [0]]);
t.done();
});
},
"Auto-Increment Primary Key - invalid key values elsewhere"
);