mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 19:49:54 +02:00
core: make all the core tests pass again
This commit is contained in:
committed by
Abdullah Atta
parent
5d2c6e0509
commit
f77a95ca2b
@@ -24,6 +24,12 @@ export class NodeStorageInterface implements IStorage {
|
||||
storage = {};
|
||||
crypto = new NNCrypto();
|
||||
|
||||
async removeMulti(keys: string[]): Promise<void> {
|
||||
for (const key of keys) {
|
||||
this.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
async write<T>(key: string, data: T): Promise<void> {
|
||||
this.storage[key] = data;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ test("search notes", () =>
|
||||
content: content
|
||||
}).then(async ({ db }) => {
|
||||
await db.notes.add(TEST_NOTE);
|
||||
let filtered = await db.lookup.notes("note the world");
|
||||
let filtered = await db.lookup.notes("note of the world").ids();
|
||||
expect(filtered).toHaveLength(1);
|
||||
}));
|
||||
|
||||
@@ -42,8 +42,8 @@ test("search notes with a locked note", () =>
|
||||
const noteId = await db.notes.add(TEST_NOTE);
|
||||
await db.vault.create("password");
|
||||
await db.vault.add(noteId);
|
||||
expect(await db.lookup.notes("note the world")).toHaveLength(1);
|
||||
expect(await db.lookup.notes("format")).toHaveLength(0);
|
||||
expect(await db.lookup.notes("note of the world").ids()).toHaveLength(1);
|
||||
expect(await db.lookup.notes("format").ids()).toHaveLength(0);
|
||||
}));
|
||||
|
||||
test("search notes with an empty note", () =>
|
||||
@@ -54,16 +54,13 @@ test("search notes with an empty note", () =>
|
||||
title: "world is a heavy tune",
|
||||
content: { type: "tiptap", data: "<p><br></p>" }
|
||||
});
|
||||
let filtered = await db.lookup.notes("heavy tune");
|
||||
let filtered = await db.lookup.notes("heavy tune").ids();
|
||||
expect(filtered).toHaveLength(1);
|
||||
}));
|
||||
|
||||
test("search notebooks", () =>
|
||||
notebookTest().then(async ({ db }) => {
|
||||
await db.notebooks.add(TEST_NOTEBOOK2);
|
||||
let filtered = db.lookup.notebooks(
|
||||
await db.notebooks.all.items(),
|
||||
"Description"
|
||||
);
|
||||
let filtered = await db.lookup.notebooks("Description").ids();
|
||||
expect(filtered.length).toBeGreaterThan(0);
|
||||
}));
|
||||
|
||||
@@ -171,7 +171,7 @@ test("[5.6] move pins to shortcuts", () =>
|
||||
expect(await migrateItem(item, 5.6, 5.7, "settings", db, "local")).toBe(
|
||||
true
|
||||
);
|
||||
const shortcuts = await db.shortcuts.all.items();
|
||||
const shortcuts = db.shortcuts.all;
|
||||
expect(item.pins).toBeUndefined();
|
||||
expect(
|
||||
shortcuts.find((s) => s.itemType === "notebook" && s.itemId === "hello")
|
||||
@@ -546,7 +546,7 @@ test("[5.9] move attachments.noteIds to relations", () =>
|
||||
.get();
|
||||
expect(attachment.noteIds).toBeUndefined();
|
||||
expect(linkedNotes).toHaveLength(1);
|
||||
expect(linkedNotes[0]).toBe("HELLO_NOTE_ID");
|
||||
expect(linkedNotes[0].toId).toBe("HELLO_NOTE_ID");
|
||||
}));
|
||||
|
||||
test.todo("[5.9] flatten attachment object", () =>
|
||||
@@ -584,8 +584,8 @@ describe("[5.9] move topics out of notebooks & use relations", () => {
|
||||
.get();
|
||||
expect(notebook.topics).toBeUndefined();
|
||||
expect(linkedNotebooks).toHaveLength(2);
|
||||
expect(linkedNotebooks.some((a) => a === "topics1")).toBeTruthy();
|
||||
expect(linkedNotebooks.some((a) => a === "topics2")).toBeTruthy();
|
||||
expect(linkedNotebooks.some((a) => a.toId === "topics1")).toBeTruthy();
|
||||
expect(linkedNotebooks.some((a) => a.toId === "topics2")).toBeTruthy();
|
||||
expect(await db.notebooks.all.count()).toBe(2);
|
||||
expect(await db.notebooks.notebook("topics1")).toBeDefined();
|
||||
expect(await db.notebooks.notebook("topics2")).toBeDefined();
|
||||
@@ -621,8 +621,8 @@ describe("[5.9] move topics out of notebooks & use relations", () => {
|
||||
.get();
|
||||
expect(note.notebooks).toBeUndefined();
|
||||
expect(linkedNotebooks).toHaveLength(2);
|
||||
expect(linkedNotebooks.some((a) => a === "topic1")).toBeTruthy();
|
||||
expect(linkedNotebooks.some((a) => a === "topic2")).toBeTruthy();
|
||||
expect(linkedNotebooks.some((a) => a.fromId === "topic1")).toBeTruthy();
|
||||
expect(linkedNotebooks.some((a) => a.fromId === "topic2")).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ import { test, expect } from "vitest";
|
||||
|
||||
test("new history session should be automatically created on note save", () =>
|
||||
noteTest({ ...TEST_NOTE, sessionId: Date.now() }).then(async ({ db, id }) => {
|
||||
const sessions = await db.noteHistory.get(id);
|
||||
const sessions = await db.noteHistory.get(id).items();
|
||||
expect(sessions).toHaveLength(1);
|
||||
await expect(db.noteHistory.content(sessions[0].id)).resolves.toMatchObject(
|
||||
TEST_NOTE.content
|
||||
@@ -53,7 +53,9 @@ test("editing the same note should create multiple history sessions", () =>
|
||||
sessionId: Date.now() + 10000
|
||||
});
|
||||
|
||||
const sessions = await db.noteHistory.get(id);
|
||||
const sessions = await db.noteHistory
|
||||
.get(id)
|
||||
.items(undefined, { sortBy: "dateModified", sortDirection: "desc" });
|
||||
expect(sessions).toHaveLength(2);
|
||||
|
||||
await expect(db.noteHistory.content(sessions[0].id)).resolves.toMatchObject(
|
||||
@@ -77,7 +79,9 @@ test("restoring an old session should replace note's content", () =>
|
||||
sessionId: Date.now() + 10000
|
||||
});
|
||||
|
||||
const [, firstVersion] = await db.noteHistory.get(id);
|
||||
const [, firstVersion] = await db.noteHistory
|
||||
.get(id)
|
||||
.items(undefined, { sortBy: "dateModified", sortDirection: "desc" });
|
||||
await db.noteHistory.restore(firstVersion.id);
|
||||
|
||||
const contentId = (await db.notes.note(id)).contentId;
|
||||
@@ -86,7 +90,9 @@ test("restoring an old session should replace note's content", () =>
|
||||
|
||||
test("date created of session should not change on edit", () =>
|
||||
noteTest({ ...TEST_NOTE, sessionId: "session" }).then(async ({ db, id }) => {
|
||||
const [{ dateCreated, dateModified }] = await db.noteHistory.get(id);
|
||||
const [{ dateCreated, dateModified }] = await db.noteHistory
|
||||
.get(id)
|
||||
.items(undefined, { sortBy: "dateModified", sortDirection: "desc" });
|
||||
|
||||
let editedContent = {
|
||||
data: TEST_NOTE.content.data + "<p>Some new content</p>",
|
||||
@@ -102,7 +108,9 @@ test("date created of session should not change on edit", () =>
|
||||
});
|
||||
|
||||
const [{ dateCreated: newDateCreated, dateModified: newDateModified }] =
|
||||
await db.noteHistory.get(id);
|
||||
await db.noteHistory
|
||||
.get(id)
|
||||
.items(undefined, { sortBy: "dateModified", sortDirection: "desc" });
|
||||
expect(newDateCreated).toBe(dateCreated);
|
||||
expect(newDateModified).toBeGreaterThan(dateModified);
|
||||
}));
|
||||
@@ -110,26 +118,22 @@ test("date created of session should not change on edit", () =>
|
||||
test("clear a note's sessions", () =>
|
||||
noteTest({ ...TEST_NOTE, sessionId: "session" }).then(async ({ db, id }) => {
|
||||
await db.noteHistory.clearSessions(id);
|
||||
|
||||
let history = await db.noteHistory.get(id);
|
||||
expect(history).toHaveLength(0);
|
||||
expect(await db.noteHistory.get(id).count()).toBe(0);
|
||||
}));
|
||||
|
||||
test("remove a single session by sessionId", () =>
|
||||
noteTest({ ...TEST_NOTE, sessionId: "iamasession" }).then(
|
||||
async ({ db, id }) => {
|
||||
const [{ id: sessionId }] = await db.noteHistory.get(id);
|
||||
const [{ id: sessionId }] = await db.noteHistory.get(id).items();
|
||||
|
||||
await db.noteHistory.remove(sessionId);
|
||||
let history = await db.noteHistory.get(sessionId);
|
||||
expect(history).toHaveLength(0);
|
||||
expect(await db.noteHistory.get(sessionId).count()).toBe(0);
|
||||
}
|
||||
));
|
||||
|
||||
test("return empty array if no history available", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
let history = await db.noteHistory.get(id);
|
||||
expect(history).toHaveLength(0);
|
||||
expect(await db.noteHistory.get(id).count()).toBe(0);
|
||||
}));
|
||||
|
||||
test("auto clear sessions if they exceed the limit", () =>
|
||||
@@ -142,15 +146,14 @@ test("auto clear sessions if they exceed the limit", () =>
|
||||
await db.notes.add({
|
||||
id: id,
|
||||
content: editedContent,
|
||||
sessionId: Date.now() + 10000
|
||||
sessionId: `${Date.now() + 10000}`
|
||||
});
|
||||
|
||||
let sessions = await db.noteHistory.get(id);
|
||||
expect(sessions).toHaveLength(2);
|
||||
expect(await db.noteHistory.get(id).count()).toBe(2);
|
||||
|
||||
await db.noteHistory.cleanup(id, 1);
|
||||
|
||||
sessions = await db.noteHistory.get(id);
|
||||
const sessions = await db.noteHistory.get(id).items();
|
||||
expect(sessions).toHaveLength(1);
|
||||
|
||||
const content = await db.noteHistory.content(sessions[0].id);
|
||||
@@ -170,7 +173,7 @@ test("save a locked note should add a locked session to note history", () =>
|
||||
sessionId: "lockedsession"
|
||||
});
|
||||
|
||||
const sessions = await db.noteHistory.get(id);
|
||||
const sessions = await db.noteHistory.get(id).items();
|
||||
expect(sessions).toHaveLength(1);
|
||||
|
||||
const lockedContent = await db.noteHistory.content(sessions[0].id);
|
||||
@@ -187,7 +190,6 @@ test("locking an old note should clear its history", () =>
|
||||
await db.vault.create("password");
|
||||
await db.vault.add(id);
|
||||
|
||||
const sessions = await db.noteHistory.get(id);
|
||||
expect(sessions).toHaveLength(0);
|
||||
expect(await db.noteHistory.get(id).count()).toBe(0);
|
||||
}
|
||||
));
|
||||
|
||||
@@ -399,15 +399,12 @@ test("get grouped notes by abc", () =>
|
||||
sortBy: "title"
|
||||
});
|
||||
|
||||
expect((await grouping.item(grouping.ids[0]))?.group?.title).toBe(
|
||||
"Conflicted"
|
||||
);
|
||||
expect((await grouping.item(grouping.ids[1]))?.group?.title).toBe("Pinned");
|
||||
expect((await grouping.item(0))?.group?.title).toBe("Conflicted");
|
||||
expect((await grouping.item(1))?.group?.title).toBe("Pinned");
|
||||
for (let i = 0; i < alphabet.length; ++i) {
|
||||
expect(
|
||||
(await grouping.item(grouping.ids[i * alphabet.length + 2]))?.group
|
||||
?.title
|
||||
).toBe(alphabet[i]);
|
||||
expect((await grouping.item(i * alphabet.length + 2))?.group?.title).toBe(
|
||||
alphabet[i]
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -430,9 +427,9 @@ test("get grouped notes by month", () =>
|
||||
});
|
||||
|
||||
for (let month = 11; month >= 0; --month) {
|
||||
expect(
|
||||
(await grouping.item(grouping.ids[(11 - month) * 5]))?.group?.title
|
||||
).toBe(MONTHS_FULL[month]);
|
||||
expect((await grouping.item((11 - month) * 5))?.group?.title).toContain(
|
||||
MONTHS_FULL[month]
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -455,9 +452,9 @@ test("get grouped notes by year", () =>
|
||||
});
|
||||
|
||||
for (let year = 2020; year <= 2025; ++year) {
|
||||
expect(
|
||||
(await grouping.item(grouping.ids[(2025 - year) * 5]))?.group?.title
|
||||
).toBe(year.toString());
|
||||
expect((await grouping.item((2025 - year) * 5))?.group?.title).toBe(
|
||||
year.toString()
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -493,9 +490,7 @@ test("get grouped notes by week", () =>
|
||||
"20 - 26 Feb, 2023"
|
||||
];
|
||||
for (let i = 1; i <= 5; ++i) {
|
||||
expect((await grouping.item(grouping.ids[i * 4]))?.group?.title).toBe(
|
||||
weeks[i - 1]
|
||||
);
|
||||
expect((await grouping.item(i * 4))?.group?.title).toBe(weeks[i - 1]);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -526,9 +521,7 @@ test("get grouped notes default", () =>
|
||||
|
||||
let i = 0;
|
||||
for (const key in ranges) {
|
||||
expect((await grouping.item(grouping.ids[i * 7]))?.group?.title).toBe(
|
||||
key
|
||||
);
|
||||
expect((await grouping.item(i * 7))?.group?.title).toBe(key);
|
||||
++i;
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -30,8 +30,8 @@ test("create a shortcut of an invalid item should throw", () =>
|
||||
test("create a shortcut of notebook", () =>
|
||||
notebookTest().then(async ({ db, id }) => {
|
||||
await db.shortcuts.add({ itemType: "notebook", itemId: id });
|
||||
expect(await db.shortcuts.exists(id)).toBe(true);
|
||||
expect(await db.shortcuts.all.has(id)).toBe(true);
|
||||
expect(db.shortcuts.exists(id)).toBe(true);
|
||||
expect(db.shortcuts.all.find((s) => s.id === id)).toBeDefined();
|
||||
}));
|
||||
|
||||
test("create a duplicate shortcut of notebook", () =>
|
||||
@@ -39,8 +39,8 @@ test("create a duplicate shortcut of notebook", () =>
|
||||
await db.shortcuts.add({ itemType: "notebook", itemId: id });
|
||||
await db.shortcuts.add({ itemType: "notebook", itemId: id });
|
||||
|
||||
expect(await db.shortcuts.all.count()).toBe(1);
|
||||
expect(await db.shortcuts.all.has(id)).toBe(true);
|
||||
expect(db.shortcuts.all).toHaveLength(1);
|
||||
expect(db.shortcuts.all.find((s) => s.id === id)).toBeDefined();
|
||||
}));
|
||||
|
||||
test("pin a tag", () =>
|
||||
@@ -48,8 +48,8 @@ test("pin a tag", () =>
|
||||
const tagId = await db.tags.add({ title: "HELLO!" });
|
||||
await db.shortcuts.add({ itemType: "tag", itemId: tagId });
|
||||
|
||||
expect(await db.shortcuts.all.count()).toBe(1);
|
||||
expect(await db.shortcuts.all.has(tagId)).toBe(true);
|
||||
expect(db.shortcuts.all).toHaveLength(1);
|
||||
expect(db.shortcuts.all.find((s) => s.id === tagId)).toBeDefined();
|
||||
}));
|
||||
|
||||
test("remove shortcut", () =>
|
||||
@@ -60,8 +60,8 @@ test("remove shortcut", () =>
|
||||
itemId: tagId
|
||||
});
|
||||
|
||||
expect(await db.shortcuts.all.count()).toBe(1);
|
||||
expect(db.shortcuts.all).toHaveLength(1);
|
||||
|
||||
await db.shortcuts.remove(shortcutId);
|
||||
expect(await db.shortcuts.all.count()).toBe(0);
|
||||
expect(db.shortcuts.all).toHaveLength(0);
|
||||
}));
|
||||
|
||||
@@ -41,19 +41,17 @@ test("permanently delete a note", () =>
|
||||
const note = await db.notes.note(noteId);
|
||||
if (!note) throw new Error("Could not find note.");
|
||||
|
||||
let sessions = await db.noteHistory.get(noteId);
|
||||
expect(sessions).toHaveLength(1);
|
||||
expect(await db.noteHistory.get(noteId).count()).toBe(1);
|
||||
|
||||
await db.notes.moveToTrash(noteId);
|
||||
|
||||
expect(await db.trash.all()).toHaveLength(1);
|
||||
expect(await db.content.get(note.contentId)).toBeDefined();
|
||||
await db.trash.delete({ id: noteId, type: "note" });
|
||||
await db.trash.delete(noteId);
|
||||
expect(await db.trash.all()).toHaveLength(0);
|
||||
expect(await db.content.get(note.contentId)).toBeUndefined();
|
||||
|
||||
sessions = await db.noteHistory.get(noteId);
|
||||
expect(sessions).toHaveLength(0);
|
||||
expect(await db.noteHistory.get(noteId).count()).toBe(0);
|
||||
}));
|
||||
|
||||
test("restore a deleted note that was in a notebook", () =>
|
||||
@@ -68,7 +66,7 @@ test("restore a deleted note that was in a notebook", () =>
|
||||
await db.notes.addToNotebook(subNotebookId, id);
|
||||
|
||||
await db.notes.moveToTrash(id);
|
||||
await db.trash.restore({ type: "note", id });
|
||||
await db.trash.restore(id);
|
||||
expect(await db.trash.all()).toHaveLength(0);
|
||||
|
||||
const note = await db.notes.note(id);
|
||||
@@ -102,7 +100,7 @@ test("restore a deleted locked note", () =>
|
||||
await db.notes.moveToTrash(id);
|
||||
expect(await db.trash.all()).toHaveLength(1);
|
||||
expect(await db.content.get(note.contentId)).toBeDefined();
|
||||
await db.trash.restore({ type: "note", id });
|
||||
await db.trash.restore(id);
|
||||
|
||||
note = await db.notes.note(id);
|
||||
expect(await db.trash.all()).toHaveLength(0);
|
||||
@@ -117,7 +115,7 @@ test("restore a deleted note that's in a deleted notebook", () =>
|
||||
await db.notes.moveToTrash(id);
|
||||
await db.notebooks.moveToTrash(notebookId);
|
||||
|
||||
await db.trash.restore({ type: "note", id });
|
||||
await db.trash.restore(id);
|
||||
const note = await db.notes.note(id);
|
||||
expect(note).toBeDefined();
|
||||
expect(
|
||||
@@ -144,7 +142,7 @@ test("restore a deleted notebook", () =>
|
||||
await db.notes.addToNotebook(id, noteId);
|
||||
|
||||
await db.notebooks.moveToTrash(id);
|
||||
await db.trash.restore({ type: "notebook", id });
|
||||
await db.trash.restore(id);
|
||||
|
||||
const notebook = db.notebooks.notebook(id);
|
||||
expect(notebook).toBeDefined();
|
||||
@@ -164,7 +162,7 @@ test("restore a notebook that has deleted notes", () =>
|
||||
|
||||
await db.notebooks.moveToTrash(id);
|
||||
await db.notes.moveToTrash(noteId);
|
||||
await db.trash.restore({ type: "notebook", id });
|
||||
await db.trash.restore(id);
|
||||
|
||||
const notebook = db.notebooks.notebook(id);
|
||||
expect(notebook).toBeDefined();
|
||||
@@ -227,8 +225,7 @@ test("clear trash should delete note content", () =>
|
||||
|
||||
const notebookId = await db.notebooks.add(TEST_NOTEBOOK);
|
||||
|
||||
let sessions = await db.noteHistory.get(noteId);
|
||||
expect(sessions).toHaveLength(1);
|
||||
expect(await db.noteHistory.get(noteId).count()).toBe(1);
|
||||
|
||||
const note = { ...(await db.notes.note(noteId)) };
|
||||
|
||||
@@ -244,6 +241,5 @@ test("clear trash should delete note content", () =>
|
||||
const content = note.contentId && (await db.content.get(note.contentId));
|
||||
expect(content).toBeUndefined();
|
||||
|
||||
sessions = await db.noteHistory.get(noteId);
|
||||
expect(sessions).toHaveLength(0);
|
||||
expect(await db.noteHistory.get(noteId).count()).toBe(0);
|
||||
}));
|
||||
|
||||
@@ -49,8 +49,10 @@ function databaseTest() {
|
||||
fs: FS,
|
||||
compressor: Compressor,
|
||||
sqliteOptions: {
|
||||
dialect: new SqliteDialect({ database: BetterSQLite3("db.sql") })
|
||||
}
|
||||
dialect: (name) =>
|
||||
new SqliteDialect({ database: BetterSQLite3(":memory:") })
|
||||
},
|
||||
batchSize: 500
|
||||
});
|
||||
return db.init().then(() => db);
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ test("unlock a note permanently", () =>
|
||||
expect(note.headline).not.toBe("");
|
||||
const content = await db.content.get(note.contentId);
|
||||
expect(content.data).toBeDefined();
|
||||
expect(typeof content.data).toBe("object");
|
||||
expect(typeof content.data).toBe("string");
|
||||
}));
|
||||
|
||||
test("save a locked note", () =>
|
||||
|
||||
@@ -78,6 +78,18 @@ export class Content implements ICollection {
|
||||
"Please use db.content.merge for merging remote content."
|
||||
);
|
||||
|
||||
if (content.noteId && !content.id) {
|
||||
// find content from noteId
|
||||
content.id = (
|
||||
await this.db
|
||||
.sql()
|
||||
.selectFrom("content")
|
||||
.where("noteId", "==", content.noteId)
|
||||
.select("id")
|
||||
.executeTakeFirst()
|
||||
)?.id;
|
||||
}
|
||||
|
||||
const id = content.id || getId();
|
||||
|
||||
const encryptedData = isCipher(content.data) ? content.data : undefined;
|
||||
@@ -107,12 +119,13 @@ export class Content implements ICollection {
|
||||
...contentData
|
||||
});
|
||||
|
||||
if (content.sessionId && contentData && content.type && content.noteId)
|
||||
if (content.sessionId && contentData && content.type && content.noteId) {
|
||||
await this.db.noteHistory.add(content.sessionId, {
|
||||
noteId: content.noteId,
|
||||
type: content.type,
|
||||
...contentData
|
||||
});
|
||||
}
|
||||
} else if (content.noteId) {
|
||||
const contentItem: ContentItem = {
|
||||
type: "tiptap",
|
||||
@@ -186,6 +199,14 @@ export class Content implements ICollection {
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
// async findByNoteId(noteId: string) {
|
||||
// await this.db
|
||||
// .sql()
|
||||
// .selectFrom("content")
|
||||
// .where("noteId", "==", noteId)
|
||||
// .execute();
|
||||
// }
|
||||
// multi(ids: string[]) {
|
||||
// return this.collection.getItems(ids);
|
||||
// }
|
||||
|
||||
@@ -103,7 +103,7 @@ export class NoteHistory implements ICollection {
|
||||
.sql()
|
||||
.selectFrom("notehistory")
|
||||
.where("noteId", "==", noteId)
|
||||
.orderBy(`dateModified asc`)
|
||||
.orderBy(`dateModified desc`)
|
||||
.select(["id", "sessionContentId"])
|
||||
.offset(limit)
|
||||
.limit(10)
|
||||
|
||||
@@ -57,7 +57,6 @@ export class SessionContent implements ICollection {
|
||||
// locked || isCipher(content.data)
|
||||
// ? content.data
|
||||
// : await this.db.compressor().compress(content.data);
|
||||
|
||||
await this.collection.upsert({
|
||||
type: "sessioncontent",
|
||||
id: makeSessionContentId(sessionId),
|
||||
|
||||
@@ -9,7 +9,7 @@ hello
|
||||
`;
|
||||
|
||||
exports[`convert HTML to markdown with codeblocks > html-to-md-codeblocks.md 1`] = `
|
||||
"Typescript is one of those languages that appear to be very simple. It's often described as \\"Javascript with types\\" and it fits that name very well. However, what many don't realize starting out with Typescript is that Typescript*is *a language and like all other languages it has it's own \\"secrets\\", it's own set of quirks.
|
||||
"Typescript is one of those languages that appear to be very simple. It's often described as "Javascript with types" and it fits that name very well. However, what many don't realize starting out with Typescript is that Typescript*is *a language and like all other languages it has it's own "secrets", it's own set of quirks.
|
||||
|
||||
When I started out with Typescript a few years back, I absolutely hated it. It was unnecessary, a box of clutter, making me write code that would never actually run. I hated defining interfaces, typing out all my functions, and thinking in terms I was not used to as a Javascript developer. Before Javascript, I had coded in C# and I had never really liked C# (I still don't) mostly for the huge amounts of boilerplate and magic involved. Typescript is heavily inspired by C# and seeing that contaminate the Javascript ecosystem irked me no end.
|
||||
|
||||
@@ -26,12 +26,12 @@ Think of a container that can take any type of item as long as it is not a circl
|
||||
\`\`\`javascript
|
||||
var container = [];
|
||||
function putIntoContainer(item) {
|
||||
if (item.type === \\"round\\") throw new Error(\\"Rounded items not supported.\\")
|
||||
if (item.type === "round") throw new Error("Rounded items not supported.")
|
||||
container.push(item);
|
||||
}
|
||||
|
||||
var square = {type: \\"square\\"}
|
||||
var circle = {type: \\"round\\"}
|
||||
var square = {type: "square"}
|
||||
var circle = {type: "round"}
|
||||
putIntoContainer(square)
|
||||
putIntoContainer(circle) // ERROR! Rounded items not supported!
|
||||
\`\`\`
|
||||
@@ -46,11 +46,11 @@ In Typescript, this will be solved much more succinctly:
|
||||
// than square or round.
|
||||
// This gives us nice auto completion and safety
|
||||
// against typos.
|
||||
type ItemTypes = \\"square\\" | \\"round\\";
|
||||
type ItemTypes = "square" | "round";
|
||||
|
||||
// Define a generic item that can be of any type
|
||||
// defined in ItemTypes.
|
||||
// i.e. Item<\\"triangle\\"> will give an error.
|
||||
// i.e. Item<"triangle"> will give an error.
|
||||
type Item<TItemType extends ItemTypes> = {
|
||||
type?: TItemType;
|
||||
width: number;
|
||||
@@ -58,15 +58,15 @@ type Item<TItemType extends ItemTypes> = {
|
||||
};
|
||||
|
||||
// This is just syntax sugar to increase readability.
|
||||
type Square = Item<\\"square\\">;
|
||||
type Circle = Item<\\"round\\">;
|
||||
type Square = Item<"square">;
|
||||
type Circle = Item<"round">;
|
||||
|
||||
// Our container is just a simple wrapper around an array
|
||||
// that accepts items of only a specific type.
|
||||
type Container<TItemType extends ItemTypes> = Array<Item<TItemType>>;
|
||||
|
||||
var squareContainer: Container<\\"square\\"> = [];
|
||||
var roundContainer: Container<\\"round\\"> = [];
|
||||
var squareContainer: Container<"square"> = [];
|
||||
var roundContainer: Container<"round"> = [];
|
||||
|
||||
// This wrapper is unnecessary, of course, because array.push
|
||||
// already does this. Only for demonstration purposes.
|
||||
@@ -82,8 +82,8 @@ var circle: Circle = { width: 200, height: 500 };
|
||||
|
||||
putIntoContainer(squareContainer, square);
|
||||
putIntoContainer(roundContainer, circle);
|
||||
putIntoContainer(roundContainer, square); // Error: Argument of type 'Square' is not assignable to parameter of type 'Item<\\"round\\">'.
|
||||
putIntoContainer(squareContainer, circle); // Error: Argument of type 'Circle' is not assignable to parameter of type 'Item<\\"square\\">'.
|
||||
putIntoContainer(roundContainer, square); // Error: Argument of type 'Square' is not assignable to parameter of type 'Item<"round">'.
|
||||
putIntoContainer(squareContainer, circle); // Error: Argument of type 'Circle' is not assignable to parameter of type 'Item<"square">'.
|
||||
\`\`\`
|
||||
|
||||
A lot more code, I know, and if you don't know how generics work that blob of code is utter nonsense. One of the main reasons I avoided Typescript for a long time. But look at the benefits:
|
||||
@@ -113,15 +113,15 @@ putIntoContainer(roundContainer, circle);
|
||||
|
||||
This is the power of generics. More specifically, this is Typescript generics at a glance.
|
||||
|
||||
But this post was supposed to be about the \\"Secrets\\" of Typescript Generics, right? Well, let's get into that.
|
||||
But this post was supposed to be about the "Secrets" of Typescript Generics, right? Well, let's get into that.
|
||||
|
||||
## 1\\\\. Type filters using ternary operators
|
||||
## 1\\. Type filters using ternary operators
|
||||
|
||||
## 2\\\\. Deeply recursive types
|
||||
## 2\\. Deeply recursive types
|
||||
|
||||
## 3\\\\. Type functions
|
||||
## 3\\. Type functions
|
||||
|
||||
## 4\\\\. Type inference using interface properties
|
||||
## 4\\. Type inference using interface properties
|
||||
|
||||
"
|
||||
`;
|
||||
@@ -230,7 +230,7 @@ Nene
|
||||
|
||||
- dasvsadv
|
||||
|
||||
- adsva\\\\\`sd
|
||||
- adsva\\\`sd
|
||||
|
||||
- vasd
|
||||
|
||||
@@ -246,9 +246,9 @@ exports[`convert HTML to text with codeblock2 > html-to-txt-codeblock2.txt 1`] =
|
||||
|
||||
exports[`convert HTML to text with codeblocks > html-to-txt-codeblocks.txt 1`] = `
|
||||
"Typescript is one of those languages that appear to be very simple. It's often
|
||||
described as \\"Javascript with types\\" and it fits that name very well. However,
|
||||
described as "Javascript with types" and it fits that name very well. However,
|
||||
what many don't realize starting out with Typescript is that Typescript is a
|
||||
language and like all other languages it has it's own \\"secrets\\", it's own set of
|
||||
language and like all other languages it has it's own "secrets", it's own set of
|
||||
quirks.
|
||||
|
||||
When I started out with Typescript a few years back, I absolutely hated it. It
|
||||
@@ -282,12 +282,12 @@ checks & conditions:
|
||||
|
||||
var container = [];
|
||||
function putIntoContainer(item) {
|
||||
if (item.type === \\"round\\") throw new Error(\\"Rounded items not supported.\\")
|
||||
if (item.type === "round") throw new Error("Rounded items not supported.")
|
||||
container.push(item);
|
||||
}
|
||||
|
||||
var square = {type: \\"square\\"}
|
||||
var circle = {type: \\"round\\"}
|
||||
var square = {type: "square"}
|
||||
var circle = {type: "round"}
|
||||
putIntoContainer(square)
|
||||
putIntoContainer(circle) // ERROR! Rounded items not supported!
|
||||
|
||||
@@ -304,11 +304,11 @@ In Typescript, this will be solved much more succinctly:
|
||||
// than square or round.
|
||||
// This gives us nice auto completion and safety
|
||||
// against typos.
|
||||
type ItemTypes = \\"square\\" | \\"round\\";
|
||||
type ItemTypes = "square" | "round";
|
||||
|
||||
// Define a generic item that can be of any type
|
||||
// defined in ItemTypes.
|
||||
// i.e. Item<\\"triangle\\"> will give an error.
|
||||
// i.e. Item<"triangle"> will give an error.
|
||||
type Item<TItemType extends ItemTypes> = {
|
||||
type?: TItemType;
|
||||
width: number;
|
||||
@@ -316,15 +316,15 @@ type Item<TItemType extends ItemTypes> = {
|
||||
};
|
||||
|
||||
// This is just syntax sugar to increase readability.
|
||||
type Square = Item<\\"square\\">;
|
||||
type Circle = Item<\\"round\\">;
|
||||
type Square = Item<"square">;
|
||||
type Circle = Item<"round">;
|
||||
|
||||
// Our container is just a simple wrapper around an array
|
||||
// that accepts items of only a specific type.
|
||||
type Container<TItemType extends ItemTypes> = Array<Item<TItemType>>;
|
||||
|
||||
var squareContainer: Container<\\"square\\"> = [];
|
||||
var roundContainer: Container<\\"round\\"> = [];
|
||||
var squareContainer: Container<"square"> = [];
|
||||
var roundContainer: Container<"round"> = [];
|
||||
|
||||
// This wrapper is unnecessary, of course, because array.push
|
||||
// already does this. Only for demonstration purposes.
|
||||
@@ -340,8 +340,8 @@ var circle: Circle = { width: 200, height: 500 };
|
||||
|
||||
putIntoContainer(squareContainer, square);
|
||||
putIntoContainer(roundContainer, circle);
|
||||
putIntoContainer(roundContainer, square); // Error: Argument of type 'Square' is not assignable to parameter of type 'Item<\\"round\\">'.
|
||||
putIntoContainer(squareContainer, circle); // Error: Argument of type 'Circle' is not assignable to parameter of type 'Item<\\"square\\">'.
|
||||
putIntoContainer(roundContainer, square); // Error: Argument of type 'Square' is not assignable to parameter of type 'Item<"round">'.
|
||||
putIntoContainer(squareContainer, circle); // Error: Argument of type 'Circle' is not assignable to parameter of type 'Item<"square">'.
|
||||
|
||||
|
||||
A lot more code, I know, and if you don't know how generics work that blob of
|
||||
@@ -372,7 +372,7 @@ putIntoContainer(roundContainer, circle);
|
||||
This is the power of generics. More specifically, this is Typescript generics at
|
||||
a glance.
|
||||
|
||||
But this post was supposed to be about the \\"Secrets\\" of Typescript Generics,
|
||||
But this post was supposed to be about the "Secrets" of Typescript Generics,
|
||||
right? Well, let's get into that.
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ export async function createTriggers(db: Kysely<DatabaseSchemaWithFTS>) {
|
||||
c.insertInto("content_fts").values({
|
||||
content_fts: sql.lit("delete"),
|
||||
id: sql.ref("old.id"),
|
||||
rowid: sql.ref("old.rowid"),
|
||||
data: sql.ref("old.data"),
|
||||
noteId: sql.ref("old.noteId")
|
||||
})
|
||||
@@ -70,7 +69,6 @@ export async function createTriggers(db: Kysely<DatabaseSchemaWithFTS>) {
|
||||
c.insertInto("content_fts").values({
|
||||
content_fts: sql.lit("delete"),
|
||||
id: sql.ref("old.id"),
|
||||
rowid: sql.ref("old.rowid"),
|
||||
data: sql.ref("old.data"),
|
||||
noteId: sql.ref("old.noteId")
|
||||
})
|
||||
@@ -78,7 +76,7 @@ export async function createTriggers(db: Kysely<DatabaseSchemaWithFTS>) {
|
||||
.addQuery((c) =>
|
||||
c.insertInto("content_fts").values({
|
||||
id: sql`new.id`,
|
||||
data: sql`IIF(new.locked == 1, "", new.data)`,
|
||||
data: sql`IIF(new.locked == 1, '', new.data)`,
|
||||
noteId: sql`new.noteId`
|
||||
})
|
||||
)
|
||||
@@ -114,7 +112,6 @@ export async function createTriggers(db: Kysely<DatabaseSchemaWithFTS>) {
|
||||
c.insertInto("notes_fts").values({
|
||||
notes_fts: sql.lit("delete"),
|
||||
id: sql.ref("old.id"),
|
||||
rowid: sql.ref("old.rowid"),
|
||||
title: sql.ref("old.title")
|
||||
})
|
||||
)
|
||||
@@ -130,7 +127,6 @@ export async function createTriggers(db: Kysely<DatabaseSchemaWithFTS>) {
|
||||
c.insertInto("notes_fts").values({
|
||||
notes_fts: sql.lit("delete"),
|
||||
id: sql.ref("old.id"),
|
||||
rowid: sql.ref("old.rowid"),
|
||||
title: sql.ref("old.title")
|
||||
})
|
||||
)
|
||||
|
||||
@@ -28,6 +28,7 @@ function createMock() {
|
||||
Object.fromEntries(ids.map((id) => [id, id]))
|
||||
);
|
||||
}
|
||||
test.todo("renable virtualized grouping tests");
|
||||
// test("fetch items in batch if not found in cache", async (t) => {
|
||||
// const mocked = createMock();
|
||||
// const grouping = new VirtualizedGrouping<string>(
|
||||
|
||||
Reference in New Issue
Block a user