diff --git a/packages/idb-bridge/src/bridge-idb.ts b/packages/idb-bridge/src/bridge-idb.ts index 836f2efa4..744ad1aef 100644 --- a/packages/idb-bridge/src/bridge-idb.ts +++ b/packages/idb-bridge/src/bridge-idb.ts @@ -635,7 +635,9 @@ export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase { this._schema = this._backend.getCurrentTransactionSchema(backendTx); - return transaction.objectStore(name); + const newObjectStore = transaction.objectStore(name); + newObjectStore._justCreated = true; + return newObjectStore; } public deleteObjectStore(name: string): void { @@ -965,7 +967,6 @@ export class BridgeIDBFactory { }); event2.eventPath = []; request.dispatchEvent(event2); - } else { if (BridgeIDBFactory.enableTracing) { console.log("dispatching 'success' event for opening db"); @@ -1046,6 +1047,11 @@ export class BridgeIDBIndex implements IDBIndex { public _deleted: boolean = false; + /** + * Was this index newly created in the current transaction? + */ + _justCreated: boolean = false; + constructor(objectStore: BridgeIDBObjectStore, name: string) { this._name = name; this._objectStore = objectStore; @@ -1078,8 +1084,16 @@ export class BridgeIDBIndex implements IDBIndex { this._backend.renameIndex(btx, this._objectStore.name, oldName, newName); + this._objectStore._transaction._db._schema = this._backend.getCurrentTransactionSchema( + btx, + ); + + this._objectStore._indexesCache.delete(oldName); + this._objectStore._indexesCache.set(newName, this); + this._name = newName; + if (this._objectStore._indexNames.indexOf(name) >= 0) { - throw new ConstraintError(); + throw new Error("internal invariant violated"); } } @@ -1089,6 +1103,14 @@ export class BridgeIDBIndex implements IDBIndex { range?: BridgeIDBKeyRange | IDBValidKey | null | undefined, direction: IDBCursorDirection = "next", ) { + if (this._deleted) { + throw new InvalidStateError( + "tried to call 'openCursor' on a deleted index", + ); + } + + console.log("opening cursor on", this); + this._confirmActiveTransaction(); if (range === null) { @@ -1418,6 +1440,8 @@ export class BridgeIDBObjectStore implements IDBObjectStore { */ _debugName: string | undefined = undefined; + _justCreated: boolean = false; + get transaction(): IDBTransaction { return this._transaction; } @@ -1642,8 +1666,8 @@ export class BridgeIDBObjectStore implements IDBObjectStore { try { keyRange = BridgeIDBKeyRange.only(valueToKey(key)); } catch (e) { - throw Error( - `invalid key (type ${typeof key}) for object store ${this._name}`, + throw new DataError( + `invalid key (type ${typeof key}) for object store '${this._name}'`, ); } } @@ -1677,6 +1701,9 @@ export class BridgeIDBObjectStore implements IDBObjectStore { if (!values) { throw Error("invariant violated"); } + if (values.length !== 1) { + throw Error("invariant violated"); + } return structuredRevive(values[0]); }; @@ -1790,7 +1817,6 @@ export class BridgeIDBObjectStore implements IDBObjectStore { }); } - // tslint:disable-next-line max-line-length // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBObjectStore-createIndex-IDBIndex-DOMString-name-DOMString-sequence-DOMString--keyPath-IDBIndexParameters-optionalParameters public createIndex( indexName: string, @@ -1839,7 +1865,9 @@ export class BridgeIDBObjectStore implements IDBObjectStore { unique, ); - return new BridgeIDBIndex(this, indexName); + const idx = this.index(indexName); + idx._justCreated = true; + return idx; } // https://w3c.github.io/IndexedDB/#dom-idbobjectstore-index @@ -1856,8 +1884,10 @@ export class BridgeIDBObjectStore implements IDBObjectStore { if (index !== undefined) { return index; } - - return new BridgeIDBIndex(this, name); + const newIndex = new BridgeIDBIndex(this, name); + this._indexesCache.set(name, newIndex); + this._transaction._usedIndexes.push(newIndex); + return newIndex; } public deleteIndex(indexName: string) { @@ -1878,6 +1908,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore { const index = this._indexesCache.get(indexName); if (index !== undefined) { index._deleted = true; + this._indexesCache.delete(indexName); } this._backend.deleteIndex(btx, this._name, indexName); @@ -2053,6 +2084,16 @@ export class BridgeIDBTransaction _aborted: boolean = false; _objectStoresCache: Map = new Map(); + /** + * Object stores used during the transaction. + */ + _usedObjectStores: BridgeIDBObjectStore[] = []; + + /** + * Object stores used during the transaction. + */ + _usedIndexes: BridgeIDBIndex[] = []; + /** * Name that can be set to identify the transaction in logs. */ @@ -2181,11 +2222,31 @@ export class BridgeIDBTransaction } } + // "Any object stores and indexes which were created during the + // transaction are now considered deleted for the purposes of other + // algorithms." + if (this._db._upgradeTransaction) { + for (const os of this._usedObjectStores) { + if (os._justCreated) { + os._deleted = true + } + } + for (const ind of this._usedIndexes) { + if (ind._justCreated) { + ind._deleted = true + } + } + } + // ("abort a transaction", step 5.1) if (this._openRequest) { this._db._upgradeTransaction = null; } + // All steps before happend synchronously. Now + // we asynchronously roll back the backend transaction, + // if necessary/possible. + const maybeBtx = this._backendTransaction; if (maybeBtx) { this._db._schema = this._backend.getInitialTransactionSchema(maybeBtx); @@ -2242,6 +2303,7 @@ export class BridgeIDBTransaction const newObjectStore = new BridgeIDBObjectStore(this, name); this._objectStoresCache.set(name, newObjectStore); + this._usedObjectStores.push(newObjectStore); return newObjectStore; } diff --git a/packages/idb-bridge/src/idb-wpt-ported/README b/packages/idb-bridge/src/idb-wpt-ported/README index 801450bb2..1947074d1 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/README +++ b/packages/idb-bridge/src/idb-wpt-ported/README @@ -7,4 +7,268 @@ The following tests are intentionally not included: * file_support.sub.html (assumes we have a DOM) * fire-error-event-exception.html (ava can't test unhandled rejections) * fire-success-event-exception.html (ava can't test unhandled rejections) -* fire-upgradeneeded-event-exception.html (ava can't test unhandled rejections) \ No newline at end of file +* fire-upgradeneeded-event-exception.html (ava can't test unhandled rejections) + +Test todo: + +bigint_value.htm +bindings-inject-keys-bypass-setters.html +bindings-inject-values-bypass-chain.html +bindings-inject-values-bypass-setters.html +blob-contenttype.any.js +blob-delete-objectstore-db.any.js +blob-valid-after-deletion.any.js +blob-valid-before-commit.any.js +clone-before-keypath-eval.html +delete-request-queue.html +get-databases.any.js +globalscope-indexedDB-SameObject.html +historical.html +idb_binary_key_conversion.htm +idb-binary-key-detached.htm +idb-binary-key-roundtrip.htm +idbcursor-advance-continue-async.htm +idbcursor-advance-exception-order.html +idbcursor_advance_index.htm +idbcursor-advance-invalid.htm +idbcursor_advance_objectstore2.htm +idbcursor_advance_objectstore3.htm +idbcursor_advance_objectstore4.htm +idbcursor_advance_objectstore5.htm +idbcursor_advance_objectstore.htm +idbcursor_continue_delete_objectstore.htm +idbcursor-continue-exception-order.htm +idbcursor_continue_invalid.htm +idbcursor-continuePrimaryKey-exception-order.htm +idbcursor-continuePrimaryKey-exceptions.htm +idbcursor-continuePrimaryKey.htm +idbcursor-direction.htm +idbcursor-direction-index.htm +idbcursor-direction-index-keyrange.htm +idbcursor-direction-objectstore.htm +idbcursor-direction-objectstore-keyrange.htm +idbcursor_iterating.htm +idbcursor_iterating_index2.htm +idbcursor_iterating_index.htm +idbcursor_iterating_objectstore2.htm +idbcursor_iterating_objectstore.htm +idbcursor-iterating-update.htm +idbcursor-key.htm +idbcursor-primarykey.htm +idbcursor-request.any.js +idbcursor-request-source.html +idbcursor-reused.htm +idbcursor-source.htm +idbcursor-update-exception-order.htm +idbcursor_update_objectstore2.htm +idbcursor_update_objectstore3.htm +idbcursor_update_objectstore4.htm +idbcursor_update_objectstore5.htm +idbcursor_update_objectstore6.htm +idbcursor_update_objectstore7.htm +idbcursor_update_objectstore8.htm +idbcursor_update_objectstore9.htm +idbcursor_update_objectstore.htm +idbdatabase_close2.htm +idbdatabase_close.htm +idbdatabase_createObjectStore10-1000ends.htm +idbdatabase_createObjectStore10-emptyname.htm +idbdatabase_createObjectStore11.htm +idbdatabase_createObjectStore2.htm +idbdatabase_createObjectStore3.htm +idbdatabase_createObjectStore4.htm +idbdatabase_createObjectStore5.htm +idbdatabase_createObjectStore6.htm +idbdatabase_createObjectStore7.htm +idbdatabase_createObjectStore8-parameters.htm +idbdatabase_createObjectStore9-invalidparameters.htm +idbdatabase_createObjectStore-createIndex-emptyname.htm +idbdatabase-createObjectStore-exception-order.htm +idbdatabase_createObjectStore.htm +idbdatabase_deleteObjectStore2.htm +idbdatabase_deleteObjectStore3.htm +idbdatabase_deleteObjectStore4-not_reused.htm +idbdatabase-deleteObjectStore-exception-order.htm +idbdatabase_deleteObjectStore.htm +idbdatabase_transaction2.htm +idbdatabase_transaction3.htm +idbdatabase_transaction4.htm +idbdatabase_transaction5.htm +idbdatabase-transaction-exception-order.html +idbdatabase_transaction.htm +idb-explicit-commit.any.js +idb-explicit-commit-throw.any.js +idbfactory-databases-opaque-origin.html +idbfactory_deleteDatabase2.htm +idbfactory_deleteDatabase3.htm +idbfactory_deleteDatabase4.htm +idbfactory_deleteDatabase.htm +idbfactory-deleteDatabase-opaque-origin.html +idbfactory-deleteDatabase-request-success.html +idbfactory-open-error-properties.html +idbfactory-open-opaque-origin.html +idbfactory-open-request-error.html +idbfactory-open-request-success.html +idbfactory-origin-isolation.html +idbindex_count2.htm +idbindex_count3.htm +idbindex_count4.htm +idbindex_count.htm +idbindex-getAll-enforcerange.html +idbindex_getAll.html +idbindex-getAllKeys-enforcerange.html +idbindex_getAllKeys.html +idbindex_get.htm +idbindex_getKey2.htm +idbindex_getKey3.htm +idbindex_getKey4.htm +idbindex_getKey5.htm +idbindex_getKey6.htm +idbindex_getKey7.htm +idbindex_getKey8.htm +idbindex_getKey.htm +idbindex_indexNames.htm +idbindex_keyPath.any.js +idbindex-multientry-arraykeypath.htm +idbindex-multientry-big.htm +idbindex-multientry.htm +idbindex-objectStore-SameObject.html +idbindex_openKeyCursor2.htm +idbindex_openKeyCursor3.htm +idbindex_openKeyCursor4.htm +idbindex_openKeyCursor.htm +idbindex-query-exception-order.html +idbindex-rename-abort.html +idbindex-rename-errors.html +idbindex-rename.html +idbindex-request-source.html +idbindex_reverse_cursor.any.js +idbindex_tombstones.any.js +idbkeyrange.htm +idbkeyrange-includes.htm +idbkeyrange_incorrect.htm +idbobjectstore_clear2.htm +idbobjectstore_clear3.htm +idbobjectstore_clear4.htm +idbobjectstore-clear-exception-order.html +idbobjectstore_clear.htm +idbobjectstore_count2.htm +idbobjectstore_count3.htm +idbobjectstore_count4.htm +idbobjectstore_count.htm +idbobjectstore_createIndex10.htm +idbobjectstore_createIndex11.htm +idbobjectstore_createIndex12.htm +idbobjectstore_createIndex13.htm +idbobjectstore_createIndex14-exception_order.htm +idbobjectstore_createIndex15-autoincrement.htm +idbobjectstore_createIndex2.htm +idbobjectstore_createIndex3-usable-right-away.htm +idbobjectstore_createIndex4-deleteIndex-event_order.htm +idbobjectstore_createIndex5-emptykeypath.htm +idbobjectstore_createIndex6-event_order.htm +idbobjectstore_createIndex7-event_order.htm +idbobjectstore_createIndex8-valid_keys.htm +idbobjectstore_createIndex9-emptyname.htm +idbobjectstore_createIndex.htm +idbobjectstore_delete2.htm +idbobjectstore_delete3.htm +idbobjectstore_delete4.htm +idbobjectstore_delete5.htm +idbobjectstore_delete6.htm +idbobjectstore_delete7.htm +idbobjectstore_deleted.htm +idbobjectstore-delete-exception-order.html +idbobjectstore_delete.htm +idbobjectstore-deleteIndex-exception-order.html +idbobjectstore_deleteIndex.htm +idbobjectstore-getAll-enforcerange.html +idbobjectstore_getAll.html +idbobjectstore-getAllKeys-enforcerange.html +idbobjectstore_getAllKeys.html +idbobjectstore_getKey.html +idbobjectstore-index-finished.html +idbobjectstore_index.htm +idbobjectstore_keyPath.any.js +idbobjectstore_openCursor.htm +idbobjectstore_openCursor_invalid.htm +idbobjectstore_openKeyCursor.htm +idbobjectstore_putall.tentative.any.js +idbobjectstore-query-exception-order.html +idbobjectstore-rename-abort.html +idbobjectstore-rename-errors.html +idbobjectstore-request-source.html +idbobjectstore-transaction-SameObject.html +idbrequest_error.html +idbrequest-onupgradeneeded.htm +idbrequest_result.html +idbtransaction_abort.htm +idbtransaction-db-SameObject.html +idbtransaction.htm +idbtransaction-objectStore-exception-order.html +idbtransaction-objectStore-finished.html +idbtransaction_objectStoreNames.html +idbversionchangeevent.htm +idb_webworkers.htm +idbworker.js +idlharness.any.js +index_sort_order.htm +interleaved-cursors-common.js +interleaved-cursors-large.html +interleaved-cursors-small.html +key-conversion-exceptions.htm +keygenerator-constrainterror.htm +keygenerator-explicit.html +keygenerator.htm +keygenerator-inject.html +keygenerator-overflow.htm +key-generators +key_invalid.htm +keyorder.htm +keypath-exceptions.htm +keypath_invalid.htm +keypath_maxsize.htm +keypath-special-identifiers.htm +key_valid.html +large-requests-abort.html +list_ordering.htm +META.yml +name-scopes.html +nested-cloning-common.js +nested-cloning-large.html +nested-cloning-large-multiple.html +nested-cloning-small.html +objectstore_keyorder.htm +open-request-queue.html +parallel-cursors-upgrade.html +request-abort-ordering.html +request-event-ordering.html +resources +string-list-ordering.htm +structured-clone.any.js +structured-clone-transaction-state.any.js +transaction-abort-generator-revert.html +transaction-abort-index-metadata-revert.html +transaction-abort-multiple-metadata-revert.html +transaction-abort-object-store-metadata-revert.html +transaction-abort-request-error.html +transaction_bubble-and-capture.htm +transaction-create_in_versionchange.htm +transaction-deactivation-timing.html +transaction-lifetime-blocked.htm +transaction-lifetime-empty.html +transaction-lifetime.htm +transaction-relaxed-durability.tentative.any.js +transaction-scheduling-across-connections.any.js +transaction-scheduling-across-databases.any.js +transaction-scheduling-mixed-scopes.any.js +transaction-scheduling-ordering.any.js +transaction-scheduling-ro-waits-for-rw.any.js +transaction-scheduling-rw-scopes.any.js +transaction-scheduling-within-database.any.js +upgrade-transaction-deactivation-timing.html +upgrade-transaction-lifecycle-backend-aborted.html +upgrade-transaction-lifecycle-committed.html +upgrade-transaction-lifecycle-user-aborted.html +value_recursive.htm +writer-starvation.htm diff --git a/packages/idb-bridge/src/idb-wpt-ported/abort-in-initial-upgradeneeded.test.ts b/packages/idb-bridge/src/idb-wpt-ported/abort-in-initial-upgradeneeded.test.ts index 70f2f2b8a..3b65a9033 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/abort-in-initial-upgradeneeded.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/abort-in-initial-upgradeneeded.test.ts @@ -6,7 +6,7 @@ test("WPT test abort-in-initial-upgradeneeded.htm", async (t) => { var db: any; var open_rq = createdb(t, undefined, 2); - open_rq.onupgradeneeded = function (e) { + open_rq.onupgradeneeded = function (e: any) { const tgt = e.target as any; db = tgt.result; t.deepEqual(db.version, 2); @@ -20,7 +20,7 @@ test("WPT test abort-in-initial-upgradeneeded.htm", async (t) => { transaction.abort(); }; - open_rq.onerror = function (e) { + open_rq.onerror = function (e: any) { const tgt = e.target as any; t.deepEqual(open_rq, e.target); t.deepEqual(tgt.result, undefined); diff --git a/packages/idb-bridge/src/idb-wpt-ported/cursor-overloads.test.ts b/packages/idb-bridge/src/idb-wpt-ported/cursor-overloads.test.ts index 7da0ea8ff..0f0713d1d 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/cursor-overloads.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/cursor-overloads.test.ts @@ -12,7 +12,7 @@ test.cb("WPT test cursor-overloads.htm", (t) => { var db: any, store: any, index: any; var request = createdb(t); - request.onupgradeneeded = function (e) { + request.onupgradeneeded = function (e: any) { db = request.result; store = db.createObjectStore("store"); index = store.createIndex("index", "value"); diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbcursor-advance-index.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-advance-index.test.ts index 2d449a9ab..fac047990 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/idbcursor-advance-index.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-advance-index.test.ts @@ -26,7 +26,7 @@ test("WPT test idbcursor_advance_index.htm", async (t) => { } }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var cursor_rq = db .transaction("test") .objectStore("test") @@ -79,7 +79,7 @@ test("WPT test idbcursor_advance_index2.htm", async (t) => { for (var i = 0; i < records.length; i++) objStore.add(records[i]); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var cursor_rq = db .transaction("test") .objectStore("test") @@ -123,7 +123,7 @@ test("WPT test idbcursor_advance_index3.htm", async (t) => { for (var i = 0; i < records.length; i++) objStore.add(records[i]); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var cursor_rq = db .transaction("test") .objectStore("test") diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbcursor-continue-index.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-continue-index.test.ts index 02f2e5c99..9b96a2e91 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/idbcursor-continue-index.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-continue-index.test.ts @@ -22,7 +22,7 @@ test.cb("WPT test idbcursor_continue_index.htm", (t) => { for (var i = 0; i < records.length; i++) objStore.add(records[i]); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var cursor_rq = db .transaction("test") .objectStore("test") @@ -65,7 +65,7 @@ test.cb("WPT idbcursor-continue-index2.htm", (t) => { for (var i = 0; i < records.length; i++) objStore.add(records[i]); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var cursor_rq = db .transaction("test") .objectStore("test") @@ -108,7 +108,7 @@ test.cb("WPT idbcursor-continue-index3.htm", (t) => { for (var i = 0; i < records.length; i++) objStore.add(records[i]); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var count = 0; var cursor_rq = db .transaction("test") @@ -159,7 +159,7 @@ test.cb("WPT idbcursor-continue-index4.htm", (t) => { for (var i = 0; i < records.length; i++) objStore.add(records[i]); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var count = 0, cursor_rq = db .transaction("test") @@ -224,7 +224,7 @@ test.cb("WPT idbcursor-continue-index5.htm", (t) => { for (var i = 0; i < records.length; i++) objStore.add(records[i]); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var count = 0, cursor_rq = db .transaction("test") @@ -281,7 +281,7 @@ test.cb("WPT idbcursor-continue-index6.htm", (t) => { for (var i = 0; i < records.length; i++) objStore.add(records[i]); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var count = 0, cursor_rq = db .transaction("test") @@ -316,7 +316,7 @@ test.cb("WPT idbcursor-continue-index6.htm", (t) => { // IDBCursor.continue() - index - throw TransactionInactiveError test.cb("WPT idbcursor-continue-index7.htm", (t) => { - var db, + var db: any, records = [ { pKey: "primaryKey_0", iKey: "indexKey_0" }, { pKey: "primaryKey_1", iKey: "indexKey_1" }, diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbcursor-continue-objectstore.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-continue-objectstore.test.ts new file mode 100644 index 000000000..ecfac82f4 --- /dev/null +++ b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-continue-objectstore.test.ts @@ -0,0 +1,240 @@ +import test from "ava"; +import { BridgeIDBCursor } from ".."; +import { BridgeIDBCursorWithValue } from "../bridge-idb"; +import { IDBDatabase } from "../idbtypes"; +import { createdb } from "./wptsupport"; + +// IDBCursor.continue() - object store - iterate to the next record +test.cb("WPT test idbcursor_continue_objectstore.htm", (t) => { + var db: any; + let count = 0; + const records = [{ pKey: "primaryKey_0" }, { pKey: "primaryKey_1" }]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("test", { + autoIncrement: true, + keyPath: "pKey", + }); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var store = db.transaction("test").objectStore("test"); + + var cursor_rq = store.openCursor(); + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + if (!cursor) { + t.deepEqual(count, records.length, "cursor run count"); + t.end(); + } + + var record = cursor.value; + t.deepEqual(record.pKey, records[count].pKey, "primary key"); + + cursor.continue(); + count++; + }; + }; +}); + +// IDBCursor.continue() - index - attempt to pass a +// key parameter that is not a valid key +test.cb("WPT test idbcursor_continue_objectstore2.htm", (t) => { + var db: any; + const records = [{ pKey: "primaryKey_0" }, { pKey: "primaryKey_1" }]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var cursor_rq = db.transaction("test").objectStore("test").openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + t.true(cursor instanceof BridgeIDBCursor, "cursor exists"); + t.throws( + () => { + cursor.continue({ foo: "42" }); + }, + { name: "DataError" }, + ); + + t.end(); + }; + }; +}); + +// IDBCursor.continue() - object store - attempt to iterate to the +// previous record when the direction is set for the next record +test.cb("WPT test idbcursor_continue_objectstore3.htm", (t) => { + var db: IDBDatabase; + const records = [{ pKey: "primaryKey_0" }, { pKey: "primaryKey_1" }]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var cursor_rq = db + .transaction("test") + .objectStore("test") + .openCursor(undefined, "next"); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + t.true(cursor instanceof BridgeIDBCursor, "cursor exist"); + t.throws( + () => { + cursor.continue(records[0].pKey); + }, + { + name: "DataError", + }, + ); + + t.end(); + }; + }; +}); + +// IDBCursor.continue() - object store - attempt to iterate to the next record when the direction is set for the previous record +test.cb("WPT test idbcursor_continue_objectstore4.htm", (t) => { + var db: any; + const records = [ + { pKey: "primaryKey_0" }, + { pKey: "primaryKey_1" }, + { pKey: "primaryKey_2" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var count = 0, + cursor_rq = db + .transaction("test") + .objectStore("test") + .openCursor(null, "prev"); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + t.true(cursor != null, "cursor exist"); + + switch (count) { + case 0: + t.deepEqual(cursor.value.pKey, records[2].pKey, "first cursor pkey"); + cursor.continue(records[1].pKey); + break; + + case 1: + t.deepEqual(cursor.value.pKey, records[1].pKey, "second cursor pkey"); + t.throws( + () => { + cursor.continue(records[2].pKey); + }, + { + name: "DataError", + }, + ); + t.end(); + break; + + default: + t.fail("Unexpected count value: " + count); + } + + count++; + }; + }; +}); + +// IDBCursor.continue() - object store - throw TransactionInactiveError +test.cb("WPT test idbcursor_continue_objectstore5.htm", (t) => { + var db: any; + const records = [{ pKey: "primaryKey_0" }, { pKey: "primaryKey_1" }]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var cursor_rq = db.transaction("test").objectStore("test").openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + t.true(cursor instanceof BridgeIDBCursor, "cursor exists"); + + e.target.transaction.abort(); + t.throws( + () => { + cursor.continue(); + }, + { + name: "TransactionInactiveError", + }, + "Calling continue() should throw an exception TransactionInactiveError when the transaction is not active.", + ); + + t.end(); + }; + }; +}); + +// IDBCursor.continue() - object store - throw InvalidStateError caused by object store been deleted +test.cb("WPT test idbcursor_continue_objectstore6.htm", (t) => { + var db: any; + const records = [{ pKey: "primaryKey_0" }, { pKey: "primaryKey_1" }]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + + var cursor_rq = objStore.openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + t.true(cursor instanceof BridgeIDBCursor, "cursor exists"); + + db.deleteObjectStore("test"); + t.throws( + () => { + cursor.continue(); + }, + { + name: "InvalidStateError", + }, + "If the cursor's source or effective object store has been deleted, the implementation MUST throw a DOMException of type InvalidStateError", + ); + + t.end(); + }; + }; +}); diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbcursor-delete-exception-order.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-delete-exception-order.test.ts new file mode 100644 index 000000000..c80e276e6 --- /dev/null +++ b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-delete-exception-order.test.ts @@ -0,0 +1,85 @@ +import test from "ava"; +import { createdb, indexeddb_test } from "./wptsupport"; + +test("WPT idbcursor-delete-exception-order.htm", async (t) => { + // 'IDBCursor.delete exception order: TransactionInactiveError vs. ReadOnlyError' + await indexeddb_test( + t, + (done, db) => { + const s = db.createObjectStore("s"); + s.put("value", "key"); + }, + (done, db) => { + const s = db.transaction("s", "readonly").objectStore("s"); + const r = s.openCursor(); + r.onsuccess = () => { + r.onsuccess = null; + setTimeout(() => { + const cursor = r.result; + t.assert(!!cursor); + t.throws( + () => {}, + { name: "TransactionInactiveError" }, + + '"Transaction inactive" check (TransactionInactivError) ' + + 'should precede "read only" check (ReadOnlyError)', + ); + done(); + }, 0); + }; + }, + ); + + indexeddb_test( + t, + (done, db) => { + const s = db.createObjectStore("s"); + s.put("value", "key"); + }, + (done, db) => { + const s = db.transaction("s", "readonly").objectStore("s"); + const r = s.openCursor(); + r.onsuccess = () => { + r.onsuccess = null; + const cursor = r.result!; + t.assert(cursor); + cursor.continue(); + t.throws( + () => { + cursor.delete(); + }, + { name: "ReadOnlyError" }, + '"Read only" check (ReadOnlyError) should precede ' + + '"got value flag" (InvalidStateError) check', + ); + + done(); + }; + }, + "IDBCursor.delete exception order: ReadOnlyError vs. InvalidStateError #1", + ); + + indexeddb_test( + t, + (done, db) => { + const s = db.createObjectStore("s"); + s.put("value", "key"); + }, + (done, db) => { + const s = db.transaction("s", "readonly").objectStore("s"); + const r = s.openKeyCursor(); + r.onsuccess = () => { + r.onsuccess = null; + const cursor = r.result; + t.throws( + () => {}, + { name: "ReadOnlyError" }, + '"Read only" check (ReadOnlyError) should precede ' + + '"key only flag" (InvalidStateError) check', + ); + done(); + }; + }, + "IDBCursor.delete exception order: ReadOnlyError vs. InvalidStateError #2", + ); +}); diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbcursor-delete-index.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-delete-index.test.ts new file mode 100644 index 000000000..0b28a4d4d --- /dev/null +++ b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-delete-index.test.ts @@ -0,0 +1,204 @@ +import test from "ava"; +import { BridgeIDBCursor } from ".."; +import { IDBCursor } from "../idbtypes"; +import { createdb, indexeddb_test } from "./wptsupport"; + +// IDBCursor.delete() - index - remove a record from the object store +test.cb("WPT idbcursor-delete-index.htm", (t) => { + var db: any; + let count = 0, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + objStore.createIndex("index", "iKey"); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = CursorDeleteRecord; + + function CursorDeleteRecord(e: any) { + var txn = db.transaction("test", "readwrite"), + cursor_rq = txn.objectStore("test").index("index").openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + t.true(cursor instanceof BridgeIDBCursor, "cursor exist"); + cursor.delete(); + }; + + txn.oncomplete = VerifyRecordWasDeleted; + } + + function VerifyRecordWasDeleted(e: any) { + var cursor_rq = db.transaction("test").objectStore("test").openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + if (!cursor) { + t.deepEqual(count, 1, "count"); + t.end(); + return; + } + + t.deepEqual(cursor.value.pKey, records[1].pKey); + t.deepEqual(cursor.value.iKey, records[1].iKey); + cursor.continue(); + count++; + }; + } +}); + +// IDBCursor.delete() - object store - attempt to remove a record in a read-only transaction +test.cb("WPT idbcursor-delete-index2.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var cursor_rq = db.transaction("test").objectStore("test").openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + t.true(cursor != null, "cursor exist"); + t.throws( + () => { + cursor.delete(); + }, + { + name: "ReadOnlyError", + }, + ); + t.end(); + }; + }; +}); + +// IDBCursor.delete() - index - attempt to remove a record in an inactive transaction +test.cb("WPT idbcursor-delete-index3.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + var index = objStore.createIndex("index", "iKey"); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + + var cursor_rq = index.openCursor(); + + let myCursor: IDBCursor | undefined; + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + t.true(cursor instanceof BridgeIDBCursor, "cursor exist"); + myCursor = cursor; + }; + + e.target.transaction.oncomplete = function (e: any) { + t.throws( + () => { + myCursor!.delete(); + }, + { name: "TransactionInactiveError" }, + ); + t.end(); + }; + }; +}); + +// IDBCursor.delete() - index - throw InvalidStateError caused by object store been deleted +test.cb("WPT idbcursor-delete-index4.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (event: any) { + db = event.target.result; + var objStore = db.createObjectStore("store", { keyPath: "pKey" }); + objStore.createIndex("index", "iKey"); + for (var i = 0; i < records.length; i++) { + objStore.add(records[i]); + } + var rq = objStore.index("index").openCursor(); + rq.onsuccess = function (event: any) { + var cursor = event.target.result; + t.true(cursor instanceof BridgeIDBCursor, "cursor exist"); + + db.deleteObjectStore("store"); + t.throws( + function () { + cursor.delete(); + }, + { name: "InvalidStateError" }, + "If the cursor's source or effective object store has been deleted, the implementation MUST throw a DOMException of type InvalidStateError", + ); + + t.end(); + }; + }; +}); + +// IDBCursor.delete() - index - throw InvalidStateError when the cursor is being iterated +test.cb("WPT idbcursor-delete-index5.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (event: any) { + db = event.target.result; + var objStore = db.createObjectStore("store", { keyPath: "pKey" }); + objStore.createIndex("index", "iKey"); + for (var i = 0; i < records.length; i++) { + objStore.add(records[i]); + } + + var rq = objStore.index("index").openCursor(); + rq.onsuccess = function (event: any) { + var cursor = event.target.result; + t.true(cursor instanceof BridgeIDBCursor, "cursor exist"); + + cursor.continue(); + t.throws( + function () { + cursor.delete(); + }, + { name: "InvalidStateError" }, + ); + + t.end(); + }; + }; +}); diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbcursor-delete-objectstore.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-delete-objectstore.test.ts new file mode 100644 index 000000000..7afe1e483 --- /dev/null +++ b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-delete-objectstore.test.ts @@ -0,0 +1,194 @@ +import test from "ava"; +import { BridgeIDBCursor } from ".."; +import { IDBCursor } from "../idbtypes"; +import { createdb, indexeddb_test } from "./wptsupport"; + +// IDBCursor.delete() - object store - remove a record from the object store +test.cb("WPT idbcursor-delete-objectstore.htm", (t) => { + var db: any, + count = 0, + records = [{ pKey: "primaryKey_0" }, { pKey: "primaryKey_1" }]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = CursorDeleteRecord; + + function CursorDeleteRecord(e: any) { + var txn = db.transaction("test", "readwrite"), + cursor_rq = txn.objectStore("test").openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + t.true(cursor != null, "cursor exist"); + cursor.delete(); + }; + + txn.oncomplete = VerifyRecordWasDeleted; + } + + function VerifyRecordWasDeleted(e: any) { + var cursor_rq = db.transaction("test").objectStore("test").openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + if (!cursor) { + t.deepEqual(count, 1, "count"); + t.end(); + } + + t.deepEqual(cursor.value.pKey, records[1].pKey); + count++; + cursor.continue(); + }; + } +}); + +// IDBCursor.delete() - object store - attempt to remove a record in a read-only transaction +test.cb("WPT idbcursor-delete-objectstore2.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var cursor_rq = db.transaction("test").objectStore("test").openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + t.true(cursor != null, "cursor exist"); + t.throws( + function () { + cursor.delete(); + }, + { name: "ReadOnlyError" }, + ); + t.end(); + }; + }; +}); + +// IDBCursor.delete() - index - attempt to remove a record in an inactive transaction +test.cb("WPT idbcursor-delete-objectstore3.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + + var cursor_rq = objStore.openCursor(); + + const window: any = {}; + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + t.true(cursor instanceof BridgeIDBCursor, "cursor exist"); + window.cursor = cursor; + }; + + e.target.transaction.oncomplete = function (e: any) { + t.throws( + function () { + window.cursor.delete(); + }, + { + name: "TransactionInactiveError", + }, + ); + t.end(); + }; + }; +}); + +// IDBCursor.delete() - object store - throw InvalidStateError caused by object store been deleted +test.cb("WPT idbcursor-delete-objectstore4.htm", (t) => { + var db: any, + records = [{ pKey: "primaryKey_0" }, { pKey: "primaryKey_1" }]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (event: any) { + db = event.target.result; + var objStore = db.createObjectStore("store", { keyPath: "pKey" }); + for (var i = 0; i < records.length; i++) { + objStore.add(records[i]); + } + var rq = objStore.openCursor(); + rq.onsuccess = function (event: any) { + var cursor = event.target.result; + t.true(cursor instanceof BridgeIDBCursor, "cursor exist"); + + db.deleteObjectStore("store"); + t.throws( + function () { + cursor.delete(); + }, + { name: "InvalidStateError" }, + "If the cursor's source or effective object store has been deleted, the implementation MUST throw a DOMException of type InvalidStateError", + ); + + t.end(); + }; + }; +}); + +// IDBCursor.delete() - object store - throw InvalidStateError when the cursor is being iterated +test.cb("WPT idbcursor-delete-objectstore5.htm", (t) => { + var db: any, + records = [{ pKey: "primaryKey_0" }, { pKey: "primaryKey_1" }]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (event: any) { + db = event.target.result; + var objStore = db.createObjectStore("store", { keyPath: "pKey" }); + for (var i = 0; i < records.length; i++) { + objStore.add(records[i]); + } + }; + + open_rq.onsuccess = function (event: any) { + var txn = db.transaction("store", "readwrite"); + var rq = txn.objectStore("store").openCursor(); + rq.onsuccess = function (event: any) { + var cursor = event.target.result; + t.true(cursor instanceof BridgeIDBCursor, "cursor exist"); + + cursor.continue(); + t.throws( + function () { + cursor.delete(); + }, + { + name: "InvalidStateError", + }, + ); + + t.end(); + }; + }; +}); diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbcursor-reused.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-reused.test.ts index 44a647dc8..3c2ee875d 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/idbcursor-reused.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-reused.test.ts @@ -14,7 +14,7 @@ test("WPT idbcursor-reused.htm", async (t) => { os.add("data2", "k2"); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var cursor: any; var count = 0; var rq = db.transaction("test").objectStore("test").openCursor(); diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbcursor-update-index.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-update-index.test.ts new file mode 100644 index 000000000..950a31e38 --- /dev/null +++ b/packages/idb-bridge/src/idb-wpt-ported/idbcursor-update-index.test.ts @@ -0,0 +1,388 @@ +import test from "ava"; +import { BridgeIDBCursor, BridgeIDBKeyRange } from ".."; +import { BridgeIDBCursorWithValue } from "../bridge-idb"; +import { IDBDatabase } from "../idbtypes"; +import { + createDatabase, + createdb, + promiseForRequest, + promiseForTransaction, +} from "./wptsupport"; + +// IDBCursor.update() - index - modify a record in the object store +test.cb("WPT test idbcursor_update_index.htm", (t) => { + var db: any, + count = 0, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + objStore.createIndex("index", "iKey"); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + + // XXX: Gecko doesn't like this + //e.target.transaction.oncomplete = t.step_func(CursorUpdateRecord); + }; + + open_rq.onsuccess = CursorUpdateRecord; + + function CursorUpdateRecord(e: any) { + var txn = db.transaction("test", "readwrite"), + cursor_rq = txn.objectStore("test").index("index").openCursor(); + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + cursor.value.iKey += "_updated"; + cursor.update(cursor.value); + }; + + txn.oncomplete = VerifyRecordWasUpdated; + } + + function VerifyRecordWasUpdated(e: any) { + var cursor_rq = db.transaction("test").objectStore("test").openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + t.deepEqual(cursor.value.iKey, records[0].iKey + "_updated"); + t.end(); + }; + } +}); + +// IDBCursor.update() - index - attempt to modify a record in a read-only transaction +test.cb("WPT test idbcursor_update_index2.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + objStore.createIndex("index", "iKey"); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var cursor_rq = db + .transaction("test") + .objectStore("test") + .index("index") + .openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + t.throws( + function () { + cursor.update(cursor.value); + }, + { name: "ReadOnlyError" }, + ); + t.end(); + }; + }; +}); + +//IDBCursor.update() - index - attempt to modify a record in an inactive transaction +test.cb("WPT test idbcursor_update_index3.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + var index = objStore.createIndex("index", "iKey"); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + + var cursor_rq = index.openCursor(); + + const window: any = {}; + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + t.true(cursor instanceof BridgeIDBCursor, "cursor exist"); + window.cursor = cursor; + window.record = cursor.value; + }; + + e.target.transaction.oncomplete = function (e: any) { + t.throws( + function () { + window.cursor.update(window.record); + }, + { name: "TransactionInactiveError" }, + ); + t.end(); + }; + }; +}); + +// IDBCursor.update() - index - attempt to modify a record when object store been deleted +test.cb("WPT test idbcursor_update_index4.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (event: any) { + db = event.target.result; + var objStore = db.createObjectStore("store", { keyPath: "pKey" }); + objStore.createIndex("index", "iKey"); + for (var i = 0; i < records.length; i++) { + objStore.add(records[i]); + } + var rq = objStore.index("index").openCursor(); + rq.onsuccess = function (event: any) { + var cursor = event.target.result; + t.true(cursor instanceof BridgeIDBCursor); + + db.deleteObjectStore("store"); + cursor.value.iKey += "_updated"; + t.throws( + function () { + cursor.update(cursor.value); + }, + { name: "InvalidStateError" }, + "If the cursor's source or effective object store has been deleted, the implementation MUST throw a DOMException of type InvalidStateError", + ); + + t.end(); + }; + }; +}); + +// IDBCursor.update() - index - throw DataCloneError +test.cb("WPT test idbcursor_update_index5.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + objStore.createIndex("index", "iKey"); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var cursor_rq = db + .transaction("test", "readwrite") + .objectStore("test") + .index("index") + .openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + t.true(cursor instanceof BridgeIDBCursor); + + var record = cursor.value; + // Original test uses different uncloneable value + record.data = { foo: "42" }; + record.data.me = record.data; + t.throws( + function () { + cursor.update(record); + }, + { name: "DataCloneError" }, + ); + t.end(); + }; + }; +}); + +// IDBCursor.update() - index - no argument +test.cb("WPT test idbcursor_update_index6.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + objStore.createIndex("index", "iKey"); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var cursor_rq = db + .transaction("test") + .objectStore("test") + .index("index") + .openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + t.true(cursor instanceof BridgeIDBCursor); + + t.throws( + function () { + cursor.update(); + }, + { + instanceOf: TypeError, + }, + ); + t.end(); + }; + }; +}); + +// IDBCursor.update() - index - throw DataError +test.cb("WPT test idbcursor_update_index7.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var objStore = db.createObjectStore("test", { keyPath: "pKey" }); + objStore.createIndex("index", "iKey"); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var cursor_rq = db + .transaction("test", "readwrite") + .objectStore("test") + .index("index") + .openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + t.true(cursor instanceof BridgeIDBCursor); + + t.throws( + function () { + cursor.update(null); + }, + { name: "DataError" }, + ); + t.end(); + }; + }; +}); + +// IDBCursor.update() - index - throw InvalidStateError when the cursor is being iterated +test.cb("WPT test idbcursor_update_index8.htm", (t) => { + var db: any, + records = [ + { pKey: "primaryKey_0", iKey: "indexKey_0" }, + { pKey: "primaryKey_1", iKey: "indexKey_1" }, + ]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var objStore = db.createObjectStore("store", { keyPath: "pKey" }); + objStore.createIndex("index", "iKey"); + + for (var i = 0; i < records.length; i++) objStore.add(records[i]); + }; + + open_rq.onsuccess = function (e: any) { + var cursor_rq = db + .transaction("store", "readwrite") + .objectStore("store") + .index("index") + .openCursor(); + + cursor_rq.onsuccess = function (e: any) { + var cursor = e.target.result; + t.true(cursor instanceof BridgeIDBCursor, "cursor exists"); + + cursor.continue(); + t.throws( + function () { + cursor.update({ pKey: "primaryKey_0", iKey: "indexKey_0_updated" }); + }, + { + name: "InvalidStateError", + }, + ); + + t.end(); + }; + }; +}); + +// Index cursor - indexed values updated during iteration +test("WPT test idbcursor_update_index9.any.js", async (t) => { + const db = await createDatabase(t, (db) => { + const store = db.createObjectStore("store"); + store.createIndex("index", "value"); + store.put({ value: 1 }, 1); + store.put({ value: 2 }, 2); + store.put({ value: 3 }, 3); + }); + + { + // Iterate over all index entries until an upper bound is reached. + // On each record found, increment the value used as the index + // key, which will make it show again up later in the iteration. + const tx = db.transaction("store", "readwrite"); + const range = BridgeIDBKeyRange.upperBound(9); + const index = tx.objectStore("store").index("index"); + const request = index.openCursor(range); + request.onsuccess = (e: any) => { + const cursor = e.target.result; + if (!cursor) return; + + const record = cursor.value; + record.value += 1; + cursor.update(record); + + cursor.continue(); + }; + + await promiseForTransaction(t, tx); + } + + { + const tx = db.transaction("store", "readonly"); + const results = await promiseForRequest( + t, + tx.objectStore("store").getAll(), + ); + t.deepEqual( + results.map((record) => record.value), + [10, 10, 10], + "Values should all be incremented until bound reached", + ); + } +}); diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbfactory-open.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbfactory-open.test.ts index 0d1f24c4b..bba9c6e54 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/idbfactory-open.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/idbfactory-open.test.ts @@ -8,7 +8,7 @@ test("WPT idbfactory-open.htm", async (t) => { await new Promise((resolve, reject) => { var open_rq = createdb(t, undefined, 9); - open_rq.onupgradeneeded = function (e) {}; + open_rq.onupgradeneeded = function (e: any) {}; open_rq.onsuccess = function (e: any) { t.deepEqual(e.target.source, null, "source"); resolve(); @@ -23,7 +23,7 @@ test("WPT idbfactory-open2.htm", async (t) => { var database_name = t.title + "-database_name"; var open_rq = createdb(t, database_name, 13); - open_rq.onupgradeneeded = function (e) {}; + open_rq.onupgradeneeded = function (e: any) {}; open_rq.onsuccess = function (e: any) { var db = e.target.result; t.deepEqual(db.name, database_name, "db.name"); @@ -302,7 +302,7 @@ test("WPT idbfactory-open10.htm", async (t) => { st.add({ i: "Joshua" }, 1); st.add({ i: "Jonas" }, 2); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { db.close(); var open_rq2 = indexedDB.open(db.name, 10); open_rq2.onupgradeneeded = function (e: any) { @@ -434,7 +434,7 @@ test("WPT idbfactory-open11.htm", async (t) => { store.add("data2", 2); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var store = db.transaction("store").objectStore("store"); t.deepEqual(store.name, "store", "store.name"); store.count().onsuccess = function (e: any) { @@ -491,7 +491,7 @@ test("WPT idbfactory-open12.htm", async (t) => { t.deepEqual(db.version, 9, "db.version"); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { t.true(e instanceof FakeEvent, "e instanceof Event"); t.false( e instanceof BridgeIDBVersionChangeEvent, diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbindex-get.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbindex-get.test.ts index 7601faada..751b4f983 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/idbindex-get.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/idbindex-get.test.ts @@ -18,7 +18,7 @@ test("WPT idbindex_get.htm", async (t) => { objStore.add(record); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var rq = db .transaction("store") .objectStore("store") @@ -53,7 +53,7 @@ test("WPT idbindex_get2.htm", async (t) => { for (var i = 0; i < records.length; i++) objStore.add(records[i]); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var rq = db .transaction("test") .objectStore("test") @@ -107,7 +107,7 @@ test("WPT idbindex_get4.htm", async (t) => { } }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var rq = db .transaction("store") .objectStore("store") diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbindex-openCursor.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbindex-openCursor.test.ts new file mode 100644 index 000000000..2dcab6034 --- /dev/null +++ b/packages/idb-bridge/src/idb-wpt-ported/idbindex-openCursor.test.ts @@ -0,0 +1,80 @@ +import test from "ava"; +import { BridgeIDBCursor } from ".."; +import { BridgeIDBCursorWithValue } from "../bridge-idb"; +import { IDBDatabase } from "../idbtypes"; +import { createdb } from "./wptsupport"; + +// IDBIndex.openCursor() - throw InvalidStateError when the index is deleted +test.cb("WPT test idbindex-openCursor.htm", (t) => { + var db; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var store = db.createObjectStore("store", { keyPath: "key" }); + var index = store.createIndex("index", "indexedProperty"); + + store.add({ key: 1, indexedProperty: "data" }); + store.deleteIndex("index"); + + t.throws( + () => { + index.openCursor(); + }, + { name: "InvalidStateError" }, + ); + + t.end(); + }; +}); + +// IDBIndex.openCursor() - throw TransactionInactiveError on aborted transaction +test.cb("WPT test idbindex-openCursor2.htm", (t) => { + var db; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var store = db.createObjectStore("store", { keyPath: "key" }); + var index = store.createIndex("index", "indexedProperty"); + store.add({ key: 1, indexedProperty: "data" }); + }; + open_rq.onsuccess = function (e: any) { + db = e.target.result; + var tx = db.transaction("store"); + var index = tx.objectStore("store").index("index"); + tx.abort(); + + t.throws( + () => { + index.openCursor(); + }, + { name: "TransactionInactiveError" }, + ); + + t.end(); + }; +}); + + +// IDBIndex.openCursor() - throw InvalidStateError on index deleted by aborted upgrade +test.cb("WPT test idbindex-openCursor3.htm", (t) => { + var db; + +var open_rq = createdb(t); +open_rq.onupgradeneeded = function(e: any) { + db = e.target.result; + var store = db.createObjectStore("store", { keyPath: "key" }); + var index = store.createIndex("index", "indexedProperty"); + store.add({ key: 1, indexedProperty: "data" }); + + e.target.transaction.abort(); + + t.throws(() => { + console.log("index before openCursor", index); + index.openCursor(); + }, { name: "InvalidStateError"}); + + t.end(); +} +}); diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-add.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-add.test.ts index b8fdb5ac3..02f05f468 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-add.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-add.test.ts @@ -17,7 +17,7 @@ test("WPT idbobjectstore_add.htm", async (t) => { objStore.add(record); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var rq = db!.transaction("store").objectStore("store").get(record.key); rq.onsuccess = function (e: any) { @@ -45,7 +45,7 @@ test("WPT idbobjectstore_add2.htm", async (t) => { objStore.add(record, key); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var rq = db.transaction("store").objectStore("store").get(key); rq.onsuccess = function (e: any) { @@ -82,7 +82,7 @@ test("WPT idbobjectstore_add3.htm", async (t) => { }; // Defer done, giving rq.onsuccess a chance to run - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { resolve(); }; }); @@ -115,7 +115,7 @@ test("WPT idbobjectstore_add4.htm", async (t) => { }; // Defer done, giving a spurious rq.onsuccess a chance to run - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { resolve(); }; }); @@ -171,7 +171,7 @@ test("WPT idbobjectstore_add6.htm", async (t) => { objStore.add(record); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var actual_keys: any[] = [], rq = db.transaction("store").objectStore("store").openCursor(); @@ -209,7 +209,7 @@ test("WPT idbobjectstore_add7.htm", async (t) => { objStore.add(record); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var actual_keys: any[] = [], rq = db.transaction("store").objectStore("store").openCursor(); @@ -251,7 +251,7 @@ test("WPT idbobjectstore_add8.htm", async (t) => { objStore.add(record); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var actual_keys: any[] = [], rq = db.transaction("store").objectStore("store").openCursor(); @@ -445,7 +445,7 @@ test("WPT idbobjectstore_add15.htm", async (t) => { db.createObjectStore("store", { keyPath: "pKey" }); }; - open_rq.onsuccess = function (event) { + open_rq.onsuccess = function (event: any) { var txn = db.transaction("store"); var ostore = txn.objectStore("store"); t.throws( diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-get.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-get.test.ts new file mode 100644 index 000000000..0c9d30b7d --- /dev/null +++ b/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-get.test.ts @@ -0,0 +1,159 @@ +import test from "ava"; +import { BridgeIDBKeyRange, BridgeIDBRequest } from ".."; +import { IDBDatabase } from "../idbtypes"; +import { createdb } from "./wptsupport"; + +// IDBObjectStore.get() - key is a number +test.cb("WPT idbobjectstore_get.htm", (t) => { + var db: any, + record = { key: 3.14159265, property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + db.createObjectStore("store", { keyPath: "key" }).add(record); + }; + + open_rq.onsuccess = function (e: any) { + var rq = db.transaction("store").objectStore("store").get(record.key); + + rq.onsuccess = function (e: any) { + t.deepEqual(e.target.result.key, record.key); + t.deepEqual(e.target.result.property, record.property); + t.end(); + }; + }; +}); + +// IDBObjectStore.get() - key is a string +test.cb("WPT idbobjectstore_get2.htm", (t) => { + var db: any, + record = { key: "this is a key that's a string", property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + db.createObjectStore("store", { keyPath: "key" }).add(record); + }; + + open_rq.onsuccess = function (e: any) { + var rq = db.transaction("store").objectStore("store").get(record.key); + + rq.onsuccess = function (e: any) { + t.deepEqual(e.target.result.key, record.key); + t.deepEqual(e.target.result.property, record.property); + t.end(); + }; + }; +}); + +// IDBObjectStore.get() - key is a date +test.cb("WPT idbobjectstore_get3.htm", (t) => { + var db: any; + const record = { key: new Date(), property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + db.createObjectStore("store", { keyPath: "key" }).add(record); + }; + + open_rq.onsuccess = function (e: any) { + var rq = db.transaction("store").objectStore("store").get(record.key); + + rq.onsuccess = function (e: any) { + t.deepEqual(e.target.result.key.valueOf(), record.key.valueOf()); + t.deepEqual(e.target.result.property, record.property); + t.end(); + }; + }; +}); + +// IDBObjectStore.get() - attempt to retrieve a record that doesn't exist +test.cb("WPT idbobjectstore_get4.htm", (t) => { + var db: any; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var rq = db.createObjectStore("store", { keyPath: "key" }).get(1); + rq.onsuccess = function (e: any) { + t.deepEqual(e.target.results, undefined); + setTimeout(function () { + t.end(); + }, 10); + }; + }; + + open_rq.onsuccess = function () {}; +}); + +// IDBObjectStore.get() - returns the record with the first key in the range +test.cb("WPT idbobjectstore_get5.htm", (t) => { + var db: any; + var open_rq = createdb(t); + + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var os = db.createObjectStore("store"); + + for (var i = 0; i < 10; i++) os.add("data" + i, i); + }; + + open_rq.onsuccess = function (e: any) { + db + .transaction("store") + .objectStore("store") + .get(BridgeIDBKeyRange.bound(3, 6)).onsuccess = function (e: any) { + t.deepEqual(e.target.result, "data3", "get(3-6)"); + t.end(); + }; + }; +}); + +// IDBObjectStore.get() - throw TransactionInactiveError on aborted transaction +test.cb("WPT idbobjectstore_get6.htm", (t) => { + var db: any; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + db.createObjectStore("store", { keyPath: "key" }); + }; + + open_rq.onsuccess = function (e: any) { + var store = db.transaction("store").objectStore("store"); + store.transaction.abort(); + t.throws( + function () { + store.get(1); + }, + { name: "TransactionInactiveError" }, + "throw TransactionInactiveError on aborted transaction.", + ); + t.end(); + }; +}); + +// IDBObjectStore.get() - throw DataError when using invalid key +test.cb("WPT idbobjectstore_get7.htm", (t) => { + var db: any; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + db.createObjectStore("store", { keyPath: "key" }); + }; + + open_rq.onsuccess = function (e: any) { + var store = db.transaction("store").objectStore("store"); + t.throws( + function () { + store.get(null); + }, + { name: "DataError" }, + "throw DataError when using invalid key.", + ); + t.end(); + }; +}); diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-put.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-put.test.ts new file mode 100644 index 000000000..3ca1b8ecb --- /dev/null +++ b/packages/idb-bridge/src/idb-wpt-ported/idbobjectstore-put.test.ts @@ -0,0 +1,449 @@ +import test from "ava"; +import { BridgeIDBKeyRange, BridgeIDBRequest } from ".."; +import { IDBDatabase } from "../idbtypes"; +import { createdb } from "./wptsupport"; + +// IDBObjectStore.put() - put with an inline key +test.cb("WPT idbobjectstore_put.htm", (t) => { + var db: any, + record = { key: 1, property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("store", { keyPath: "key" }); + + objStore.put(record); + }; + + open_rq.onsuccess = function (e: any) { + var rq = db.transaction("store").objectStore("store").get(record.key); + + rq.onsuccess = function (e: any) { + t.deepEqual(e.target.result.property, record.property); + t.deepEqual(e.target.result.key, record.key); + t.end(); + }; + }; +}); + +// IDBObjectStore.put() - put with an out-of-line key +test.cb("WPT idbobjectstore_put2.htm", (t) => { + var db: any, + key = 1, + record = { property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("store"); + + objStore.put(record, key); + }; + + open_rq.onsuccess = function (e: any) { + var rq = db.transaction("store").objectStore("store").get(key); + + rq.onsuccess = function (e: any) { + t.deepEqual(e.target.result.property, record.property); + + t.end(); + }; + }; +}); + +// IDBObjectStore.put() - put with an out-of-line key +test.cb("WPT idbobjectstore_put3.htm", (t) => { + var db: any, + success_event: any, + record = { key: 1, property: "data" }, + record_put = { key: 1, property: "changed", more: ["stuff", 2] }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("store", { keyPath: "key" }); + objStore.put(record); + + var rq = objStore.put(record_put); + rq.onerror = () => t.fail("error on put"); + + rq.onsuccess = function (e: any) { + success_event = true; + }; + }; + + open_rq.onsuccess = function (e: any) { + t.true(success_event); + + var rq = db.transaction("store").objectStore("store").get(1); + + rq.onsuccess = function (e: any) { + var rec = e.target.result; + + t.deepEqual(rec.key, record_put.key); + t.deepEqual(rec.property, record_put.property); + t.deepEqual(rec.more, record_put.more); + + t.end(); + }; + }; +}); + +// IDBObjectStore.put() - put where an index has unique:true specified +test.cb("WPT idbobjectstore_put4.htm", (t) => { + var db: any, + record = { key: 1, property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("store", { autoIncrement: true }); + objStore.createIndex("i1", "property", { unique: true }); + objStore.put(record); + + var rq = objStore.put(record); + rq.onsuccess = () => t.fail("success on putting duplicate indexed record"); + + rq.onerror = function (e: any) { + t.deepEqual(rq.error.name, "ConstraintError"); + t.deepEqual(e.target.error.name, "ConstraintError"); + + t.deepEqual(e.type, "error"); + + e.preventDefault(); + e.stopPropagation(); + }; + }; + + // Defer done, giving a spurious rq.onsuccess a chance to run + open_rq.onsuccess = function (e: any) { + t.end(); + }; +}); + +// IDBObjectStore.put() - object store's key path is an object attribute +test.cb("WPT idbobjectstore_put5.htm", (t) => { + var db: any, + record = { test: { obj: { key: 1 } }, property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("store", { keyPath: "test.obj.key" }); + objStore.put(record); + }; + + open_rq.onsuccess = function (e: any) { + var rq = db + .transaction("store") + .objectStore("store") + .get(record.test.obj.key); + + rq.onsuccess = function (e: any) { + t.deepEqual(e.target.result.property, record.property); + + t.end(); + }; + }; +}); + +// IDBObjectStore.put() - autoIncrement and inline keys +test.cb("WPT idbobjectstore_put6.htm", (t) => { + var db: any, + record = { property: "data" }, + expected_keys = [1, 2, 3, 4]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("store", { + keyPath: "key", + autoIncrement: true, + }); + + objStore.put(record); + objStore.put(record); + objStore.put(record); + objStore.put(record); + }; + + open_rq.onsuccess = function (e: any) { + var actual_keys: any[] = [], + rq = db.transaction("store").objectStore("store").openCursor(); + + rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + if (cursor) { + actual_keys.push(cursor.value.key); + cursor.continue(); + } else { + t.deepEqual(actual_keys, expected_keys); + t.end(); + } + }; + }; +}); + +// IDBObjectStore.put() - autoIncrement and out-of-line keys +test.cb("WPT idbobjectstore_put7.htm", (t) => { + var db: any, + record = { property: "data" }, + expected_keys = [1, 2, 3, 4]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("store", { autoIncrement: true }); + + objStore.put(record); + objStore.put(record); + objStore.put(record); + objStore.put(record); + }; + + open_rq.onsuccess = function (e) { + var actual_keys: any[] = [], + rq = db.transaction("store").objectStore("store").openCursor(); + + rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + if (cursor) { + actual_keys.push(cursor.key); + cursor.continue(); + } else { + t.deepEqual(actual_keys, expected_keys); + t.end(); + } + }; + }; +}); + +// IDBObjectStore.put() - object store has autoIncrement:true and the key path is an object attribute +test.cb("WPT idbobjectstore_put8.htm", (t) => { + var db: any, + record = { property: "data" }, + expected_keys = [1, 2, 3, 4]; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + var objStore = db.createObjectStore("store", { + keyPath: "test.obj.key", + autoIncrement: true, + }); + + objStore.put(record); + objStore.put(record); + objStore.put(record); + objStore.put(record); + }; + + open_rq.onsuccess = function (e: any) { + var actual_keys: any[] = [], + rq = db.transaction("store").objectStore("store").openCursor(); + + rq.onsuccess = function (e: any) { + var cursor = e.target.result; + + if (cursor) { + actual_keys.push(cursor.value.test.obj.key); + cursor.continue(); + } else { + t.deepEqual(actual_keys, expected_keys); + t.end(); + } + }; + }; +}); + +//IDBObjectStore.put() - Attempt to put a record that does not meet the constraints of an object store's inline key requirements +test.cb("WPT idbobjectstore_put9.htm", (t) => { + var record = { key: 1, property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + var rq, + db = e.target.result, + objStore = db.createObjectStore("store", { keyPath: "key" }); + + t.throws( + function () { + rq = objStore.put(record, 1); + }, + { name: "DataError" }, + ); + + t.deepEqual(rq, undefined); + t.end(); + }; +}); + +//IDBObjectStore.put() - Attempt to call 'put' without an key parameter when the object store uses out-of-line keys +test.cb("WPT idbobjectstore_put10.htm", (t) => { + var db: any, + record = { property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var rq, + objStore = db.createObjectStore("store", { keyPath: "key" }); + + t.throws( + function () { + rq = objStore.put(record); + }, + { name: "DataError" }, + ); + + t.deepEqual(rq, undefined); + t.end(); + }; +}); + +// IDBObjectStore.put() - Attempt to put a record where the record's key does not meet the constraints of a valid key +test.cb("WPT idbobjectstore_put11.htm", (t) => { + var db: any, + record = { key: { value: 1 }, property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var rq, + objStore = db.createObjectStore("store", { keyPath: "key" }); + + t.throws( + function () { + rq = objStore.put(record); + }, + { name: "DataError" }, + ); + + t.deepEqual(rq, undefined); + t.end(); + }; +}); + +// IDBObjectStore.put() - Attempt to put a record where the record's in-line key is not defined +test.cb("WPT idbobjectstore_put12.htm", (t) => { + var db: any, + record = { property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var rq, + objStore = db.createObjectStore("store", { keyPath: "key" }); + + t.throws( + function () { + rq = objStore.put(record); + }, + { name: "DataError" }, + ); + + t.deepEqual(rq, undefined); + t.end(); + }; +}); + +// IDBObjectStore.put() - Attempt to put a record where the out of line key provided does not meet the constraints of a valid key +test.cb("WPT idbobjectstore_put13.htm", (t) => { + var db: any, + record = { property: "data" }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var rq, + objStore = db.createObjectStore("store"); + + t.throws( + function () { + rq = objStore.put(record, { value: 1 }); + }, + { + name: "DataError", + }, + ); + + t.deepEqual(rq, undefined); + t.end(); + }; +}); + +// IDBObjectStore.put() - Put a record where a value being indexed does not meet the constraints of a valid key +test.cb("WPT idbobjectstore_put14.htm", (t) => { + var db: any, + record = { key: 1, indexedProperty: { property: "data" } }; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (e: any) { + db = e.target.result; + + var rq, + objStore = db.createObjectStore("store", { keyPath: "key" }); + + objStore.createIndex("index", "indexedProperty"); + + rq = objStore.put(record); + + t.true(rq instanceof BridgeIDBRequest); + rq.onsuccess = function () { + t.end(); + }; + }; +}); + +// IDBObjectStore.put() - If the transaction this IDBObjectStore belongs to has its mode set to readonly, throw ReadOnlyError +test.cb("WPT idbobjectstore_put15.htm", (t) => { + var db: any; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (event: any) { + db = event.target.result; + db.createObjectStore("store", { keyPath: "pKey" }); + }; + + open_rq.onsuccess = function (event: any) { + var txn = db.transaction("store"); + var ostore = txn.objectStore("store"); + t.throws( + function () { + ostore.put({ pKey: "primaryKey_0" }); + }, + { + name: "ReadOnlyError", + }, + ); + t.end(); + }; +}); + +// IDBObjectStore.put() - If the object store has been deleted, the implementation must throw a DOMException of type InvalidStateError +test.cb("WPT idbobjectstore_put16.htm", (t) => { + var db: any, ostore: any; + + var open_rq = createdb(t); + open_rq.onupgradeneeded = function (event: any) { + db = event.target.result; + ostore = db.createObjectStore("store", { keyPath: "pKey" }); + db.deleteObjectStore("store"); + t.throws( + function () { + ostore.put({ pKey: "primaryKey_0" }); + }, + { + name: "InvalidStateError", + }, + ); + t.end(); + }; +}); diff --git a/packages/idb-bridge/src/idb-wpt-ported/idbtransaction-oncomplete.test.ts b/packages/idb-bridge/src/idb-wpt-ported/idbtransaction-oncomplete.test.ts index 8e0b43877..8f54fb7cb 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/idbtransaction-oncomplete.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/idbtransaction-oncomplete.test.ts @@ -20,7 +20,7 @@ test("WPT idbtransaction-oncomplete.htm", async (t) => { }; }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { stages.push("success"); // Making a totally new transaction to check diff --git a/packages/idb-bridge/src/idb-wpt-ported/keypath.test.ts b/packages/idb-bridge/src/idb-wpt-ported/keypath.test.ts index 61e416a53..20ec6f3fa 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/keypath.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/keypath.test.ts @@ -15,14 +15,14 @@ test("WPT test keypath.htm", async (t) => { const store_name = "store-" + Date.now() + Math.random(); var open_rq = createdb(t); - open_rq.onupgradeneeded = function (e) { + open_rq.onupgradeneeded = function (e: any) { db = (e.target as any).result; var objStore = db.createObjectStore(store_name, { keyPath: keypath }); for (var i = 0; i < objects.length; i++) objStore.add(objects[i]); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var actual_keys: any[] = [], rq = db.transaction(store_name).objectStore(store_name).openCursor(); diff --git a/packages/idb-bridge/src/idb-wpt-ported/request-bubble-and-capture.test.ts b/packages/idb-bridge/src/idb-wpt-ported/request-bubble-and-capture.test.ts index c59a63bc6..a7541a683 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/request-bubble-and-capture.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/request-bubble-and-capture.test.ts @@ -32,7 +32,7 @@ test("WPT request_bubble-and-capture.htm", async (t) => { ); }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { log("open_rq.success")(e); t.deepEqual( events, diff --git a/packages/idb-bridge/src/idb-wpt-ported/transaction-requestqueue.test.ts b/packages/idb-bridge/src/idb-wpt-ported/transaction-requestqueue.test.ts index edf98eb54..707bb5255 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/transaction-requestqueue.test.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/transaction-requestqueue.test.ts @@ -23,7 +23,7 @@ test("transaction-requestqueue.htm", async (t) => { } }; - open_rq.onsuccess = function (e) { + open_rq.onsuccess = function (e: any) { var txn = db.transaction(["os2", "os1", "os3", "os5"]); txn.objectStore("os1").openCursor().onsuccess = reg("txn"); txn.objectStore("os3").openCursor().onsuccess = reg("txn"); diff --git a/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts b/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts index 5f6b0a040..d6c0b011a 100644 --- a/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts +++ b/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts @@ -12,9 +12,9 @@ import { import { MemoryBackend } from "../MemoryBackend"; import { compareKeys } from "../util/cmp"; -BridgeIDBFactory.enableTracing = false; +BridgeIDBFactory.enableTracing = true; const backend = new MemoryBackend(); -backend.enableTracing = false; +backend.enableTracing = true; export const idbFactory = new BridgeIDBFactory(backend); const self = { @@ -43,12 +43,6 @@ export function assert_key_equals( } } -export function assert_equals(actual: any, expected: any) { - if (actual !== expected) { - throw Error("assert_equals failed"); - } -} - function makeDatabaseName(testCase: string): string { return "db-" + testCase; } @@ -59,7 +53,7 @@ function makeDatabaseName(testCase: string): string { // other event causes the promise to reject with an error. This is correct in // most cases, but insufficient for indexedDB.open(), which issues // "upgradeneded" events under normal operation. -function promiseForRequest( +export function promiseForRequest( t: ExecutionContext, request: IDBRequest, ): Promise { @@ -75,6 +69,23 @@ function promiseForRequest( }); } +// Promise that resolves when an IDBTransaction completes. +// +// The promise resolves with undefined if IDBTransaction receives the "complete" +// event, and rejects with an error for any other event. +export function promiseForTransaction( + t: ExecutionContext, + request: IDBTransaction, +) { + return new Promise((resolve, reject) => { + request.addEventListener("complete", (evt: any) => { + resolve(evt.target.result); + }); + request.addEventListener("abort", (evt: any) => reject(evt.target.error)); + request.addEventListener("error", (evt: any) => reject(evt.target.error)); + }); +} + type MigrationCallback = ( db: IDBDatabase, tx: IDBTransaction, @@ -430,7 +441,7 @@ export function format_value(val: any, seen?: any): string { // }, // (test_object, db_connection, open_request) => { // // Test logic. -// test_object.done(); +// test_object.end(); // }, // 'Test case description'); export function indexeddb_test( @@ -460,7 +471,7 @@ export function indexeddb_test( var db = open.result; t.teardown(function () { // If open didn't succeed already, ignore the error. - open.onerror = function (e) { + open.onerror = function (e: any) { e.preventDefault(); }; db.close();