mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-22 06:29:29 +01:00
test: improve overall test coverage
This commit is contained in:
3
packages/core/__e2e__/__snapshots__/offers.test.js.snap
Normal file
3
packages/core/__e2e__/__snapshots__/offers.test.js.snap
Normal file
@@ -0,0 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`get offer code: offer-code 1`] = `"123"`;
|
||||
77
packages/core/__e2e__/monographs.test.js
Normal file
77
packages/core/__e2e__/monographs.test.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import { databaseTest, noteTest, StorageInterface } from "../__tests__/utils";
|
||||
|
||||
const user = {
|
||||
email: process.env.EMAIL,
|
||||
password: process.env.PASSWORD,
|
||||
hashedPassword: process.env.HASHED_PASSWORD,
|
||||
};
|
||||
|
||||
jest.setTimeout(15 * 1000);
|
||||
|
||||
beforeEach(() => {
|
||||
StorageInterface.clear();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
const db = await databaseTest();
|
||||
await db.monographs.init();
|
||||
|
||||
for (const id of db.monographs.monographs) {
|
||||
await db.monographs.unpublish(id);
|
||||
}
|
||||
|
||||
StorageInterface.clear();
|
||||
});
|
||||
|
||||
// test("get monographs", () =>
|
||||
// databaseTest().then(async (db) => {
|
||||
// await db.user.login(user.email, user.password, user.hashedPassword);
|
||||
|
||||
// await db.monographs.init();
|
||||
|
||||
// expect(db.monographs.all).toBeGreaterThanOrEqual(0);
|
||||
// }));
|
||||
|
||||
test("publish a monograph", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await db.user.login(user.email, user.password, user.hashedPassword);
|
||||
await db.monographs.init();
|
||||
|
||||
const monographId = await db.monographs.publish(id);
|
||||
|
||||
expect(db.monographs.all.find((m) => m.id === id)).toBeDefined();
|
||||
|
||||
const monograph = await db.monographs.get(monographId);
|
||||
const note = db.notes.note(id);
|
||||
expect(monograph.id).toBe(monographId);
|
||||
expect(monograph.title).toBe(note.title);
|
||||
}));
|
||||
|
||||
test("update a published monograph", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await db.user.login(user.email, user.password, user.hashedPassword);
|
||||
await db.monographs.init();
|
||||
|
||||
const monographId = await db.monographs.publish(id);
|
||||
let monograph = await db.monographs.get(monographId);
|
||||
const note = db.notes.note(id);
|
||||
expect(monograph.title).toBe(note.title);
|
||||
|
||||
const editedTitle = "EDITED TITLE OF MY NOTE!";
|
||||
await db.notes.add({ id, title: editedTitle });
|
||||
await db.monographs.publish(id);
|
||||
monograph = await db.monographs.get(monographId);
|
||||
expect(monograph.title).toBe(editedTitle);
|
||||
}));
|
||||
|
||||
test("unpublish a monograph", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await db.user.login(user.email, user.password, user.hashedPassword);
|
||||
await db.monographs.init();
|
||||
|
||||
await db.monographs.publish(id);
|
||||
expect(db.monographs.all.find((m) => m.id === id)).toBeDefined();
|
||||
|
||||
await db.monographs.unpublish(id);
|
||||
expect(db.monographs.all.find((m) => m.id === id)).toBeUndefined();
|
||||
}));
|
||||
18
packages/core/__e2e__/offers.test.js
Normal file
18
packages/core/__e2e__/offers.test.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import hosts from "../utils/constants";
|
||||
import Offers from "../api/offers";
|
||||
|
||||
test("get offer code", async () => {
|
||||
const offers = new Offers();
|
||||
hosts.SUBSCRIPTIONS_HOST = "https://subscriptions.streetwriters.co";
|
||||
expect(await offers.getCode("TESTOFFER", "android")).toMatchSnapshot(
|
||||
"offer-code"
|
||||
);
|
||||
});
|
||||
|
||||
test("get invalid offer code", async () => {
|
||||
const offers = new Offers();
|
||||
hosts.SUBSCRIPTIONS_HOST = "https://subscriptions.streetwriters.co";
|
||||
await expect(() => offers.getCode("INVALIDOFFER", "android")).rejects.toThrow(
|
||||
/Not found/i
|
||||
);
|
||||
});
|
||||
@@ -7,18 +7,10 @@ const user = {
|
||||
hashedPassword: process.env.HASHED_PASSWORD,
|
||||
};
|
||||
|
||||
test.skip("refresh token concurrently", async () => {
|
||||
test("refresh token concurrently", async () => {
|
||||
const db = new DB(StorageInterface);
|
||||
await db.init();
|
||||
|
||||
db.host({
|
||||
API_HOST: "http://192.168.10.29:5264",
|
||||
AUTH_HOST: "http://192.168.10.29:8264",
|
||||
SSE_HOST: "http://192.168.10.29:7264",
|
||||
SUBSCRIPTIONS_HOST: "http://192.168.10.29:9264",
|
||||
ISSUES_HOST: "http://192.168.10.29:2624",
|
||||
});
|
||||
|
||||
await expect(
|
||||
db.user.login(user.email, user.password, user.hashedPassword)
|
||||
).resolves.not.toThrow();
|
||||
@@ -36,18 +28,10 @@ test.skip("refresh token concurrently", async () => {
|
||||
).toHaveLength(4);
|
||||
}, 30000);
|
||||
|
||||
test.skip("refresh token using the same refresh_token multiple time", async () => {
|
||||
test("refresh token using the same refresh_token multiple time", async () => {
|
||||
const db = new DB(StorageInterface);
|
||||
await db.init();
|
||||
|
||||
db.host({
|
||||
API_HOST: "http://192.168.10.29:5264",
|
||||
AUTH_HOST: "http://192.168.10.29:8264",
|
||||
SSE_HOST: "http://192.168.10.29:7264",
|
||||
SUBSCRIPTIONS_HOST: "http://192.168.10.29:9264",
|
||||
ISSUES_HOST: "http://192.168.10.29:2624",
|
||||
});
|
||||
|
||||
await expect(
|
||||
db.user.login(user.email, user.password, user.hashedPassword)
|
||||
).resolves.not.toThrow();
|
||||
|
||||
72
packages/core/__e2e__/user-manager.test.js
Normal file
72
packages/core/__e2e__/user-manager.test.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import { databaseTest } from "../__tests__/utils";
|
||||
|
||||
const user = {
|
||||
email: process.env.EMAIL,
|
||||
password: process.env.PASSWORD,
|
||||
hashed: process.env.HASHED_PASSWORD,
|
||||
};
|
||||
|
||||
// test("signup user and check for token", async () => {
|
||||
// const db = new DB(StorageInterface);
|
||||
// const usermanager = new UserManager(db);
|
||||
|
||||
// await expect(
|
||||
// usermanager.signup(user.email, user.password)
|
||||
// ).resolves.not.toThrow();
|
||||
|
||||
// await expect(usermanager.tokenManager.getToken()).resolves.toBeDefined();
|
||||
// }, 30000);
|
||||
|
||||
test(
|
||||
"login user and check for token",
|
||||
() =>
|
||||
databaseTest().then(async (db) => {
|
||||
await expect(
|
||||
db.user.login(user.email, user.password, user.hashed)
|
||||
).resolves.not.toThrow();
|
||||
|
||||
await expect(db.user.tokenManager.getToken()).resolves.toBeDefined();
|
||||
}),
|
||||
30000
|
||||
);
|
||||
|
||||
test(
|
||||
"login user and get user data",
|
||||
() =>
|
||||
databaseTest().then(async (db) => {
|
||||
await db.user.login(user.email, user.password, user.hashed);
|
||||
|
||||
const userData = await db.user.getUser();
|
||||
expect(userData.email).toBe(user.email);
|
||||
}),
|
||||
30000
|
||||
);
|
||||
|
||||
test(
|
||||
"login user and logout user",
|
||||
() =>
|
||||
databaseTest().then(async (db) => {
|
||||
await db.user.login(user.email, user.password, user.hashed);
|
||||
|
||||
await expect(db.user.logout()).resolves.not.toThrow();
|
||||
}),
|
||||
30000
|
||||
);
|
||||
|
||||
// test("login user and delete user", async () => {
|
||||
// const db = new DB(StorageInterface);
|
||||
// const usermanager = new UserManager(db);
|
||||
|
||||
// await usermanager.login(user.email, user.password, user.hashed);
|
||||
|
||||
// await expect(usermanager.deleteUser(user.password)).resolves.toBe(true);
|
||||
// }, 30000);
|
||||
|
||||
// test("login user and get user sessions", async () => {
|
||||
// const db = new DB(StorageInterface);
|
||||
// const usermanager = new UserManager(db);
|
||||
|
||||
// await usermanager.login(user.email, user.password);
|
||||
|
||||
// await usermanager.getSessions();
|
||||
// }, 30000);
|
||||
@@ -1,67 +0,0 @@
|
||||
import UserManager from "../api/user-manager";
|
||||
import DB from "../api";
|
||||
import StorageInterface from "../__mocks__/storage.mock";
|
||||
|
||||
const user = {
|
||||
email: process.env.EMAIL,
|
||||
password: process.env.PASSWORD,
|
||||
};
|
||||
|
||||
test.skip("signup user and check for token", async () => {
|
||||
const db = new DB(StorageInterface);
|
||||
const usermanager = new UserManager(db);
|
||||
|
||||
await expect(
|
||||
usermanager.signup(user.email, user.password)
|
||||
).resolves.not.toThrow();
|
||||
|
||||
await expect(usermanager.tokenManager.getToken()).resolves.toBeDefined();
|
||||
}, 30000);
|
||||
|
||||
test.skip("login user and check for token", async () => {
|
||||
const db = new DB(StorageInterface);
|
||||
const usermanager = new UserManager(db);
|
||||
|
||||
await expect(
|
||||
usermanager.login(user.email, user.password)
|
||||
).resolves.not.toThrow();
|
||||
|
||||
await expect(usermanager.tokenManager.getToken()).resolves.toBeDefined();
|
||||
}, 30000);
|
||||
|
||||
test.skip("login user and get user data", async () => {
|
||||
const db = new DB(StorageInterface);
|
||||
const usermanager = new UserManager(db);
|
||||
|
||||
await usermanager.login(user.email, user.password);
|
||||
|
||||
const userData = await usermanager.getUser();
|
||||
expect(userData.email).toBe(process.env.EMAIL);
|
||||
}, 30000);
|
||||
|
||||
test.skip("login user and logout user", async () => {
|
||||
const db = new DB(StorageInterface);
|
||||
const usermanager = new UserManager(db);
|
||||
|
||||
await usermanager.login(user.email, user.password);
|
||||
|
||||
await expect(usermanager.logout()).resolves.not.toThrow();
|
||||
}, 30000);
|
||||
|
||||
test.skip("login user and delete user", async () => {
|
||||
const db = new DB(StorageInterface);
|
||||
const usermanager = new UserManager(db);
|
||||
|
||||
await usermanager.login(user.email, user.password);
|
||||
|
||||
await expect(usermanager.deleteUser(user.password)).resolves.toBe(true);
|
||||
}, 30000);
|
||||
|
||||
test("login user and get user sessions", async () => {
|
||||
const db = new DB(StorageInterface);
|
||||
const usermanager = new UserManager(db);
|
||||
|
||||
await usermanager.login(user.email, user.password);
|
||||
|
||||
await usermanager.getSessions();
|
||||
}, 30000);
|
||||
@@ -1,4 +1,8 @@
|
||||
import { databaseTest } from "./utils";
|
||||
import { databaseTest, notebookTest, StorageInterface } from "./utils";
|
||||
|
||||
beforeEach(() => {
|
||||
StorageInterface.clear();
|
||||
});
|
||||
|
||||
test("settings' dateModified should not update on init", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
@@ -29,3 +33,75 @@ test("tag alias should update if aliases in settings update", () =>
|
||||
});
|
||||
expect(db.tags.tag(tag.id).alias).toBe("hello232");
|
||||
}));
|
||||
|
||||
test("save group options", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
const groupOptions = {
|
||||
groupBy: "abc",
|
||||
sortBy: "dateCreated",
|
||||
sortDirection: "asc",
|
||||
};
|
||||
await db.settings.setGroupOptions("home", groupOptions);
|
||||
expect(db.settings.getGroupOptions("home")).toMatchObject(groupOptions);
|
||||
}));
|
||||
|
||||
test("save toolbar config", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
const toolbarConfig = {
|
||||
preset: "custom",
|
||||
config: ["bold", "italic"],
|
||||
};
|
||||
await db.settings.setToolbarConfig("mobile", toolbarConfig);
|
||||
expect(db.settings.getToolbarConfig("mobile")).toMatchObject(toolbarConfig);
|
||||
}));
|
||||
|
||||
test("pinning an invalid item should throw", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
await expect(() => db.settings.pin("lolo", {})).rejects.toThrow(
|
||||
/item cannot be pinned/i
|
||||
);
|
||||
}));
|
||||
|
||||
test("pin a notebook", () =>
|
||||
notebookTest().then(async ({ db, id }) => {
|
||||
await db.settings.pin("notebook", { id });
|
||||
expect(db.settings.pins).toHaveLength(1);
|
||||
expect(db.settings.pins[0].id).toBe(id);
|
||||
}));
|
||||
|
||||
test("pin an already pinned notebook", () =>
|
||||
notebookTest().then(async ({ db, id }) => {
|
||||
await db.settings.pin("notebook", { id });
|
||||
await db.settings.pin("notebook", { id });
|
||||
|
||||
expect(db.settings.pins).toHaveLength(1);
|
||||
expect(db.settings.pins[0].id).toBe(id);
|
||||
}));
|
||||
|
||||
test("pin a topic", () =>
|
||||
notebookTest().then(async ({ db, id }) => {
|
||||
const notebook = db.notebooks.notebook(id)._notebook;
|
||||
const topic = notebook.topics[0];
|
||||
await db.settings.pin("topic", { id: topic.id, notebookId: id });
|
||||
expect(db.settings.pins).toHaveLength(1);
|
||||
expect(db.settings.pins[0].id).toBe(topic.id);
|
||||
}));
|
||||
|
||||
test("pin a tag", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
const tag = await db.tags.add("HELLO!");
|
||||
await db.settings.pin("tag", { id: tag.id });
|
||||
expect(db.settings.pins).toHaveLength(1);
|
||||
expect(db.settings.pins[0].id).toBe(tag.id);
|
||||
}));
|
||||
|
||||
test("unpin a pinned item", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
const tag = await db.tags.add("HELLO!");
|
||||
await db.settings.pin("tag", { id: tag.id });
|
||||
expect(db.settings.pins).toHaveLength(1);
|
||||
expect(db.settings.pins[0].id).toBe(tag.id);
|
||||
|
||||
await db.settings.unpin(tag.id);
|
||||
expect(db.settings.pins).toHaveLength(0);
|
||||
}));
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
import { enableFetchMocks } from "jest-fetch-mock";
|
||||
import { StorageInterface, databaseTest } from "./utils";
|
||||
|
||||
const SUCCESS_LOGIN_RESPONSE = {
|
||||
access_token: "access_token",
|
||||
refresh_token: "refresh_token",
|
||||
|
||||
scope: "sync",
|
||||
expires_in: 3600,
|
||||
};
|
||||
|
||||
const SUCCESS_USER_RESPONSE = {
|
||||
id: "0",
|
||||
email: process.env.EMAIL,
|
||||
salt: "",
|
||||
};
|
||||
|
||||
function mock(expiry = 3600) {
|
||||
fetch
|
||||
.mockResponseOnce(
|
||||
JSON.stringify({ ...SUCCESS_LOGIN_RESPONSE, expires_in: expiry }),
|
||||
{
|
||||
headers: { "Content-Type": "application/json" },
|
||||
}
|
||||
)
|
||||
.mockResponseOnce(JSON.stringify(SUCCESS_USER_RESPONSE), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
enableFetchMocks();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fetch.resetMocks();
|
||||
StorageInterface.clear();
|
||||
});
|
||||
|
||||
test("no user should be returned when not logged in", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
expect(await db.user.getUser()).toBeUndefined();
|
||||
}));
|
||||
|
||||
test("undefined user should not be set", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
await db.user.setUser();
|
||||
expect(await db.user.getUser()).toBeUndefined();
|
||||
}));
|
||||
|
||||
test("login user", async () =>
|
||||
databaseTest().then(async (db) => {
|
||||
mock();
|
||||
await db.user.login("myuser", "mylogin", true, "mylogin");
|
||||
const dbuser = await db.user.getUser();
|
||||
expect(dbuser.email).toBe(SUCCESS_USER_RESPONSE.email);
|
||||
expect(dbuser.id).toBe(SUCCESS_USER_RESPONSE.id);
|
||||
}));
|
||||
|
||||
test("login user with wrong password", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
fetch.mockResponseOnce(
|
||||
JSON.stringify({
|
||||
error_description: "Username or password is incorrect.",
|
||||
}),
|
||||
{ status: 400, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
await expect(
|
||||
db.user.login("myuser", "wrongpassword", true, "wrongpassword")
|
||||
).rejects.toThrow(/Username or password is incorrect./);
|
||||
}));
|
||||
|
||||
test("failed login with unknown error", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
fetch.mockResponseOnce(JSON.stringify({}), { status: 400 });
|
||||
await expect(
|
||||
db.user.login("myuser", "wrongpassword", true, "wrongpassword")
|
||||
).rejects.toThrow(/Request failed with status code: /);
|
||||
}));
|
||||
|
||||
test("signup user", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
fetch.mockResponseOnce(undefined, { status: 200 });
|
||||
mock();
|
||||
await db.user.signup(SUCCESS_USER_RESPONSE.email, "password");
|
||||
const dbuser = await db.user.getUser();
|
||||
expect(dbuser.email).toBe(SUCCESS_USER_RESPONSE.email);
|
||||
}));
|
||||
|
||||
test("logout user", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
mock();
|
||||
await db.user.login("myuser", "mylogin", true, "mylogin");
|
||||
const dbuser = await db.user.getUser();
|
||||
expect(dbuser.email).toBe(SUCCESS_USER_RESPONSE.email);
|
||||
await db.user.logout();
|
||||
expect(await db.user.getUser()).toBeUndefined();
|
||||
}));
|
||||
|
||||
test("refresh user's token", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
mock();
|
||||
await db.user.login(
|
||||
SUCCESS_USER_RESPONSE.email,
|
||||
"mylogin",
|
||||
true,
|
||||
"mylogin"
|
||||
);
|
||||
const token = await db.user.tokenManager.getToken();
|
||||
await db.user.tokenManager.saveToken({ ...token, expires_in: -2000 });
|
||||
fetch.mockResponseOnce(
|
||||
JSON.stringify({
|
||||
...SUCCESS_LOGIN_RESPONSE,
|
||||
access_token: "new_token",
|
||||
refresh_token: "new_refresh_token",
|
||||
expires_in: 3600,
|
||||
}),
|
||||
{
|
||||
headers: { "Content-Type": "application/json" },
|
||||
}
|
||||
);
|
||||
const { access_token, refresh_token } =
|
||||
await db.user.tokenManager.getToken();
|
||||
expect(refresh_token).toBe("new_refresh_token");
|
||||
expect(access_token).toBe("new_token");
|
||||
}));
|
||||
|
||||
test("refresh user's token when its not expired", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
mock();
|
||||
await db.user.login(
|
||||
SUCCESS_USER_RESPONSE.email,
|
||||
"mylogin",
|
||||
true,
|
||||
"mylogin"
|
||||
);
|
||||
expect(await db.user.tokenManager.getAccessToken()).toBe("access_token");
|
||||
const dbuser = await db.user.getUser();
|
||||
expect(dbuser.email).toBe(SUCCESS_USER_RESPONSE.email);
|
||||
}));
|
||||
|
||||
test("refresh token for non existent user should do nothing", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
fetch.mockResponseOnce(JSON.stringify(SUCCESS_LOGIN_RESPONSE));
|
||||
expect(await db.user.tokenManager.getAccessToken()).toBeUndefined();
|
||||
const dbuser = await db.user.getUser();
|
||||
expect(dbuser).toBeUndefined();
|
||||
}));
|
||||
13
packages/core/api/__tests__/__snapshots__/debug.test.js.snap
Normal file
13
packages/core/api/__tests__/__snapshots__/debug.test.js.snap
Normal file
@@ -0,0 +1,13 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`strip note with content: stripped-note-with-content 1`] = `"{\\"title\\":true,\\"description\\":false,\\"headline\\":true,\\"colored\\":false,\\"type\\":\\"note\\",\\"tags\\":[],\\"id\\":\\"hello\\",\\"contentId\\":\\"hello2\\",\\"dateModified\\":123,\\"dateEdited\\":123,\\"dateCreated\\":123,\\"additionalData\\":{\\"content\\":\\"{\\\\\\"title\\\\\\":false,\\\\\\"description\\\\\\":false,\\\\\\"headline\\\\\\":false,\\\\\\"colored\\\\\\":false,\\\\\\"type\\\\\\":\\\\\\"tiptap\\\\\\",\\\\\\"id\\\\\\":\\\\\\"hello\\\\\\",\\\\\\"dateModified\\\\\\":123,\\\\\\"dateEdited\\\\\\":123,\\\\\\"dateCreated\\\\\\":123}\\"}}"`;
|
||||
|
||||
exports[`strip note: stripped-note 1`] = `"{\\"title\\":true,\\"description\\":false,\\"headline\\":true,\\"colored\\":false,\\"type\\":\\"note\\",\\"tags\\":[],\\"id\\":\\"hello\\",\\"contentId\\":\\"hello2\\",\\"dateModified\\":123,\\"dateEdited\\":123,\\"dateCreated\\":123}"`;
|
||||
|
||||
exports[`strip notebook: stripped-notebook 1`] = `"{\\"title\\":true,\\"description\\":true,\\"headline\\":false,\\"colored\\":false,\\"type\\":\\"notebook\\",\\"id\\":\\"hello\\",\\"dateModified\\":123,\\"dateEdited\\":123,\\"dateCreated\\":123,\\"additionalData\\":[{\\"type\\":\\"topic\\",\\"id\\":\\"hello\\",\\"notebookId\\":\\"hello23\\",\\"title\\":\\"hello\\",\\"dateCreated\\":123,\\"dateEdited\\":123,\\"notes\\":[],\\"dateModified\\":123}]}"`;
|
||||
|
||||
exports[`strip tag: stripped-tag 1`] = `"{\\"title\\":true,\\"description\\":false,\\"headline\\":false,\\"colored\\":false,\\"type\\":\\"tag\\",\\"noteIds\\":[],\\"id\\":\\"hello\\",\\"dateModified\\":123,\\"dateEdited\\":123,\\"dateCreated\\":123}"`;
|
||||
|
||||
exports[`strip topic: stripped-topic 1`] = `"{\\"title\\":true,\\"description\\":false,\\"headline\\":false,\\"colored\\":false,\\"type\\":\\"topic\\",\\"notes\\":[],\\"id\\":\\"hello\\",\\"dateModified\\":123,\\"dateEdited\\":123,\\"dateCreated\\":123}"`;
|
||||
|
||||
exports[`strip trashed note: stripped-trashed-note 1`] = `"{\\"title\\":true,\\"description\\":false,\\"headline\\":true,\\"colored\\":false,\\"type\\":\\"trash\\",\\"tags\\":[],\\"id\\":\\"hello\\",\\"contentId\\":\\"hello2\\",\\"dateModified\\":123,\\"dateEdited\\":123,\\"dateDeleted\\":123,\\"dateCreated\\":123}"`;
|
||||
@@ -0,0 +1,82 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`get android pricing tier get monthly android tier: monthly-android-pricing 1`] = `
|
||||
Object {
|
||||
"country": "Pakistan",
|
||||
"countryCode": "PK",
|
||||
"discount": 78,
|
||||
"sku": "com.streetwriters.notesnook.sub.mo.tier3",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`get android pricing tier get yearly android tier: yearly-android-pricing 1`] = `
|
||||
Object {
|
||||
"country": "Pakistan",
|
||||
"countryCode": "PK",
|
||||
"discount": 80,
|
||||
"sku": "com.streetwriters.notesnook.sub.yr.tier3",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`get ios pricing tier get monthly ios tier: monthly-ios-pricing 1`] = `
|
||||
Object {
|
||||
"country": "Pakistan",
|
||||
"countryCode": "PK",
|
||||
"discount": 78,
|
||||
"sku": "com.streetwriters.notesnook.sub.mo.tier3",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`get ios pricing tier get yearly ios tier: yearly-ios-pricing 1`] = `
|
||||
Object {
|
||||
"country": "Pakistan",
|
||||
"countryCode": "PK",
|
||||
"discount": 80,
|
||||
"sku": "com.streetwriters.notesnook.sub.yr.tier3",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`get monthly price: monthly-pricing 1`] = `
|
||||
Object {
|
||||
"country": "Pakistan",
|
||||
"countryCode": "PK",
|
||||
"discount": 78,
|
||||
"price": 0.99,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`get undefined price: monthly-pricing 1`] = `
|
||||
Object {
|
||||
"country": "Pakistan",
|
||||
"countryCode": "PK",
|
||||
"discount": 78,
|
||||
"price": 0.99,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`get web pricing tier get monthly web tier: monthly-web-pricing 1`] = `
|
||||
Object {
|
||||
"country": "Pakistan",
|
||||
"countryCode": "PK",
|
||||
"discount": 78,
|
||||
"sku": "763943",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`get web pricing tier get yearly web tier: yearly-web-pricing 1`] = `
|
||||
Object {
|
||||
"country": "Pakistan",
|
||||
"countryCode": "PK",
|
||||
"discount": 80,
|
||||
"sku": "763945",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`get yearly price: yearly-pricing 1`] = `
|
||||
Object {
|
||||
"country": "Pakistan",
|
||||
"countryCode": "PK",
|
||||
"discount": 80,
|
||||
"price": 9.99,
|
||||
}
|
||||
`;
|
||||
125
packages/core/api/__tests__/debug.test.js
Normal file
125
packages/core/api/__tests__/debug.test.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import Debug from "../debug";
|
||||
import { noteTest, notebookTest, databaseTest } from "../../__tests__/utils";
|
||||
import { enableFetchMocks, disableFetchMocks } from "jest-fetch-mock";
|
||||
|
||||
test("strip empty item shouldn't throw", () => {
|
||||
const debug = new Debug();
|
||||
expect(debug.strip()).toBe("{}");
|
||||
});
|
||||
|
||||
test("strip note", () =>
|
||||
noteTest().then(({ db, id }) => {
|
||||
const note = db.notes.note(id)._note;
|
||||
const debug = new Debug();
|
||||
expect(debug.strip(normalizeItem(note))).toMatchSnapshot("stripped-note");
|
||||
}));
|
||||
|
||||
test("strip trashed note", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await db.notes.delete(id);
|
||||
const note = db.trash.all[0];
|
||||
const debug = new Debug();
|
||||
expect(debug.strip(normalizeItem(note))).toMatchSnapshot(
|
||||
"stripped-trashed-note"
|
||||
);
|
||||
}));
|
||||
|
||||
test("strip note with content", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
const note = db.notes.note(id)._note;
|
||||
const debug = new Debug();
|
||||
|
||||
const content = await db.content.raw(note.contentId);
|
||||
note.additionalData = {
|
||||
content: db.debug.strip(normalizeItem(content)),
|
||||
};
|
||||
|
||||
expect(debug.strip(normalizeItem(note))).toMatchSnapshot(
|
||||
"stripped-note-with-content"
|
||||
);
|
||||
}));
|
||||
|
||||
test("strip notebook", () =>
|
||||
notebookTest().then(async ({ db, id }) => {
|
||||
const notebook = db.notebooks.notebook(id)._notebook;
|
||||
const debug = new Debug();
|
||||
notebook.additionalData = notebook.topics.map((topic) =>
|
||||
normalizeItem(topic)
|
||||
);
|
||||
expect(debug.strip(normalizeItem(notebook))).toMatchSnapshot(
|
||||
"stripped-notebook"
|
||||
);
|
||||
}));
|
||||
|
||||
test("strip topic", () =>
|
||||
notebookTest().then(async ({ db, id }) => {
|
||||
const notebook = db.notebooks.notebook(id)._notebook;
|
||||
const debug = new Debug();
|
||||
expect(debug.strip(normalizeItem(notebook.topics[0]))).toMatchSnapshot(
|
||||
"stripped-topic"
|
||||
);
|
||||
}));
|
||||
|
||||
test("strip tag", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
const tag = await db.tags.add("Hello tag");
|
||||
const debug = new Debug();
|
||||
expect(debug.strip(normalizeItem(tag))).toMatchSnapshot("stripped-tag");
|
||||
}));
|
||||
|
||||
test("reporting empty issue should return undefined", async () => {
|
||||
const debug = new Debug();
|
||||
expect(await debug.report()).toBeUndefined();
|
||||
});
|
||||
|
||||
const SUCCESS_REPORT_RESPONSE = {
|
||||
url: "https://reported/",
|
||||
};
|
||||
|
||||
test("reporting issue should return issue url", async () => {
|
||||
enableFetchMocks();
|
||||
|
||||
const debug = new Debug();
|
||||
|
||||
fetch.mockResponseOnce(JSON.stringify(SUCCESS_REPORT_RESPONSE), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
expect(
|
||||
await debug.report({
|
||||
title: "I am title",
|
||||
body: "I am body",
|
||||
userId: "anything",
|
||||
})
|
||||
).toBe(SUCCESS_REPORT_RESPONSE.url);
|
||||
|
||||
disableFetchMocks();
|
||||
});
|
||||
|
||||
test("reporting invalid issue should return undefined", async () => {
|
||||
enableFetchMocks();
|
||||
|
||||
const debug = new Debug();
|
||||
|
||||
fetch.mockResponseOnce(
|
||||
JSON.stringify({
|
||||
error_description: "Invalid issue.",
|
||||
}),
|
||||
{ status: 400, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
|
||||
expect(await debug.report({})).toBeUndefined();
|
||||
|
||||
disableFetchMocks();
|
||||
});
|
||||
|
||||
function normalizeItem(item) {
|
||||
item.id = "hello";
|
||||
item.notebookId = "hello23";
|
||||
item.dateModified = 123;
|
||||
item.dateEdited = 123;
|
||||
item.dateCreated = 123;
|
||||
if (item.dateDeleted) item.dateDeleted = 123;
|
||||
if (item.contentId) item.contentId = "hello2";
|
||||
return item;
|
||||
}
|
||||
1
packages/core/api/__tests__/mfa-manager.test.js
Normal file
1
packages/core/api/__tests__/mfa-manager.test.js
Normal file
@@ -0,0 +1 @@
|
||||
test.todo("skipped test");
|
||||
1
packages/core/api/__tests__/migrations.test.js
Normal file
1
packages/core/api/__tests__/migrations.test.js
Normal file
@@ -0,0 +1 @@
|
||||
test.todo("skipped test");
|
||||
1
packages/core/api/__tests__/outbox.test.js
Normal file
1
packages/core/api/__tests__/outbox.test.js
Normal file
@@ -0,0 +1 @@
|
||||
test.todo("skipped test");
|
||||
18
packages/core/api/__tests__/pricing.test.js
Normal file
18
packages/core/api/__tests__/pricing.test.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import Pricing from "../pricing";
|
||||
|
||||
test.each(["monthly", "yearly", undefined])(`get %s price`, async (period) => {
|
||||
const pricing = new Pricing();
|
||||
const price = await pricing.price(period);
|
||||
expect(price).toMatchSnapshot(`${period || "monthly"}-pricing`);
|
||||
});
|
||||
|
||||
describe.each(["android", "ios", "web"])(`get %s pricing tier`, (platform) => {
|
||||
test.each(["monthly", "yearly"])(
|
||||
`get %s ${platform} tier`,
|
||||
async (period) => {
|
||||
const pricing = new Pricing();
|
||||
const price = await pricing.sku(platform, period);
|
||||
expect(price).toMatchSnapshot(`${period}-${platform}-pricing`);
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import hosts from "../utils/constants";
|
||||
|
||||
export default class Debug {
|
||||
strip(item) {
|
||||
if (!item) return "{}";
|
||||
return JSON.stringify({
|
||||
title: !!item.title,
|
||||
description: !!item.description,
|
||||
@@ -9,6 +10,8 @@ export default class Debug {
|
||||
colored: !!item.color,
|
||||
type: item.type,
|
||||
notebooks: item.notebooks,
|
||||
notes: item.notes,
|
||||
noteIds: item.noteIds,
|
||||
tags: item.tags,
|
||||
id: item.id,
|
||||
contentId: item.contentId,
|
||||
|
||||
@@ -8,11 +8,11 @@ class Monographs {
|
||||
*/
|
||||
constructor(db) {
|
||||
this._db = db;
|
||||
this.monographs = [];
|
||||
this.monographs = undefined;
|
||||
}
|
||||
|
||||
async deinit() {
|
||||
this.monographs = [];
|
||||
this.monographs = undefined;
|
||||
await this._db.storage.write("monographs", this.monographs);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class Monographs {
|
||||
* @returns {boolean} Whether note is published or not.
|
||||
*/
|
||||
isPublished(noteId) {
|
||||
return this.monographs.indexOf(noteId) > -1;
|
||||
return this.monographs && this.monographs.indexOf(noteId) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,6 +46,8 @@ class Monographs {
|
||||
* @returns Monograph Id
|
||||
*/
|
||||
monograph(noteId) {
|
||||
if (!this.monographs) return;
|
||||
|
||||
return this.monographs[this.monographs.indexOf(noteId)];
|
||||
}
|
||||
|
||||
@@ -55,7 +57,7 @@ class Monographs {
|
||||
* @param {{password: string, selfDestruct: boolean}} opts Publish options
|
||||
* @returns
|
||||
*/
|
||||
async publish(noteId, opts) {
|
||||
async publish(noteId, opts = { password: undefined, selfDestruct: false }) {
|
||||
if (!this.monographs) await this.init();
|
||||
|
||||
let update = !!this.isPublished(noteId);
|
||||
@@ -116,8 +118,8 @@ class Monographs {
|
||||
const token = await this._db.user.tokenManager.getAccessToken();
|
||||
if (!user || !token) throw new Error("Please login to publish a note.");
|
||||
|
||||
const note = this._db.notes.note(noteId);
|
||||
if (!note) throw new Error("No such note found.");
|
||||
// const note = this._db.notes.note(noteId);
|
||||
// if (!note) throw new Error("No such note found.");
|
||||
|
||||
if (!this.isPublished(noteId))
|
||||
throw new Error("This note is not published.");
|
||||
@@ -128,9 +130,15 @@ class Monographs {
|
||||
}
|
||||
|
||||
get all() {
|
||||
if (!this.monographs) return [];
|
||||
|
||||
return this._db.notes.all.filter(
|
||||
(note) => this.monographs.indexOf(note.id) > -1
|
||||
);
|
||||
}
|
||||
|
||||
async get(monographId) {
|
||||
return await http.get(`${Constants.API_HOST}/monographs/${monographId}`);
|
||||
}
|
||||
}
|
||||
export default Monographs;
|
||||
|
||||
@@ -27,7 +27,7 @@ class Pricing {
|
||||
* discount: number
|
||||
* }>}
|
||||
*/
|
||||
price(period) {
|
||||
price(period = "monthly") {
|
||||
return http.get(`${BASE_URL}/${period}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ class Settings {
|
||||
}
|
||||
|
||||
isPinned(id) {
|
||||
return this._settings.pins.findIndex((v) => v.data.id === id) > -1;
|
||||
return !!this._settings.pins.find((v) => v.data.id === id);
|
||||
}
|
||||
|
||||
get pins() {
|
||||
|
||||
@@ -1,421 +0,0 @@
|
||||
import Database from "../../index";
|
||||
import { NodeStorageInterface } from "../../../__mocks__/node-storage.mock";
|
||||
import FS from "../../../__mocks__/fs.mock";
|
||||
import { CHECK_IDS, EV, EVENTS } from "../../../common";
|
||||
import EventSource from "eventsource";
|
||||
import { delay } from "../../../__tests__/utils";
|
||||
|
||||
test.skip(
|
||||
"case 1: device A & B should only download the changes from device C (no uploading)",
|
||||
async () => {
|
||||
const types = [];
|
||||
function onSyncProgress({ type }) {
|
||||
types.push(type);
|
||||
}
|
||||
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
deviceA.eventManager.subscribe(EVENTS.syncProgress, onSyncProgress);
|
||||
deviceB.eventManager.subscribe(EVENTS.syncProgress, onSyncProgress);
|
||||
|
||||
const deviceC = await initializeDevice("deviceC");
|
||||
|
||||
await deviceC.notes.add({ title: "new note 1" });
|
||||
await syncAndWait(deviceC, deviceC);
|
||||
|
||||
expect(types.every((t) => t === "download")).toBe(true);
|
||||
|
||||
await cleanup(deviceA, deviceB, deviceC);
|
||||
},
|
||||
600 * 1000
|
||||
);
|
||||
|
||||
test.skip(
|
||||
"case 3: Device A & B have unsynced changes but server has nothing",
|
||||
async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const note1Id = await deviceA.notes.add({
|
||||
title: "Test note from device A",
|
||||
});
|
||||
const note2Id = await deviceB.notes.add({
|
||||
title: "Test note from device B",
|
||||
});
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
expect(deviceA.notes.note(note2Id)).toBeTruthy();
|
||||
expect(deviceB.notes.note(note1Id)).toBeTruthy();
|
||||
expect(deviceA.notes.note(note1Id)).toBeTruthy();
|
||||
expect(deviceB.notes.note(note2Id)).toBeTruthy();
|
||||
|
||||
await cleanup(deviceA, deviceA);
|
||||
},
|
||||
30 * 1000
|
||||
);
|
||||
|
||||
// test.skip(
|
||||
// "case 4: Device A's sync is interrupted halfway and Device B makes some changes afterwards and syncs.",
|
||||
// async () => {
|
||||
// const deviceA = await initializeDevice("deviceA");
|
||||
// const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
// const unsyncedNoteIds = [];
|
||||
// for (let i = 0; i < 10; ++i) {
|
||||
// const id = await deviceA.notes.add({
|
||||
// title: `Test note ${i} from device A`,
|
||||
// });
|
||||
// unsyncedNoteIds.push(id);
|
||||
// }
|
||||
|
||||
// const half = unsyncedNoteIds.length / 2 + 1;
|
||||
// deviceA.eventManager.subscribe(
|
||||
// EVENTS.syncProgress,
|
||||
// async ({ type, current }) => {
|
||||
// if (type === "upload" && current === half) {
|
||||
// await deviceA.syncer.stop();
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
|
||||
// await expect(deviceA.sync(true)).rejects.toThrow();
|
||||
|
||||
// let syncedNoteIds = [];
|
||||
// for (let i = 0; i < unsyncedNoteIds.length; ++i) {
|
||||
// const expectedNoteId = unsyncedNoteIds[i];
|
||||
// if (deviceB.notes.note(expectedNoteId))
|
||||
// syncedNoteIds.push(expectedNoteId);
|
||||
// }
|
||||
// expect(
|
||||
// syncedNoteIds.length === half - 1 || syncedNoteIds.length === half
|
||||
// ).toBe(true);
|
||||
|
||||
// const deviceBNoteId = await deviceB.notes.add({
|
||||
// title: "Test note of case 4 from device B",
|
||||
// });
|
||||
|
||||
// await deviceB.sync(true);
|
||||
|
||||
// await syncAndWait(deviceA, deviceB);
|
||||
|
||||
// expect(deviceA.notes.note(deviceBNoteId)).toBeTruthy();
|
||||
// expect(
|
||||
// unsyncedNoteIds
|
||||
// .map((id) => !!deviceB.notes.note(id))
|
||||
// .every((res) => res === true)
|
||||
// ).toBe(true);
|
||||
|
||||
// await cleanup(deviceA, deviceB);
|
||||
// },
|
||||
// 60 * 1000
|
||||
// );
|
||||
|
||||
// test.only(
|
||||
// "case 5: Device A's sync is interrupted halfway and Device B makes changes on the same note's content that didn't get synced on Device A due to interruption.",
|
||||
// async () => {
|
||||
// const deviceA = await initializeDevice("deviceA");
|
||||
// const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
// const noteIds = [];
|
||||
// for (let i = 0; i < 10; ++i) {
|
||||
// const id = await deviceA.notes.add({
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: `<p>deviceA=true</p>`,
|
||||
// },
|
||||
// });
|
||||
// noteIds.push(id);
|
||||
// }
|
||||
|
||||
// await deviceA.sync(true);
|
||||
// await deviceB.sync(true);
|
||||
|
||||
// const unsyncedNoteIds = [];
|
||||
// for (let id of noteIds) {
|
||||
// const noteId = await deviceA.notes.add({
|
||||
// id,
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: `<p>deviceA=true+changed=true</p>`,
|
||||
// },
|
||||
// });
|
||||
// unsyncedNoteIds.push(noteId);
|
||||
// }
|
||||
|
||||
// deviceA.eventManager.subscribe(
|
||||
// EVENTS.syncProgress,
|
||||
// async ({ type, total, current }) => {
|
||||
// const half = total / 2 + 1;
|
||||
// if (type === "upload" && current === half) {
|
||||
// await deviceA.syncer.stop();
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
|
||||
// await expect(deviceA.sync(true)).rejects.toThrow();
|
||||
|
||||
// await delay(10 * 1000);
|
||||
|
||||
// for (let id of unsyncedNoteIds) {
|
||||
// await deviceB.notes.add({
|
||||
// id,
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: "<p>changes from device B</p>",
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
// const error = await withError(async () => {
|
||||
// await deviceB.sync(true);
|
||||
// await deviceA.sync(true);
|
||||
// });
|
||||
|
||||
// expect(error).not.toBeInstanceOf(NoErrorThrownError);
|
||||
// expect(error.message.includes("Merge")).toBeTruthy();
|
||||
|
||||
// await cleanup(deviceA, deviceB);
|
||||
// },
|
||||
// 60 * 1000
|
||||
// );
|
||||
|
||||
test.skip(
|
||||
"issue: running force sync from device A makes device B always download everything",
|
||||
async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
await syncAndWait(deviceA, deviceB, true);
|
||||
|
||||
const handler = jest.fn();
|
||||
deviceB.eventManager.subscribe(EVENTS.syncProgress, handler);
|
||||
|
||||
await deviceB.sync(true);
|
||||
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
|
||||
await cleanup(deviceB);
|
||||
},
|
||||
60 * 1000
|
||||
);
|
||||
|
||||
test.skip(
|
||||
"issue: colors are not properly created if multiple notes are synced together",
|
||||
async () => {
|
||||
const deviceA = await initializeDevice("deviceA", [CHECK_IDS.noteColor]);
|
||||
const deviceB = await initializeDevice("deviceB", [CHECK_IDS.noteColor]);
|
||||
|
||||
const noteIds = [];
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
const id = await deviceA.notes.add({
|
||||
content: {
|
||||
type: "tiptap",
|
||||
data: `<p>deviceA=true</p>`,
|
||||
},
|
||||
});
|
||||
noteIds.push(id);
|
||||
}
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
for (let noteId of noteIds) {
|
||||
await deviceA.notes.note(noteId).color("purple");
|
||||
expect(deviceB.notes.note(noteId)).toBeTruthy();
|
||||
expect(deviceB.notes.note(noteId).data.color).toBeUndefined();
|
||||
}
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
await delay(2000);
|
||||
|
||||
const purpleColor = deviceB.colors.tag("purple");
|
||||
console.log(noteIds, purpleColor.noteIds);
|
||||
expect(noteIds.every((id) => purpleColor.noteIds.indexOf(id) > -1)).toBe(
|
||||
true
|
||||
);
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
},
|
||||
60 * 1000
|
||||
);
|
||||
|
||||
test.skip(
|
||||
"issue: new topic on device A gets replaced by the new topic on device B",
|
||||
async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const id = await deviceA.notebooks.add({ title: "Notebook 1" });
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
expect(deviceB.notebooks.notebook(id)).toBeDefined();
|
||||
|
||||
await deviceA.notebooks.notebook(id).topics.add("Topic 1");
|
||||
|
||||
// to create a conflict
|
||||
await delay(1500);
|
||||
|
||||
await deviceB.notebooks.notebook(id).topics.add("Topic 2");
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
await delay(1000);
|
||||
|
||||
await syncAndWait(deviceB, deviceB, false);
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
},
|
||||
60 * 1000
|
||||
);
|
||||
|
||||
test.skip(
|
||||
"issue: remove notebook reference from notes that are removed from topic during merge",
|
||||
async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const id = await deviceA.notebooks.add({
|
||||
title: "Notebook 1",
|
||||
topics: ["Topic 1"],
|
||||
});
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
expect(deviceB.notebooks.notebook(id)).toBeDefined();
|
||||
|
||||
const noteA = await deviceA.notes.add({ title: "Note 1" });
|
||||
await deviceA.notes.move({ id, topic: "Topic 1" }, noteA);
|
||||
|
||||
expect(
|
||||
deviceA.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
await delay(2000);
|
||||
|
||||
const noteB = await deviceB.notes.add({ title: "Note 2" });
|
||||
await deviceB.notes.move({ id, topic: "Topic 1" }, noteB);
|
||||
|
||||
expect(
|
||||
deviceB.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
await syncAndWait(deviceB, deviceA, false);
|
||||
|
||||
expect(
|
||||
deviceA.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
expect(
|
||||
deviceB.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
expect(deviceA.notes.note(noteA).data.notebooks).toHaveLength(0);
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
},
|
||||
60 * 1000
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @returns {Promise<Database>}
|
||||
*/
|
||||
async function initializeDevice(id, capabilities = []) {
|
||||
EV.subscribe(EVENTS.userCheckStatus, async (type) => {
|
||||
return {
|
||||
type,
|
||||
result: capabilities.indexOf(type) > -1,
|
||||
};
|
||||
});
|
||||
|
||||
const device = new Database(new NodeStorageInterface(), EventSource, FS);
|
||||
device.host({
|
||||
API_HOST: "http://192.168.10.29:5264",
|
||||
AUTH_HOST: "http://192.168.10.29:8264",
|
||||
SSE_HOST: "http://192.168.10.29:7264",
|
||||
ISSUES_HOST: "http://192.168.10.29:2624",
|
||||
SUBSCRIPTIONS_HOST: "http://192.168.10.29:9264",
|
||||
});
|
||||
|
||||
await device.init(id);
|
||||
await device.user.login("enkaboot@gmail.com", process.env.PASSWORD);
|
||||
await device.user.resetUser(false);
|
||||
|
||||
device.eventManager.subscribe(
|
||||
EVENTS.databaseSyncRequested,
|
||||
async (full, force) => {
|
||||
await device.sync(full, force);
|
||||
}
|
||||
);
|
||||
|
||||
await syncAndWait(device, device);
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
async function cleanup(...devices) {
|
||||
for (let device of devices) {
|
||||
await device.user.logout();
|
||||
device.eventManager.unsubscribeAll();
|
||||
}
|
||||
EV.unsubscribeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Database} device
|
||||
* @returns
|
||||
*/
|
||||
// function waitForSyncCompleted(device) {
|
||||
// return new Promise((resolve) =>
|
||||
// device.eventManager.subscribe(EVENTS.syncCompleted, () => {
|
||||
// resolve();
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Database} deviceA
|
||||
* @param {Database} deviceB
|
||||
* @returns
|
||||
*/
|
||||
function syncAndWait(deviceA, deviceB, force = false) {
|
||||
return new Promise((resolve) => {
|
||||
const ref = deviceB.eventManager.subscribe(EVENTS.syncCompleted, () => {
|
||||
ref.unsubscribe();
|
||||
resolve();
|
||||
});
|
||||
deviceA.sync(true, force);
|
||||
});
|
||||
}
|
||||
|
||||
class NoErrorThrownError extends Error {}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Function} call
|
||||
* @returns {Promise<Error>}
|
||||
*/
|
||||
async function withError(call) {
|
||||
try {
|
||||
await call();
|
||||
|
||||
throw new NoErrorThrownError();
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
375
packages/core/api/sync/__tests__/sync.test.skip.js
Normal file
375
packages/core/api/sync/__tests__/sync.test.skip.js
Normal file
@@ -0,0 +1,375 @@
|
||||
import Database from "../../index";
|
||||
import { NodeStorageInterface } from "../../../__mocks__/node-storage.mock";
|
||||
import FS from "../../../__mocks__/fs.mock";
|
||||
import { CHECK_IDS, EV, EVENTS } from "../../../common";
|
||||
import EventSource from "eventsource";
|
||||
import { delay } from "../../../__tests__/utils";
|
||||
|
||||
jest.setTimeout(100 * 1000);
|
||||
|
||||
test("case 1: device A & B should only download the changes from device C (no uploading)", async () => {
|
||||
const types = [];
|
||||
function onSyncProgress({ type }) {
|
||||
types.push(type);
|
||||
}
|
||||
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
deviceA.eventManager.subscribe(EVENTS.syncProgress, onSyncProgress);
|
||||
deviceB.eventManager.subscribe(EVENTS.syncProgress, onSyncProgress);
|
||||
|
||||
const deviceC = await initializeDevice("deviceC");
|
||||
|
||||
await deviceC.notes.add({ title: "new note 1" });
|
||||
await syncAndWait(deviceC, deviceC);
|
||||
|
||||
expect(types.every((t) => t === "download")).toBe(true);
|
||||
|
||||
await cleanup(deviceA, deviceB, deviceC);
|
||||
});
|
||||
|
||||
test("case 3: Device A & B have unsynced changes but server has nothing", async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const note1Id = await deviceA.notes.add({
|
||||
title: "Test note from device A",
|
||||
});
|
||||
const note2Id = await deviceB.notes.add({
|
||||
title: "Test note from device B",
|
||||
});
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
expect(deviceA.notes.note(note2Id)).toBeTruthy();
|
||||
expect(deviceB.notes.note(note1Id)).toBeTruthy();
|
||||
expect(deviceA.notes.note(note1Id)).toBeTruthy();
|
||||
expect(deviceB.notes.note(note2Id)).toBeTruthy();
|
||||
|
||||
await cleanup(deviceA, deviceA);
|
||||
});
|
||||
|
||||
// test(
|
||||
// "case 4: Device A's sync is interrupted halfway and Device B makes some changes afterwards and syncs.",
|
||||
// async () => {
|
||||
// const deviceA = await initializeDevice("deviceA");
|
||||
// const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
// const unsyncedNoteIds = [];
|
||||
// for (let i = 0; i < 10; ++i) {
|
||||
// const id = await deviceA.notes.add({
|
||||
// title: `Test note ${i} from device A`,
|
||||
// });
|
||||
// unsyncedNoteIds.push(id);
|
||||
// }
|
||||
|
||||
// const half = unsyncedNoteIds.length / 2 + 1;
|
||||
// deviceA.eventManager.subscribe(
|
||||
// EVENTS.syncProgress,
|
||||
// async ({ type, current }) => {
|
||||
// if (type === "upload" && current === half) {
|
||||
// await deviceA.syncer.stop();
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
|
||||
// await expect(deviceA.sync(true)).rejects.toThrow();
|
||||
|
||||
// let syncedNoteIds = [];
|
||||
// for (let i = 0; i < unsyncedNoteIds.length; ++i) {
|
||||
// const expectedNoteId = unsyncedNoteIds[i];
|
||||
// if (deviceB.notes.note(expectedNoteId))
|
||||
// syncedNoteIds.push(expectedNoteId);
|
||||
// }
|
||||
// expect(
|
||||
// syncedNoteIds.length === half - 1 || syncedNoteIds.length === half
|
||||
// ).toBe(true);
|
||||
|
||||
// const deviceBNoteId = await deviceB.notes.add({
|
||||
// title: "Test note of case 4 from device B",
|
||||
// });
|
||||
|
||||
// await deviceB.sync(true);
|
||||
|
||||
// await syncAndWait(deviceA, deviceB);
|
||||
|
||||
// expect(deviceA.notes.note(deviceBNoteId)).toBeTruthy();
|
||||
// expect(
|
||||
// unsyncedNoteIds
|
||||
// .map((id) => !!deviceB.notes.note(id))
|
||||
// .every((res) => res === true)
|
||||
// ).toBe(true);
|
||||
|
||||
// await cleanup(deviceA, deviceB);
|
||||
// },
|
||||
//
|
||||
// );
|
||||
|
||||
// test.only(
|
||||
// "case 5: Device A's sync is interrupted halfway and Device B makes changes on the same note's content that didn't get synced on Device A due to interruption.",
|
||||
// async () => {
|
||||
// const deviceA = await initializeDevice("deviceA");
|
||||
// const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
// const noteIds = [];
|
||||
// for (let i = 0; i < 10; ++i) {
|
||||
// const id = await deviceA.notes.add({
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: `<p>deviceA=true</p>`,
|
||||
// },
|
||||
// });
|
||||
// noteIds.push(id);
|
||||
// }
|
||||
|
||||
// await deviceA.sync(true);
|
||||
// await deviceB.sync(true);
|
||||
|
||||
// const unsyncedNoteIds = [];
|
||||
// for (let id of noteIds) {
|
||||
// const noteId = await deviceA.notes.add({
|
||||
// id,
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: `<p>deviceA=true+changed=true</p>`,
|
||||
// },
|
||||
// });
|
||||
// unsyncedNoteIds.push(noteId);
|
||||
// }
|
||||
|
||||
// deviceA.eventManager.subscribe(
|
||||
// EVENTS.syncProgress,
|
||||
// async ({ type, total, current }) => {
|
||||
// const half = total / 2 + 1;
|
||||
// if (type === "upload" && current === half) {
|
||||
// await deviceA.syncer.stop();
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
|
||||
// await expect(deviceA.sync(true)).rejects.toThrow();
|
||||
|
||||
// await delay(10 * 1000);
|
||||
|
||||
// for (let id of unsyncedNoteIds) {
|
||||
// await deviceB.notes.add({
|
||||
// id,
|
||||
// content: {
|
||||
// type: "tiptap",
|
||||
// data: "<p>changes from device B</p>",
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
// const error = await withError(async () => {
|
||||
// await deviceB.sync(true);
|
||||
// await deviceA.sync(true);
|
||||
// });
|
||||
|
||||
// expect(error).not.toBeInstanceOf(NoErrorThrownError);
|
||||
// expect(error.message.includes("Merge")).toBeTruthy();
|
||||
|
||||
// await cleanup(deviceA, deviceB);
|
||||
// },
|
||||
//
|
||||
// );
|
||||
|
||||
test("issue: running force sync from device A makes device B always download everything", async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
await syncAndWait(deviceA, deviceB, true);
|
||||
|
||||
const handler = jest.fn();
|
||||
deviceB.eventManager.subscribe(EVENTS.syncProgress, handler);
|
||||
|
||||
await deviceB.sync(true);
|
||||
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
|
||||
await cleanup(deviceB);
|
||||
});
|
||||
|
||||
test("issue: colors are not properly created if multiple notes are synced together", async () => {
|
||||
const deviceA = await initializeDevice("deviceA", [CHECK_IDS.noteColor]);
|
||||
const deviceB = await initializeDevice("deviceB", [CHECK_IDS.noteColor]);
|
||||
|
||||
const noteIds = [];
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
const id = await deviceA.notes.add({
|
||||
content: {
|
||||
type: "tiptap",
|
||||
data: `<p>deviceA=true</p>`,
|
||||
},
|
||||
});
|
||||
noteIds.push(id);
|
||||
}
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
for (let noteId of noteIds) {
|
||||
await deviceA.notes.note(noteId).color("purple");
|
||||
expect(deviceB.notes.note(noteId)).toBeTruthy();
|
||||
expect(deviceB.notes.note(noteId).data.color).toBeUndefined();
|
||||
}
|
||||
|
||||
await syncAndWait(deviceA, deviceB);
|
||||
|
||||
await delay(2000);
|
||||
|
||||
const purpleColor = deviceB.colors.tag("purple");
|
||||
console.log(noteIds, purpleColor.noteIds);
|
||||
expect(noteIds.every((id) => purpleColor.noteIds.indexOf(id) > -1)).toBe(
|
||||
true
|
||||
);
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
});
|
||||
|
||||
test("issue: new topic on device A gets replaced by the new topic on device B", async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const id = await deviceA.notebooks.add({ title: "Notebook 1" });
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
expect(deviceB.notebooks.notebook(id)).toBeDefined();
|
||||
|
||||
await deviceA.notebooks.notebook(id).topics.add("Topic 1");
|
||||
|
||||
// to create a conflict
|
||||
await delay(1500);
|
||||
|
||||
await deviceB.notebooks.notebook(id).topics.add("Topic 2");
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
await delay(1000);
|
||||
|
||||
await syncAndWait(deviceB, deviceB, false);
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
|
||||
|
||||
expect(deviceA.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
expect(deviceB.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
});
|
||||
|
||||
test("issue: remove notebook reference from notes that are removed from topic during merge", async () => {
|
||||
const deviceA = await initializeDevice("deviceA");
|
||||
const deviceB = await initializeDevice("deviceB");
|
||||
|
||||
const id = await deviceA.notebooks.add({
|
||||
title: "Notebook 1",
|
||||
topics: ["Topic 1"],
|
||||
});
|
||||
|
||||
await syncAndWait(deviceA, deviceB, false);
|
||||
|
||||
expect(deviceB.notebooks.notebook(id)).toBeDefined();
|
||||
|
||||
const noteA = await deviceA.notes.add({ title: "Note 1" });
|
||||
await deviceA.notes.move({ id, topic: "Topic 1" }, noteA);
|
||||
|
||||
expect(
|
||||
deviceA.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
await delay(2000);
|
||||
|
||||
const noteB = await deviceB.notes.add({ title: "Note 2" });
|
||||
await deviceB.notes.move({ id, topic: "Topic 1" }, noteB);
|
||||
|
||||
expect(
|
||||
deviceB.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
await syncAndWait(deviceB, deviceA, false);
|
||||
|
||||
expect(
|
||||
deviceA.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
expect(
|
||||
deviceB.notebooks.notebook(id).topics.topic("Topic 1").totalNotes
|
||||
).toBe(1);
|
||||
|
||||
expect(deviceA.notes.note(noteA).data.notebooks).toHaveLength(0);
|
||||
|
||||
await cleanup(deviceA, deviceB);
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @returns {Promise<Database>}
|
||||
*/
|
||||
async function initializeDevice(id, capabilities = []) {
|
||||
console.time("Init device");
|
||||
EV.subscribe(EVENTS.userCheckStatus, async (type) => {
|
||||
return {
|
||||
type,
|
||||
result: capabilities.indexOf(type) > -1,
|
||||
};
|
||||
});
|
||||
|
||||
const device = new Database(new NodeStorageInterface(), EventSource, FS);
|
||||
// device.host({
|
||||
// API_HOST: "http://192.168.10.29:5264",
|
||||
// AUTH_HOST: "http://192.168.10.29:8264",
|
||||
// SSE_HOST: "http://192.168.10.29:7264",
|
||||
// ISSUES_HOST: "http://192.168.10.29:2624",
|
||||
// SUBSCRIPTIONS_HOST: "http://192.168.10.29:9264",
|
||||
// });
|
||||
|
||||
await device.init(id);
|
||||
|
||||
await device.user.login(
|
||||
process.env.EMAIL,
|
||||
process.env.PASSWORD,
|
||||
process.env.HASHED_PASSWORD
|
||||
);
|
||||
|
||||
await device.user.resetUser(false);
|
||||
|
||||
device.eventManager.subscribe(
|
||||
EVENTS.databaseSyncRequested,
|
||||
async (full, force) => {
|
||||
await device.sync(full, force);
|
||||
}
|
||||
);
|
||||
|
||||
console.timeEnd("Init device");
|
||||
return device;
|
||||
}
|
||||
|
||||
async function cleanup(...devices) {
|
||||
for (let device of devices) {
|
||||
await device.user.logout();
|
||||
device.eventManager.unsubscribeAll();
|
||||
}
|
||||
EV.unsubscribeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Database} deviceA
|
||||
* @param {Database} deviceB
|
||||
* @returns
|
||||
*/
|
||||
function syncAndWait(deviceA, deviceB, force = false) {
|
||||
return new Promise((resolve) => {
|
||||
const ref = deviceB.eventManager.subscribe(EVENTS.syncCompleted, () => {
|
||||
ref.unsubscribe();
|
||||
resolve();
|
||||
});
|
||||
deviceA.sync(true, force);
|
||||
});
|
||||
}
|
||||
@@ -100,19 +100,5 @@ class Collector {
|
||||
...(await this._serialize(item)),
|
||||
};
|
||||
}
|
||||
|
||||
filter(data, predicate) {
|
||||
const arrays = ["notes", "notebooks", "content", "attachments", "settings"];
|
||||
const newData = {};
|
||||
for (let array of arrays) {
|
||||
if (!data[array]) continue;
|
||||
this.logger.info(`Filtering from ${data[array].length} ${array}`);
|
||||
newData[array] = data[array].filter(predicate);
|
||||
this.logger.info(
|
||||
`Length after filtering ${array}: ${newData[array].length}`
|
||||
);
|
||||
}
|
||||
return newData;
|
||||
}
|
||||
}
|
||||
export default Collector;
|
||||
|
||||
@@ -2,7 +2,7 @@ import "../types";
|
||||
import http from "../utils/http";
|
||||
import constants from "../utils/constants";
|
||||
import TokenManager from "./token-manager";
|
||||
import { EV, EVENTS, setUserPersonalizationBytes } from "../common";
|
||||
import { EV, EVENTS } from "../common";
|
||||
|
||||
const ENDPOINTS = {
|
||||
signup: "/users",
|
||||
@@ -44,7 +44,6 @@ class UserManager {
|
||||
async init() {
|
||||
const user = await this.getUser();
|
||||
if (!user) return;
|
||||
setUserPersonalizationBytes(user.salt);
|
||||
}
|
||||
|
||||
async signup(email, password) {
|
||||
@@ -58,8 +57,8 @@ class UserManager {
|
||||
return await this._login({ email, password, hashedPassword });
|
||||
}
|
||||
|
||||
async login(email, password) {
|
||||
return this._login({ email, password });
|
||||
async login(email, password, hashedPassword = undefined) {
|
||||
return this._login({ email, password, hashedPassword });
|
||||
}
|
||||
|
||||
async mfaLogin(email, password, { code, method }) {
|
||||
@@ -87,7 +86,6 @@ class UserManager {
|
||||
);
|
||||
|
||||
const user = await this.fetchUser();
|
||||
setUserPersonalizationBytes(user.salt);
|
||||
await this._storage.deriveCryptoKey(`_uk_@${user.email}`, {
|
||||
password,
|
||||
salt: user.salt,
|
||||
|
||||
@@ -65,15 +65,3 @@ export const EVENTS = {
|
||||
};
|
||||
|
||||
export const CURRENT_DATABASE_VERSION = 5.6;
|
||||
|
||||
export function setUserPersonalizationBytes(userSalt) {
|
||||
USER_PERSONALIZATION_HASH = new Uint8Array(
|
||||
Buffer.from(userSalt, "base64")
|
||||
).slice(0, 8);
|
||||
if (
|
||||
!USER_PERSONALIZATION_HASH.length ||
|
||||
!USER_PERSONALIZATION_HASH.byteLength
|
||||
)
|
||||
USER_PERSONALIZATION_HASH = undefined;
|
||||
}
|
||||
export var USER_PERSONALIZATION_HASH = null;
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
import { extractHostname } from "./hostname";
|
||||
|
||||
function isProduction() {
|
||||
return (
|
||||
process.env.NODE_ENV === "production" || process.env.NODE_ENV === "test"
|
||||
);
|
||||
}
|
||||
|
||||
const hosts = {
|
||||
API_HOST:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "https://api.notesnook.com"
|
||||
: "http://localhost:5264",
|
||||
AUTH_HOST:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "https://auth.streetwriters.co"
|
||||
: "http://localhost:8264",
|
||||
SSE_HOST:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "https://events.streetwriters.co"
|
||||
: "http://localhost:7264",
|
||||
SUBSCRIPTIONS_HOST:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "https://subscriptions.streetwriters.co"
|
||||
: "http://localhost:9264",
|
||||
ISSUES_HOST:
|
||||
process.env.NODE_ENV === "production"
|
||||
? "https://issues.streetwriters.co"
|
||||
: "http://localhost:2624",
|
||||
API_HOST: isProduction()
|
||||
? "https://api.notesnook.com"
|
||||
: "http://localhost:5264",
|
||||
AUTH_HOST: isProduction()
|
||||
? "https://auth.streetwriters.co"
|
||||
: "http://localhost:8264",
|
||||
SSE_HOST: isProduction()
|
||||
? "https://events.streetwriters.co"
|
||||
: "http://localhost:7264",
|
||||
SUBSCRIPTIONS_HOST: isProduction()
|
||||
? "https://subscriptions.streetwriters.co"
|
||||
: "http://localhost:9264",
|
||||
ISSUES_HOST: isProduction()
|
||||
? "https://issues.streetwriters.co"
|
||||
: "http://localhost:2624",
|
||||
};
|
||||
|
||||
export default hosts;
|
||||
|
||||
Reference in New Issue
Block a user