more WPTs
This commit is contained in:
parent
f9df95ded5
commit
c363458374
@ -1,5 +1,5 @@
|
|||||||
import test from "ava";
|
import test from "ava";
|
||||||
import { createdb, idbFactory } from "./wptsupport";
|
import { createdb, format_value, idbFactory } from "./wptsupport";
|
||||||
|
|
||||||
// IDBFactory.open() - request has no source
|
// IDBFactory.open() - request has no source
|
||||||
test("WPT idbfactory-open.htm", async (t) => {
|
test("WPT idbfactory-open.htm", async (t) => {
|
||||||
@ -57,19 +57,411 @@ test("WPT idbfactory-open3.htm", async (t) => {
|
|||||||
t.pass();
|
t.pass();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// IDBFactory.open() - new database has default version
|
// IDBFactory.open() - new database has default version
|
||||||
test("WPT idbfactory-open4.htm", async (t) => {
|
test("WPT idbfactory-open4.htm", async (t) => {
|
||||||
const indexedDB = idbFactory;
|
const indexedDB = idbFactory;
|
||||||
await new Promise<void>((resolve, reject) => {
|
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) {
|
open_rq.onupgradeneeded = function (e: any) {
|
||||||
t.deepEqual(e.target.result.version, 1, "db.version");
|
t.deepEqual(e.target.result.version, 1, "db.version");
|
||||||
};
|
};
|
||||||
open_rq.onsuccess = function(e: any) {
|
open_rq.onsuccess = function (e: any) {
|
||||||
t.deepEqual(e.target.result.version, 1, "db.version");
|
t.deepEqual(e.target.result.version, 1, "db.version");
|
||||||
resolve();
|
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_rq2 = indexedDB.open(db.name, 14);
|
||||||
|
open_rq2.onupgradeneeded = function () {};
|
||||||
|
open_rq2.onsuccess = open_previous_db;
|
||||||
|
open_rq2.onerror = () => t.fail("Unexpected error");
|
||||||
|
};
|
||||||
|
|
||||||
|
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();
|
t.pass();
|
||||||
|
@ -301,3 +301,124 @@ export function createNotBooksStore(
|
|||||||
store.createIndex("not_by_title", "title", { unique: true });
|
store.createIndex("not_by_title", "title", { unique: true });
|
||||||
return store;
|
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) +
|
||||||
|
"]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user