Chromium Code Reviews| Index: third_party/WebKit/LayoutTests/storage/indexeddb/rename-object-store.html |
| diff --git a/third_party/WebKit/LayoutTests/storage/indexeddb/rename-object-store.html b/third_party/WebKit/LayoutTests/storage/indexeddb/rename-object-store.html |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c418d772ec7788fc39a1cfe61827da6fda58ebbc |
| --- /dev/null |
| +++ b/third_party/WebKit/LayoutTests/storage/indexeddb/rename-object-store.html |
| @@ -0,0 +1,519 @@ |
| +<!DOCTYPE html> |
| +<title>IndexedDB v2: object store renaming support</title> |
|
jsbell
2016/08/26 23:20:50
nit: I wouldn't bother with the 'v2' here. (I thin
pwnall
2016/08/29 19:24:12
Done.
|
| +<script src='../../resources/testharness.js'></script> |
| +<link rel="help" |
| + href="https://w3c.github.io/IndexedDB/#dom-idbobjectstore-name"> |
| +<link rel="author" href="pwnall@chromium.org" title="Victor Costan"> |
| +<script src='../../resources/testharnessreport.js'></script> |
| +<script> |
| + |
| +// Returns an IndexedDB database name likely to be unique to the test case. |
| +const databaseName = function(testCase) { |
| + return 'db' + self.location.pathname + '-' + testCase.name; |
| +}; |
| + |
| +// Creates an EventWatcher covering all the events that can be issued by |
| +// IndexedDB requests and transactions. |
| +const requestWatcher = function(testCase, request) { |
| + return new EventWatcher(testCase, request, |
| + ['abort', 'complete', 'error', 'success', 'upgradeneeded']); |
|
jsbell
2016/08/26 23:20:50
This is only used for requests here, so I'd remove
pwnall
2016/08/29 19:24:12
Done.
|
| +}; |
| + |
| +// Migrates an IndexedDB database whose name is unique for the test case. |
| +// |
| +// setupCallback will be called during a versionchange transaction, and will be |
| +// given the created database and the versionchange transaction. |
| +// |
| +// Returns a promise that yields an IndexedDB open success event. |
| +const migrateDatabase = function(testCase, newVersion, setupCallback) { |
| + // We cannot use eventWatcher.wait_for('upgradeneeded') here, because |
| + // the versionchange transaction auto-commits before the Promise's then |
| + // callback gets called. |
| + return new Promise((resolve, reject) => { |
| + const request = indexedDB.open(databaseName(testCase), newVersion); |
| + request.onupgradeneeded = (event) => { |
|
jsbell
2016/08/26 23:20:50
nit: don't need () around single argument (here an
pwnall
2016/08/29 19:24:12
Done.
|
| + const eventWatcher = requestWatcher(testCase, request); |
| + const database = event.target.result; |
| + const transaction = event.target.transaction; |
| + setupCallback(database, transaction); |
| + resolve(eventWatcher.wait_for('success')); |
| + }; |
| + request.onerror = |
|
jsbell
2016/08/26 23:20:50
Do you need to reject() here or does that get hand
pwnall
2016/08/29 19:24:12
Done.
You were right, I needed the reject(). Witho
|
| + testCase.unreached_func('indexedDB.open issued an error event'); |
| + }); |
| +}; |
| + |
| +// Creates an IndexedDB database whose name is unique for the test case. |
| +// |
| +// setupCallback will be called during a versionchange transaction, and will be |
| +// given the created database and the versionchange transaction. |
| +// |
| +// Returns a promise that yields an IndexedDB open success event. |
|
jsbell
2016/08/26 23:20:50
In every use of this, only the result is used not
pwnall
2016/08/29 19:24:12
Done.
|
| +const createDatabase = function(testCase, setupCallback) { |
| + const request = indexedDB.deleteDatabase(databaseName(testCase)); |
| + const eventWatcher = requestWatcher(testCase, request); |
| + |
| + return eventWatcher.wait_for('success').then((event) => { |
| + return migrateDatabase(testCase, 1, setupCallback); |
|
jsbell
2016/08/26 23:20:50
... => { return y; }
can be simplified to:
... =
pwnall
2016/08/29 19:24:12
Done.
I did this simplification whenever possible
|
| + }); |
| +}; |
| + |
| +// Creates a 'books' object store whose contents closely resembles the first |
| +// example in the IndexedDB specification. |
| +const createBooksStore = function(testCase, database) { |
| + const store = database.createObjectStore('books', |
| + { keyPath: 'isbn', autoIncrement: true }); |
| + const index = store.createIndex('by_author', 'author'); |
| + store.put({title: 'Quarry Memories', author: 'Fred', isbn: 123456}); |
| + store.put({title: 'Water Buffaloes', author: 'Fred', isbn: 234567}); |
| + store.put({title: 'Bedrock Nights', author: 'Barney', isbn: 345678}); |
| + return store; |
| +}; |
| + |
| +// Creates a 'not_books' object store used to test renaming into existing or |
| +// deleted store names. |
| +const createNotBooksStore = function(testCase, database) { |
| + const store = database.createObjectStore('not_books'); |
| + return store; |
| +}; |
| + |
| +// Renames the 'books' store into a 'not books' store. |
| +// |
| +// Returns a Promise that yields an IndexedDB open success event. |
| +const renameBooksStore = function(testCase) { |
| + return migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + store.name = 'renamed_books'; |
| + }); |
| +}; |
| + |
| +// Verifies that an object store's contents matches the contents used to create |
| +// the books store in the test database's version 1. |
| +const checkStoreContents = function(testCase, store) { |
| + const request = store.get(123456); |
| + const eventWatcher = requestWatcher(testCase, request); |
| + return eventWatcher.wait_for('success').then(() => { |
| + let result = request.result; |
| + testCase.step(() => { |
| + assert_equals(result.isbn, 123456); |
| + assert_equals(result.author, 'Fred'); |
| + assert_equals(result.title, 'Quarry Memories'); |
| + }); |
| + }); |
| +}; |
| + |
| +// Verifies that an object store's index matches the index used to create the |
| +// books store in the test database's version 1. |
| +const checkStoreIndex = function(testCase, store) { |
| + testCase.step(() => { |
| + assert_array_equals(store.indexNames, ['by_author']); |
| + }); |
| + const index = store.index('by_author'); |
| + const request = index.get('Barney'); |
| + const eventWatcher = requestWatcher(testCase, request); |
| + return eventWatcher.wait_for('success').then(() => { |
| + let result = request.result; |
| + testCase.step(() => { |
| + assert_equals(result.isbn, 345678); |
| + assert_equals(result.title, 'Bedrock Nights'); |
| + }); |
| + }); |
| +}; |
| + |
| +// Verifies that an object store's key generator is in the same state as the |
| +// key generator created for the books store in the test database's version 1. |
| +const checkStoreGenerator = function(testCase, store, expectedKey) { |
| + const request = store.put({title: 'Bedrock Nights II', author: 'Barney'}); |
| + const eventWatcher = requestWatcher(testCase, request); |
| + return eventWatcher.wait_for('success').then(() => { |
| + let result = request.result; |
| + testCase.step(() => { |
| + assert_equals(result, expectedKey); |
| + }); |
| + }); |
| +}; |
| + |
| +promise_test(testCase => { |
| + let bookStore = null, bookStore2 = null; |
| + let renamedBookStore = null, renamedBookStore2 = null; |
| + return createDatabase(testCase, (database, transaction) => { |
| + bookStore = createBooksStore(testCase, database); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + testCase.step(() => { |
| + assert_array_equals(database.objectStoreNames, ['books']); |
| + }); |
| + const transaction = database.transaction('books', 'readonly'); |
| + bookStore2 = transaction.objectStore('books'); |
| + // If checkStoreContents fails here, its implementation is incorrect. |
| + return checkStoreContents(testCase, bookStore2).then(() => { |
| + database.close(); |
| + }); |
| + }).then(() => { |
| + return migrateDatabase(testCase, 2, (database, transaction) => { |
| + renamedBookStore = transaction.objectStore('books'); |
| + renamedBookStore.name = 'renamed_books'; |
| + testCase.step(() => { |
| + assert_equals(renamedBookStore.name, 'renamed_books'); |
| + }); |
| + }); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + testCase.step(() => { |
| + assert_array_equals(database.objectStoreNames, ['renamed_books']); |
| + }); |
| + const transaction = database.transaction('renamed_books', 'readonly'); |
| + renamedBookStore2 = transaction.objectStore('renamed_books'); |
| + return checkStoreContents(testCase, renamedBookStore2).then(() => { |
| + database.close(); |
| + }); |
| + }).then(() => { |
| + testCase.step(() => { |
| + assert_equals(bookStore.name, 'books'); |
| + assert_equals(bookStore2.name, 'books'); |
| + assert_equals(renamedBookStore.name, 'renamed_books'); |
| + assert_equals(renamedBookStore2.name, 'renamed_books'); |
| + }); |
| + }); |
| +}, 'IndexedDB object store rename in new transaction', { |
| + assert: 'An object store can be renamed in a new version change transaction', |
|
jsbell
2016/08/26 23:20:50
Does this assert: test property add much? They're
pwnall
2016/08/29 19:24:12
I removed all of them. I was following the guide b
|
| +}); |
| + |
| +promise_test(testCase => { |
| + let renamedBookStore = null, renamedBookStore2 = null; |
| + return createDatabase(testCase, (database, transaction) => { |
| + renamedBookStore = createBooksStore(testCase, database); |
| + renamedBookStore.name = 'renamed_books'; |
| + testCase.step(() => { |
| + assert_equals(renamedBookStore.name, 'renamed_books'); |
| + }); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + testCase.step(() => { |
| + assert_array_equals(database.objectStoreNames, ['renamed_books']); |
| + }); |
| + const transaction = database.transaction('renamed_books', 'readonly'); |
| + renamedBookStore2 = transaction.objectStore('renamed_books'); |
| + return checkStoreContents(testCase, renamedBookStore2).then(() => { |
| + database.close(); |
| + }); |
| + }).then(() => { |
| + testCase.step(() => { |
| + assert_equals(renamedBookStore.name, 'renamed_books'); |
| + assert_equals(renamedBookStore2.name, 'renamed_books'); |
| + }); |
| + }); |
| +}, 'IndexedDB object store rename in the transaction where it is created', { |
| + assert: |
| + 'An object store can be renamed in the transaction where it is created', |
| +}); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + // If checkStoreIndex fails here, its implementation is incorrect. |
| + return checkStoreIndex(testCase, store).then(() => { |
| + database.close(); |
| + }); |
| + }).then(() => { |
| + return renameBooksStore(testCase); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + const transaction = database.transaction('renamed_books', 'readonly'); |
| + const store = transaction.objectStore('renamed_books'); |
| + return checkStoreIndex(testCase, store).then(() => { |
| + database.close(); |
| + }); |
| + }); |
| +}, 'IndexedDB object store rename covers index', { |
| + assert: 'Renaming an object store maintains its indexes', |
| +}); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + const transaction = database.transaction('books', 'readwrite'); |
| + // If checkStoreGenerator fails here, its implementation is incorrect. |
| + const store = transaction.objectStore('books'); |
| + return checkStoreGenerator(testCase, store, 345679).then(() => { |
| + database.close(); |
| + }); |
| + }).then(() => { |
| + return renameBooksStore(testCase); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + const transaction = database.transaction('renamed_books', 'readwrite'); |
| + const store = transaction.objectStore('renamed_books'); |
| + return checkStoreGenerator(testCase, store, 345680).then(() => { |
| + database.close(); |
| + }); |
| + }); |
| +}, 'IndexedDB object store rename covers key generator', { |
| + assert: 'Renaming an object store maintains its key generator', |
| +}); |
| + |
| +promise_test(testCase => { |
| + const dbName = databaseName(testCase); |
| + let bookStore = null, bookStore2 = null, bookStore3 = null; |
| + return createDatabase(testCase, (database, transaction) => { |
| + bookStore = createBooksStore(testCase, database); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + database.close(); |
| + }).then(() => { |
| + return new Promise((resolve, reject) => { |
| + const request = indexedDB.open(dbName, 2); |
| + request.onupgradeneeded = (event) => { |
| + const database = event.target.result; |
| + const transaction = event.target.transaction; |
| + bookStore2 = transaction.objectStore('books'); |
| + bookStore2.name = 'renamed_books'; |
| + testCase.step(() => { |
| + assert_equals(bookStore.name, 'books'); |
| + assert_equals(bookStore2.name, 'renamed_books'); |
| + }); |
| + request.onerror = (event) => { |
| + event.preventDefault(); |
| + resolve(event); |
| + } |
| + transaction.onabort = () => null; |
| + transaction.onerror = () => null; |
| + transaction.abort(); |
| + }; |
| + request.onerror = |
| + testCase.unreached_func('indexedDB.open issued an error event'); |
| + request.onsuccess = testCase.unreached_func( |
| + 'indexedDB.open was not supposed to succeed'); |
| + }); |
| + }).then((event) => { |
| + testCase.step(() => { |
| + assert_equals(bookStore.name, 'books', |
| + 'object store rename not reverted after transaction abort'); |
| + }); |
| + |
| + const request = indexedDB.open(dbName, 1); |
| + const eventWatcher = requestWatcher(testCase, request); |
| + return eventWatcher.wait_for('success'); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + const transaction = database.transaction('books', 'readonly'); |
| + bookStore3 = transaction.objectStore('books'); |
| + return checkStoreContents(testCase, bookStore3).then(() => { |
| + database.close(); |
| + }); |
| + }).then(() => { |
| + testCase.step(() => { |
| + assert_equals(bookStore.name, 'books'); |
| + assert_equals(bookStore2.name, 'books'); |
| + assert_equals(bookStore3.name, 'books'); |
| + }); |
| + }); |
| +}, 'IndexedDB object store rename in aborted transaction', { |
| + assert: 'An object store rename is correctly reverted', |
| +}); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + database.close(); |
| + }).then(() => { |
| + return migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + database.deleteObjectStore('books'); |
| + testCase.step(() => { |
| + assert_throws('InvalidStateError', |
| + () => { store.name = 'renamed_books'; }); |
| + }); |
| + }); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + database.close(); |
| + }); |
| +}, 'IndexedDB object store rename covers key generator', { |
| + assert: 'Renaming an object store maintains its key generator', |
| +}); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + |
| + testCase.step(() => { |
| + assert_throws('InvalidStateError', |
| + () => { store.name = 'renamed_books'; }); |
| + }); |
| + database.close(); |
| + }); |
| +}, 'IndexedDB deleted object store rename throws', { |
| + assert: 'Renaming a deleted object store throws InvalidStateError', |
| +}); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + |
| + testCase.step(() => { |
| + assert_throws('InvalidStateError', |
| + () => { store.name = 'renamed_books'; }); |
| + }); |
| + database.close(); |
| + }); |
| +}, 'IndexedDB object store rename throws in a readwrite transaction', { |
| + assert: 'Renaming an object store in a readwrite transaction throws InvalidStateError', |
| +}); |
| + |
| +promise_test(testCase => { |
| + let bookStore = null; |
| + return createDatabase(testCase, (database, transaction) => { |
| + bookStore = createBooksStore(testCase, database); |
| + }).then((event) => { |
| + testCase.step(() => { |
| + assert_throws('TransactionInactiveError', |
| + () => { bookStore.name = 'renamed_books'; }); |
| + }); |
| + const database = event.target.result; |
| + database.close(); |
| + }); |
| +}, 'IndexedDB object store rename throws in an inactive transaction', { |
| + assert: 'Renaming an object store in an inactive transaction throws TransactionInactiveError', |
| +}); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + database.close(); |
| + }).then(() => { |
| + return migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + store.name = 'books'; |
| + }); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + testCase.step(() => { |
| + assert_array_equals(database.objectStoreNames, ['books']); |
| + }); |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + return checkStoreContents(testCase, store).then(() => { |
| + database.close(); |
| + }); |
| + }); |
| +}, 'IndexedDB object store rename to the same name succeeds', { |
| + assert: 'Renaming an object store to the same name does not throw', |
| +}); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + createNotBooksStore(testCase, database); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + database.close(); |
| + }).then(() => { |
| + return migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + |
| + testCase.step(() => { |
| + assert_throws('ConstraintError', |
| + () => { store.name = 'not_books'; }); |
| + }); |
| + }); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + testCase.step(() => { |
| + assert_array_equals(database.objectStoreNames, |
| + ['books', 'not_books']); |
| + }); |
| + const transaction = database.transaction('books', 'readonly'); |
| + const store = transaction.objectStore('books'); |
| + return checkStoreContents(testCase, store).then(() => { |
| + database.close(); |
| + }); |
| + }); |
| +}, 'IndexedDB object store rename to the name of another store throws', { |
| + assert: 'Renaming an object store to the name of another store throws ConstraintError', |
| +}); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + createNotBooksStore(testCase, database); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + database.close(); |
| + }).then(() => { |
| + return migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + database.deleteObjectStore('not_books'); |
| + store.name = 'not_books'; |
| + }); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + testCase.step(() => { |
| + assert_array_equals(database.objectStoreNames, ['not_books']); |
| + }); |
| + const transaction = database.transaction('not_books', 'readonly'); |
| + const store = transaction.objectStore('not_books'); |
| + return checkStoreContents(testCase, store).then(() => { |
| + database.close(); |
| + }); |
| + }); |
| +}, 'IndexedDB object store rename to the name of a deleted store succeeds', { |
| + assert: 'Renaming an object store to the name of a deleted store does not throw', |
| +}); |
| + |
| +promise_test(testCase => { |
| + return createDatabase(testCase, (database, transaction) => { |
| + createBooksStore(testCase, database); |
| + createNotBooksStore(testCase, database); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + database.close(); |
| + }).then(() => { |
| + return migrateDatabase(testCase, 2, (database, transaction) => { |
| + const store = transaction.objectStore('books'); |
| + |
| + testCase.step(() => { |
| + store.name = 42; |
| + assert_equals(store.name, "42"); |
| + store.name = true; |
| + assert_equals(store.name, "true"); |
| + store.name = () => null; |
| + assert_equals(store.name, "() => null"); |
| + store.name = undefined; |
| + assert_equals(store.name, "undefined"); |
| + }); |
| + }); |
| + }).then((event) => { |
| + const database = event.target.result; |
| + testCase.step(() => { |
| + assert_array_equals(database.objectStoreNames, |
| + ['not_books', 'undefined']); |
| + }); |
| + const transaction = database.transaction('undefined', 'readonly'); |
| + const store = transaction.objectStore('undefined'); |
| + return checkStoreContents(testCase, store).then(() => { |
| + database.close(); |
| + }); |
| + }); |
| +}, 'IndexedDB object store rename to non-string name XXX', { |
| + assert: 'Renaming an object store to the name of another store throws ConstraintError', |
| +}); |
| +</script> |