diff --git a/apps/web/__e2e__/models/base-item.model.ts b/apps/web/__e2e__/models/base-item.model.ts
index ce5b3a909..6f4e9ca8e 100644
--- a/apps/web/__e2e__/models/base-item.model.ts
+++ b/apps/web/__e2e__/models/base-item.model.ts
@@ -25,7 +25,7 @@ export class BaseItemModel {
private readonly titleText: Locator;
readonly descriptionText: Locator;
- constructor(protected readonly locator: Locator) {
+ constructor(readonly locator: Locator) {
this.page = locator.page();
this.titleText = this.locator.locator(getTestId(`title`));
this.descriptionText = this.locator.locator(getTestId(`description`));
diff --git a/apps/web/__e2e__/models/items-view.model.ts b/apps/web/__e2e__/models/items-view.model.ts
index 91c696472..25d3c9753 100644
--- a/apps/web/__e2e__/models/items-view.model.ts
+++ b/apps/web/__e2e__/models/items-view.model.ts
@@ -26,14 +26,13 @@ import { fillItemDialog } from "./utils";
export class ItemsViewModel extends BaseViewModel {
private readonly createButton: Locator;
-
- constructor(page: Page, private readonly id: "topics" | "tags") {
- super(page, id, id);
- this.createButton = page.locator(getTestId(`${id}-action-button`));
+ constructor(page: Page) {
+ super(page, "tags", "tags");
+ this.createButton = page.locator(getTestId(`tags-action-button`));
}
async createItem(item: Item) {
- const titleToCompare = this.id === "tags" ? `#${item.title}` : item.title;
+ const titleToCompare = `#${item.title}`;
await this.createButton.first().click();
await fillItemDialog(this.page, item);
@@ -43,13 +42,9 @@ export class ItemsViewModel extends BaseViewModel {
}
async findItem(item: Item) {
- const titleToCompare = this.id === "tags" ? `#${item.title}` : item.title;
+ const titleToCompare = `#${item.title}`;
for await (const _item of this.iterateItems()) {
- const itemModel = new ItemModel(
- _item,
- // TODO:
- this.id === "topics" ? "topic" : "tag"
- );
+ const itemModel = new ItemModel(_item, "tag");
const title = await itemModel.getTitle();
if (title === titleToCompare) return itemModel;
}
diff --git a/apps/web/__e2e__/models/notebook-item.model.ts b/apps/web/__e2e__/models/notebook-item.model.ts
index d310b5e6b..2d37c92f5 100644
--- a/apps/web/__e2e__/models/notebook-item.model.ts
+++ b/apps/web/__e2e__/models/notebook-item.model.ts
@@ -21,11 +21,11 @@ import { Locator } from "@playwright/test";
import { BaseItemModel } from "./base-item.model";
import { ContextMenuModel } from "./context-menu.model";
import { ToggleModel } from "./toggle.model";
-import { ItemsViewModel } from "./items-view.model";
import { Notebook } from "./types";
import { confirmDialog, fillNotebookDialog } from "./utils";
import { NotesViewModel } from "./notes-view.model";
import { getTestId } from "../utils";
+import { SubnotebooksViewModel } from "./subnotebooks-view.model";
export class NotebookItemModel extends BaseItemModel {
private readonly contextMenu: ContextMenuModel;
@@ -37,7 +37,7 @@ export class NotebookItemModel extends BaseItemModel {
async openNotebook() {
await this.locator.click();
return {
- topics: new ItemsViewModel(this.page, "topics"),
+ subNotebooks: new SubnotebooksViewModel(this.page),
notes: new NotesViewModel(this.page, "notebook", "notes")
};
}
diff --git a/apps/web/__e2e__/models/subnotebooks-view.model.ts b/apps/web/__e2e__/models/subnotebooks-view.model.ts
new file mode 100644
index 000000000..d4064107a
--- /dev/null
+++ b/apps/web/__e2e__/models/subnotebooks-view.model.ts
@@ -0,0 +1,54 @@
+/*
+This file is part of the Notesnook project (https://notesnook.com/)
+
+Copyright (C) 2023 Streetwriters (Private) Limited
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import { Locator, Page } from "@playwright/test";
+import { getTestId } from "../utils";
+import { BaseViewModel } from "./base-view.model";
+import { NotebookItemModel } from "./notebook-item.model";
+import { Notebook } from "./types";
+import { fillNotebookDialog } from "./utils";
+
+export class SubnotebooksViewModel extends BaseViewModel {
+ private readonly createButton: Locator;
+
+ constructor(readonly page: Page) {
+ super(page, "subnotebooks", "subnotebooks");
+ this.createButton = page
+ .locator(getTestId("subnotebooks-action-button"))
+ .first();
+ }
+
+ async createNotebook(notebook: Notebook) {
+ await this.createButton.click();
+
+ await fillNotebookDialog(this.page, notebook);
+
+ await this.waitForItem(notebook.title);
+ return await this.findNotebook(notebook);
+ }
+
+ async findNotebook(notebook: Partial) {
+ for await (const item of this.iterateItems()) {
+ const notebookModel = new NotebookItemModel(item);
+ if ((await notebookModel.getTitle()) === notebook.title)
+ return notebookModel;
+ }
+ return undefined;
+ }
+}
diff --git a/apps/web/__e2e__/notebooks.test.ts b/apps/web/__e2e__/notebooks.test.ts
index 353b1047e..ac557449c 100644
--- a/apps/web/__e2e__/notebooks.test.ts
+++ b/apps/web/__e2e__/notebooks.test.ts
@@ -50,14 +50,16 @@ test("create a note inside a notebook", async ({ page }) => {
expect(note).toBeDefined();
});
-test("create a note inside a topic", async ({ page }) => {
+test("create a note inside a subnotebook", async ({ page }) => {
const app = new AppModel(page);
await app.goto();
const notebooks = await app.goToNotebooks();
const notebook = await notebooks.createNotebook(NOTEBOOK);
- const { topics } = (await notebook?.openNotebook()) || {};
- const topic = await topics?.findItem({ title: NOTEBOOK.topics[0] });
- const notes = await topic?.open();
+ const { subNotebooks } = (await notebook?.openNotebook()) || {};
+ const subNotebook = await subNotebooks?.createNotebook({
+ title: "Subnotebook 1"
+ });
+ const { notes } = (await subNotebook?.openNotebook()) || {};
const note = await notes?.createNote(NOTE);
@@ -72,8 +74,7 @@ test("edit a notebook", async ({ page }) => {
const item: Notebook = {
title: "An Edited Notebook",
- description: "A new edited description",
- topics: ["Topic 1", "Topic 2", "Topic 3"]
+ description: "A new edited description"
};
await notebook?.editNotebook(item);
@@ -121,10 +122,11 @@ test("permanently delete a notebook", async ({ page }) => {
await notebook?.moveToTrash();
const trash = await app.goToTrash();
const trashItem = await trash.findItem(NOTEBOOK.title);
+ if (!trashItem) throw new Error("No trash item found.");
await trashItem?.delete();
- expect(await trashItem?.isPresent()).toBe(false);
+ await expect(trashItem.locator).toBeHidden();
});
test("pin a notebook", async ({ page }) => {
@@ -197,28 +199,28 @@ test("delete all notes within a notebook", async ({ page }) => {
expect(await notes.isEmpty()).toBe(true);
});
-test("delete all notes within a topic", async ({ page }) => {
- const app = new AppModel(page);
- await app.goto();
- const notebooks = await app.goToNotebooks();
- const notebook = await notebooks.createNotebook(NOTEBOOK);
- const { topics } = (await notebook?.openNotebook()) || {};
- const topic = await topics?.findItem({ title: NOTEBOOK.topics[0] });
- let notes = await topic?.open();
- for (let i = 0; i < 2; ++i) {
- await notes?.createNote({
- title: `Note ${i}`,
- content: NOTE.content
- });
- }
- await app.goBack();
- await app.goBack();
+// test("delete all notes within a topic", async ({ page }) => {
+// const app = new AppModel(page);
+// await app.goto();
+// const notebooks = await app.goToNotebooks();
+// const notebook = await notebooks.createNotebook(NOTEBOOK);
+// const { topics } = (await notebook?.openNotebook()) || {};
+// const topic = await topics?.findItem({ title: NOTEBOOK.topics[0] });
+// let notes = await topic?.open();
+// for (let i = 0; i < 2; ++i) {
+// await notes?.createNote({
+// title: `Note ${i}`,
+// content: NOTE.content
+// });
+// }
+// await app.goBack();
+// await app.goBack();
- await notebook?.moveToTrash(true);
+// await notebook?.moveToTrash(true);
- notes = await app.goToNotes();
- expect(await notes.isEmpty()).toBe(true);
-});
+// notes = await app.goToNotes();
+// expect(await notes.isEmpty()).toBe(true);
+// });
test(`sort notebooks`, async ({ page }, info) => {
info.setTimeout(2 * 60 * 1000);
@@ -243,7 +245,7 @@ test(`sort notebooks`, async ({ page }, info) => {
});
if (!sortResult) return;
- expect(await notebooks.isEmpty()).toBeFalsy();
+ await expect(notebooks.items).toHaveCount(titles.length);
});
}
}
diff --git a/apps/web/__e2e__/topics.test.ts b/apps/web/__e2e__/topics.test.ts
deleted file mode 100644
index 9fdeb2f6b..000000000
--- a/apps/web/__e2e__/topics.test.ts
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
-This file is part of the Notesnook project (https://notesnook.com/)
-
-Copyright (C) 2023 Streetwriters (Private) Limited
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see .
-*/
-
-import { test, expect } from "@playwright/test";
-import { AppModel } from "./models/app.model";
-import { Item } from "./models/types";
-import { NOTEBOOK, sortByOptions, orderByOptions, NOTE } from "./utils";
-
-test("create shortcut of a topic", async ({ page }) => {
- const app = new AppModel(page);
- await app.goto();
- const notebooks = await app.goToNotebooks();
- const notebook = await notebooks.createNotebook(NOTEBOOK);
- const { topics } = (await notebook?.openNotebook()) || {};
- const topic = await topics?.findItem({ title: NOTEBOOK.topics[0] });
-
- await topic?.createShortcut();
-
- expect(await topic?.isShortcut()).toBe(true);
- const allShortcuts = await app.navigation.getShortcuts();
- expect(allShortcuts.includes(NOTEBOOK.topics[0])).toBeTruthy();
-});
-
-test("remove shortcut of a topic", async ({ page }) => {
- const app = new AppModel(page);
- await app.goto();
- const notebooks = await app.goToNotebooks();
- const notebook = await notebooks.createNotebook(NOTEBOOK);
- const { topics } = (await notebook?.openNotebook()) || {};
- const topic = await topics?.findItem({ title: NOTEBOOK.topics[0] });
- await topic?.createShortcut();
-
- await topic?.removeShortcut();
-
- expect(await topic?.isShortcut()).toBe(false);
- const allShortcuts = await app.navigation.getShortcuts();
- expect(allShortcuts.includes(NOTEBOOK.topics[0])).toBeFalsy();
-});
-
-test("delete a topic", async ({ page }) => {
- const app = new AppModel(page);
- await app.goto();
- const notebooks = await app.goToNotebooks();
- const notebook = await notebooks.createNotebook(NOTEBOOK);
- const { topics } = (await notebook?.openNotebook()) || {};
- const topic = await topics?.findItem({ title: NOTEBOOK.topics[0] });
-
- await topic?.deleteWithNotes();
-
- expect(await app.toasts.waitForToast("1 topic deleted")).toBe(true);
- expect(await topics?.findItem({ title: NOTEBOOK.topics[0] })).toBeUndefined();
-});
-
-test("edit topics individually", async ({ page }) => {
- const app = new AppModel(page);
- await app.goto();
- const notebooks = await app.goToNotebooks();
- const notebook = await notebooks.createNotebook(NOTEBOOK);
- const { topics } = (await notebook?.openNotebook()) || {};
-
- const editedTopics: Item[] = [];
- for (const title of NOTEBOOK.topics) {
- const topic = await topics?.findItem({ title });
- const editedTopic: Item = { title: `${title} (edited)` };
- await topic?.editItem(editedTopic);
- editedTopics.push(editedTopic);
- }
-
- for (const topic of editedTopics) {
- expect(await topics?.findItem(topic)).toBeDefined();
- }
-});
-
-test("delete all notes within a topic", async ({ page }) => {
- const app = new AppModel(page);
- await app.goto();
- const notebooks = await app.goToNotebooks();
- const notebook = await notebooks.createNotebook(NOTEBOOK);
- const { topics } = (await notebook?.openNotebook()) || {};
- const topic = await topics?.findItem({ title: NOTEBOOK.topics[0] });
- let notes = await topic?.open();
- for (let i = 0; i < 2; ++i) {
- await notes?.createNote({
- title: `Note ${i}`,
- content: NOTE.content
- });
- }
- await app.goBack();
-
- await topic?.deleteWithNotes(true);
-
- notes = await app.goToNotes();
- expect(await notes.isEmpty()).toBe(true);
-});
-
-test(`sort topics`, async ({ page }, info) => {
- info.setTimeout(1 * 60 * 1000);
-
- const app = new AppModel(page);
- await app.goto();
- const notebooks = await app.goToNotebooks();
- const notebook = await notebooks.createNotebook({
- ...NOTEBOOK,
- topics: ["title1", "title2", "title3", "title4", "title5"]
- });
- const { topics } = (await notebook?.openNotebook()) || {};
-
- for (const sortBy of sortByOptions) {
- for (const orderBy of orderByOptions) {
- await test.step(`sort by ${sortBy}, order by ${orderBy}`, async () => {
- const sortResult = await topics?.sort({
- orderBy,
- sortBy
- });
- if (!sortResult) return;
-
- expect(await topics?.isEmpty()).toBeFalsy();
- });
- }
- }
-});
-
-test.skip("search topics", async ({ page }) => {
- const app = new AppModel(page);
- await app.goto();
- const notebooks = await app.goToNotebooks();
- const notebook = await notebooks.createNotebook({
- ...NOTEBOOK,
- topics: ["title1", "title2", "title3", "title4", "title5"]
- });
- await notebook?.openNotebook();
-
- const search = await app.search("1", "topics");
- const topic = await search?.findItem({ title: "title1" });
-
- expect((await topic?.getTitle()) === "title1").toBeTruthy();
-});
diff --git a/apps/web/__e2e__/utils/index.ts b/apps/web/__e2e__/utils/index.ts
index b37deca1f..e40bedb2e 100644
--- a/apps/web/__e2e__/utils/index.ts
+++ b/apps/web/__e2e__/utils/index.ts
@@ -21,7 +21,12 @@ import fs from "fs";
import dotenv from "dotenv";
import path from "path";
import { Locator, Page } from "@playwright/test";
-import { GroupByOptions, OrderByOptions, SortByOptions } from "../models/types";
+import {
+ GroupByOptions,
+ Notebook,
+ OrderByOptions,
+ SortByOptions
+} from "../models/types";
type Note = {
title: string;
@@ -45,10 +50,14 @@ const USER = {
}
};
-const NOTEBOOK = {
+const NOTEBOOK: Notebook = {
title: "Test notebook 1",
- description: "This is test notebook 1",
- topics: ["Topic 1", "Very long topic 2", "Topic 3"]
+ description: "This is test notebook 1"
+ // subNotebooks: [
+ // { title: "Sub notebook 1" },
+ // { title: "Very long sub notebook 2" },
+ // { title: "Sub notebook 3" }
+ // ]
};
const NOTE: Note = {
diff --git a/apps/web/src/views/notebook.tsx b/apps/web/src/views/notebook.tsx
index fc8202537..b7d6587e4 100644
--- a/apps/web/src/views/notebook.tsx
+++ b/apps/web/src/views/notebook.tsx
@@ -222,7 +222,7 @@ function SubNotebooks({
}, [contextNotes, context]);
return (
-
+