aboutsummaryrefslogtreecommitdiff
path: root/packages/idb-bridge
diff options
context:
space:
mode:
Diffstat (limited to 'packages/idb-bridge')
-rw-r--r--packages/idb-bridge/src/idb-wpt-ported/idbfactory-open.test.ts406
-rw-r--r--packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts121
2 files changed, 520 insertions, 7 deletions
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 68d58a162..411a6c69a 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
@@ -1,5 +1,5 @@
import test from "ava";
-import { createdb, idbFactory } from "./wptsupport";
+import { createdb, format_value, idbFactory } from "./wptsupport";
// IDBFactory.open() - request has no source
test("WPT idbfactory-open.htm", async (t) => {
@@ -57,19 +57,411 @@ test("WPT idbfactory-open3.htm", async (t) => {
t.pass();
});
-
// IDBFactory.open() - new database has default version
test("WPT idbfactory-open4.htm", async (t) => {
const indexedDB = idbFactory;
await new Promise<void>((resolve, reject) => {
- var open_rq = createdb(t, __filename + '-database_name');
+ var open_rq = createdb(t, __filename + "-database_name");
+
+ open_rq.onupgradeneeded = function (e: any) {
+ t.deepEqual(e.target.result.version, 1, "db.version");
+ };
+ open_rq.onsuccess = function (e: any) {
+ t.deepEqual(e.target.result.version, 1, "db.version");
+ resolve();
+ };
+ });
+ t.pass();
+});
+
+// IDBFactory.open() - new database is empty
+test("WPT idbfactory-open5.htm", async (t) => {
+ const indexedDB = idbFactory;
+ await new Promise<void>((resolve, reject) => {
+ var open_rq = createdb(t, __filename + "-database_name");
+
+ open_rq.onupgradeneeded = function () {};
+ open_rq.onsuccess = function (e: any) {
+ t.deepEqual(
+ e.target.result.objectStoreNames.length,
+ 0,
+ "objectStoreNames.length",
+ );
+ resolve();
+ };
+ });
+ t.pass();
+});
+
+// IDBFactory.open() - open database with a lower version than current
+test("WPT idbfactory-open6.htm", async (t) => {
+ const indexedDB = idbFactory;
+ await new Promise<void>((resolve, reject) => {
+ var open_rq = createdb(t, undefined, 13);
+ var did_upgrade = false;
+ var open_rq2: any;
+
+ open_rq.onupgradeneeded = function () {};
+ open_rq.onsuccess = function (e: any) {
+ var db = e.target.result;
+ db.close();
- open_rq.onupgradeneeded = function(e: any) {
- t.deepEqual(e.target.result.version, 1, "db.version");
+ open_rq2 = indexedDB.open(db.name, 14);
+ open_rq2.onupgradeneeded = function () {};
+ open_rq2.onsuccess = open_previous_db;
+ open_rq2.onerror = () => t.fail("Unexpected error");
};
- open_rq.onsuccess = function(e: any) {
- t.deepEqual(e.target.result.version, 1, "db.version");
+
+ function open_previous_db(e: any) {
+ var open_rq3 = indexedDB.open(e.target.result.name, 13);
+ open_rq3.onerror = function (e: any) {
+ t.deepEqual(e.target.error.name, "VersionError", "e.target.error.name");
+ open_rq2.result.close();
resolve();
+ };
+ open_rq3.onupgradeneeded = () => t.fail("Unexpected upgradeneeded");
+ open_rq3.onsuccess = () => t.fail("Unexpected success");
+ }
+ });
+ t.pass();
+});
+
+// IDBFactory.open() - open database with a higher version than current
+test("WPT idbfactory-open7.htm", async (t) => {
+ const indexedDB = idbFactory;
+ await new Promise<void>((resolve, reject) => {
+ var open_rq = createdb(t, undefined, 13);
+ var did_upgrade = false;
+ var open_rq2: any;
+
+ open_rq.onupgradeneeded = function () {};
+ open_rq.onsuccess = function (e: any) {
+ var db = e.target.result;
+ db.close();
+
+ open_rq2 = indexedDB.open(db.name, 14);
+ open_rq2.onupgradeneeded = function () {
+ did_upgrade = true;
+ };
+ open_rq2.onsuccess = open_current_db;
+ open_rq2.onerror = () => t.fail("Unexpected error");
+ };
+
+ function open_current_db(e: any) {
+ var open_rq3 = indexedDB.open(e.target.result.name);
+ open_rq3.onsuccess = function (e: any) {
+ t.deepEqual(e.target.result.version, 14, "db.version");
+ open_rq2.result.close();
+ open_rq3.result.close();
+ resolve();
+ };
+ open_rq3.onupgradeneeded = () => t.fail("Unexpected upgradeneeded");
+ open_rq3.onerror = () => t.fail("Unexpected error");
+
+ t.true(did_upgrade, "did upgrade");
+ }
+ });
+ t.pass();
+});
+
+// IDBFactory.open() - error in version change transaction aborts open
+test("WPT idbfactory-open8.htm", async (t) => {
+ const indexedDB = idbFactory;
+ await new Promise<void>((resolve, reject) => {
+ var open_rq = createdb(t, undefined, 13);
+ var did_upgrade = false;
+ var did_db_abort = false;
+
+ open_rq.onupgradeneeded = function (e: any) {
+ did_upgrade = true;
+ e.target.result.onabort = function () {
+ did_db_abort = true;
+ };
+ e.target.transaction.abort();
+ };
+ open_rq.onerror = function (e: any) {
+ t.true(did_upgrade);
+ t.deepEqual(e.target.error.name, "AbortError", "target.error");
+ resolve();
+ };
+ });
+ t.pass();
+});
+
+// IDBFactory.open() - errors in version argument
+test("WPT idbfactory-open9.htm", async (t) => {
+ const indexedDB = idbFactory;
+ function should_throw(val: any, name?: string) {
+ if (!name) {
+ name = typeof val == "object" && val ? "object" : format_value(val);
+ }
+ t.throws(
+ () => {
+ indexedDB.open("test", val);
+ },
+ { instanceOf: TypeError },
+ "Calling open() with version argument " +
+ name +
+ " should throw TypeError.",
+ );
+ }
+
+ should_throw(-1);
+ should_throw(-0.5);
+ should_throw(0);
+ should_throw(0.5);
+ should_throw(0.8);
+ should_throw(0x20000000000000); // Number.MAX_SAFE_INTEGER + 1
+ should_throw(NaN);
+ should_throw(Infinity);
+ should_throw(-Infinity);
+ should_throw("foo");
+ should_throw(null);
+ should_throw(false);
+
+ should_throw({
+ toString: function () {
+ t.fail("toString should not be called for ToPrimitive [Number]");
+ },
+ valueOf: function () {
+ return 0;
+ },
+ });
+ should_throw(
+ {
+ toString: function () {
+ return 0;
+ },
+ valueOf: function () {
+ return {};
+ },
+ },
+ "object (second)",
+ );
+ should_throw(
+ {
+ toString: function () {
+ return {};
+ },
+ valueOf: function () {
+ return {};
+ },
+ },
+ "object (third)",
+ );
+
+ /* Valid */
+
+ async function should_work(val: any, expected_version: number) {
+ var name = format_value(val);
+ var dbname = "test-db-does-not-exist";
+
+ await t.notThrowsAsync(async () => {
+ return new Promise<void>((resolve, reject) => {
+ indexedDB.deleteDatabase(dbname);
+ var rq = indexedDB.open(dbname, val);
+ rq.onupgradeneeded = function () {
+ var db = rq.result;
+ t.deepEqual(db.version, expected_version, "version");
+ rq!.transaction!.abort();
+ };
+ rq.onsuccess = () => t.fail("open should fail");
+ rq.onerror = () => resolve();
+ });
+ }, "Calling open() with version argument " + name + " should not throw.");
+ }
+
+ await should_work(1.5, 1);
+ await should_work(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER); // 0x20000000000000 - 1
+ await should_work(undefined, 1);
+});
+
+// IDBFactory.open() - error in version change transaction aborts open
+test("WPT idbfactory-open10.htm", async (t) => {
+ const indexedDB = idbFactory;
+ await new Promise<void>((resolve, reject) => {
+ var db: any, db2: any;
+ var open_rq = createdb(t, undefined, 9);
+
+ open_rq.onupgradeneeded = function (e: any) {
+ db = e.target.result;
+
+ var st = db.createObjectStore("store");
+ st.createIndex("index", "i");
+
+ t.deepEqual(db.version, 9, "first db.version");
+ t.true(
+ db.objectStoreNames.contains("store"),
+ "objectStoreNames contains store",
+ );
+ t.true(st.indexNames.contains("index"), "indexNames contains index");
+
+ st.add({ i: "Joshua" }, 1);
+ st.add({ i: "Jonas" }, 2);
+ };
+ open_rq.onsuccess = function (e) {
+ db.close();
+ var open_rq2 = indexedDB.open(db.name, 10);
+ open_rq2.onupgradeneeded = function (e: any) {
+ db2 = e.target.result;
+
+ db2.createObjectStore("store2");
+
+ var store = open_rq2.transaction!.objectStore("store");
+ store.createIndex("index2", "i");
+
+ t.deepEqual(db2.version, 10, "db2.version");
+
+ t.true(
+ db2.objectStoreNames.contains("store"),
+ "second objectStoreNames contains store",
+ );
+ t.true(
+ db2.objectStoreNames.contains("store2"),
+ "second objectStoreNames contains store2",
+ );
+ t.true(
+ store.indexNames.contains("index"),
+ "second indexNames contains index",
+ );
+ t.true(
+ store.indexNames.contains("index2"),
+ "second indexNames contains index2",
+ );
+
+ store.add({ i: "Odin" }, 3);
+ store.put({ i: "Sicking" }, 2);
+
+ open_rq2.transaction!.abort();
+ };
+ open_rq2.onerror = function () {
+ t.deepEqual(db2.version, 9, "db2.version after error");
+ t.true(
+ db2.objectStoreNames.contains("store"),
+ "objectStoreNames contains store after error",
+ );
+ t.false(
+ db2.objectStoreNames.contains("store2"),
+ "objectStoreNames not contains store2 after error",
+ );
+
+ var open_rq3 = indexedDB.open(db.name);
+ open_rq3.onsuccess = function (e: any) {
+ var db3 = e.target.result;
+
+ t.true(
+ db3.objectStoreNames.contains("store"),
+ "third objectStoreNames contains store",
+ );
+ t.false(
+ db3.objectStoreNames.contains("store2"),
+ "third objectStoreNames contains store2",
+ );
+
+ var st = db3.transaction("store").objectStore("store");
+
+ t.deepEqual(db3.version, 9, "db3.version");
+
+ t.true(
+ st.indexNames.contains("index"),
+ "third indexNames contains index",
+ );
+ t.false(
+ st.indexNames.contains("index2"),
+ "third indexNames contains index2",
+ );
+
+ st.openCursor(null, "prev").onsuccess = function (e: any) {
+ t.deepEqual(e.target.result.key, 2, "opencursor(prev) key");
+ t.deepEqual(
+ e.target.result.value.i,
+ "Jonas",
+ "opencursor(prev) value",
+ );
+ };
+ st.get(3).onsuccess = function (e: any) {
+ t.deepEqual(e.target.result, undefined, "get(3)");
+ };
+
+ var idx = st.index("index");
+ idx.getKey("Jonas").onsuccess = function (e: any) {
+ t.deepEqual(e.target.result, 2, "getKey(Jonas)");
+ };
+ idx.getKey("Odin").onsuccess = function (e: any) {
+ t.deepEqual(e.target.result, undefined, "getKey(Odin)");
+ };
+ idx.getKey("Sicking").onsuccess = function (e: any) {
+ t.deepEqual(e.target.result, undefined, "getKey(Sicking)");
+ db3.close();
+ resolve();
+ };
+ };
+ };
+ };
+ });
+ t.pass();
+});
+
+// IDBFactory.open() - second open's transaction is available to get objectStores
+test("WPT idbfactory-open11.htm", async (t) => {
+ const indexedDB = idbFactory;
+ await new Promise<void>((resolve, reject) => {
+ var db: any;
+ var count_done = 0;
+ var open_rq = createdb(t);
+
+ open_rq.onupgradeneeded = function (e: any) {
+ db = e.target.result;
+
+ db.createObjectStore("store");
+ assert_true(
+ db.objectStoreNames.contains("store"),
+ "objectStoreNames contains store",
+ );
+
+ var store = e.target.transaction.objectStore("store");
+ assert_equals(store.name, "store", "store.name");
+
+ store.add("data", 1);
+
+ store.count().onsuccess = this.step_func(function (e) {
+ assert_equals(e.target.result, 1, "count()");
+ count_done++;
+ });
+
+ store.add("data2", 2);
+ };
+ open_rq.onsuccess = function (e) {
+ var store = db.transaction("store").objectStore("store");
+ assert_equals(store.name, "store", "store.name");
+ store.count().onsuccess = this.step_func(function (e) {
+ assert_equals(e.target.result, 2, "count()");
+ count_done++;
+ });
+ db.close();
+
+ var open_rq2 = indexedDB.open(db.name, 10);
+ open_rq2.onupgradeneeded = function (e: any) {
+ var db2 = e.target.result;
+ t.true(
+ db2.objectStoreNames.contains("store"),
+ "objectStoreNames contains store",
+ );
+ var store = open_rq2.transaction!.objectStore("store");
+ t.deepEqual(store.name, "store", "store.name");
+
+ store.add("data3", 3);
+
+ store.count().onsuccess = function (e: any) {
+ t.deepEqual(e.target.result, 3, "count()");
+ count_done++;
+
+ t.deepEqual(count_done, 3, "count_done");
+
+ db2.close();
+ resolve();
+ };
+ };
};
});
t.pass();
diff --git a/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts b/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
index 2d52ea074..4a7205f8d 100644
--- a/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
+++ b/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
@@ -301,3 +301,124 @@ export function createNotBooksStore(
store.createIndex("not_by_title", "title", { unique: true });
return store;
}
+
+/*
+ * Return a string truncated to the given length, with ... added at the end
+ * if it was longer.
+ */
+function truncate(s: string, len: number): string {
+ if (s.length > len) {
+ return s.substring(0, len - 3) + "...";
+ }
+ return s;
+}
+
+var replacements = {
+ "0": "0",
+ "1": "x01",
+ "2": "x02",
+ "3": "x03",
+ "4": "x04",
+ "5": "x05",
+ "6": "x06",
+ "7": "x07",
+ "8": "b",
+ "9": "t",
+ "10": "n",
+ "11": "v",
+ "12": "f",
+ "13": "r",
+ "14": "x0e",
+ "15": "x0f",
+ "16": "x10",
+ "17": "x11",
+ "18": "x12",
+ "19": "x13",
+ "20": "x14",
+ "21": "x15",
+ "22": "x16",
+ "23": "x17",
+ "24": "x18",
+ "25": "x19",
+ "26": "x1a",
+ "27": "x1b",
+ "28": "x1c",
+ "29": "x1d",
+ "30": "x1e",
+ "31": "x1f",
+ "0xfffd": "ufffd",
+ "0xfffe": "ufffe",
+ "0xffff": "uffff",
+};
+
+/*
+ * Convert a value to a nice, human-readable string
+ */
+export function format_value(val: any, seen?: any): string {
+ if (!seen) {
+ seen = [];
+ }
+ if (typeof val === "object" && val !== null) {
+ if (seen.indexOf(val) >= 0) {
+ return "[...]";
+ }
+ seen.push(val);
+ }
+ if (Array.isArray(val)) {
+ let output = "[";
+ // @ts-ignore
+ if (val.beginEllipsis !== undefined) {
+ output += "…, ";
+ }
+ output += val
+ .map(function (x) {
+ return format_value(x, seen);
+ })
+ .join(", ");
+ // @ts-ignore
+ if (val.endEllipsis !== undefined) {
+ output += ", …";
+ }
+ return output + "]";
+ }
+
+ switch (typeof val) {
+ case "string":
+ val = val.replace(/\\/g, "\\\\");
+ for (var p in replacements) {
+ // @ts-ignore
+ var replace = "\\" + replacements[p];
+ // @ts-ignore
+ val = val.replace(RegExp(String.fromCharCode(p), "g"), replace);
+ }
+ return '"' + val.replace(/"/g, '\\"') + '"';
+ case "boolean":
+ case "undefined":
+ return String(val);
+ case "number":
+ // In JavaScript, -0 === 0 and String(-0) == "0", so we have to
+ // special-case.
+ if (val === -0 && 1 / val === -Infinity) {
+ return "-0";
+ }
+ return String(val);
+ case "object":
+ if (val === null) {
+ return "null";
+ }
+
+ /* falls through */
+ default:
+ try {
+ return typeof val + ' "' + truncate(String(val), 1000) + '"';
+ } catch (e) {
+ return (
+ "[stringifying object threw " +
+ String(e) +
+ " with type " +
+ String(typeof e) +
+ "]"
+ );
+ }
+ }
+}