blob: ed67dc0f924fabc52708229999cedc91601c8cf4 [file] [log] [blame]
if (this.importScripts) {
importScripts('../../../resources/js-test.js');
importScripts('shared.js');
}
description("Test that expected exceptions are thrown per IndexedDB spec.");
indexedDBTest(prepareDatabase, testDatabase);
function prepareDatabase()
{
db = event.target.result;
evalAndLog("store = db.createObjectStore('store')");
evalAndLog("index = store.createIndex('index', 'id')");
evalAndLog("store.put({id: 'a'}, 1)");
evalAndLog("store.put({id: 'b'}, 2)");
evalAndLog("store.put({id: 'c'}, 3)");
evalAndLog("store.put({id: 'd'}, 4)");
evalAndLog("store.put({id: 'e'}, 5)");
evalAndLog("store.put({id: 'f'}, 6)");
evalAndLog("store.put({id: 'g'}, 7)");
evalAndLog("store.put({id: 'h'}, 8)");
evalAndLog("store.put({id: 'i'}, 9)");
evalAndLog("store.put({id: 'j'}, 10)");
evalAndLog("otherStore = db.createObjectStore('otherStore')");
evalAndLog("inlineKeyStore = db.createObjectStore('inlineKeyStore', {keyPath: 'id'})");
evalAndLog("request = inlineKeyStore.put({id: 0})");
shouldBeEqualToString("request.readyState", "pending");
debug("");
debug("3.2.1 The IDBRequest Interface");
debug("");
debug("IDBRequest.error");
debug("When the done flag is false, getting this property must throw a DOMException of type InvalidStateError.");
evalAndExpectException("request.error", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
debug("");
debug("IDBRequest.result");
debug("When the done flag is false, getting this property must throw a DOMException of type InvalidStateError.");
evalAndExpectException("request.result", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
debug("");
debug("3.2.3 Opening a database");
debug("");
debug("IDBFactory.cmp()");
debug("One of the supplied keys was not a valid key.");
evalAndExpectException("indexedDB.cmp(null, 0)", "0", "'DataError'");
}
function testDatabase()
{
evalAndLog("db.close()");
debug("");
debug("3.2.4 Database");
request = evalAndLog("indexedDB.open(dbname, 2)");
request.onerror = unexpectedErrorCallback;
request.onblocked = unexpectedBlockedCallback;
request.onsuccess = checkTransactionAndObjectStoreExceptions;
request.onupgradeneeded = function() {
db = request.result;
var trans = request.transaction;
trans.onabort = unexpectedAbortCallback;
debug("");
debug("IDBDatabase.createObjectStore()");
debug("If an objectStore with the same name already exists, the implementation must throw a DOMException of type ConstraintError.");
evalAndExpectException("db.createObjectStore('store')", "0", "'ConstraintError'");
debug("If keyPath is not a valid key path then a DOMException of type SyntaxError must be thrown.");
evalAndExpectException("db.createObjectStore('fail', {keyPath: '-invalid-'})", "DOMException.SYNTAX_ERR", "'SyntaxError'");
debug("If the optionalParameters parameter is specified, and autoIncrement is set to true, and the keyPath parameter is specified to the empty string, or specified to an Array, this function must throw a InvalidAccessError exception.");
evalAndExpectException("db.createObjectStore('fail', {autoIncrement: true, keyPath: ''})", "DOMException.INVALID_ACCESS_ERR", "'InvalidAccessError'");
evalAndExpectException("db.createObjectStore('fail', {autoIncrement: true, keyPath: ['a']})", "DOMException.INVALID_ACCESS_ERR", "'InvalidAccessError'");
debug("");
debug("IDBDatabase.deleteObjectStore()");
debug("There is no object store with the given name, compared in a case-sensitive manner, in the connected database.");
evalAndExpectException("db.deleteObjectStore('no-such-store')", "DOMException.NOT_FOUND_ERR", "'NotFoundError'");
debug("");
debug("IDBDatabase.transaction()");
debug('If this method is called on IDBDatabase object for which a "versionchange" transaction is still running, a InvalidStateError exception must be thrown.');
evalAndExpectException("db.transaction('store')", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
};
}
function checkTransactionAndObjectStoreExceptions()
{
debug("One of the names provided in the storeNames argument doesn't exist in this database.");
evalAndExpectException("db.transaction('no-such-store')", "DOMException.NOT_FOUND_ERR", "'NotFoundError'");
debug("The value for the mode parameter is invalid.");
evalAndExpectExceptionClass("db.transaction('store', 'invalid-mode')", "TypeError");
debug("The function was called with an empty list of store names");
evalAndExpectException("db.transaction([])", "DOMException.INVALID_ACCESS_ERR", "'InvalidAccessError'");
debug("");
debug("One more IDBDatabase.createObjectStore() test:");
debug('If this function is called from outside a "versionchange" transaction callback ... the implementation must throw a DOMException of type InvalidStateError.');
evalAndExpectException("db.createObjectStore('fail')", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
debug("");
debug("One more IDBDatabase.deleteObjectStore() test:");
debug('If this function is called from outside a "versionchange" transaction callback ... the implementation must throw a DOMException of type InvalidStateError.');
evalAndExpectException("db.deleteObjectStore('fail')", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
prepareStoreAndIndex();
}
function prepareStoreAndIndex()
{
debug("");
debug("Prepare an object store and index from an inactive transaction for later use.");
evalAndLog("finishedTransaction = inactiveTransaction = db.transaction('store')");
inactiveTransaction.onabort = unexpectedAbortCallback;
evalAndLog("storeFromInactiveTransaction = inactiveTransaction.objectStore('store')");
evalAndLog("indexFromInactiveTransaction = storeFromInactiveTransaction.index('index')");
evalAndLog("request = storeFromInactiveTransaction.openCursor()");
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
evalAndLog("cursorFromInactiveTransaction = request.result");
};
inactiveTransaction.oncomplete = testObjectStore;
}
function testObjectStore()
{
debug("");
debug("3.2.5 Object Store");
evalAndLog("ro_transaction = db.transaction('store', 'readonly')");
evalAndLog("storeFromReadOnlyTransaction = ro_transaction.objectStore('store')");
evalAndLog("rw_transaction = db.transaction('store', 'readwrite')");
evalAndLog("store = rw_transaction.objectStore('store')");
debug("");
debug("IDBObjectStore.add()");
debug('This method throws a DOMException of type ReadOnlyError if the transaction which this IDBObjectStore belongs to is has its mode set to "readonly".');
evalAndExpectException("storeFromReadOnlyTransaction.add(0, 0)", "0", "'ReadOnlyError'");
// "If any of the following conditions are true, this method throws a DOMException of type DataError:" - covered in objectstore-basics.html
debug("The transaction this IDBObjectStore belongs to is not active.");
evalAndExpectException("storeFromInactiveTransaction.add(0, 0)", "0", "'TransactionInactiveError'");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("The data being stored could not be cloned by the internal structured cloning algorithm.");
evalAndExpectException("store.add(self, 0)", "DOMException.DATA_CLONE_ERR"); // FIXME: Test 'DataCloneError' name when DOM4 exceptions are used in binding.
debug("");
debug("IDBObjectStore.clear()");
debug('This method throws a DOMException of type ReadOnlyError if the transaction which this IDBObjectStore belongs to is has its mode set to "readonly".');
evalAndExpectException("storeFromReadOnlyTransaction.clear()", "0", "'ReadOnlyError'");
debug("The transaction this IDBObjectStore belongs to is not active.");
evalAndExpectException("storeFromInactiveTransaction.clear()", "0", "'TransactionInactiveError'");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("");
debug("IDBObjectStore.count()");
debug("If the optional key parameter is not a valid key or a key range, this method throws a DOMException of type DataError.");
evalAndExpectException("store.count({})", "0", "'DataError'");
debug("The transaction this IDBObjectStore belongs to is not active.");
evalAndExpectException("storeFromInactiveTransaction.count()", "0", "'TransactionInactiveError'");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("");
debug("IDBObjectStore.delete()");
debug('This method throws a DOMException of type ReadOnlyError if the transaction which this IDBObjectStore belongs to is has its mode set to "readonly".');
evalAndExpectException("storeFromReadOnlyTransaction.delete(0)", "0", "'ReadOnlyError'");
debug("If the key parameter is not a valid key or a key range this method throws a DOMException of type DataError.");
evalAndExpectException("store.delete({})", "0", "'DataError'");
debug("The transaction this IDBObjectStore belongs to is not active.");
evalAndExpectException("storeFromInactiveTransaction.add(0, 0)", "0", "'TransactionInactiveError'");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("");
debug("IDBObjectStore.get()");
debug("If the key parameter is not a valid key or a key range, this method throws a DOMException of type DataError.");
evalAndExpectException("store.get({})", "0", "'DataError'");
debug("The transaction this IDBObjectStore belongs to is not active.");
evalAndExpectException("storeFromInactiveTransaction.get(0)", "0", "'TransactionInactiveError'");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("");
debug("IDBObjectStore.index()");
debug("There is no index with the given name, compared in a case-sensitive manner, in the connected database.");
evalAndExpectException("store.index('no-such-index')", "DOMException.NOT_FOUND_ERR", "'NotFoundError'");
debug("Occurs if a request is made on a source object that has been deleted or removed, or if the transaction the object store belongs to has finished.");
evalAndExpectException("storeFromInactiveTransaction.index('index')", "11", "'InvalidStateError'");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("");
debug("IDBObjectStore.openCursor()");
debug("If the range parameter is specified but is not a valid key or a key range, this method throws a DOMException of type DataError.");
evalAndExpectException("store.openCursor({})", "0", "'DataError'");
debug("The transaction this IDBObjectStore belongs to is not active.");
evalAndExpectException("storeFromInactiveTransaction.openCursor()", "0", "'TransactionInactiveError'");
debug("The value for the direction parameter is invalid.");
evalAndExpectExceptionClass("store.openCursor(0, 'invalid-direction')", "TypeError");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("");
debug("IDBObjectStore.put()");
debug('This method throws a DOMException of type ReadOnlyError if the transaction which this IDBObjectStore belongs to is has its mode set to "readonly".');
evalAndExpectException("storeFromReadOnlyTransaction.put(0, 0)", "0", "'ReadOnlyError'");
// "If any of the following conditions are true, this method throws a DOMException of type DataError:" - covered in objectstore-basics.html
debug("The transaction this IDBObjectStore belongs to is not active.");
evalAndExpectException("storeFromInactiveTransaction.put(0, 0)", "0", "'TransactionInactiveError'");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("The data being stored could not be cloned by the internal structured cloning algorithm.");
evalAndExpectException("store.put(self, 0)", "DOMException.DATA_CLONE_ERR"); // FIXME: Test 'DataCloneError' name when DOM4 exceptions are used in binding.
evalAndLog("db.close()");
evalAndLog("ro_transaction.oncomplete = transactionComplete");
evalAndLog("rw_transaction.oncomplete = transactionComplete");
}
var numCompleted = 0;
function transactionComplete(evt)
{
preamble(evt);
numCompleted++;
if (numCompleted == 1) {
debug("First transaction completed");
return;
}
evalAndLog("request = indexedDB.open(dbname, 3)");
request.onerror = unexpectedErrorCallback;
request.onblocked = unexpectedBlockedCallback;
evalAndLog("request.onupgradeneeded = onUpgradeNeeded3");
}
function onUpgradeNeeded3()
{
db = request.result;
var trans = request.transaction;
trans.onabort = unexpectedAbortCallback;
trans.oncomplete = testOutsideVersionChangeTransaction;
store = trans.objectStore('store');
debug("");
debug("IDBObjectStore.createIndex()");
debug("If an index with the same name already exists, the implementation must throw a DOMException of type ConstraintError. ");
evalAndExpectException("store.createIndex('index', 'keyPath')", "0", "'ConstraintError'");
debug("If keyPath is not a valid key path then a DOMException of type SyntaxError must be thrown.");
evalAndExpectException("store.createIndex('fail', '-invalid-')", "DOMException.SYNTAX_ERR", "'SyntaxError'");
debug("If keyPath is an Array and the multiEntry property in the optionalParameters is true, then a DOMException of type InvalidAccessError must be thrown.");
evalAndExpectException("store.createIndex('fail', ['a'], {multiEntry: true})", "DOMException.INVALID_ACCESS_ERR", "'InvalidAccessError'");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("");
debug("IDBObjectStore.deleteIndex()");
debug("There is no index with the given name, compared in a case-sensitive manner, in the connected database.");
evalAndExpectException("store.deleteIndex('no-such-index')", "DOMException.NOT_FOUND_ERR", "'NotFoundError'");
}
function testOutsideVersionChangeTransaction() {
debug("");
debug("One more IDBObjectStore.createIndex() test:");
debug('If this function is called from outside a "versionchange" transaction callback ... the implementation must throw a DOMException of type InvalidStateError.');
evalAndExpectException("db.transaction('store').objectStore('store').createIndex('fail', 'keyPath')", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
debug("");
debug("One more IDBObjectStore.deleteIndex() test:");
debug('If this function is called from outside a "versionchange" transaction callback ... the implementation must throw a DOMException of type InvalidStateError.');
evalAndExpectException("db.transaction('store').objectStore('store').deleteIndex('fail', 'keyPath')", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
testIndex();
}
function testIndex()
{
debug("");
debug("3.2.6 Index");
evalAndLog("indexFromReadOnlyTransaction = db.transaction('store', 'readonly').objectStore('store').index('index')");
evalAndLog("index = db.transaction('store', 'readwrite').objectStore('store').index('index')");
debug("");
debug("IDBIndex.count()");
debug("If the optional key parameter is not a valid key or a key range, this method throws a DOMException of type DataError.");
evalAndExpectException("index.count({})", "0", "'DataError'");
debug("The transaction this IDBIndex belongs to is not active.");
evalAndExpectException("indexFromInactiveTransaction.count()", "0", "'TransactionInactiveError'");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("");
debug("IDBIndex.get()");
debug("If the key parameter is not a valid key or a key range, this method throws a DOMException of type DataError.");
evalAndExpectException("index.get({})", "0", "'DataError'");
debug("The transaction this IDBIndex belongs to is not active.");
evalAndExpectException("indexFromInactiveTransaction.get(0)", "0", "'TransactionInactiveError'");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("");
debug("IDBIndex.getKey()");
debug("If the key parameter is not a valid key or a key range, this method throws a DOMException of type DataError.");
evalAndExpectException("index.getKey({})", "0", "'DataError'");
debug("The transaction this IDBIndex belongs to is not active.");
evalAndExpectException("indexFromInactiveTransaction.getKey(0)", "0", "'TransactionInactiveError'");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("");
debug("IDBIndex.openCursor()");
debug("If the range parameter is specified but is not a valid key or a key range, this method throws a DOMException of type DataError.");
evalAndExpectException("index.openCursor({})", "0", "'DataError'");
debug("The transaction this IDBIndex belongs to is not active.");
evalAndExpectException("indexFromInactiveTransaction.openCursor()", "0", "'TransactionInactiveError'");
debug("The value for the direction parameter is invalid.");
evalAndExpectExceptionClass("index.openCursor(0, 'invalid-direction')", "TypeError");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
debug("");
debug("IDBIndex.openKeyCursor()");
debug("If the range parameter is specified but is not a valid key or a key range, this method throws a DOMException of type DataError.");
evalAndExpectException("index.openKeyCursor({})", "0", "'DataError'");
debug("The transaction this IDBIndex belongs to is not active.");
evalAndExpectException("indexFromInactiveTransaction.openKeyCursor()", "0", "'TransactionInactiveError'");
debug("The value for the direction parameter is invalid.");
evalAndExpectExceptionClass("index.openKeyCursor(0, 'invalid-direction')", "TypeError");
// "Occurs if a request is made on a source object that has been deleted or removed." - covered in deleted-objects.html
testCursor();
}
function testCursor()
{
debug("");
debug("3.2.7 Cursor");
evalAndLog("transaction = db.transaction(['store', 'inlineKeyStore'], 'readwrite')");
makeCursor();
function makeCursor() {
evalAndLog("request = transaction.objectStore('store').openCursor()");
primaryCursorRequest = request;
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
evalAndLog("cursor = request.result");
request.onsuccess = null;
makeKeyCursor();
};
}
function makeKeyCursor() {
evalAndLog("request = transaction.objectStore('store').index('index').openKeyCursor()");
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
evalAndLog("keyCursor = request.result");
request.onsuccess = null;
makeReverseCursor();
};
}
function makeReverseCursor() {
evalAndLog("request = transaction.objectStore('store').openCursor(IDBKeyRange.lowerBound(-Infinity), 'prev')");
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
evalAndLog("reverseCursor = request.result");
request.onsuccess = null;
makeInlineCursor();
};
}
function makeInlineCursor() {
evalAndLog("request = transaction.objectStore('inlineKeyStore').openCursor()");
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
evalAndLog("inlineCursor = request.result");
request.onsuccess = null;
testCursorAdvance();
};
}
function testCursorAdvance() {
debug("");
debug("IDBCursor.advance()");
debug("Calling this method more than once before new cursor data has been loaded is not allowed and results in a DOMException of type InvalidStateError being thrown.");
debug("If the value for count is 0 (zero) or a negative number, this method must throw a JavaScript TypeError exception.");
evalAndExpectExceptionClass("cursor.advance(0)", "TypeError");
evalAndLog("cursor.advance(1)");
evalAndExpectException("cursor.advance(1)", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
debug("The transaction this IDBCursor belongs to is not active.");
evalAndExpectException("cursorFromInactiveTransaction.advance(1)", "0", "'TransactionInactiveError'");
primaryCursorRequest.onsuccess = testCursorContinue;
}
function testCursorContinue() {
debug("");
debug("IDBCursor.continue()");
debug("The parameter is not a valid key.");
evalAndExpectException("cursor.continue({})", "0", "'DataError'");
debug("The parameter is less than or equal to this cursor's position and this cursor's direction is \"next\" or \"nextunique\".");
evalAndExpectException("cursor.continue(-Infinity)", "0", "'DataError'");
debug("The parameter is greater than or equal to this cursor's position and this cursor's direction is \"prev\" or \"prevunique\".");
evalAndExpectException("reverseCursor.continue(100)", "0", "'DataError'");
debug("Calling this method more than once before new cursor data has been loaded is not allowed and results in a DOMException of type InvalidStateError being thrown.");
evalAndLog("cursor.continue()");
evalAndExpectException("cursor.continue()", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
debug("The transaction this IDBCursor belongs to is not active.");
evalAndExpectException("cursorFromInactiveTransaction.continue()", "0", "'TransactionInactiveError'");
testCursorDelete();
}
function testCursorDelete() {
debug("");
debug("IDBCursor.delete()");
debug("If this cursor's got value flag is false, or if this cursor was created using openKeyCursor a DOMException of type InvalidStateError is thrown.");
evalAndExpectException("keyCursor.delete()", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
debug("The transaction this IDBCursor belongs to is not active.");
evalAndExpectException("cursorFromInactiveTransaction.delete()", "0", "'TransactionInactiveError'");
primaryCursorRequest.onsuccess = testCursorUpdate;
}
function testCursorUpdate() {
debug("");
debug("IDBCursor.update()");
debug("If this cursor's got value flag is false or if this cursor was created using openKeyCursor. This method throws a DOMException of type InvalidStateError.");
evalAndExpectException("keyCursor.update({})", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
debug("If the effective object store of this cursor uses in-line keys and evaluating the key path of the value parameter results in a different value than the cursor's effective key, this method throws a DOMException of type DataError.");
evalAndExpectException("inlineCursor.update({id: 1})", "0", "'DataError'");
debug("If the structured clone algorithm throws an exception, that exception is rethrown.");
evalAndExpectException("cursor.update(self)", "DOMException.DATA_CLONE_ERR"); // FIXME: Test 'DataCloneError' name when DOM4 exceptions are used in binding.
debug("The transaction this IDBCursor belongs to is not active.");
evalAndExpectException("cursorFromInactiveTransaction.update({})", "0", "'TransactionInactiveError'");
primaryCursorRequest.onsuccess = null;
makeReadOnlyCursor();
}
// Can't have both transactions running at once, so these tests must be separated out.
function makeReadOnlyCursor() {
evalAndLog("readOnlyTransaction = db.transaction('store', 'readonly')");
evalAndLog("request = readOnlyTransaction.objectStore('store').openCursor()");
request.onerror = unexpectedErrorCallback;
request.onsuccess = function() {
evalAndLog("cursorFromReadOnlyTransaction = request.result");
doReadOnlyCursorTests();
};
}
function doReadOnlyCursorTests() {
debug("");
debug("One more IDBCursor.delete() test:");
debug('This method throws a DOMException of type ReadOnlyError if the transaction which this IDBCursor belongs to has its mode set to "readonly".');
evalAndExpectException("cursorFromReadOnlyTransaction.delete()", "0", "'ReadOnlyError'");
debug("");
debug("One more IDBCursor.update() test:");
debug('This method throws a DOMException of type ReadOnlyError if the transaction which this IDBCursor belongs to has its mode set to "readonly".');
evalAndExpectException("cursorFromReadOnlyTransaction.update({})", "0", "'ReadOnlyError'");
testTransaction();
}
}
function testTransaction()
{
debug("");
debug("3.2.8 Transaction");
debug("");
debug("IDBTransaction.abort()");
debug("If this transaction is finished, throw a DOMException of type InvalidStateError. ");
evalAndExpectException("finishedTransaction.abort()", "DOMException.INVALID_STATE_ERR", "'InvalidStateError'");
debug("If the requested object store is not in this transaction's scope.");
evalAndExpectException("db.transaction('store').objectStore('otherStore')", "DOMException.NOT_FOUND_ERR", "'NotFoundError'");
finishJSTest();
}