mirror of
https://github.com/wassname/talk.git
synced 2026-08-03 13:20:59 +08:00
Full PromisifiedStorage + Simplifications
This commit is contained in:
@@ -8,7 +8,7 @@ import AppContainer from "talk-auth/containers/AppContainer";
|
||||
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
|
||||
import { PostMessageService } from "talk-framework/lib/postMessage";
|
||||
import { RestClient } from "talk-framework/lib/rest";
|
||||
import { createInMemoryStorage } from "talk-framework/lib/storage";
|
||||
import { createPromisifiedStorage } from "talk-framework/lib/storage";
|
||||
|
||||
import createEnvironment from "./createEnvironment";
|
||||
import createFluentBundle from "./createFluentBundle";
|
||||
@@ -23,8 +23,8 @@ function createTestRenderer(initialView: string): ReactTestRenderer {
|
||||
const context: TalkContext = {
|
||||
relayEnvironment: environment,
|
||||
localeBundles: [createFluentBundle()],
|
||||
localStorage: createInMemoryStorage(),
|
||||
sessionStorage: createInMemoryStorage(),
|
||||
localStorage: createPromisifiedStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
rest: new RestClient("http://localhost/api"),
|
||||
postMessage: new PostMessageService(),
|
||||
browserInfo: { ios: false },
|
||||
|
||||
@@ -11,7 +11,7 @@ import { animationFrame, timeout } from "talk-common/utils";
|
||||
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
|
||||
import { PostMessageService } from "talk-framework/lib/postMessage";
|
||||
import { RestClient } from "talk-framework/lib/rest";
|
||||
import { createInMemoryStorage } from "talk-framework/lib/storage";
|
||||
import { createPromisifiedStorage } from "talk-framework/lib/storage";
|
||||
|
||||
import createEnvironment from "./createEnvironment";
|
||||
import createFluentBundle from "./createFluentBundle";
|
||||
@@ -33,8 +33,8 @@ beforeEach(() => {
|
||||
context = {
|
||||
relayEnvironment: environment,
|
||||
localeBundles: [createFluentBundle()],
|
||||
localStorage: createInMemoryStorage(),
|
||||
sessionStorage: createInMemoryStorage(),
|
||||
localStorage: createPromisifiedStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
rest: new RestClient("http://localhost/api"),
|
||||
postMessage: new PostMessageService(),
|
||||
browserInfo: { ios: false },
|
||||
|
||||
@@ -11,7 +11,7 @@ import { animationFrame, timeout } from "talk-common/utils";
|
||||
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
|
||||
import { PostMessageService } from "talk-framework/lib/postMessage";
|
||||
import { RestClient } from "talk-framework/lib/rest";
|
||||
import { createInMemoryStorage } from "talk-framework/lib/storage";
|
||||
import { createPromisifiedStorage } from "talk-framework/lib/storage";
|
||||
|
||||
import createEnvironment from "./createEnvironment";
|
||||
import createFluentBundle from "./createFluentBundle";
|
||||
@@ -33,8 +33,8 @@ beforeEach(() => {
|
||||
context = {
|
||||
relayEnvironment: environment,
|
||||
localeBundles: [createFluentBundle()],
|
||||
localStorage: createInMemoryStorage(),
|
||||
sessionStorage: createInMemoryStorage(),
|
||||
localStorage: createPromisifiedStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
rest: new RestClient("http://localhost/api"),
|
||||
postMessage: new PostMessageService(),
|
||||
browserInfo: { ios: false },
|
||||
|
||||
@@ -1,15 +1,51 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`withPymStorage should handle handle errors 1`] = `"[{\\"key\\":\\"pymStorage.localStorage.error\\",\\"value\\":\\"{\\\\\\"id\\\\\\":\\\\\\"0\\\\\\",\\\\\\"error\\\\\\":\\\\\\"error\\\\\\"}\\"}]"`;
|
||||
exports[`withPymStorage should clear storage 1`] = `"{}"`;
|
||||
|
||||
exports[`withPymStorage should clear storage 2`] = `
|
||||
Array [
|
||||
Object {
|
||||
"key": "pymStorage.localStorage.response",
|
||||
"value": "{\\"id\\":\\"0\\"}",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`withPymStorage should get key of storage 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"key": "pymStorage.localStorage.response",
|
||||
"value": "{\\"id\\":\\"0\\",\\"result\\":\\"b\\"}",
|
||||
},
|
||||
Object {
|
||||
"key": "pymStorage.localStorage.response",
|
||||
"value": "{\\"id\\":\\"0\\",\\"result\\":null}",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`withPymStorage should get length of storage 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"key": "pymStorage.localStorage.response",
|
||||
"value": "{\\"id\\":\\"0\\",\\"result\\":3}",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`withPymStorage should handle handle errors 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"key": "pymStorage.localStorage.error",
|
||||
"value": "{\\"id\\":\\"0\\",\\"error\\":\\"error\\"}",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`withPymStorage should handle unknown method 1`] = `"[{\\"key\\":\\"pymStorage.localStorage.error\\",\\"value\\":\\"{\\\\\\"id\\\\\\":\\\\\\"0\\\\\\",\\\\\\"error\\\\\\":\\\\\\"Unknown method unknown\\\\\\"}\\"}]"`;
|
||||
|
||||
exports[`withPymStorage should set, get and remove item 1`] = `
|
||||
Object {
|
||||
"talkPymStorage:key": "test",
|
||||
}
|
||||
`;
|
||||
exports[`withPymStorage should set, get and remove item 1`] = `"{\\"talk:key\\":\\"test\\"}"`;
|
||||
|
||||
exports[`withPymStorage should set, get and remove item 2`] = `Object {}`;
|
||||
exports[`withPymStorage should set, get and remove item 2`] = `"{}"`;
|
||||
|
||||
exports[`withPymStorage should set, get and remove item 3`] = `"[{\\"key\\":\\"pymStorage.localStorage.response\\",\\"value\\":\\"{\\\\\\"id\\\\\\":\\\\\\"0\\\\\\"}\\"},{\\"key\\":\\"pymStorage.localStorage.response\\",\\"value\\":\\"{\\\\\\"id\\\\\\":\\\\\\"1\\\\\\",\\\\\\"result\\\\\\":\\\\\\"test\\\\\\"}\\"},{\\"key\\":\\"pymStorage.localStorage.response\\",\\"value\\":\\"{\\\\\\"id\\\\\\":\\\\\\"2\\\\\\"}\\"}]"`;
|
||||
|
||||
@@ -1,23 +1,8 @@
|
||||
import sinon from "sinon";
|
||||
|
||||
import { createInMemoryStorage } from "../testUtils";
|
||||
import withPymStorage from "./withPymStorage";
|
||||
|
||||
// tslint:disable:max-classes-per-file
|
||||
|
||||
class FakeStorage {
|
||||
public store: Record<string, string> = {};
|
||||
|
||||
public setItem(key: string, value: string) {
|
||||
this.store[key] = value;
|
||||
}
|
||||
public removeItem(key: string) {
|
||||
delete this.store[key];
|
||||
}
|
||||
public getItem(key: string) {
|
||||
return this.store[key];
|
||||
}
|
||||
}
|
||||
|
||||
class PymStub {
|
||||
public listeners: Record<string, ((msg: string) => void)> = {};
|
||||
public messages: Array<{ key: string; value: string }> = [];
|
||||
@@ -38,10 +23,8 @@ class PymStub {
|
||||
describe("withPymStorage", () => {
|
||||
it("should set, get and remove item", () => {
|
||||
const pym = new PymStub("localStorage");
|
||||
const storage = new FakeStorage();
|
||||
withPymStorage(storage as any, "localStorage", "talkPymStorage:")(
|
||||
pym as any
|
||||
);
|
||||
const storage = createInMemoryStorage();
|
||||
withPymStorage(storage, "localStorage", "talk:")(pym as any);
|
||||
pym.listeners["pymStorage.localStorage.request"](
|
||||
JSON.stringify({
|
||||
id: "0",
|
||||
@@ -49,7 +32,7 @@ describe("withPymStorage", () => {
|
||||
parameters: { key: "key", value: "test" },
|
||||
})
|
||||
);
|
||||
expect(storage.store).toMatchSnapshot();
|
||||
expect(storage.toString()).toMatchSnapshot();
|
||||
pym.listeners["pymStorage.localStorage.request"](
|
||||
JSON.stringify({
|
||||
id: "1",
|
||||
@@ -64,15 +47,72 @@ describe("withPymStorage", () => {
|
||||
parameters: { key: "key" },
|
||||
})
|
||||
);
|
||||
expect(storage.store).toMatchSnapshot();
|
||||
expect(storage.toString()).toMatchSnapshot();
|
||||
expect(JSON.stringify(pym.messages)).toMatchSnapshot();
|
||||
});
|
||||
it("should get key of storage", () => {
|
||||
const pym = new PymStub("localStorage");
|
||||
const storage = createInMemoryStorage({
|
||||
a: "1",
|
||||
b: "2",
|
||||
c: "3",
|
||||
});
|
||||
withPymStorage(storage, "localStorage", "")(pym as any);
|
||||
pym.listeners["pymStorage.localStorage.request"](
|
||||
JSON.stringify({
|
||||
id: "0",
|
||||
method: "key",
|
||||
parameters: { n: 1 },
|
||||
})
|
||||
);
|
||||
pym.listeners["pymStorage.localStorage.request"](
|
||||
JSON.stringify({
|
||||
id: "0",
|
||||
method: "key",
|
||||
parameters: { n: 3 },
|
||||
})
|
||||
);
|
||||
expect(pym.messages).toMatchSnapshot();
|
||||
});
|
||||
it("should get length of storage", () => {
|
||||
const pym = new PymStub("localStorage");
|
||||
const storage = createInMemoryStorage({
|
||||
a: "1",
|
||||
b: "2",
|
||||
c: "3",
|
||||
});
|
||||
withPymStorage(storage, "localStorage", "")(pym as any);
|
||||
pym.listeners["pymStorage.localStorage.request"](
|
||||
JSON.stringify({
|
||||
id: "0",
|
||||
method: "length",
|
||||
parameters: {},
|
||||
})
|
||||
);
|
||||
expect(pym.messages).toMatchSnapshot();
|
||||
});
|
||||
it("should clear storage", () => {
|
||||
const pym = new PymStub("localStorage");
|
||||
const storage = createInMemoryStorage({
|
||||
a: "1",
|
||||
b: "2",
|
||||
c: "3",
|
||||
});
|
||||
withPymStorage(storage, "localStorage", "")(pym as any);
|
||||
pym.listeners["pymStorage.localStorage.request"](
|
||||
JSON.stringify({
|
||||
id: "0",
|
||||
method: "clear",
|
||||
parameters: {},
|
||||
})
|
||||
);
|
||||
expect(storage.toString()).toMatchSnapshot();
|
||||
expect(pym.messages).toMatchSnapshot();
|
||||
});
|
||||
it("should handle unknown method", () => {
|
||||
const pym = new PymStub("localStorage");
|
||||
const storage = new FakeStorage();
|
||||
withPymStorage(storage as any, "localStorage", "talkPymStorage:")(
|
||||
pym as any
|
||||
);
|
||||
const storage = createInMemoryStorage();
|
||||
withPymStorage(storage, "localStorage", "talk:")(pym as any);
|
||||
pym.listeners["pymStorage.localStorage.request"](
|
||||
JSON.stringify({
|
||||
id: "0",
|
||||
@@ -84,14 +124,12 @@ describe("withPymStorage", () => {
|
||||
});
|
||||
it("should handle handle errors", () => {
|
||||
const pym = new PymStub("localStorage");
|
||||
const storage = new FakeStorage();
|
||||
const storage = createInMemoryStorage();
|
||||
sinon
|
||||
.mock(storage)
|
||||
.expects("getItem")
|
||||
.throws("error");
|
||||
withPymStorage(storage as any, "localStorage", "talkPymStorage:")(
|
||||
pym as any
|
||||
);
|
||||
withPymStorage(storage, "localStorage", "talk:")(pym as any);
|
||||
pym.listeners["pymStorage.localStorage.request"](
|
||||
JSON.stringify({
|
||||
id: "0",
|
||||
@@ -99,6 +137,6 @@ describe("withPymStorage", () => {
|
||||
parameters: {},
|
||||
})
|
||||
);
|
||||
expect(JSON.stringify(pym.messages)).toMatchSnapshot();
|
||||
expect(pym.messages).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { prefixStorage } from "../utils";
|
||||
import { Decorator } from "./types";
|
||||
|
||||
const withPymStorage = (
|
||||
storage: Storage,
|
||||
type: "localStorage" | "sessionStorage",
|
||||
prefix = "talkPymStorage:"
|
||||
prefix = "talk:"
|
||||
): Decorator => pym => {
|
||||
pym.onMessage(`pymStorage.${type}.request`, (msg: any) => {
|
||||
const { id, method, parameters } = JSON.parse(msg);
|
||||
const { key, value } = parameters;
|
||||
const prefixedKey = `${prefix}${key}`;
|
||||
const { n, key, value } = parameters;
|
||||
const prefixedStorage = prefixStorage(storage, prefix);
|
||||
|
||||
// Variable for the method return value.
|
||||
let result;
|
||||
@@ -25,13 +26,22 @@ const withPymStorage = (
|
||||
try {
|
||||
switch (method) {
|
||||
case "setItem":
|
||||
result = storage.setItem(prefixedKey, value);
|
||||
result = prefixedStorage.setItem(key, value);
|
||||
break;
|
||||
case "getItem":
|
||||
result = storage.getItem(prefixedKey);
|
||||
result = prefixedStorage.getItem(key);
|
||||
break;
|
||||
case "removeItem":
|
||||
result = storage.removeItem(prefixedKey);
|
||||
result = prefixedStorage.removeItem(key);
|
||||
break;
|
||||
case "key":
|
||||
result = prefixedStorage.key(n);
|
||||
break;
|
||||
case "length":
|
||||
result = prefixedStorage.length;
|
||||
break;
|
||||
case "clear":
|
||||
result = prefixedStorage.clear();
|
||||
break;
|
||||
default:
|
||||
sendError(`Unknown method ${method}`);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import createInMemoryStorage from "./InMemoryStorage";
|
||||
|
||||
it("should set and unset values", () => {
|
||||
const storage = createInMemoryStorage();
|
||||
storage.setItem("test", "value");
|
||||
expect(storage.getItem("test")).toBe("value");
|
||||
storage.removeItem("test");
|
||||
expect(storage.getItem("test")).toBeNull();
|
||||
});
|
||||
|
||||
it("should return length", () => {
|
||||
const storage = createInMemoryStorage();
|
||||
storage.setItem("a", "value");
|
||||
storage.setItem("b", "value");
|
||||
storage.setItem("c", "value");
|
||||
expect(storage.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should nth key", () => {
|
||||
const storage = createInMemoryStorage();
|
||||
storage.setItem("a", "0");
|
||||
storage.setItem("b", "1");
|
||||
storage.setItem("c", "2");
|
||||
expect(storage.key(2)).toBe("c");
|
||||
});
|
||||
|
||||
it("accepts predefined data", () => {
|
||||
const storage = createInMemoryStorage({
|
||||
a: "0",
|
||||
b: "1",
|
||||
c: "2",
|
||||
});
|
||||
expect(storage.toString()).toMatchSnapshot();
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* InMemoryStorage is a dumb implementation of the Storage interface that will
|
||||
* not persist the data at all. It implements the Storage interface found:
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/Storage
|
||||
*/
|
||||
class InMemoryStorage implements Storage {
|
||||
private data: Record<string, string>;
|
||||
|
||||
constructor(data: Record<string, string> = {}) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return Object.keys(this.data).length;
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.data = {};
|
||||
}
|
||||
|
||||
public key(n: number) {
|
||||
if (this.length <= n) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Object.keys(this.data)[n];
|
||||
}
|
||||
|
||||
public getItem(key: string) {
|
||||
return this.data[key] || null;
|
||||
}
|
||||
|
||||
public setItem(key: string, value: string) {
|
||||
this.data[key] = value;
|
||||
}
|
||||
|
||||
public removeItem(key: string) {
|
||||
delete this.data[key];
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return JSON.stringify(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
export default function createInMemoryStorage(data?: Record<string, string>) {
|
||||
return new InMemoryStorage(data);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`accepts predefined data 1`] = `"{\\"a\\":\\"0\\",\\"b\\":\\"1\\",\\"c\\":\\"2\\"}"`;
|
||||
@@ -0,0 +1 @@
|
||||
export { default as createInMemoryStorage } from "./InMemoryStorage";
|
||||
@@ -0,0 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`should call clear 1`] = `"{\\"a\\":\\"0\\",\\"b\\":\\"1\\",\\"d\\":\\"3\\"}"`;
|
||||
@@ -1,2 +1,4 @@
|
||||
export { default as buildURL } from "./buildURL";
|
||||
export { default as ensureEndSlash } from "./ensureEndSlash";
|
||||
export { default as startsWith } from "./startsWith";
|
||||
export { default as prefixStorage } from "./prefixStorage";
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import sinon from "sinon";
|
||||
import { createInMemoryStorage } from "../testUtils";
|
||||
import prefixStorage from "./prefixStorage";
|
||||
|
||||
it("should get nth key", () => {
|
||||
const storage = createInMemoryStorage({
|
||||
a: "0",
|
||||
b: "1",
|
||||
"talk:c": "2",
|
||||
d: "3",
|
||||
"talk:e": "4",
|
||||
});
|
||||
|
||||
const prefixed = prefixStorage(storage, "talk:");
|
||||
expect(prefixed.key(0)).toBe("talk:c");
|
||||
expect(prefixed.key(1)).toBe("talk:e");
|
||||
expect(prefixed.key(2)).toBeNull();
|
||||
});
|
||||
|
||||
it("should call clear", () => {
|
||||
const storage = createInMemoryStorage({
|
||||
a: "0",
|
||||
b: "1",
|
||||
"talk:c": "2",
|
||||
d: "3",
|
||||
"talk:e": "4",
|
||||
});
|
||||
|
||||
const prefixed = prefixStorage(storage, "talk:");
|
||||
prefixed.clear();
|
||||
expect(storage.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should call length", () => {
|
||||
const storage = createInMemoryStorage({
|
||||
a: "0",
|
||||
b: "1",
|
||||
"talk:c": "2",
|
||||
d: "3",
|
||||
"talk:e": "4",
|
||||
});
|
||||
|
||||
const prefixed = prefixStorage(storage, "talk:");
|
||||
expect(prefixed.length).toBe(2);
|
||||
});
|
||||
|
||||
it("should prefix setItem", () => {
|
||||
const storage = {
|
||||
setItem: sinon.mock().withArgs("talk:key", "value"),
|
||||
};
|
||||
|
||||
const prefixed = prefixStorage(storage as any, "talk:");
|
||||
prefixed.setItem("key", "value");
|
||||
storage.setItem.verify();
|
||||
});
|
||||
|
||||
it("should prefix removeItem", () => {
|
||||
const storage = {
|
||||
removeItem: sinon.mock().withArgs("talk:key"),
|
||||
};
|
||||
|
||||
const prefixed = prefixStorage(storage as any, "talk:");
|
||||
prefixed.removeItem("key");
|
||||
storage.removeItem.verify();
|
||||
});
|
||||
|
||||
it("should prefix getItem", () => {
|
||||
const ret = "value";
|
||||
const storage = {
|
||||
getItem: sinon
|
||||
.mock()
|
||||
.withArgs("talk:key")
|
||||
.returns(ret),
|
||||
};
|
||||
|
||||
const prefixed = prefixStorage(storage as any, "talk:");
|
||||
expect(prefixed.getItem("key")).toBe(ret);
|
||||
(storage.getItem as any).verify();
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
import startsWith from "./startsWith";
|
||||
|
||||
/**
|
||||
* PrefixedStorage decorates a Storage and prefixes keys in
|
||||
* getItem, setItem and removeItem with given prefix.
|
||||
*/
|
||||
class PrefixedStorage implements Storage {
|
||||
private storage: Storage;
|
||||
private prefix: string;
|
||||
|
||||
constructor(storage: Storage, prefix: string) {
|
||||
this.storage = storage;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
get length() {
|
||||
let count = 0;
|
||||
for (let i = 0; i < this.storage.length; i++) {
|
||||
if (startsWith(this.storage.key(i)!, this.prefix)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public clear() {
|
||||
const toBeDeleted = [];
|
||||
for (let i = 0; i < this.storage.length; i++) {
|
||||
const key = this.storage.key(i)!;
|
||||
if (startsWith(key, this.prefix)) {
|
||||
toBeDeleted.push(key);
|
||||
}
|
||||
}
|
||||
toBeDeleted.forEach(key => this.storage.removeItem(key));
|
||||
}
|
||||
|
||||
public key(n: number) {
|
||||
let count = 0;
|
||||
for (let i = 0; i < this.storage.length; i++) {
|
||||
const key = this.storage.key(i)!;
|
||||
if (startsWith(key, this.prefix)) {
|
||||
if (count === n) {
|
||||
return key;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public getItem(key: string) {
|
||||
return this.storage.getItem(`${this.prefix}${key}`);
|
||||
}
|
||||
|
||||
public setItem(key: string, value: string) {
|
||||
return this.storage.setItem(`${this.prefix}${key}`, value);
|
||||
}
|
||||
|
||||
public removeItem(key: string) {
|
||||
return this.storage.removeItem(`${this.prefix}${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
export default function prefixStorage(storage: Storage, prefix: string) {
|
||||
return new PrefixedStorage(storage, prefix);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import startsWith from "./startsWith";
|
||||
|
||||
it("should work correctly", () => {
|
||||
const str1 = "Saturday night plans";
|
||||
expect(startsWith(str1, "Sat")).toBe(true);
|
||||
expect(startsWith(str1, "Sat", 3)).toBe(false);
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
/** A substitute for string.startsWith */
|
||||
export default function startsWith(str: string, search: string, pos?: number) {
|
||||
return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
|
||||
}
|
||||
@@ -24,16 +24,10 @@ export interface TalkContext {
|
||||
timeagoFormatter?: Formatter;
|
||||
|
||||
/** Local Storage */
|
||||
localStorage: Storage;
|
||||
localStorage: PromisifiedStorage;
|
||||
|
||||
/** Session storage */
|
||||
sessionStorage: Storage;
|
||||
|
||||
/** Session Storage over pym */
|
||||
pymLocalStorage?: PromisifiedStorage;
|
||||
|
||||
/** Session storage over pym */
|
||||
pymSessionStorage?: PromisifiedStorage;
|
||||
sessionStorage: PromisifiedStorage;
|
||||
|
||||
/** media query values for testing purposes */
|
||||
mediaQueryValues?: MediaQueryMatchers;
|
||||
|
||||
@@ -128,14 +128,12 @@ export default async function createContext({
|
||||
registerClickFarAway,
|
||||
rest: new RestClient("/api", tokenGetter),
|
||||
postMessage: new PostMessageService(),
|
||||
localStorage: createLocalStorage(),
|
||||
sessionStorage: createSessionStorage(),
|
||||
pymLocalStorage:
|
||||
(pym && (inIframe && createPymStorage(pym, "localStorage"))) ||
|
||||
createPromisifiedStorage(createLocalStorage("talkPym")),
|
||||
pymSessionStorage:
|
||||
(pym && (inIframe && createPymStorage(pym, "sessionStorage"))) ||
|
||||
createPromisifiedStorage(createSessionStorage("talkPym")),
|
||||
localStorage:
|
||||
(pym && inIframe && createPymStorage(pym, "localStorage")) ||
|
||||
createPromisifiedStorage(createLocalStorage()),
|
||||
sessionStorage:
|
||||
(pym && inIframe && createPymStorage(pym, "sessionStorage")) ||
|
||||
createPromisifiedStorage(createSessionStorage()),
|
||||
browserInfo: getBrowserInfo(),
|
||||
};
|
||||
|
||||
|
||||
@@ -16,10 +16,19 @@ it("should return length", () => {
|
||||
expect(storage.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should nth value", () => {
|
||||
it("should nth key", () => {
|
||||
const storage = createInMemoryStorage();
|
||||
storage.setItem("a", "a");
|
||||
storage.setItem("b", "b");
|
||||
storage.setItem("c", "c");
|
||||
storage.setItem("a", "0");
|
||||
storage.setItem("b", "1");
|
||||
storage.setItem("c", "2");
|
||||
expect(storage.key(2)).toBe("c");
|
||||
});
|
||||
|
||||
it("accepts predefined data", () => {
|
||||
const storage = createInMemoryStorage({
|
||||
a: "0",
|
||||
b: "1",
|
||||
c: "2",
|
||||
});
|
||||
expect(storage.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/Storage
|
||||
*/
|
||||
class InMemoryStorage implements Storage {
|
||||
private storage: Record<string, string>;
|
||||
private data: Record<string, string>;
|
||||
|
||||
constructor() {
|
||||
this.storage = {};
|
||||
constructor(data: Record<string, string> = {}) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return Object.keys(this.storage).length;
|
||||
return Object.keys(this.data).length;
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.storage = {};
|
||||
this.data = {};
|
||||
}
|
||||
|
||||
public key(n: number) {
|
||||
@@ -24,26 +24,26 @@ class InMemoryStorage implements Storage {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.storage[Object.keys(this.storage)[n]];
|
||||
return Object.keys(this.data)[n];
|
||||
}
|
||||
|
||||
public getItem(key: string) {
|
||||
return this.storage[key] || null;
|
||||
return this.data[key] || null;
|
||||
}
|
||||
|
||||
public setItem(key: string, value: string) {
|
||||
this.storage[key] = value;
|
||||
this.data[key] = value;
|
||||
}
|
||||
|
||||
public removeItem(key: string) {
|
||||
delete this.storage[key];
|
||||
delete this.data[key];
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return JSON.stringify(this.storage);
|
||||
return JSON.stringify(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
export default function createInMemoryStorage() {
|
||||
return new InMemoryStorage();
|
||||
export default function createInMemoryStorage(data?: Record<string, string>) {
|
||||
return new InMemoryStorage(data);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import prefixStorage from "./prefixStorage";
|
||||
|
||||
export default function createLocalStorage(prefix = "talk"): Storage {
|
||||
export default function createLocalStorage(prefix = "talk:"): Storage {
|
||||
return prefixStorage(window.localStorage, prefix);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
import createInMemoryStorage from "./InMemoryStorage";
|
||||
import createPromisifiedStorage from "./PromisifiedStorage";
|
||||
|
||||
it("should set and unset values", () => {
|
||||
it("should set and unset values", async () => {
|
||||
const storage = createPromisifiedStorage(createInMemoryStorage());
|
||||
expect(storage.setItem("test", "value")).resolves.toBeUndefined();
|
||||
expect(storage.getItem("test")).resolves.toBe("value");
|
||||
await expect(storage.setItem("test", "value")).resolves.toBeUndefined();
|
||||
await expect(storage.getItem("test")).resolves.toBe("value");
|
||||
storage.removeItem("test");
|
||||
expect(storage.getItem("test")).resolves.toBeUndefined();
|
||||
await expect(storage.getItem("test")).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it("should return length", async () => {
|
||||
const storage = createPromisifiedStorage(createInMemoryStorage());
|
||||
storage.setItem("a", "value");
|
||||
storage.setItem("b", "value");
|
||||
storage.setItem("c", "value");
|
||||
await expect(storage.length).resolves.toBe(3);
|
||||
});
|
||||
|
||||
it("should nth value", async () => {
|
||||
const storage = createPromisifiedStorage(createInMemoryStorage());
|
||||
storage.setItem("a", "a");
|
||||
storage.setItem("b", "b");
|
||||
storage.setItem("c", "c");
|
||||
await expect(storage.key(2)).resolves.toBe("c");
|
||||
});
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import createInMemoryStorage from "./InMemoryStorage";
|
||||
|
||||
export interface PromisifiedStorage {
|
||||
length: Promise<number>;
|
||||
|
||||
clear(): Promise<void>;
|
||||
|
||||
key(n: number): Promise<string | null>;
|
||||
|
||||
/**
|
||||
* value = storage[key]
|
||||
*/
|
||||
@@ -23,6 +31,18 @@ class BackedPromisifedStorage implements PromisifiedStorage {
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return Promise.resolve(this.storage.length);
|
||||
}
|
||||
|
||||
public clear() {
|
||||
return Promise.resolve(this.storage.clear());
|
||||
}
|
||||
|
||||
public key(n: number) {
|
||||
return Promise.resolve(this.storage.key(n));
|
||||
}
|
||||
|
||||
public getItem(key: string) {
|
||||
return Promise.resolve(this.storage.getItem(key));
|
||||
}
|
||||
@@ -36,6 +56,8 @@ class BackedPromisifedStorage implements PromisifiedStorage {
|
||||
}
|
||||
}
|
||||
|
||||
export default function createPromisifiedStorage(storage: Storage) {
|
||||
export default function createPromisifiedStorage(
|
||||
storage: Storage = createInMemoryStorage()
|
||||
) {
|
||||
return new BackedPromisifedStorage(storage);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,49 @@ describe("PymStorage", () => {
|
||||
expect(promise).resolves.toBe("value");
|
||||
});
|
||||
|
||||
it("should get length", () => {
|
||||
const pym = new PymStub("localStorage");
|
||||
const storage = createPymStorage(pym as any, "localStorage");
|
||||
const promise = storage.length;
|
||||
const { key, value } = pym.messages.pop()!;
|
||||
expect(key).toBe(`pymStorage.localStorage.request`);
|
||||
const { id, method, parameters } = JSON.parse(value);
|
||||
expect(method).toBe("length");
|
||||
expect(parameters).toEqual({});
|
||||
pym.listeners["pymStorage.localStorage.response"](
|
||||
JSON.stringify({ id, result: 3 })
|
||||
);
|
||||
expect(promise).resolves.toBe(3);
|
||||
});
|
||||
|
||||
it("should get key", () => {
|
||||
const pym = new PymStub("localStorage");
|
||||
const storage = createPymStorage(pym as any, "localStorage");
|
||||
const promise = storage.key(2);
|
||||
const { key, value } = pym.messages.pop()!;
|
||||
expect(key).toBe(`pymStorage.localStorage.request`);
|
||||
const { id, method, parameters } = JSON.parse(value);
|
||||
expect(method).toBe("key");
|
||||
expect(parameters).toEqual({ n: 2 });
|
||||
pym.listeners["pymStorage.localStorage.response"](
|
||||
JSON.stringify({ id, result: "myKey" })
|
||||
);
|
||||
expect(promise).resolves.toBe("myKey");
|
||||
});
|
||||
|
||||
it("should clear", () => {
|
||||
const pym = new PymStub("localStorage");
|
||||
const storage = createPymStorage(pym as any, "localStorage");
|
||||
const promise = storage.clear();
|
||||
const { key, value } = pym.messages.pop()!;
|
||||
expect(key).toBe(`pymStorage.localStorage.request`);
|
||||
const { id, method, parameters } = JSON.parse(value);
|
||||
expect(method).toBe("clear");
|
||||
expect(parameters).toEqual({});
|
||||
pym.listeners["pymStorage.localStorage.response"](JSON.stringify({ id }));
|
||||
expect(promise).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
describe("on error", () => {
|
||||
it("should reject set item", () => {
|
||||
const pym = new PymStub("localStorage");
|
||||
|
||||
@@ -20,7 +20,7 @@ class PymStorage implements PromisifiedStorage {
|
||||
/** Requests method with parameters over pym. */
|
||||
private call<T>(
|
||||
method: string,
|
||||
parameters: { key: string; value?: string }
|
||||
parameters: Record<string, any> = {}
|
||||
): Promise<T> {
|
||||
const id = uuid();
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -55,6 +55,15 @@ class PymStorage implements PromisifiedStorage {
|
||||
this.listen();
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.call<number>("length");
|
||||
}
|
||||
public key(n: number) {
|
||||
return this.call<string | null>("key", { n });
|
||||
}
|
||||
public clear() {
|
||||
return this.call<void>("clear");
|
||||
}
|
||||
public setItem(key: string, value: string) {
|
||||
return this.call<void>("setItem", { key, value });
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import prefixStorage from "./prefixStorage";
|
||||
|
||||
export default function createSessionStorage(prefix = "talk"): Storage {
|
||||
export default function createSessionStorage(prefix = "talk:"): Storage {
|
||||
return prefixStorage(window.sessionStorage, prefix);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`accepts predefined data 1`] = `"{\\"a\\":\\"0\\",\\"b\\":\\"1\\",\\"c\\":\\"2\\"}"`;
|
||||
@@ -0,0 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`should call clear 1`] = `"{\\"a\\":\\"0\\",\\"b\\":\\"1\\",\\"d\\":\\"3\\"}"`;
|
||||
@@ -1,54 +1,47 @@
|
||||
import sinon from "sinon";
|
||||
import createInMemoryStorage from "./InMemoryStorage";
|
||||
import prefixStorage from "./prefixStorage";
|
||||
|
||||
it("should call clear", () => {
|
||||
const storage = {
|
||||
clear: sinon.mock().once(),
|
||||
};
|
||||
it("should get nth key", () => {
|
||||
const storage = createInMemoryStorage({
|
||||
a: "0",
|
||||
b: "1",
|
||||
"talk:c": "2",
|
||||
d: "3",
|
||||
"talk:e": "4",
|
||||
});
|
||||
|
||||
const prefixed = prefixStorage(storage as any, "talk");
|
||||
const prefixed = prefixStorage(storage, "talk:");
|
||||
expect(prefixed.key(0)).toBe("talk:c");
|
||||
expect(prefixed.key(1)).toBe("talk:e");
|
||||
expect(prefixed.key(2)).toBeNull();
|
||||
});
|
||||
|
||||
it("should call clear", () => {
|
||||
const storage = createInMemoryStorage({
|
||||
a: "0",
|
||||
b: "1",
|
||||
"talk:c": "2",
|
||||
d: "3",
|
||||
"talk:e": "4",
|
||||
});
|
||||
|
||||
const prefixed = prefixStorage(storage, "talk:");
|
||||
prefixed.clear();
|
||||
storage.clear.verify();
|
||||
expect(storage.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should call length", () => {
|
||||
const ret = 10;
|
||||
const storage = {
|
||||
get length() {
|
||||
return ret;
|
||||
},
|
||||
};
|
||||
const storage = createInMemoryStorage({
|
||||
a: "0",
|
||||
b: "1",
|
||||
"talk:c": "2",
|
||||
d: "3",
|
||||
"talk:e": "4",
|
||||
});
|
||||
|
||||
const prefixed = prefixStorage(storage as any, "talk");
|
||||
expect(prefixed.length).toBe(ret);
|
||||
});
|
||||
|
||||
it("should call key", () => {
|
||||
const ret = "value";
|
||||
const storage = {
|
||||
key: sinon
|
||||
.mock()
|
||||
.withArgs(3)
|
||||
.returns(ret),
|
||||
};
|
||||
|
||||
const prefixed = prefixStorage(storage as any, "talk");
|
||||
expect(prefixed.key(3)).toBe(ret);
|
||||
(storage.key as any).verify();
|
||||
});
|
||||
|
||||
it("should call key", () => {
|
||||
const ret = "value";
|
||||
const storage = {
|
||||
key: sinon
|
||||
.mock()
|
||||
.withArgs(3)
|
||||
.returns(ret),
|
||||
};
|
||||
|
||||
const prefixed = prefixStorage(storage as any, "talk");
|
||||
expect(prefixed.key(3)).toBe(ret);
|
||||
(storage.key as any).verify();
|
||||
const prefixed = prefixStorage(storage, "talk:");
|
||||
expect(prefixed.length).toBe(2);
|
||||
});
|
||||
|
||||
it("should prefix setItem", () => {
|
||||
@@ -56,7 +49,7 @@ it("should prefix setItem", () => {
|
||||
setItem: sinon.mock().withArgs("talk:key", "value"),
|
||||
};
|
||||
|
||||
const prefixed = prefixStorage(storage as any, "talk");
|
||||
const prefixed = prefixStorage(storage as any, "talk:");
|
||||
prefixed.setItem("key", "value");
|
||||
storage.setItem.verify();
|
||||
});
|
||||
@@ -66,7 +59,7 @@ it("should prefix removeItem", () => {
|
||||
removeItem: sinon.mock().withArgs("talk:key"),
|
||||
};
|
||||
|
||||
const prefixed = prefixStorage(storage as any, "talk");
|
||||
const prefixed = prefixStorage(storage as any, "talk:");
|
||||
prefixed.removeItem("key");
|
||||
storage.removeItem.verify();
|
||||
});
|
||||
@@ -80,7 +73,7 @@ it("should prefix getItem", () => {
|
||||
.returns(ret),
|
||||
};
|
||||
|
||||
const prefixed = prefixStorage(storage as any, "talk");
|
||||
const prefixed = prefixStorage(storage as any, "talk:");
|
||||
expect(prefixed.getItem("key")).toBe(ret);
|
||||
(storage.getItem as any).verify();
|
||||
});
|
||||
|
||||
@@ -12,27 +12,50 @@ class PrefixedStorage implements Storage {
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.storage.length;
|
||||
let count = 0;
|
||||
for (let i = 0; i < this.storage.length; i++) {
|
||||
if (this.storage.key(i)!.startsWith(this.prefix)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.storage.clear();
|
||||
const toBeDeleted = [];
|
||||
for (let i = 0; i < this.storage.length; i++) {
|
||||
const key = this.storage.key(i)!;
|
||||
if (key.startsWith(this.prefix)) {
|
||||
toBeDeleted.push(key);
|
||||
}
|
||||
}
|
||||
toBeDeleted.forEach(key => this.storage.removeItem(key));
|
||||
}
|
||||
|
||||
public key(n: number) {
|
||||
return this.storage.key(n);
|
||||
let count = 0;
|
||||
for (let i = 0; i < this.storage.length; i++) {
|
||||
const key = this.storage.key(i)!;
|
||||
if (key.startsWith(this.prefix)) {
|
||||
if (count === n) {
|
||||
return key;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public getItem(key: string) {
|
||||
return this.storage.getItem(`${this.prefix}:${key}`);
|
||||
return this.storage.getItem(`${this.prefix}${key}`);
|
||||
}
|
||||
|
||||
public setItem(key: string, value: string) {
|
||||
return this.storage.setItem(`${this.prefix}:${key}`, value);
|
||||
return this.storage.setItem(`${this.prefix}${key}`, value);
|
||||
}
|
||||
|
||||
public removeItem(key: string) {
|
||||
return this.storage.removeItem(`${this.prefix}:${key}`);
|
||||
return this.storage.removeItem(`${this.prefix}${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,24 +33,3 @@ it("Removes auth token from localStorage", () => {
|
||||
commit(environment, { authToken: null }, context as any);
|
||||
expect(context.localStorage.getItem("authToken")).toBeNull();
|
||||
});
|
||||
|
||||
it("Sets auth token to pymLocalStorage", async () => {
|
||||
const context = {
|
||||
pymLocalStorage: createInMemoryStorage(),
|
||||
localStorage: createInMemoryStorage(),
|
||||
};
|
||||
const authToken = "auth token";
|
||||
commit(environment, { authToken }, context as any);
|
||||
expect(source.get(LOCAL_ID)!.authToken).toEqual(authToken);
|
||||
expect(await context.pymLocalStorage.getItem("authToken")).toEqual(authToken);
|
||||
});
|
||||
|
||||
it("Removes auth token from pymLocalStorage", async () => {
|
||||
const context = {
|
||||
pymLocalStorage: createInMemoryStorage(),
|
||||
localStorage: createInMemoryStorage(),
|
||||
};
|
||||
localStorage.setItem("authToken", "tmp");
|
||||
commit(environment, { authToken: null }, context as any);
|
||||
expect(await context.pymLocalStorage.getItem("authToken")).toBeNull();
|
||||
});
|
||||
|
||||
@@ -13,16 +13,15 @@ export type SetAuthTokenMutation = (input: SetAuthTokenInput) => Promise<void>;
|
||||
export async function commit(
|
||||
environment: Environment,
|
||||
input: SetAuthTokenInput,
|
||||
{ localStorage, pymLocalStorage }: TalkContext
|
||||
{ localStorage }: TalkContext
|
||||
) {
|
||||
return commitLocalUpdate(environment, store => {
|
||||
const record = store.get(LOCAL_ID)!;
|
||||
record.setValue(input.authToken, "authToken");
|
||||
const storage = pymLocalStorage || localStorage;
|
||||
if (input.authToken) {
|
||||
storage.setItem("authToken", input.authToken);
|
||||
localStorage.setItem("authToken", input.authToken);
|
||||
} else {
|
||||
storage.removeItem("authToken");
|
||||
localStorage.removeItem("authToken");
|
||||
}
|
||||
// Increment auth revision to indicate a change in auth state.
|
||||
record.setValue(record.getValue("authRevision") + 1, "authRevision");
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import {
|
||||
createInMemoryStorage,
|
||||
createPromisifiedStorage,
|
||||
} from "talk-framework/lib/storage";
|
||||
|
||||
export default function createFakePymStorage() {
|
||||
return createPromisifiedStorage(createInMemoryStorage());
|
||||
}
|
||||
@@ -4,7 +4,6 @@ export {
|
||||
} from "./createRelayEnvironment";
|
||||
export { default as createFluentBundle } from "./createFluentBundle";
|
||||
export { default as createSinonStub } from "./createSinonStub";
|
||||
export { default as createFakePymStorage } from "./createFakePymStorage";
|
||||
export {
|
||||
default as removeFragmentRefs,
|
||||
NoFragmentRefs,
|
||||
|
||||
@@ -3,10 +3,9 @@ import { noop } from "lodash";
|
||||
import React from "react";
|
||||
import sinon from "sinon";
|
||||
|
||||
import { PropTypesOf } from "talk-framework/types";
|
||||
|
||||
import { timeout } from "talk-common/utils";
|
||||
import { createFakePymStorage } from "talk-framework/testHelpers";
|
||||
import { createPromisifiedStorage } from "talk-framework/lib/storage";
|
||||
import { PropTypesOf } from "talk-framework/types";
|
||||
import { PostCommentFormContainer } from "./PostCommentFormContainer";
|
||||
|
||||
const contextKey = "postCommentFormBody";
|
||||
@@ -16,7 +15,7 @@ it("renders correctly", async () => {
|
||||
// tslint:disable-next-line:no-empty
|
||||
createComment: (() => {}) as any,
|
||||
assetID: "asset-id",
|
||||
pymSessionStorage: createFakePymStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
};
|
||||
|
||||
const wrapper = shallow(<PostCommentFormContainer {...props} />);
|
||||
@@ -30,10 +29,10 @@ it("renders with initialValues", async () => {
|
||||
// tslint:disable-next-line:no-empty
|
||||
createComment: (() => {}) as any,
|
||||
assetID: "asset-id",
|
||||
pymSessionStorage: createFakePymStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
};
|
||||
|
||||
await props.pymSessionStorage.setItem(contextKey, "Hello World!");
|
||||
await props.sessionStorage.setItem(contextKey, "Hello World!");
|
||||
|
||||
const wrapper = shallow(<PostCommentFormContainer {...props} />);
|
||||
await timeout();
|
||||
@@ -46,10 +45,10 @@ it("save values", async () => {
|
||||
// tslint:disable-next-line:no-empty
|
||||
createComment: (() => {}) as any,
|
||||
assetID: "asset-id",
|
||||
pymSessionStorage: createFakePymStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
};
|
||||
|
||||
await props.pymSessionStorage.setItem(contextKey, "Hello World!");
|
||||
await props.sessionStorage.setItem(contextKey, "Hello World!");
|
||||
|
||||
const wrapper = shallow(<PostCommentFormContainer {...props} />);
|
||||
await timeout();
|
||||
@@ -58,7 +57,7 @@ it("save values", async () => {
|
||||
.first()
|
||||
.props()
|
||||
.onChange({ values: { body: "changed" } });
|
||||
expect(await props.pymSessionStorage.getItem(contextKey)).toBe("changed");
|
||||
expect(await props.sessionStorage.getItem(contextKey)).toBe("changed");
|
||||
});
|
||||
|
||||
it("creates a comment", async () => {
|
||||
@@ -76,10 +75,10 @@ it("creates a comment", async () => {
|
||||
// tslint:disable-next-line:no-empty
|
||||
createComment: createCommentStub,
|
||||
assetID,
|
||||
pymSessionStorage: createFakePymStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
};
|
||||
|
||||
await props.pymSessionStorage.setItem(contextKey, "Hello World!");
|
||||
await props.sessionStorage.setItem(contextKey, "Hello World!");
|
||||
|
||||
const wrapper = shallow(<PostCommentFormContainer {...props} />);
|
||||
await timeout();
|
||||
|
||||
@@ -13,7 +13,7 @@ import { CreateCommentMutation, withCreateCommentMutation } from "../mutations";
|
||||
interface InnerProps {
|
||||
createComment: CreateCommentMutation;
|
||||
assetID: string;
|
||||
pymSessionStorage: PromisifiedStorage;
|
||||
sessionStorage: PromisifiedStorage;
|
||||
}
|
||||
|
||||
interface State {
|
||||
@@ -32,7 +32,7 @@ export class PostCommentFormContainer extends Component<InnerProps, State> {
|
||||
}
|
||||
|
||||
private async init() {
|
||||
const body = await this.props.pymSessionStorage.getItem(contextKey);
|
||||
const body = await this.props.sessionStorage.getItem(contextKey);
|
||||
if (body) {
|
||||
this.setState({
|
||||
initialValues: {
|
||||
@@ -67,9 +67,9 @@ export class PostCommentFormContainer extends Component<InnerProps, State> {
|
||||
|
||||
private handleOnChange: PostCommentFormProps["onChange"] = state => {
|
||||
if (state.values.body) {
|
||||
this.props.pymSessionStorage.setItem(contextKey, state.values.body);
|
||||
this.props.sessionStorage.setItem(contextKey, state.values.body);
|
||||
} else {
|
||||
this.props.pymSessionStorage.removeItem(contextKey);
|
||||
this.props.sessionStorage.removeItem(contextKey);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -87,8 +87,8 @@ export class PostCommentFormContainer extends Component<InnerProps, State> {
|
||||
}
|
||||
}
|
||||
|
||||
const enhanced = withContext(({ pymSessionStorage }) => ({
|
||||
pymSessionStorage,
|
||||
const enhanced = withContext(({ sessionStorage }) => ({
|
||||
sessionStorage,
|
||||
}))(withCreateCommentMutation(PostCommentFormContainer));
|
||||
export type PostCommentFormContainerProps = PropTypesOf<typeof enhanced>;
|
||||
export default enhanced;
|
||||
|
||||
@@ -3,13 +3,10 @@ import { noop } from "lodash";
|
||||
import React from "react";
|
||||
import sinon from "sinon";
|
||||
|
||||
import { PropTypesOf } from "talk-framework/types";
|
||||
|
||||
import { timeout } from "talk-common/utils";
|
||||
import {
|
||||
createFakePymStorage,
|
||||
removeFragmentRefs,
|
||||
} from "talk-framework/testHelpers";
|
||||
import { createPromisifiedStorage } from "talk-framework/lib/storage";
|
||||
import { removeFragmentRefs } from "talk-framework/testHelpers";
|
||||
import { PropTypesOf } from "talk-framework/types";
|
||||
import { ReplyCommentFormContainer } from "./ReplyCommentFormContainer";
|
||||
|
||||
const ReplyCommentFormContainerN = removeFragmentRefs(
|
||||
@@ -29,7 +26,7 @@ it("renders correctly", async () => {
|
||||
comment: {
|
||||
id: "comment-id",
|
||||
},
|
||||
pymSessionStorage: createFakePymStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
autofocus: false,
|
||||
};
|
||||
|
||||
@@ -48,11 +45,11 @@ it("renders with initialValues", async () => {
|
||||
comment: {
|
||||
id: "comment-id",
|
||||
},
|
||||
pymSessionStorage: createFakePymStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
autofocus: false,
|
||||
};
|
||||
|
||||
await props.pymSessionStorage.setItem(
|
||||
await props.sessionStorage.setItem(
|
||||
getContextKey(props.comment.id),
|
||||
"Hello World!"
|
||||
);
|
||||
@@ -72,11 +69,11 @@ it("save values", async () => {
|
||||
comment: {
|
||||
id: "comment-id",
|
||||
},
|
||||
pymSessionStorage: createFakePymStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
autofocus: false,
|
||||
};
|
||||
|
||||
await props.pymSessionStorage.setItem(
|
||||
await props.sessionStorage.setItem(
|
||||
getContextKey(props.comment.id),
|
||||
"Hello World!"
|
||||
);
|
||||
@@ -89,7 +86,7 @@ it("save values", async () => {
|
||||
.props()
|
||||
.onChange({ values: { body: "changed" } });
|
||||
expect(
|
||||
await props.pymSessionStorage.getItem(getContextKey(props.comment.id))
|
||||
await props.sessionStorage.getItem(getContextKey(props.comment.id))
|
||||
).toBe("changed");
|
||||
});
|
||||
|
||||
@@ -108,12 +105,12 @@ it("creates a comment", async () => {
|
||||
comment: {
|
||||
id: "comment-id",
|
||||
},
|
||||
pymSessionStorage: createFakePymStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
onClose: onCloseStub,
|
||||
autofocus: false,
|
||||
};
|
||||
|
||||
await props.pymSessionStorage.setItem(
|
||||
await props.sessionStorage.setItem(
|
||||
getContextKey(props.comment.id),
|
||||
"Hello World!"
|
||||
);
|
||||
@@ -146,12 +143,12 @@ it("closes on cancel", async () => {
|
||||
comment: {
|
||||
id: "comment-id",
|
||||
},
|
||||
pymSessionStorage: createFakePymStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
onClose: onCloseStub,
|
||||
autofocus: false,
|
||||
};
|
||||
|
||||
await props.pymSessionStorage.setItem(
|
||||
await props.sessionStorage.setItem(
|
||||
getContextKey(props.comment.id),
|
||||
"Hello World!"
|
||||
);
|
||||
@@ -166,7 +163,7 @@ it("closes on cancel", async () => {
|
||||
|
||||
// Removes saved value.
|
||||
expect(
|
||||
await props.pymSessionStorage.getItem(getContextKey(props.comment.id))
|
||||
await props.sessionStorage.getItem(getContextKey(props.comment.id))
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
@@ -181,7 +178,7 @@ it("autofocuses", async () => {
|
||||
comment: {
|
||||
id: "comment-id",
|
||||
},
|
||||
pymSessionStorage: createFakePymStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
autofocus: true,
|
||||
};
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { CreateCommentMutation, withCreateCommentMutation } from "../mutations";
|
||||
|
||||
interface InnerProps {
|
||||
createComment: CreateCommentMutation;
|
||||
pymSessionStorage: PromisifiedStorage;
|
||||
sessionStorage: PromisifiedStorage;
|
||||
comment: CommentData;
|
||||
asset: AssetData;
|
||||
onClose?: () => void;
|
||||
@@ -45,7 +45,7 @@ export class ReplyCommentFormContainer extends Component<InnerProps, State> {
|
||||
};
|
||||
|
||||
private async init() {
|
||||
const body = await this.props.pymSessionStorage.getItem(this.contextKey);
|
||||
const body = await this.props.sessionStorage.getItem(this.contextKey);
|
||||
if (body) {
|
||||
this.setState({
|
||||
initialValues: {
|
||||
@@ -59,7 +59,7 @@ export class ReplyCommentFormContainer extends Component<InnerProps, State> {
|
||||
}
|
||||
|
||||
private handleOnCancel = () => {
|
||||
this.props.pymSessionStorage.removeItem(this.contextKey);
|
||||
this.props.sessionStorage.removeItem(this.contextKey);
|
||||
if (this.props.onClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
@@ -76,7 +76,7 @@ export class ReplyCommentFormContainer extends Component<InnerProps, State> {
|
||||
...input,
|
||||
});
|
||||
|
||||
this.props.pymSessionStorage.removeItem(this.contextKey);
|
||||
this.props.sessionStorage.removeItem(this.contextKey);
|
||||
if (this.props.onClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
@@ -92,9 +92,9 @@ export class ReplyCommentFormContainer extends Component<InnerProps, State> {
|
||||
|
||||
private handleOnChange: ReplyCommentFormProps["onChange"] = state => {
|
||||
if (state.values.body) {
|
||||
this.props.pymSessionStorage.setItem(this.contextKey, state.values.body);
|
||||
this.props.sessionStorage.setItem(this.contextKey, state.values.body);
|
||||
} else {
|
||||
this.props.pymSessionStorage.removeItem(this.contextKey);
|
||||
this.props.sessionStorage.removeItem(this.contextKey);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -114,8 +114,8 @@ export class ReplyCommentFormContainer extends Component<InnerProps, State> {
|
||||
);
|
||||
}
|
||||
}
|
||||
const enhanced = withContext(({ pymSessionStorage, browserInfo }) => ({
|
||||
pymSessionStorage,
|
||||
const enhanced = withContext(({ sessionStorage, browserInfo }) => ({
|
||||
sessionStorage,
|
||||
// Disable autofocus on ios and enable for the rest.
|
||||
autofocus: !browserInfo.ios,
|
||||
}))(
|
||||
|
||||
@@ -3,10 +3,7 @@ import { Environment, RecordSource } from "relay-runtime";
|
||||
import { timeout } from "talk-common/utils";
|
||||
import { TalkContext } from "talk-framework/lib/bootstrap";
|
||||
import { LOCAL_ID } from "talk-framework/lib/relay";
|
||||
import {
|
||||
createInMemoryStorage,
|
||||
createPromisifiedStorage,
|
||||
} from "talk-framework/lib/storage";
|
||||
import { createPromisifiedStorage } from "talk-framework/lib/storage";
|
||||
import { createRelayEnvironment } from "talk-framework/testHelpers";
|
||||
|
||||
import initLocalState from "./initLocalState";
|
||||
@@ -24,7 +21,7 @@ beforeEach(() => {
|
||||
|
||||
it("init local state", async () => {
|
||||
const context: Partial<TalkContext> = {
|
||||
pymLocalStorage: createPromisifiedStorage(createInMemoryStorage()),
|
||||
localStorage: createPromisifiedStorage(),
|
||||
};
|
||||
await initLocalState(environment, context as any);
|
||||
await timeout();
|
||||
@@ -33,7 +30,7 @@ it("init local state", async () => {
|
||||
|
||||
it("set assetID from query", async () => {
|
||||
const context: Partial<TalkContext> = {
|
||||
pymLocalStorage: createPromisifiedStorage(createInMemoryStorage()),
|
||||
localStorage: createPromisifiedStorage(),
|
||||
};
|
||||
const assetID = "asset-id";
|
||||
const previousLocation = location.toString();
|
||||
@@ -50,7 +47,7 @@ it("set assetID from query", async () => {
|
||||
|
||||
it("set commentID from query", async () => {
|
||||
const context: Partial<TalkContext> = {
|
||||
pymLocalStorage: createPromisifiedStorage(createInMemoryStorage()),
|
||||
localStorage: createPromisifiedStorage(),
|
||||
};
|
||||
const commentID = "comment-id";
|
||||
const previousLocation = location.toString();
|
||||
@@ -67,10 +64,10 @@ it("set commentID from query", async () => {
|
||||
|
||||
it("set authToken from localStorage", async () => {
|
||||
const context: Partial<TalkContext> = {
|
||||
pymLocalStorage: createPromisifiedStorage(createInMemoryStorage()),
|
||||
localStorage: createPromisifiedStorage(),
|
||||
};
|
||||
const authToken = "auth-token";
|
||||
context.pymLocalStorage!.setItem("authToken", authToken);
|
||||
context.localStorage!.setItem("authToken", authToken);
|
||||
await initLocalState(environment, context as any);
|
||||
expect(source.get(LOCAL_ID)!.authToken).toBe(authToken);
|
||||
});
|
||||
|
||||
@@ -20,9 +20,9 @@ import {
|
||||
*/
|
||||
export default async function initLocalState(
|
||||
environment: Environment,
|
||||
{ pymLocalStorage }: TalkContext
|
||||
{ localStorage }: TalkContext
|
||||
) {
|
||||
const authToken = await pymLocalStorage!.getItem("authToken");
|
||||
const authToken = await localStorage!.getItem("authToken");
|
||||
|
||||
commitLocalUpdate(environment, s => {
|
||||
// TODO: (cvle) move local, auth token and network initialization to framework.
|
||||
|
||||
@@ -6,8 +6,7 @@ import { Environment, RecordProxy, RecordSourceProxy } from "relay-runtime";
|
||||
import { TalkContext, TalkContextProvider } from "talk-framework/lib/bootstrap";
|
||||
import { PostMessageService } from "talk-framework/lib/postMessage";
|
||||
import { RestClient } from "talk-framework/lib/rest";
|
||||
import { createInMemoryStorage } from "talk-framework/lib/storage";
|
||||
import { createFakePymStorage } from "talk-framework/testHelpers";
|
||||
import { createPromisifiedStorage } from "talk-framework/lib/storage";
|
||||
import AppContainer from "talk-stream/containers/AppContainer";
|
||||
|
||||
import createEnvironment from "./createEnvironment";
|
||||
@@ -40,10 +39,8 @@ export default function create(params: CreateParams) {
|
||||
const context: TalkContext = {
|
||||
relayEnvironment: environment,
|
||||
localeBundles: [createFluentBundle()],
|
||||
localStorage: createInMemoryStorage(),
|
||||
sessionStorage: createInMemoryStorage(),
|
||||
pymLocalStorage: createFakePymStorage(),
|
||||
pymSessionStorage: createFakePymStorage(),
|
||||
localStorage: createPromisifiedStorage(),
|
||||
sessionStorage: createPromisifiedStorage(),
|
||||
rest: new RestClient("http://localhost/api"),
|
||||
postMessage: new PostMessageService(),
|
||||
browserInfo: { ios: false },
|
||||
|
||||
Reference in New Issue
Block a user