summaryrefslogtreecommitdiff
path: root/packages/idb-bridge/src/util
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2019-06-21 19:18:36 +0200
committerFlorian Dold <florian.dold@gmail.com>2019-06-21 19:18:36 +0200
commita4e4125cca8644703d7cff527a39c1a5a9842eba (patch)
treefb4de931ea0db1f314fcf6850806989a40c9e76e /packages/idb-bridge/src/util
parent2ee9431f1ba5bf67546bbf85758a01991c40673f (diff)
downloadwallet-core-a4e4125cca8644703d7cff527a39c1a5a9842eba.tar.gz
wallet-core-a4e4125cca8644703d7cff527a39c1a5a9842eba.tar.bz2
wallet-core-a4e4125cca8644703d7cff527a39c1a5a9842eba.zip
idb: tests working
Diffstat (limited to 'packages/idb-bridge/src/util')
-rw-r--r--packages/idb-bridge/src/util/FakeEventTarget.ts262
-rw-r--r--packages/idb-bridge/src/util/getIndexKeys.test.ts24
-rw-r--r--packages/idb-bridge/src/util/getIndexKeys.ts28
-rw-r--r--packages/idb-bridge/src/util/makeStoreKeyValue.test.ts42
-rw-r--r--packages/idb-bridge/src/util/makeStoreKeyValue.ts24
5 files changed, 247 insertions, 133 deletions
diff --git a/packages/idb-bridge/src/util/FakeEventTarget.ts b/packages/idb-bridge/src/util/FakeEventTarget.ts
index 3c7eaf468..f20432df0 100644
--- a/packages/idb-bridge/src/util/FakeEventTarget.ts
+++ b/packages/idb-bridge/src/util/FakeEventTarget.ts
@@ -14,164 +14,172 @@
permissions and limitations under the License.
*/
-
import { InvalidStateError } from "./errors";
import FakeEvent from "./FakeEvent";
import { EventCallback, EventType } from "./types";
type EventTypeProp =
- | "onabort"
- | "onblocked"
- | "oncomplete"
- | "onerror"
- | "onsuccess"
- | "onupgradeneeded"
- | "onversionchange";
+ | "onabort"
+ | "onblocked"
+ | "oncomplete"
+ | "onerror"
+ | "onsuccess"
+ | "onupgradeneeded"
+ | "onversionchange";
interface Listener {
- callback: EventCallback;
- capture: boolean;
- type: EventType;
+ callback: EventCallback;
+ capture: boolean;
+ type: EventType;
}
const stopped = (event: FakeEvent, listener: Listener) => {
- return (
- event.immediatePropagationStopped ||
- (event.eventPhase === event.CAPTURING_PHASE &&
- listener.capture === false) ||
- (event.eventPhase === event.BUBBLING_PHASE && listener.capture === true)
- );
+ return (
+ event.immediatePropagationStopped ||
+ (event.eventPhase === event.CAPTURING_PHASE &&
+ listener.capture === false) ||
+ (event.eventPhase === event.BUBBLING_PHASE && listener.capture === true)
+ );
};
// http://www.w3.org/TR/dom/#concept-event-listener-invoke
const invokeEventListeners = (event: FakeEvent, obj: FakeEventTarget) => {
- event.currentTarget = obj;
-
- // The callback might cause obj.listeners to mutate as we traverse it.
- // Take a copy of the array so that nothing sneaks in and we don't lose
- // our place.
- for (const listener of obj.listeners.slice()) {
- if (event.type !== listener.type || stopped(event, listener)) {
- continue;
- }
-
- // @ts-ignore
- listener.callback.call(event.currentTarget, event);
+ event.currentTarget = obj;
+
+ // The callback might cause obj.listeners to mutate as we traverse it.
+ // Take a copy of the array so that nothing sneaks in and we don't lose
+ // our place.
+ for (const listener of obj.listeners.slice()) {
+ if (event.type !== listener.type || stopped(event, listener)) {
+ continue;
}
- const typeToProp: { [key in EventType]: EventTypeProp } = {
- abort: "onabort",
- blocked: "onblocked",
- complete: "oncomplete",
- error: "onerror",
- success: "onsuccess",
- upgradeneeded: "onupgradeneeded",
- versionchange: "onversionchange",
+ console.log(`invoking ${event.type} event listener`, listener);
+ // @ts-ignore
+ listener.callback.call(event.currentTarget, event);
+ }
+
+ const typeToProp: { [key in EventType]: EventTypeProp } = {
+ abort: "onabort",
+ blocked: "onblocked",
+ complete: "oncomplete",
+ error: "onerror",
+ success: "onsuccess",
+ upgradeneeded: "onupgradeneeded",
+ versionchange: "onversionchange",
+ };
+ const prop = typeToProp[event.type];
+ if (prop === undefined) {
+ throw new Error(`Unknown event type: "${event.type}"`);
+ }
+
+ const callback = event.currentTarget[prop];
+ if (callback) {
+ const listener = {
+ callback,
+ capture: false,
+ type: event.type,
};
- const prop = typeToProp[event.type];
- if (prop === undefined) {
- throw new Error(`Unknown event type: "${event.type}"`);
- }
-
- const callback = event.currentTarget[prop];
- if (callback) {
- const listener = {
- callback,
- capture: false,
- type: event.type,
- };
- if (!stopped(event, listener)) {
- // @ts-ignore
- listener.callback.call(event.currentTarget, event);
- }
+ if (!stopped(event, listener)) {
+ console.log(`invoking on${event.type} event listener`, listener);
+ // @ts-ignore
+ listener.callback.call(event.currentTarget, event);
}
+ }
};
abstract class FakeEventTarget {
- public readonly listeners: Listener[] = [];
-
- // These will be overridden in individual subclasses and made not readonly
- public readonly onabort: EventCallback | null | undefined;
- public readonly onblocked: EventCallback | null | undefined;
- public readonly oncomplete: EventCallback | null | undefined;
- public readonly onerror: EventCallback | null | undefined;
- public readonly onsuccess: EventCallback | null | undefined;
- public readonly onupgradeneeded: EventCallback | null | undefined;
- public readonly onversionchange: EventCallback | null | undefined;
-
- public addEventListener(
- type: EventType,
- callback: EventCallback,
- capture = false,
- ) {
- this.listeners.push({
- callback,
- capture,
- type,
- });
- }
-
- public removeEventListener(
- type: EventType,
- callback: EventCallback,
- capture = false,
- ) {
- const i = this.listeners.findIndex(listener => {
- return (
- listener.type === type &&
- listener.callback === callback &&
- listener.capture === capture
- );
- });
-
- this.listeners.splice(i, 1);
+ public readonly listeners: Listener[] = [];
+
+ // These will be overridden in individual subclasses and made not readonly
+ public readonly onabort: EventCallback | null | undefined;
+ public readonly onblocked: EventCallback | null | undefined;
+ public readonly oncomplete: EventCallback | null | undefined;
+ public readonly onerror: EventCallback | null | undefined;
+ public readonly onsuccess: EventCallback | null | undefined;
+ public readonly onupgradeneeded: EventCallback | null | undefined;
+ public readonly onversionchange: EventCallback | null | undefined;
+
+ static enableTracing: boolean = true;
+
+ public addEventListener(
+ type: EventType,
+ callback: EventCallback,
+ capture = false,
+ ) {
+ this.listeners.push({
+ callback,
+ capture,
+ type,
+ });
+ }
+
+ public removeEventListener(
+ type: EventType,
+ callback: EventCallback,
+ capture = false,
+ ) {
+ const i = this.listeners.findIndex(listener => {
+ return (
+ listener.type === type &&
+ listener.callback === callback &&
+ listener.capture === capture
+ );
+ });
+
+ this.listeners.splice(i, 1);
+ }
+
+ // http://www.w3.org/TR/dom/#dispatching-events
+ public dispatchEvent(event: FakeEvent) {
+ if (event.dispatched || !event.initialized) {
+ throw new InvalidStateError("The object is in an invalid state.");
}
+ event.isTrusted = false;
- // http://www.w3.org/TR/dom/#dispatching-events
- public dispatchEvent(event: FakeEvent) {
- if (event.dispatched || !event.initialized) {
- throw new InvalidStateError("The object is in an invalid state.");
- }
- event.isTrusted = false;
+ event.dispatched = true;
+ event.target = this;
+ // NOT SURE WHEN THIS SHOULD BE SET event.eventPath = [];
- event.dispatched = true;
- event.target = this;
- // NOT SURE WHEN THIS SHOULD BE SET event.eventPath = [];
+ event.eventPhase = event.CAPTURING_PHASE;
+ if (FakeEventTarget.enableTracing) {
+ console.log(
+ `dispatching '${event.type}' event along path with ${event.eventPath.length} elements`,
+ );
+ }
+ for (const obj of event.eventPath) {
+ if (!event.propagationStopped) {
+ invokeEventListeners(event, obj);
+ }
+ }
- event.eventPhase = event.CAPTURING_PHASE;
- for (const obj of event.eventPath) {
- if (!event.propagationStopped) {
- invokeEventListeners(event, obj);
- }
- }
+ event.eventPhase = event.AT_TARGET;
+ if (!event.propagationStopped) {
+ invokeEventListeners(event, event.target);
+ }
- event.eventPhase = event.AT_TARGET;
+ if (event.bubbles) {
+ event.eventPath.reverse();
+ event.eventPhase = event.BUBBLING_PHASE;
+ if (event.eventPath.length === 0 && event.type === "error") {
+ console.error("Unhandled error event: ", event.target);
+ }
+ for (const obj of event.eventPath) {
if (!event.propagationStopped) {
- invokeEventListeners(event, event.target);
- }
-
- if (event.bubbles) {
- event.eventPath.reverse();
- event.eventPhase = event.BUBBLING_PHASE;
- if (event.eventPath.length === 0 && event.type === "error") {
- console.error("Unhandled error event: ", event.target);
- }
- for (const obj of event.eventPath) {
- if (!event.propagationStopped) {
- invokeEventListeners(event, obj);
- }
- }
+ invokeEventListeners(event, obj);
}
+ }
+ }
- event.dispatched = false;
- event.eventPhase = event.NONE;
- event.currentTarget = null;
+ event.dispatched = false;
+ event.eventPhase = event.NONE;
+ event.currentTarget = null;
- if (event.canceled) {
- return false;
- }
- return true;
+ if (event.canceled) {
+ return false;
}
+ return true;
+ }
}
export default FakeEventTarget;
diff --git a/packages/idb-bridge/src/util/getIndexKeys.test.ts b/packages/idb-bridge/src/util/getIndexKeys.test.ts
new file mode 100644
index 000000000..e1bc9dd00
--- /dev/null
+++ b/packages/idb-bridge/src/util/getIndexKeys.test.ts
@@ -0,0 +1,24 @@
+import test from "ava";
+import { getIndexKeys } from "./getIndexKeys";
+
+test("basics", (t) => {
+ t.deepEqual(getIndexKeys({foo: 42}, "foo", false), [42]);
+ t.deepEqual(getIndexKeys({foo: {bar: 42}}, "foo.bar", false), [42]);
+ t.deepEqual(getIndexKeys({foo: [42, 43]}, "foo.0", false), [42]);
+ t.deepEqual(getIndexKeys({foo: [42, 43]}, "foo.1", false), [43]);
+
+ t.deepEqual(getIndexKeys([1, 2, 3], "", false), [[1, 2, 3]]);
+
+ t.throws(() => {
+ getIndexKeys({foo: 42}, "foo.bar", false);
+ });
+
+ t.deepEqual(getIndexKeys({foo: 42}, "foo", true), [42]);
+ t.deepEqual(getIndexKeys({foo: 42, bar: 10}, ["foo", "bar"], true), [42, 10]);
+ t.deepEqual(getIndexKeys({foo: 42, bar: 10}, ["foo", "bar"], false), [[42, 10]]);
+ t.deepEqual(getIndexKeys({foo: 42, bar: 10}, ["foo", "bar", "spam"], true), [42, 10]);
+
+ t.throws(() => {
+ getIndexKeys({foo: 42, bar: 10}, ["foo", "bar", "spam"], false);
+ });
+});
diff --git a/packages/idb-bridge/src/util/getIndexKeys.ts b/packages/idb-bridge/src/util/getIndexKeys.ts
new file mode 100644
index 000000000..416cf9ea2
--- /dev/null
+++ b/packages/idb-bridge/src/util/getIndexKeys.ts
@@ -0,0 +1,28 @@
+import { Key, Value, KeyPath } from "./types";
+import extractKey from "./extractKey";
+import valueToKey from "./valueToKey";
+
+export function getIndexKeys(
+ value: Value,
+ keyPath: KeyPath,
+ multiEntry: boolean,
+): Key[] {
+ if (multiEntry && Array.isArray(keyPath)) {
+ const keys = [];
+ for (const subkeyPath of keyPath) {
+ const key = extractKey(subkeyPath, value);
+ try {
+ const k = valueToKey(key);
+ keys.push(k);
+ } catch {
+ // Ignore invalid subkeys
+ }
+ }
+ return keys;
+ } else {
+ let key = extractKey(keyPath, value);
+ return [valueToKey(key)];
+ }
+}
+
+export default getIndexKeys;
diff --git a/packages/idb-bridge/src/util/makeStoreKeyValue.test.ts b/packages/idb-bridge/src/util/makeStoreKeyValue.test.ts
new file mode 100644
index 000000000..7820875c3
--- /dev/null
+++ b/packages/idb-bridge/src/util/makeStoreKeyValue.test.ts
@@ -0,0 +1,42 @@
+import test from 'ava';
+import { makeStoreKeyValue } from "./makeStoreKeyValue";
+
+test("basics", (t) => {
+ let result;
+
+ result = makeStoreKeyValue({ name: "Florian" }, undefined, 42, true, "id");
+ t.is(result.updatedKeyGenerator, 43);
+ t.is(result.key, 42);
+ t.is(result.value.name, "Florian");
+ t.is(result.value.id, 42);
+
+ result = makeStoreKeyValue({ name: "Florian", id: 10 }, undefined, 5, true, "id");
+ t.is(result.updatedKeyGenerator, 11);
+ t.is(result.key, 10);
+ t.is(result.value.name, "Florian");
+ t.is(result.value.id, 10);
+
+ result = makeStoreKeyValue({ name: "Florian", id: 5 }, undefined, 10, true, "id");
+ t.is(result.updatedKeyGenerator, 10);
+ t.is(result.key, 5);
+ t.is(result.value.name, "Florian");
+ t.is(result.value.id, 5);
+
+ result = makeStoreKeyValue({ name: "Florian", id: "foo" }, undefined, 10, true, "id");
+ t.is(result.updatedKeyGenerator, 10);
+ t.is(result.key, "foo");
+ t.is(result.value.name, "Florian");
+ t.is(result.value.id, "foo");
+
+ result = makeStoreKeyValue({ name: "Florian" }, "foo", 10, true, null);
+ t.is(result.updatedKeyGenerator, 10);
+ t.is(result.key, "foo");
+ t.is(result.value.name, "Florian");
+ t.is(result.value.id, undefined);
+
+ result = makeStoreKeyValue({ name: "Florian" }, undefined, 10, true, null);
+ t.is(result.updatedKeyGenerator, 11);
+ t.is(result.key, 10);
+ t.is(result.value.name, "Florian");
+ t.is(result.value.id, undefined);
+});
diff --git a/packages/idb-bridge/src/util/makeStoreKeyValue.ts b/packages/idb-bridge/src/util/makeStoreKeyValue.ts
index 4850cec26..4f45e0d8a 100644
--- a/packages/idb-bridge/src/util/makeStoreKeyValue.ts
+++ b/packages/idb-bridge/src/util/makeStoreKeyValue.ts
@@ -63,10 +63,14 @@ export function makeStoreKeyValue(
updatedKeyGenerator = currentKeyGenerator + 1;
} else if (typeof maybeInlineKey === "number") {
key = maybeInlineKey;
- updatedKeyGenerator = maybeInlineKey;
+ if (maybeInlineKey >= currentKeyGenerator) {
+ updatedKeyGenerator = maybeInlineKey + 1;
+ } else {
+ updatedKeyGenerator = currentKeyGenerator;
+ }
} else {
key = maybeInlineKey;
- updatedKeyGenerator = currentKeyGenerator + 1;
+ updatedKeyGenerator = currentKeyGenerator;
}
return {
key: key,
@@ -84,9 +88,17 @@ export function makeStoreKeyValue(
};
}
} else {
- // (no, no, yes)
- // (no, no, no)
- throw new DataError();
+ if (autoIncrement) {
+ // (no, no, yes)
+ return {
+ key: currentKeyGenerator,
+ value: value,
+ updatedKeyGenerator: currentKeyGenerator + 1,
+ }
+ } else {
+ // (no, no, no)
+ throw new DataError();
+ }
}
}
-} \ No newline at end of file
+}