From a6d816a2c28335d7154c4d5be7a3d9c6d758f7e8 Mon Sep 17 00:00:00 2001 From: Blink WPT Bot Date: Tue, 14 Jan 2025 11:15:16 -0800 Subject: [PATCH] IDB WPTs: Extend transaction-abort tests to workers (#50037) The update modifies transaction-abort related WPTs to run not only in window environments but also on dedicated, service, and shared workers. Bug: 41455766 Change-Id: I126876a53632a87d287ec91ec243144145c381a9 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5912454 Commit-Queue: Rahul Singh Auto-Submit: Garima Chadha Reviewed-by: Steve Becker Reviewed-by: Rahul Singh Cr-Commit-Position: refs/heads/main@{#1405121} Co-authored-by: Garima Chadha --- .../transaction-abort-generator-revert.any.js | 124 +++++++ .../transaction-abort-generator-revert.html | 110 ------ ...saction-abort-index-metadata-revert.any.js | 338 ++++++++++++++++++ ...ansaction-abort-index-metadata-revert.html | 278 -------------- ...tion-abort-multiple-metadata-revert.any.js | 325 +++++++++++++++++ ...action-abort-multiple-metadata-revert.html | 293 --------------- ...-abort-object-store-metadata-revert.any.js | 274 ++++++++++++++ ...on-abort-object-store-metadata-revert.html | 235 ------------ .../transaction-abort-request-error.any.js | 69 ++++ .../transaction-abort-request-error.html | 68 ---- 10 files changed, 1130 insertions(+), 984 deletions(-) create mode 100644 IndexedDB/transaction-abort-generator-revert.any.js delete mode 100644 IndexedDB/transaction-abort-generator-revert.html create mode 100644 IndexedDB/transaction-abort-index-metadata-revert.any.js delete mode 100644 IndexedDB/transaction-abort-index-metadata-revert.html create mode 100644 IndexedDB/transaction-abort-multiple-metadata-revert.any.js delete mode 100644 IndexedDB/transaction-abort-multiple-metadata-revert.html create mode 100644 IndexedDB/transaction-abort-object-store-metadata-revert.any.js delete mode 100644 IndexedDB/transaction-abort-object-store-metadata-revert.html create mode 100644 IndexedDB/transaction-abort-request-error.any.js delete mode 100644 IndexedDB/transaction-abort-request-error.html diff --git a/IndexedDB/transaction-abort-generator-revert.any.js b/IndexedDB/transaction-abort-generator-revert.any.js new file mode 100644 index 00000000000000..0c6ffa52d276e4 --- /dev/null +++ b/IndexedDB/transaction-abort-generator-revert.any.js @@ -0,0 +1,124 @@ +// META: title=IndexedDB: aborting transactions reverts an object store's key generator state +// META: global=window,worker +// META: script=resources/support-promises.js +// META: script=resources/support.js + +// Spec: https://w3c.github.io/IndexedDB/#abort-transaction + +'use strict'; + +promise_test( + testCase => { + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then(() => { + return new Promise((resolve, reject) => { + const request = indexedDB.open(databaseName(testCase), 2); + request.onupgradeneeded = testCase.step_func(event => { + const database = event.target.result; + const transaction = event.target.transaction; + const store = transaction.objectStore('books'); + const request2 = + store.put({title: 'Bedrock Nights II', author: 'Barney'}); + request2.onerror = testCase.unreached_func( + 'IDBObjectStore.put() should not receive an error request'); + request2.onsuccess = testCase.step_func(event => { + assert_equals( + event.target.result, 345679, + 'The key generator\'s current number should be set by ' + + 'the last put operation in the database creation ' + + 'transaction'); + + request.onerror = event => { + event.preventDefault(); + resolve(event); + }; + request.onsuccess = () => reject(new Error( + 'indexedDB.open should not succeed after the ' + + 'versionchange transaction is aborted')); + + transaction.abort(); + }); + }); + request.onerror = event => reject(event.target.error); + request.onsuccess = () => reject(new Error( + 'indexedDB.open should not succeed without creating a ' + + 'versionchange transaction')); + }); + }) + .then(() => { + return openDatabase(testCase, 1); + }) + .then(database => { + const transaction = database.transaction(['books'], 'readwrite'); + const store = transaction.objectStore('books'); + + return checkStoreGenerator( + testCase, store, 345679, + 'The key generator\'s current number should be reverted after the ' + + 'transaction modifying it is aborted') + .then(() => database.close()); + }); + }, + 'The current number of a key generator is reverted when a versionchange ' + + 'transaction aborts'); + +promise_test( + testCase => { + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + }) + .then(database => { + return new Promise((resolve, reject) => { + const transaction = + database.transaction(['books'], 'readwrite'); + const store = transaction.objectStore('books'); + const request = store.put( + {title: 'Bedrock Nights II', author: 'Barney'}); + request.onerror = testCase.unreached_func( + 'IDBObjectStore.put() should not receive an error request'); + request.onsuccess = testCase.step_func(event => { + assert_equals( + event.target.result, 345679, + 'The key generator\'s current number should be set by the ' + + 'last put operation in the database creation transaction'); + + transaction.onabort = event => { + event.preventDefault(); + resolve(event); + }; + transaction.abort(); + }); + transaction.onabort = () => reject(new Error( + 'The aborted readwrite transaction should not receive an ' + + 'abort event before IDBTransaction.abort() is called')); + transaction.oncomplete = () => reject(new Error( + 'The aborted readwrite transaction should not receive a ' + + 'completed event')); + transaction.onerror = () => reject(new Error( + 'The aborted readwrite transaction should not receive an ' + + 'error event')); + }) + .then(() => database); + }) + .then(database => { + const transaction = database.transaction(['books'], 'readwrite'); + const store = transaction.objectStore('books'); + + return checkStoreGenerator( + testCase, store, 345679, + 'The key generator\'s current number should be reverted after the ' + + 'transaction modifying it is aborted') + .then(() => database.close()); + }); + }, + 'The current number of a key generator is reverted when a readwrite ' + + 'transaction aborts'); diff --git a/IndexedDB/transaction-abort-generator-revert.html b/IndexedDB/transaction-abort-generator-revert.html deleted file mode 100644 index bbe0338c3acdc8..00000000000000 --- a/IndexedDB/transaction-abort-generator-revert.html +++ /dev/null @@ -1,110 +0,0 @@ - - -IndexedDB: aborting transactions reverts an object store's key generator state - - - - - - diff --git a/IndexedDB/transaction-abort-index-metadata-revert.any.js b/IndexedDB/transaction-abort-index-metadata-revert.any.js new file mode 100644 index 00000000000000..9b429795c48f38 --- /dev/null +++ b/IndexedDB/transaction-abort-index-metadata-revert.any.js @@ -0,0 +1,338 @@ +// META: title=IndexedDB: aborting transactions reverts index metadata +// META: global=window,worker +// META: script=resources/support-promises.js +// META: script=resources/support.js + +// Spec: https://w3c.github.io/IndexedDB/#abort-transaction + +'use strict'; + +promise_test( + testCase => { + let store = null; + let index = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + store = createNotBooksStore(testCase, database); + index = store.index('not_by_author'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames should include newly created indexes ' + + 'before the transaction is aborted'); + + transaction.abort(); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, immediately after ' + + 'IDBTransaction.abort() returns'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames should stop including the newly ' + + 'created indexes immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, after the transaction is ' + + 'aborted'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames should stop including the newly ' + + 'created indexes after the transaction is aborted'); + }); + }, + 'Created stores get their indexes marked as deleted after the transaction ' + + 'that created them aborts'); + +promise_test( + testCase => { + let store = null; + let index = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + createNotBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + store = transaction.objectStore('not_books'); + index = store.index('not_by_author'); + + database.deleteObjectStore('not_books'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, immediately after ' + + 'IDBDatabase.deleteObjectStore() returns'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames should be empty immediately after ' + + 'IDBDatabase.deleteObjectStore() returns'); + + transaction.abort(); + assert_throws_dom( + 'TransactionInactiveError', () => index.get('query'), + 'IDBIndex.get should throw TransactionInactiveError, indicating ' + + 'that the index is no longer marked for deletion, immediately ' + + 'after IDBTransaction.abort() returns'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames should include the deleted indexes ' + + 'immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'TransactionInactiveError', () => index.get('query'), + 'IDBIndex.get should throw TransactionInactiveError, indicating ' + + 'that the index is no longer marked for deletion, after the ' + + 'transaction is aborted'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames should include the deleted indexes ' + + 'after the transaction is aborted'); + }); + }, + 'Deleted stores get their indexes marked as not-deleted after the ' + + 'transaction that deleted them aborts'); + +promise_test( + testCase => { + let store = null; + let index = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + store = createNotBooksStore(testCase, database); + index = store.index('not_by_author'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames should include newly created indexes ' + + 'before the transaction is aborted'); + + database.deleteObjectStore('not_books'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, immediately after ' + + 'IDBDatabase.deleteObjectStore() returns'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames should be empty immediately after ' + + 'IDBDatabase.deleteObjectStore() returns'); + + transaction.abort(); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is still marked for deletion, immediately after ' + + 'IDBTransaction.abort() returns'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames should not include the newly ' + + 'created indexes immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is still marked for deletion, after the transaction ' + + 'is aborted'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames should not include the newly ' + + 'created indexes after the transaction is aborted'); + }); + }, + 'Created+deleted stores still have their indexes marked as deleted after ' + + 'the transaction aborts'); + +promise_test(testCase => { + let store = null; + let index = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + createNotBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + store = transaction.objectStore('not_books'); + index = store.createIndex('not_by_isbn', 'isbn'); + assert_array_equals( + store.indexNames, + ['not_by_author', 'not_by_isbn', 'not_by_title'], + 'IDBObjectStore.indexNames should include newly created indexes ' + + 'before the transaction is aborted'); + + transaction.abort(); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, immediately after ' + + 'IDBTransaction.abort() returns'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames should stop including the newly ' + + 'created index immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, after the transaction is ' + + 'aborted'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames should stop including the newly ' + + 'created index after the transaction is aborted'); + }); +}, 'Created indexes get marked as deleted after their transaction aborts'); + +promise_test(testCase => { + let store = null; + let index = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + createNotBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + store = transaction.objectStore('not_books'); + index = store.index('not_by_author'); + + store.deleteIndex('not_by_author'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, immediately after ' + + 'IDBObjectStore.deleteIndex() returns'); + assert_array_equals( + store.indexNames, ['not_by_title'], + 'IDBObjectStore.indexNames should not include the deleted index ' + + 'immediately after IDBObjectStore.deleteIndex() returns'); + + transaction.abort(); + assert_throws_dom( + 'TransactionInactiveError', () => index.get('query'), + 'IDBIndex.get should throw TransactionInactiveError, indicating ' + + 'that the index is no longer marked for deletion, immediately ' + + 'after IDBTransaction.abort() returns'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames should include the deleted indexes ' + + 'immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'TransactionInactiveError', () => index.get('query'), + 'IDBIndex.get should throw TransactionInactiveError, indicating ' + + 'that the index is no longer marked for deletion, after the ' + + 'transaction is aborted'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames should include the deleted indexes ' + + 'after the transaction is aborted'); + }); +}, 'Deleted indexes get marked as not-deleted after the transaction aborts'); + +promise_test( + testCase => { + let store = null; + let index = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + createNotBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + store = transaction.objectStore('not_books'); + index = store.createIndex('not_by_isbn', 'isbn'); + assert_array_equals( + store.indexNames, + ['not_by_author', 'not_by_isbn', 'not_by_title'], + 'IDBObjectStore.indexNames should include newly created indexes ' + + 'before the transaction is aborted'); + + store.deleteIndex('not_by_isbn'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, immediately after ' + + 'IDBObjectStore.deleteIndex() returns'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames should not include the deleted index ' + + 'immediately after IDBObjectStore.deleteIndex() returns'); + + transaction.abort(); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is still marked for deletion, immediately after ' + + 'IDBTransaction.abort() returns'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames should stop including the newly ' + + 'created index immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, after the transaction is ' + + 'aborted'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames should stop including the newly ' + + 'created index after the transaction is aborted'); + }); + }, + 'Created+deleted indexes are still marked as deleted after their ' + + 'transaction aborts'); diff --git a/IndexedDB/transaction-abort-index-metadata-revert.html b/IndexedDB/transaction-abort-index-metadata-revert.html deleted file mode 100644 index 54a873d8758c6a..00000000000000 --- a/IndexedDB/transaction-abort-index-metadata-revert.html +++ /dev/null @@ -1,278 +0,0 @@ - - -IndexedDB: aborting transactions reverts index metadata - - - - - - diff --git a/IndexedDB/transaction-abort-multiple-metadata-revert.any.js b/IndexedDB/transaction-abort-multiple-metadata-revert.any.js new file mode 100644 index 00000000000000..9161af8e2baad3 --- /dev/null +++ b/IndexedDB/transaction-abort-multiple-metadata-revert.any.js @@ -0,0 +1,325 @@ +// META: title=IndexedDB: aborting transactions reverts multiple operations on the same metadata +// META: global=window,worker +// META: script=resources/support-promises.js +// META: script=resources/support.js + +// Spec: https://w3c.github.io/IndexedDB/#abort-transaction + +'use strict'; + +promise_test( + testCase => { + let store = null; + let index = null; + let migrationTransaction = null; + let migrationDatabase = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + store = createNotBooksStore(testCase, database); + migrationDatabase = database; + migrationTransaction = transaction; + assert_array_equals( + database.objectStoreNames, ['books', 'not_books'], + 'IDBDatabase.objectStoreNames should include a newly created ' + + 'store before the transaction is aborted'); + assert_array_equals( + transaction.objectStoreNames, ['books', 'not_books'], + 'IDBTransaction.objectStoreNames should include a newly created ' + + 'store before the transaction is aborted'); + + index = store.index('not_by_author'); + store.deleteIndex('not_by_author'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, immediately after ' + + 'IDBObjectStore.deleteIndex() returns'); + assert_array_equals( + store.indexNames, ['not_by_title'], + 'IDBObjectStore.indexNames should not include the deleted index ' + + 'immediately after IDBObjectStore.deleteIndex() returns'); + + transaction.abort(); + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is marked for deletion, immediately after ' + + 'IDBTransaction.abort() returns'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is still marked for deletion, immediately after ' + + 'IDBTransaction.abort() returns'); + assert_array_equals( + transaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should stop including the newly ' + + 'created store immediately after IDBTransaction.abort() returns'); + assert_array_equals( + database.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should stop including the newly ' + + 'created store immediately after IDBTransaction.abort() returns'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames for the newly created store should be ' + + 'empty immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is marked for deletion, after the transaction is ' + + 'aborted'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is still marked for deletion, after the transaction ' + + 'is aborted'); + assert_array_equals( + migrationDatabase.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should stop including the newly ' + + 'created store after the transaction is aborted'); + assert_array_equals( + migrationTransaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should stop including the newly ' + + 'created store after the transaction is aborted'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames for the newly created store should be ' + + 'empty after the transaction is aborted'); + }); + }, + 'Deleted indexes in newly created stores are still marked as deleted ' + + 'after the transaction aborts'); + +promise_test( + testCase => { + let store = null; + let index = null; + let migrationTransaction = null; + let migrationDatabase = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + createNotBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + migrationDatabase = database; + migrationTransaction = transaction; + store = transaction.objectStore('not_books'); + index = store.index('not_by_author'); + store.deleteIndex('not_by_author'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, immediately after ' + + 'IDBObjectStore.deleteIndex() returns'); + assert_array_equals( + store.indexNames, ['not_by_title'], + 'IDBObjectStore.indexNames should not include the deleted index ' + + 'immediately after IDBObjectStore.deleteIndex() returns'); + + database.deleteObjectStore('not_books'); + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is marked for deletion, immediately after ' + + 'IDBDatabase.deleteObjectStore() returns'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is still marked for deletion, immediately after ' + + 'IDBObjectStore.deleteIndex() returns'); + assert_array_equals( + transaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should stop including the ' + + 'deleted store immediately after IDBDatabase.deleteObjectStore() ' + + 'returns'); + assert_array_equals( + database.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should stop including the newly ' + + 'created store immediately after IDBDatabase.deleteObjectStore() ' + + 'returns'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames for the deleted store should be empty ' + + 'immediately after IDBDatabase.deleteObjectStore() returns'); + + transaction.abort(); + assert_throws_dom( + 'TransactionInactiveError', () => store.get('query'), + 'IDBObjectStore.get should throw TransactionInactiveError, ' + + 'indicating that the store is no longer marked for deletion, ' + + 'immediately after IDBTransaction.abort() returns'); + assert_throws_dom( + 'TransactionInactiveError', () => index.get('query'), + 'IDBIndex.get should throw TransactionInactiveError, indicating ' + + 'that the index is no longer marked for deletion, immediately ' + + 'after IDBObjectStore.deleteIndex() returns'); + assert_array_equals( + database.objectStoreNames, ['books', 'not_books'], + 'IDBDatabase.objectStoreNames should include the deleted store ' + + 'store immediately after IDBTransaction.abort() returns'); + assert_array_equals( + transaction.objectStoreNames, ['books', 'not_books'], + 'IDBTransaction.objectStoreNames should include the deleted ' + + 'store immediately after IDBTransaction.abort() returns'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames for the deleted store should not be ' + + 'empty any more immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'TransactionInactiveError', () => store.get('query'), + 'IDBObjectStore.get should throw TransactionInactiveError, ' + + 'indicating that the store is no longer marked for deletion, ' + + 'after the transaction is aborted'); + assert_throws_dom( + 'TransactionInactiveError', () => index.get('query'), + 'IDBIndex.get should throw TransactionInactiveError, indicating ' + + 'that the index is no longer marked for deletion, after the ' + + 'transaction is aborted'); + assert_array_equals( + migrationDatabase.objectStoreNames, ['books', 'not_books'], + 'IDBDatabase.objectStoreNames should include the previously ' + + 'deleted store after the transaction is aborted'); + assert_array_equals( + migrationTransaction.objectStoreNames, ['books', 'not_books'], + 'IDBTransaction.objectStoreNames should include the previously ' + + 'deleted store after the transaction is aborted'); + assert_array_equals( + store.indexNames, ['not_by_author', 'not_by_title'], + 'IDBObjectStore.indexNames for the deleted store should not be ' + + 'empty after the transaction is aborted'); + }); + }, + 'Deleted indexes in deleted stores are still marked as not-deleted after ' + + 'the transaction aborts'); + +promise_test( + testCase => { + let store = null; + let index = null; + let migrationTransaction = null; + let migrationDatabase = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + store = createNotBooksStore(testCase, database); + migrationDatabase = database; + migrationTransaction = transaction; + index = store.index('not_by_author'); + store.deleteIndex('not_by_author'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is marked for deletion, immediately after ' + + 'IDBObjectStore.deleteIndex() returns'); + assert_array_equals( + store.indexNames, ['not_by_title'], + 'IDBObjectStore.indexNames should not include the deleted index ' + + 'immediately after IDBObjectStore.deleteIndex() returns'); + + database.deleteObjectStore('not_books'); + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is marked for deletion, immediately after ' + + 'IDBDatabase.deleteObjectStore() returns'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is still marked for deletion, immediately after ' + + 'IDBDatabase.deleteObjectStore() returns'); + assert_array_equals( + transaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should stop including the ' + + 'deleted store immediately after IDBDatabase.deleteObjectStore() ' + + 'returns'); + assert_array_equals( + database.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should stop including the newly ' + + 'created store immediately after IDBDatabase.deleteObjectStore() ' + + 'returns'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames should be empty immediately after ' + + 'IDBDatabase.deleteObjectStore() returns'); + + transaction.abort(); + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is still marked for deletion, immediately after ' + + 'IDBTransaction.abort() returns'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is still marked for deletion, immediately after ' + + 'IDBTransaction.abort() returns'); + assert_array_equals( + transaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should not include the newly ' + + 'created store immediately after IDBTransaction.abort() returns'); + assert_array_equals( + database.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should not include the newly ' + + 'created store immediately after IDBTransaction.abort() returns'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames should be empty immediately after ' + + 'IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is still marked for deletion, after the ' + + 'transaction is aborted'); + assert_throws_dom( + 'InvalidStateError', () => index.get('query'), + 'IDBIndex.get should throw InvalidStateError, indicating that ' + + 'the index is still marked for deletion, after the transaction ' + + 'is aborted'); + assert_array_equals( + migrationDatabase.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should not include the newly ' + + 'created store after the transaction is aborted'); + assert_array_equals( + migrationTransaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should not include the newly ' + + 'created store after the transaction is aborted'); + assert_array_equals( + store.indexNames, [], + 'IDBObjectStore.indexNames should be empty after the transaction ' + + 'is aborted'); + }); + }, + 'Deleted indexes in created+deleted stores are still marked as deleted ' + + 'after their transaction aborts'); diff --git a/IndexedDB/transaction-abort-multiple-metadata-revert.html b/IndexedDB/transaction-abort-multiple-metadata-revert.html deleted file mode 100644 index 18abd0588c4197..00000000000000 --- a/IndexedDB/transaction-abort-multiple-metadata-revert.html +++ /dev/null @@ -1,293 +0,0 @@ - - -IndexedDB: aborting transactions reverts multiple operations on the same metadata - - - - - - diff --git a/IndexedDB/transaction-abort-object-store-metadata-revert.any.js b/IndexedDB/transaction-abort-object-store-metadata-revert.any.js new file mode 100644 index 00000000000000..d5f14b54de4803 --- /dev/null +++ b/IndexedDB/transaction-abort-object-store-metadata-revert.any.js @@ -0,0 +1,274 @@ +// META: title=IndexedDB: aborting transactions reverts object store metadata +// META: global=window,worker +// META: script=resources/support-promises.js +// META: script=resources/support.js + +// Spec: https://w3c.github.io/IndexedDB/#abort-transaction + +'use strict'; + +promise_test(testCase => { + let store = null; + let migrationTransaction = null; + let migrationDatabase = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + store = createNotBooksStore(testCase, database); + migrationDatabase = database; + migrationTransaction = transaction; + assert_array_equals( + database.objectStoreNames, ['books', 'not_books'], + 'IDBDatabase.objectStoreNames should include a newly created ' + + 'store before the transaction is aborted'); + assert_array_equals( + transaction.objectStoreNames, ['books', 'not_books'], + 'IDBTransaction.objectStoreNames should include a newly created ' + + 'store before the transaction is aborted'); + + transaction.abort(); + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is marked for deletion, immediately after ' + + 'IDBTransaction.abort() returns'); + assert_array_equals( + transaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should stop including the newly ' + + 'created store immediately after IDBTransaction.abort() returns'); + assert_array_equals( + database.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should stop including the newly ' + + 'created store immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is marked for deletion, after the transaction is ' + + 'aborted'); + assert_array_equals( + migrationDatabase.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should stop including the newly ' + + 'created store after the transaction is aborted'); + assert_array_equals( + migrationTransaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should stop including the newly ' + + 'created store after the transaction is aborted'); + }); +}, 'Created stores get marked as deleted after their transaction aborts'); + +promise_test(testCase => { + let store = null; + let migrationTransaction = null; + let migrationDatabase = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + createNotBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + migrationDatabase = database; + migrationTransaction = transaction; + store = transaction.objectStore('not_books'); + + database.deleteObjectStore('not_books'); + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is marked for deletion, immediately after ' + + 'IDBDatabase.deleteObjectStore() returns'); + assert_array_equals( + transaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should stop including the ' + + 'deleted store immediately after IDBDatabase.deleteObjectStore() ' + + 'returns'); + assert_array_equals( + database.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should stop including the newly ' + + 'created store immediately after IDBDatabase.deleteObjectStore() ' + + 'returns'); + + transaction.abort(); + assert_throws_dom( + 'TransactionInactiveError', () => store.get('query'), + 'IDBObjectStore.get should throw TransactionInactiveError, ' + + 'indicating that the store is no longer marked for deletion, ' + + 'immediately after IDBTransaction.abort() returns'); + assert_array_equals( + database.objectStoreNames, ['books', 'not_books'], + 'IDBDatabase.objectStoreNames should include the deleted store ' + + 'store immediately after IDBTransaction.abort() returns'); + assert_array_equals( + transaction.objectStoreNames, ['books', 'not_books'], + 'IDBTransaction.objectStoreNames should include the deleted ' + + 'store immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'TransactionInactiveError', () => store.get('query'), + 'IDBObjectStore.get should throw TransactionInactiveError, ' + + 'indicating that the store is no longer marked for deletion, ' + + 'after the transaction is aborted'); + assert_array_equals( + migrationDatabase.objectStoreNames, ['books', 'not_books'], + 'IDBDatabase.objectStoreNames should include the previously ' + + 'deleted store after the transaction is aborted'); + assert_array_equals( + migrationTransaction.objectStoreNames, ['books', 'not_books'], + 'IDBTransaction.objectStoreNames should include the previously ' + + 'deleted store after the transaction is aborted'); + }); +}, 'Deleted stores get marked as not-deleted after the transaction aborts'); + +promise_test( + testCase => { + let store = null; + let migrationTransaction = null; + let migrationDatabase = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + store = createNotBooksStore(testCase, database); + migrationDatabase = database; + migrationTransaction = transaction; + assert_array_equals( + database.objectStoreNames, ['books', 'not_books'], + 'IDBDatabase.objectStoreNames should include a newly created ' + + 'store before the transaction is aborted'); + assert_array_equals( + transaction.objectStoreNames, ['books', 'not_books'], + 'IDBTransaction.objectStoreNames should include a newly created ' + + 'store before the transaction is aborted'); + + database.deleteObjectStore('not_books'); + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is marked for deletion, immediately after ' + + 'IDBDatabase.deleteObjectStore() returns'); + assert_array_equals( + transaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should stop including the ' + + 'deleted store immediately after IDBDatabase.deleteObjectStore() ' + + 'returns'); + assert_array_equals( + database.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should stop including the newly ' + + 'created store immediately after IDBDatabase.deleteObjectStore() ' + + 'returns'); + + transaction.abort(); + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is still marked for deletion, immediately after ' + + 'IDBTransaction.abort() returns'); + assert_array_equals( + transaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should not include the newly ' + + 'created store immediately after IDBTransaction.abort() returns'); + assert_array_equals( + database.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should not include the newly ' + + 'created store immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_throws_dom( + 'InvalidStateError', () => store.get('query'), + 'IDBObjectStore.get should throw InvalidStateError, indicating ' + + 'that the store is still marked for deletion, after the ' + + 'transaction is aborted'); + assert_array_equals( + migrationDatabase.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should not include the newly ' + + 'created store after the transaction is aborted'); + assert_array_equals( + migrationTransaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should not include the newly ' + + 'created store after the transaction is aborted'); + }); + }, + 'Created+deleted stores are still marked as deleted after their ' + + 'transaction aborts'); + +promise_test( + testCase => { + let migrationTransaction = null; + let migrationDatabase = null; + return createDatabase( + testCase, + (database, transaction) => { + createBooksStore(testCase, database); + createNotBooksStore(testCase, database); + }) + .then(database => { + database.close(); + }) + .then( + () => migrateDatabase( + testCase, 2, + (database, transaction) => { + migrationDatabase = database; + migrationTransaction = transaction; + + database.deleteObjectStore('not_books'); + assert_array_equals( + transaction.objectStoreNames, ['books'], + 'IDBTransaction.objectStoreNames should stop including the ' + + 'deleted store immediately after IDBDatabase.deleteObjectStore() ' + + 'returns'); + assert_array_equals( + database.objectStoreNames, ['books'], + 'IDBDatabase.objectStoreNames should stop including the newly ' + + 'created store immediately after IDBDatabase.deleteObjectStore() ' + + 'returns'); + + transaction.abort(); + assert_array_equals( + database.objectStoreNames, ['books', 'not_books'], + 'IDBDatabase.objectStoreNames should include the deleted store ' + + 'store immediately after IDBTransaction.abort() returns'); + assert_array_equals( + transaction.objectStoreNames, ['books', 'not_books'], + 'IDBTransaction.objectStoreNames should include the deleted ' + + 'store immediately after IDBTransaction.abort() returns'); + })) + .then(() => { + assert_array_equals( + migrationDatabase.objectStoreNames, ['books', 'not_books'], + 'IDBDatabase.objectStoreNames should include the previously ' + + 'deleted store after the transaction is aborted'); + assert_array_equals( + migrationTransaction.objectStoreNames, ['books', 'not_books'], + 'IDBTransaction.objectStoreNames should include the previously ' + + 'deleted store after the transaction is aborted'); + }); + }, + 'Un-instantiated deleted stores get marked as not-deleted after the ' + + 'transaction aborts'); diff --git a/IndexedDB/transaction-abort-object-store-metadata-revert.html b/IndexedDB/transaction-abort-object-store-metadata-revert.html deleted file mode 100644 index c31537bc5cda38..00000000000000 --- a/IndexedDB/transaction-abort-object-store-metadata-revert.html +++ /dev/null @@ -1,235 +0,0 @@ - - -IndexedDB: aborting transactions reverts object store metadata - - - - - - diff --git a/IndexedDB/transaction-abort-request-error.any.js b/IndexedDB/transaction-abort-request-error.any.js new file mode 100644 index 00000000000000..a7ddd02f1d2ad8 --- /dev/null +++ b/IndexedDB/transaction-abort-request-error.any.js @@ -0,0 +1,69 @@ +// META: title=IndexedDB: Test error events fired at requests from aborted transaction +// META: global=window,worker +// META: script=resources/support-promises.js +// META: script=resources/support.js + +// Spec: https://w3c.github.io/IndexedDB/#abort-transaction + +'use strict'; + +indexeddb_test( + (t, db) => { + db.createObjectStore('store'); + }, + (t, db) => { + const tx = db.transaction('store', 'readonly'); + const request = tx.objectStore('store').get(0); + tx.abort(); + request.onsuccess = t.unreached_func('request should not succeed'); + + let connection_saw_error = false; + let transaction_saw_error = false; + + request.onerror = t.step_func(e => { + assert_equals( + request.readyState, 'done', 'Request\'s done flag should be set'); + assert_equals( + request.result, undefined, 'Request\'s result should be undefined'); + assert_equals( + request.error.name, 'AbortError', + 'Request\'s error should be AbortError'); + + assert_equals(e.target, request, 'event target should be request'); + assert_equals(e.type, 'error', 'Event type should be error'); + assert_true(e.bubbles, 'Event should bubble'); + assert_true(e.cancelable, 'Event should cancelable'); + + assert_true( + connection_saw_error, 'Event propagated through connection'); + assert_true( + transaction_saw_error, 'Event propagated through transaction'); + t.done(); + }); + + // Event propagates via "get the parent" on request and transaction. + + db.addEventListener( + 'error', t.step_func(e => { + connection_saw_error = true; + assert_equals(e.target, request, 'event target should be request'); + assert_equals(e.type, 'error', 'Event type should be error'); + assert_true(e.bubbles, 'Event should bubble'); + assert_true(e.cancelable, 'Event should cancelable'); + }), + true); + + tx.addEventListener( + 'error', t.step_func(e => { + transaction_saw_error = true; + assert_equals(e.target, request, 'event target should be request'); + assert_equals(e.type, 'error', 'Event type should be error'); + assert_true(e.bubbles, 'Event should bubble'); + assert_true(e.cancelable, 'Event should cancelable'); + + assert_true( + connection_saw_error, 'Event propagated through connection'); + }), + true); + }, + 'Properties of error events fired at requests when aborting a transaction'); diff --git a/IndexedDB/transaction-abort-request-error.html b/IndexedDB/transaction-abort-request-error.html deleted file mode 100644 index fa828286f4d7c4..00000000000000 --- a/IndexedDB/transaction-abort-request-error.html +++ /dev/null @@ -1,68 +0,0 @@ - - -IndexedDB: Test error events fired at requests from aborted transaction - - - - - - -