get IDB tests to pass again, add new one

This commit is contained in:
Florian Dold 2021-02-16 10:31:55 +01:00
parent d384bd5c62
commit d1f00aeaa2
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
8 changed files with 239 additions and 119 deletions

View File

@ -17,7 +17,6 @@
import { BridgeIDBDatabaseInfo, BridgeIDBKeyRange } from "./bridge-idb"; import { BridgeIDBDatabaseInfo, BridgeIDBKeyRange } from "./bridge-idb";
import { import {
IDBCursorDirection, IDBCursorDirection,
IDBKeyPath,
IDBTransactionMode, IDBTransactionMode,
IDBValidKey, IDBValidKey,
} from "./idbtypes"; } from "./idbtypes";

View File

@ -61,7 +61,11 @@ import FakeEventTarget from "./util/FakeEventTarget";
import { normalizeKeyPath } from "./util/normalizeKeyPath"; import { normalizeKeyPath } from "./util/normalizeKeyPath";
import openPromise from "./util/openPromise"; import openPromise from "./util/openPromise";
import queueTask from "./util/queueTask"; import queueTask from "./util/queueTask";
import { structuredClone, structuredEncapsulate, structuredRevive } from "./util/structuredClone"; import {
structuredClone,
structuredEncapsulate,
structuredRevive,
} from "./util/structuredClone";
import validateKeyPath from "./util/validateKeyPath"; import validateKeyPath from "./util/validateKeyPath";
import valueToKey from "./util/valueToKey"; import valueToKey from "./util/valueToKey";
@ -266,7 +270,7 @@ export class BridgeIDBCursor implements IDBCursor {
const transaction = this._effectiveObjectStore._transaction; const transaction = this._effectiveObjectStore._transaction;
if (transaction._state !== "active") { if (!transaction._active) {
throw new TransactionInactiveError(); throw new TransactionInactiveError();
} }
@ -322,7 +326,7 @@ export class BridgeIDBCursor implements IDBCursor {
public continue(key?: IDBValidKey) { public continue(key?: IDBValidKey) {
const transaction = this._effectiveObjectStore._transaction; const transaction = this._effectiveObjectStore._transaction;
if (transaction._state !== "active") { if (!transaction._active) {
throw new TransactionInactiveError(); throw new TransactionInactiveError();
} }
@ -384,7 +388,7 @@ export class BridgeIDBCursor implements IDBCursor {
public delete() { public delete() {
const transaction = this._effectiveObjectStore._transaction; const transaction = this._effectiveObjectStore._transaction;
if (transaction._state !== "active") { if (!transaction._active) {
throw new TransactionInactiveError(); throw new TransactionInactiveError();
} }
@ -455,7 +459,7 @@ export class BridgeIDBCursorWithValue extends BridgeIDBCursor {
* Ensure that an active version change transaction is currently running. * Ensure that an active version change transaction is currently running.
*/ */
const confirmActiveVersionchangeTransaction = (database: BridgeIDBDatabase) => { const confirmActiveVersionchangeTransaction = (database: BridgeIDBDatabase) => {
if (!database._runningVersionchangeTransaction) { if (!database._upgradeTransaction) {
throw new InvalidStateError(); throw new InvalidStateError();
} }
@ -467,11 +471,11 @@ const confirmActiveVersionchangeTransaction = (database: BridgeIDBDatabase) => {
); );
const transaction = transactions[transactions.length - 1]; const transaction = transactions[transactions.length - 1];
if (!transaction || transaction._state === "finished") { if (!transaction || transaction._finished) {
throw new InvalidStateError(); throw new InvalidStateError();
} }
if (transaction._state !== "active") { if (!transaction._active) {
throw new TransactionInactiveError(); throw new TransactionInactiveError();
} }
@ -480,12 +484,13 @@ const confirmActiveVersionchangeTransaction = (database: BridgeIDBDatabase) => {
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#database-interface // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#database-interface
/** @public */ /** @public */
export class BridgeIDBDatabase extends FakeEventTarget { export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
_closePending = false; _closePending = false;
_closed = false; _closed = false;
_runningVersionchangeTransaction = false;
_transactions: Array<BridgeIDBTransaction> = []; _transactions: Array<BridgeIDBTransaction> = [];
_upgradeTransaction: BridgeIDBTransaction | null = null;
_backendConnection: DatabaseConnection; _backendConnection: DatabaseConnection;
_backend: Backend; _backend: Backend;
@ -499,8 +504,10 @@ export class BridgeIDBDatabase extends FakeEventTarget {
return this._schema.databaseVersion; return this._schema.databaseVersion;
} }
get objectStoreNames(): FakeDOMStringList { get objectStoreNames(): DOMStringList {
return fakeDOMStringList(Object.keys(this._schema.objectStores)).sort(); return fakeDOMStringList(
Object.keys(this._schema.objectStores),
).sort() as DOMStringList;
} }
/** /**
@ -509,9 +516,11 @@ export class BridgeIDBDatabase extends FakeEventTarget {
_closeConnection() { _closeConnection() {
this._closePending = true; this._closePending = true;
// Spec is unclear what "complete" means, we assume it's
// the same as "finished".
const transactionsComplete = this._transactions.every( const transactionsComplete = this._transactions.every(
(transaction: BridgeIDBTransaction) => { (transaction: BridgeIDBTransaction) => {
return transaction._state === "finished"; return transaction._finished;
}, },
); );
@ -525,6 +534,13 @@ export class BridgeIDBDatabase extends FakeEventTarget {
} }
} }
/**
* Refresh the schema by querying it from the backend.
*/
_refreshSchema() {
this._schema = this._backend.getSchema(this._backendConnection);
}
constructor(backend: Backend, backendConnection: DatabaseConnection) { constructor(backend: Backend, backendConnection: DatabaseConnection) {
super(); super();
@ -537,7 +553,10 @@ export class BridgeIDBDatabase extends FakeEventTarget {
// http://w3c.github.io/IndexedDB/#dom-idbdatabase-createobjectstore // http://w3c.github.io/IndexedDB/#dom-idbdatabase-createobjectstore
public createObjectStore( public createObjectStore(
name: string, name: string,
options: { autoIncrement?: boolean; keyPath?: IDBKeyPath } | null = {}, options: {
autoIncrement?: boolean;
keyPath?: null | IDBKeyPath | IDBKeyPath[];
} | null = {},
): BridgeIDBObjectStore { ): BridgeIDBObjectStore {
if (name === undefined) { if (name === undefined) {
throw new TypeError(); throw new TypeError();
@ -572,7 +591,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
transaction._backend.createObjectStore( transaction._backend.createObjectStore(
backendTx, backendTx,
name, name,
(keyPath !== null) ? normalizeKeyPath(keyPath) : null, keyPath !== null ? normalizeKeyPath(keyPath) : null,
autoIncrement, autoIncrement,
); );
@ -593,6 +612,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
storeNames: string | string[], storeNames: string | string[],
mode?: IDBTransactionMode, mode?: IDBTransactionMode,
backendTransaction?: DatabaseTransaction, backendTransaction?: DatabaseTransaction,
openRequest?: BridgeIDBOpenDBRequest,
): BridgeIDBTransaction { ): BridgeIDBTransaction {
mode = mode !== undefined ? mode : "readonly"; mode = mode !== undefined ? mode : "readonly";
if ( if (
@ -603,16 +623,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
throw new TypeError("Invalid mode: " + mode); throw new TypeError("Invalid mode: " + mode);
} }
const hasActiveVersionchange = this._transactions.some( if (this._upgradeTransaction) {
(transaction: BridgeIDBTransaction) => {
return (
transaction._state === "active" &&
transaction.mode === "versionchange" &&
transaction._db === this
);
},
);
if (hasActiveVersionchange) {
throw new InvalidStateError(); throw new InvalidStateError();
} }
@ -627,7 +638,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
throw new InvalidAccessError(); throw new InvalidAccessError();
} }
for (const storeName of storeNames) { for (const storeName of storeNames) {
if (this.objectStoreNames.indexOf(storeName) < 0) { if (!this.objectStoreNames.contains(storeName)) {
throw new NotFoundError( throw new NotFoundError(
"No objectStore named " + storeName + " in this database", "No objectStore named " + storeName + " in this database",
); );
@ -639,9 +650,12 @@ export class BridgeIDBDatabase extends FakeEventTarget {
mode, mode,
this, this,
backendTransaction, backendTransaction,
openRequest,
); );
this._transactions.push(tx); this._transactions.push(tx);
queueTask(() => tx._start()); queueTask(() => tx._start());
// "When a transaction is created its active flag is initially set."
tx._active = true;
return tx; return tx;
} }
@ -809,20 +823,25 @@ export class BridgeIDBFactory {
dbconn, dbconn,
requestedVersion, requestedVersion,
); );
db._runningVersionchangeTransaction = true;
const transaction = db._internalTransaction( const transaction = db._internalTransaction(
[], [],
"versionchange", "versionchange",
backendTransaction, backendTransaction,
request,
); );
db._upgradeTransaction = transaction;
const event = new BridgeIDBVersionChangeEvent("upgradeneeded", { const event = new BridgeIDBVersionChangeEvent("upgradeneeded", {
newVersion: version, newVersion: version,
oldVersion: existingVersion, oldVersion: existingVersion,
}); });
request.result = db; transaction._active = true;
request.readyState = "done"; request.readyState = "done";
request.result = db;
request.transaction = transaction; request.transaction = transaction;
request.dispatchEvent(event); request.dispatchEvent(event);
@ -832,15 +851,30 @@ export class BridgeIDBFactory {
// We don't explicitly exit the versionchange transaction, // We don't explicitly exit the versionchange transaction,
// since this is already done by the BridgeIDBTransaction. // since this is already done by the BridgeIDBTransaction.
db._runningVersionchangeTransaction = false; db._upgradeTransaction = null;
const event2 = new FakeEvent("success", { // We re-use the same transaction (as per spec) here.
bubbles: false, transaction._active = true;
cancelable: false, if (transaction._aborted) {
}); request.result = undefined;
event2.eventPath = [request]; request.error = new AbortError();
request.readyState = "done";
const event2 = new FakeEvent("error", {
bubbles: false,
cancelable: false,
});
event2.eventPath = [request];
request.dispatchEvent(event2);
} else {
console.log(`dispatching success event, _active=${transaction._active}`);
const event2 = new FakeEvent("success", {
bubbles: false,
cancelable: false,
});
event2.eventPath = [request];
request.dispatchEvent(event2); request.dispatchEvent(event2);
}
} }
this.connections.push(db); this.connections.push(db);
@ -871,7 +905,7 @@ const confirmActiveTransaction = (
throw new InvalidStateError(); throw new InvalidStateError();
} }
if (index._objectStore._transaction._state !== "active") { if (!index._objectStore._transaction._active) {
throw new TransactionInactiveError(); throw new TransactionInactiveError();
} }
@ -931,11 +965,11 @@ export class BridgeIDBIndex implements IDBIndex {
set name(name: any) { set name(name: any) {
const transaction = this._objectStore._transaction; const transaction = this._objectStore._transaction;
if (!transaction._db._runningVersionchangeTransaction) { if (!transaction._db._upgradeTransaction) {
throw new InvalidStateError(); throw new InvalidStateError();
} }
if (transaction._state !== "active") { if (!transaction._active) {
throw new TransactionInactiveError(); throw new TransactionInactiveError();
} }
@ -1282,7 +1316,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
get _indexNames(): FakeDOMStringList { get _indexNames(): FakeDOMStringList {
return fakeDOMStringList( return fakeDOMStringList(
Object.keys(this._schema.objectStores[this._name].indexes), Object.keys(this._schema.objectStores[this._name].indexes),
).sort() ).sort();
} }
get indexNames(): DOMStringList { get indexNames(): DOMStringList {
@ -1330,7 +1364,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
set name(newName: any) { set name(newName: any) {
const transaction = this._transaction; const transaction = this._transaction;
if (!transaction._db._runningVersionchangeTransaction) { if (!transaction._db._upgradeTransaction) {
throw new InvalidStateError(); throw new InvalidStateError();
} }
@ -1581,7 +1615,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
throw new TypeError(); throw new TypeError();
} }
if (!this._transaction._db._runningVersionchangeTransaction) { if (!this._transaction._db._upgradeTransaction) {
throw new InvalidStateError(); throw new InvalidStateError();
} }
@ -1628,7 +1662,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
throw new TypeError(); throw new TypeError();
} }
if (this._transaction._state === "finished") { if (this._transaction._finished) {
throw new InvalidStateError(); throw new InvalidStateError();
} }
@ -1649,7 +1683,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
throw new InvalidStateError(); throw new InvalidStateError();
} }
if (!this._transaction._db._runningVersionchangeTransaction) { if (!this._transaction._db._upgradeTransaction) {
throw new InvalidStateError(); throw new InvalidStateError();
} }
@ -1755,6 +1789,7 @@ export class BridgeIDBRequest extends FakeEventTarget implements IDBRequest {
cancelable: true, cancelable: true,
}); });
event.eventPath = []; event.eventPath = [];
this.dispatchEvent(event); this.dispatchEvent(event);
} }
@ -1791,24 +1826,41 @@ export class BridgeIDBOpenDBRequest
export class BridgeIDBTransaction export class BridgeIDBTransaction
extends FakeEventTarget extends FakeEventTarget
implements IDBTransaction { implements IDBTransaction {
public _state: "active" | "inactive" | "committing" | "finished" = "active"; _committed: boolean = false;
public _started = false; /**
public _objectStoresCache: Map<string, BridgeIDBObjectStore> = new Map(); * A transaction is active as long as new operations can be
* placed against it.
*/
_active: boolean = false;
_started: boolean = false;
_aborted: boolean = false;
_objectStoresCache: Map<string, BridgeIDBObjectStore> = new Map();
public _backendTransaction?: DatabaseTransaction; /**
* https://www.w3.org/TR/IndexedDB-2/#transaction-lifetime-concept
*
* When a transaction is committed or aborted, it is said to be finished.
*/
get _finished(): boolean {
return this._committed || this._aborted;
}
public _objectStoreNames: FakeDOMStringList; _openRequest: BridgeIDBOpenDBRequest | null = null;
_backendTransaction?: DatabaseTransaction;
_objectStoreNames: FakeDOMStringList;
get objectStoreNames(): DOMStringList { get objectStoreNames(): DOMStringList {
return this._objectStoreNames as DOMStringList; return this._objectStoreNames as DOMStringList;
} }
public mode: IDBTransactionMode; mode: IDBTransactionMode;
public _db: BridgeIDBDatabase; _db: BridgeIDBDatabase;
get db(): IDBDatabase { get db(): IDBDatabase {
return this.db; return this._db;
} }
public _error: Error | null = null; _error: Error | null = null;
get error(): DOMException { get error(): DOMException {
return this._error as DOMException; return this._error as DOMException;
@ -1823,7 +1875,7 @@ export class BridgeIDBTransaction
public _scope: Set<string>; public _scope: Set<string>;
private _requests: Array<{ private _requests: Array<{
operation: () => void; operation: () => Promise<void>;
request: BridgeIDBRequest; request: BridgeIDBRequest;
}> = []; }> = [];
@ -1836,6 +1888,7 @@ export class BridgeIDBTransaction
mode: IDBTransactionMode, mode: IDBTransactionMode,
db: BridgeIDBDatabase, db: BridgeIDBDatabase,
backendTransaction?: DatabaseTransaction, backendTransaction?: DatabaseTransaction,
openRequest?: BridgeIDBOpenDBRequest,
) { ) {
super(); super();
@ -1850,11 +1903,17 @@ export class BridgeIDBTransaction
this._objectStoreNames = fakeDOMStringList(Array.from(this._scope).sort()); this._objectStoreNames = fakeDOMStringList(Array.from(this._scope).sort());
this._db._transactions.push(this); this._db._transactions.push(this);
this._openRequest = openRequest ?? null;
} }
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-aborting-a-transaction // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-aborting-a-transaction
async _abort(errName: string | null) { async _abort(errName: string | null) {
this._state = "finished"; if (BridgeIDBFactory.enableTracing) {
console.log("TRACE: aborting transaction");
}
this._aborted = true;
if (errName !== null) { if (errName !== null) {
const e = new Error(); const e = new Error();
@ -1862,30 +1921,45 @@ export class BridgeIDBTransaction
this._error = e; this._error = e;
} }
if (BridgeIDBFactory.enableTracing) {
console.log(`TRACE: aborting ${this._requests.length} requests`);
}
// Should this directly remove from _requests? // Should this directly remove from _requests?
for (const { request } of this._requests) { for (const { request } of this._requests) {
console.log("ready state:", request.readyState);
if (request.readyState !== "done") { if (request.readyState !== "done") {
request.readyState = "done"; // This will cancel execution of this request's operation // This will cancel execution of this request's operation
if (request._source) { request.readyState = "done";
request.result = undefined; if (BridgeIDBFactory.enableTracing) {
request.error = new AbortError(); console.log("dispatching error event");
const event = new FakeEvent("error", {
bubbles: true,
cancelable: true,
});
event.eventPath = [this._db, this];
request.dispatchEvent(event);
} }
request.result = undefined;
request.error = new AbortError();
const event = new FakeEvent("error", {
bubbles: true,
cancelable: true,
});
event.eventPath = [request, this, this._db];
console.log("dispatching error event for request after abort");
request.dispatchEvent(event);
} }
} }
// ("abort a transaction", step 5.1)
if (this._openRequest) {
this._db._upgradeTransaction = null;
}
// Only roll back if we actually executed the scheduled operations. // Only roll back if we actually executed the scheduled operations.
const maybeBtx = this._backendTransaction; const maybeBtx = this._backendTransaction;
if (maybeBtx) { if (maybeBtx) {
await this._backend.rollback(maybeBtx); await this._backend.rollback(maybeBtx);
} }
this._db._refreshSchema();
queueTask(() => { queueTask(() => {
const event = new FakeEvent("abort", { const event = new FakeEvent("abort", {
bubbles: true, bubbles: true,
@ -1894,20 +1968,24 @@ export class BridgeIDBTransaction
event.eventPath = [this._db]; event.eventPath = [this._db];
this.dispatchEvent(event); this.dispatchEvent(event);
}); });
if (this._openRequest) {
this._openRequest.transaction = null;
this._openRequest.result = undefined;
this._openRequest.readyState = "pending";
}
} }
public abort() { public abort() {
if (this._state === "committing" || this._state === "finished") { if (this._finished) {
throw new InvalidStateError(); throw new InvalidStateError();
} }
this._state = "active";
this._abort(null); this._abort(null);
} }
// http://w3c.github.io/IndexedDB/#dom-idbtransaction-objectstore // http://w3c.github.io/IndexedDB/#dom-idbtransaction-objectstore
public objectStore(name: string): BridgeIDBObjectStore { public objectStore(name: string): BridgeIDBObjectStore {
if (this._state !== "active") { if (!this._active) {
throw new InvalidStateError(); throw new InvalidStateError();
} }
@ -1925,7 +2003,7 @@ export class BridgeIDBTransaction
const operation = obj.operation; const operation = obj.operation;
let request = obj.hasOwnProperty("request") ? obj.request : null; let request = obj.hasOwnProperty("request") ? obj.request : null;
if (this._state !== "active") { if (!this._active) {
throw new TransactionInactiveError(); throw new TransactionInactiveError();
} }
@ -2001,10 +2079,8 @@ export class BridgeIDBTransaction
request.result = result; request.result = result;
request.error = undefined; request.error = undefined;
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-fire-a-success-event // https://www.w3.org/TR/IndexedDB-2/#fire-error-event
if (this._state === "inactive") { this._active = true;
this._state = "active";
}
event = new FakeEvent("success", { event = new FakeEvent("success", {
bubbles: false, bubbles: false,
cancelable: false, cancelable: false,
@ -2014,9 +2090,11 @@ export class BridgeIDBTransaction
event.eventPath = [request, this, this._db]; event.eventPath = [request, this, this._db];
request.dispatchEvent(event); request.dispatchEvent(event);
} catch (err) { } catch (err) {
if (this._state !== "committing") { if (BridgeIDBFactory.enableTracing) {
this._abort("AbortError"); console.log("TRACING: caught error in transaction success event handler");
} }
this._abort("AbortError");
this._active = false;
throw err; throw err;
} }
} catch (err) { } catch (err) {
@ -2028,9 +2106,7 @@ export class BridgeIDBTransaction
request.error = err; request.error = err;
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-fire-an-error-event // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-fire-an-error-event
if (this._state === "inactive") { this._active = true;
this._state = "active";
}
event = new FakeEvent("error", { event = new FakeEvent("error", {
bubbles: true, bubbles: true,
cancelable: true, cancelable: true,
@ -2040,9 +2116,7 @@ export class BridgeIDBTransaction
event.eventPath = [this._db, this]; event.eventPath = [this._db, this];
request.dispatchEvent(event); request.dispatchEvent(event);
} catch (err) { } catch (err) {
if (this._state !== "committing") { this._abort("AbortError");
this._abort("AbortError");
}
throw err; throw err;
} }
if (!event.canceled) { if (!event.canceled) {
@ -2061,17 +2135,13 @@ export class BridgeIDBTransaction
return; return;
} }
if (this._state !== "finished" && this._state !== "committing") { if (!this._finished && !this._committed) {
if (BridgeIDBFactory.enableTracing) { if (BridgeIDBFactory.enableTracing) {
console.log("finishing transaction"); console.log("finishing transaction");
} }
this._state = "committing";
await this._backend.commit(this._backendTransaction); await this._backend.commit(this._backendTransaction);
this._committed = true;
this._state = "finished";
if (!this._error) { if (!this._error) {
if (BridgeIDBFactory.enableTracing) { if (BridgeIDBFactory.enableTracing) {
console.log("dispatching 'complete' event on transaction"); console.log("dispatching 'complete' event on transaction");
@ -2089,15 +2159,19 @@ export class BridgeIDBTransaction
this._resolveWait(); this._resolveWait();
} }
if (this._aborted) {
this._resolveWait();
}
} }
public commit() { public commit() {
if (this._state !== "active") { // The current spec doesn't even have an explicit commit method.
// We still support it, effectively as a "no-operation" that
// prevents new operations from being scheduled.
if (!this._active) {
throw new InvalidStateError(); throw new InvalidStateError();
} }
this._active = false;
this._state = "committing";
// We now just wait for auto-commit ...
} }
public toString() { public toString() {

View File

@ -0,0 +1,34 @@
import test from "ava";
import { createdb } from "./wptsupport";
test("WPT test abort-in-initial-upgradeneeded.htm", async (t) => {
await new Promise<void>((resolve, reject) => {
var db: any;
var open_rq = createdb(t, undefined, 2);
open_rq.onupgradeneeded = function (e) {
const tgt = e.target as any;
db = tgt.result;
t.assert(db.version === 2);
var transaction = tgt.transaction;
transaction.oncomplete = () => t.fail("unexpected transaction.complete");
transaction.onabort = function (e: any) {
console.log(`version: ${e.target.db.version}`);
t.deepEqual(e.target.db.version, 0);
};
db.onabort = function () {};
transaction.abort();
};
open_rq.onerror = function (e) {
const tgt = e.target as any;
t.deepEqual(open_rq, e.target);
t.deepEqual(tgt.result, undefined);
t.deepEqual(tgt.error.name, "AbortError");
console.log(`version (onerror): ${db.version}`);
t.deepEqual(db.version, 0);
t.deepEqual(open_rq.transaction, null);
resolve();
};
});
});

View File

@ -24,23 +24,24 @@ test.cb("WPT test value.htm, array", (t) => {
}); });
test.cb("WPT test value.htm, date", (t) => { test.cb("WPT test value.htm, date", (t) => {
const value = new Date(); const value = new Date();
const _instanceof = Date; const _instanceof = Date;
t.plan(1); t.plan(1);
createdb(t).onupgradeneeded = function (e: IDBVersionChangeEvent) { createdb(t).onupgradeneeded = function (e: IDBVersionChangeEvent) {
(e.target as any).result.createObjectStore("store").add(value, 1); (e.target as any).result.createObjectStore("store").add(value, 1);
(e.target as any).onsuccess = (e: any) => { (e.target as any).onsuccess = (e: any) => {
console.log("in first onsuccess"); console.log("in first onsuccess");
e.target.result e.target.result
.transaction("store") .transaction("store")
.objectStore("store") .objectStore("store")
.get(1).onsuccess = (e: any) => { .get(1).onsuccess = (e: any) => {
t.assert(e.target.result instanceof _instanceof, "instanceof"); console.log("target", e.target);
t.end(); console.log("result", e.target.result);
}; t.assert(e.target.result instanceof _instanceof, "instanceof");
t.end();
}; };
}; };
}); };
});

View File

@ -23,8 +23,18 @@ export function createdb(
return rq_open; return rq_open;
} }
export function assert_key_equals(actual: any, expected: any, description?: string) { export function assert_key_equals(
actual: any,
expected: any,
description?: string,
) {
if (0 != compareKeys(actual, expected)) { if (0 != compareKeys(actual, expected)) {
throw Error("expected keys to be the same"); throw Error("expected keys to be the same");
} }
} }
export function assert_equals(actual: any, expected: any) {
if (actual !== expected) {
throw Error("assert_equals failed");
}
}

View File

@ -97,13 +97,14 @@ abstract class FakeEventTarget implements EventTarget {
public readonly listeners: Listener[] = []; public readonly listeners: Listener[] = [];
// These will be overridden in individual subclasses and made not readonly // These will be overridden in individual subclasses and made not readonly
public readonly onabort: EventListener | null | undefined; public readonly onabort: EventListener | null = null;
public readonly onblocked: EventListener | null | undefined; public readonly onblocked: EventListener | null = null;
public readonly oncomplete: EventListener | null | undefined; public readonly oncomplete: EventListener | null = null;
public readonly onerror: EventListener | null | undefined; public readonly onerror: EventListener | null = null;
public readonly onsuccess: EventListener | null | undefined; public readonly onsuccess: EventListener | null = null;
public readonly onupgradeneeded: EventListener | null | undefined; public readonly onclose: EventListener | null = null;
public readonly onversionchange: EventListener | null | undefined; public readonly onupgradeneeded: EventListener | null = null;
public readonly onversionchange: EventListener | null = null;
static enableTracing: boolean = false; static enableTracing: boolean = false;

View File

@ -17,7 +17,7 @@
import { IDBKeyPath } from "../idbtypes"; import { IDBKeyPath } from "../idbtypes";
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-valid-key-path // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-valid-key-path
const validateKeyPath = (keyPath: IDBKeyPath, parent?: "array" | "string") => { const validateKeyPath = (keyPath: IDBKeyPath | IDBKeyPath[], parent?: "array" | "string") => {
// This doesn't make sense to me based on the spec, but it is needed to pass the W3C KeyPath tests (see same // This doesn't make sense to me based on the spec, but it is needed to pass the W3C KeyPath tests (see same
// comment in extractKey) // comment in extractKey)
let myKeyPath: IDBKeyPath | IDBKeyPath[] = keyPath; let myKeyPath: IDBKeyPath | IDBKeyPath[] = keyPath;

View File

@ -5,6 +5,7 @@
"module": "ESNext", "module": "ESNext",
"moduleResolution": "node", "moduleResolution": "node",
"target": "ES6", "target": "ES6",
"allowJs": true,
"noImplicitAny": true, "noImplicitAny": true,
"outDir": "lib", "outDir": "lib",
"declaration": true, "declaration": true,