diff options
| author | Florian Dold <florian@dold.me> | 2021-02-16 17:18:40 +0100 | 
|---|---|---|
| committer | Florian Dold <florian@dold.me> | 2021-02-16 17:18:40 +0100 | 
| commit | c363458374b1e5f97768c1cafc84a5dcb65921d5 (patch) | |
| tree | 9f089762a61ee32cf096152fc06488db62fda66c /packages/idb-bridge | |
| parent | f9df95ded5ba1d6a6edadd24615c5e175ea5bac8 (diff) | |
more WPTs
Diffstat (limited to 'packages/idb-bridge')
| -rw-r--r-- | packages/idb-bridge/src/idb-wpt-ported/idbfactory-open.test.ts | 406 | ||||
| -rw-r--r-- | packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts | 121 | 
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) + +          "]" +        ); +      } +  } +} | 
