= {
+ testDir,
+ outputDir,
+ expect: {
+ timeout: 10000
+ },
+ use: {
+ acceptDownloads: true,
+ trace: "retain-on-failure",
+ screenshot: "only-on-failure",
+ video: "retry-with-video",
+ viewport: {
+ width: 1920,
+ height: 1080
+ }
+ },
+ timeout: 60000,
+ globalTimeout: 5400000,
+ workers: process.env.CI ? 1 : undefined,
+ forbidOnly: !!process.env.CI,
+ retries: process.env.CI ? 3 : 0,
+ reporter: process.env.CI
+ ? [["dot"], ["json", { outputFile: path.join(outputDir, "report.json") }]]
+ : "line",
+ projects: [],
+ globalSetup: "./__tests__/electron-test/global-setup.ts"
+};
+
+const metadata = {
+ platform: process.platform,
+ headless: "headed",
+ browserName: "electron",
+ channel: undefined,
+ mode: "default",
+ video: false
+};
+
+config.projects?.push({
+ name: "notesnook-desktop",
+ // Share screenshots with chromium.
+ snapshotPathTemplate:
+ "{testDir}/{testFileDir}/{testFileName}-snapshots/{arg}-electron{ext}",
+ use: {
+ browserName: "chromium",
+ headless: false
+ },
+ testDir: "__tests__",
+ metadata
+});
+
+config.projects?.push({
+ name: "notesnook-web",
+ // Share screenshots with chromium.
+ snapshotPathTemplate:
+ "{testDir}/{testFileDir}/{testFileName}-snapshots/{arg}-electron{ext}",
+ use: {
+ browserName: "chromium",
+ headless: false
+ },
+ testDir: path.resolve(__dirname, "../web/__e2e__"),
+ metadata
+});
+
+export default config;
diff --git a/apps/desktop/scripts/build.mjs b/apps/desktop/scripts/build.mjs
index 870641870..665a722fd 100644
--- a/apps/desktop/scripts/build.mjs
+++ b/apps/desktop/scripts/build.mjs
@@ -33,6 +33,7 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const root = args.root || path.join(__dirname, "..");
const skipTscBuild = args.skipTscBuild || false;
+const buildForTesting = args.test || false;
const webAppPath = path.resolve(path.join(__dirname, "..", "..", "web"));
@@ -51,7 +52,11 @@ console.log("removed build folder");
if (args.rebuild || !existsSync(path.join(webAppPath, "build"))) {
console.log("rebuilding...");
await exec(
- "node scripts/execute.mjs @notesnook/web:build:desktop",
+ `node scripts/execute.mjs ${
+ buildForTesting
+ ? "@notesnook/web:build:test:desktop"
+ : "@notesnook/web:build:desktop"
+ }`,
path.join(__dirname, "..", "..", "..")
);
}
diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json
index c5f8b44da..5d94c8055 100644
--- a/apps/desktop/tsconfig.json
+++ b/apps/desktop/tsconfig.json
@@ -1,9 +1,12 @@
{
- "extends": "../../tsconfig",
+ "extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist/",
"lib": ["ESNext"],
- "moduleResolution": "Bundler"
+ "moduleResolution": "Bundler",
+ "paths": {
+ "@nn/test": ["./__tests__/electron-test/index.ts"]
+ }
},
- "include": ["src", "global.d.ts"]
+ "include": ["src", "global.d.ts", "__tests__"]
}
diff --git a/apps/web/__e2e__/app-lock.test.ts b/apps/web/__e2e__/app-lock.test.ts
index 63e75945e..be7453715 100644
--- a/apps/web/__e2e__/app-lock.test.ts
+++ b/apps/web/__e2e__/app-lock.test.ts
@@ -18,7 +18,8 @@ along with this program. If not, see .
*/
import { AppModel } from "./models/app.model";
-import { test, expect, APP_LOCK_PASSWORD, USER } from "./utils";
+import { APP_LOCK_PASSWORD, USER } from "./utils";
+import { test, expect } from "@nn/test";
test("don't show status bar lock app button to unauthenticated user", async ({
page
diff --git a/apps/web/__e2e__/backups.test.ts b/apps/web/__e2e__/backups.test.ts
index d444f1f2c..4a1b1989d 100644
--- a/apps/web/__e2e__/backups.test.ts
+++ b/apps/web/__e2e__/backups.test.ts
@@ -17,7 +17,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
import { AppModel } from "./models/app.model";
-import { test, expect, USER } from "./utils";
+import { USER } from "./utils";
+import { test, expect } from "@nn/test";
test("create an unencrypted backup", async ({ page }) => {
const app = new AppModel(page);
diff --git a/apps/web/__e2e__/checkout.test.ts b/apps/web/__e2e__/checkout.test.ts
index 32b204dee..f69fb99ca 100644
--- a/apps/web/__e2e__/checkout.test.ts
+++ b/apps/web/__e2e__/checkout.test.ts
@@ -17,10 +17,10 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Page } from "@playwright/test";
-import { test, expect, USER } from "./utils";
+import { USER } from "./utils";
import { AppModel } from "./models/app.model";
import { PriceItem } from "./models/types";
+import { test, expect, Page } from "@nn/test";
test.setTimeout(45 * 1000);
test.describe.configure({ mode: "serial" });
diff --git a/apps/web/__e2e__/colors.test.ts b/apps/web/__e2e__/colors.test.ts
index a047051f0..3636f4c19 100644
--- a/apps/web/__e2e__/colors.test.ts
+++ b/apps/web/__e2e__/colors.test.ts
@@ -17,7 +17,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
import { AppModel } from "./models/app.model";
-import { test, expect, getTestId, NOTE } from "./utils";
+import { getTestId, NOTE } from "./utils";
+import { test, expect } from "@nn/test";
test("delete the last note of a color", async ({ page }) => {
const app = new AppModel(page);
diff --git a/apps/web/__e2e__/drag-drop.test.ts b/apps/web/__e2e__/drag-drop.test.ts
index 1be83ba46..f07193755 100644
--- a/apps/web/__e2e__/drag-drop.test.ts
+++ b/apps/web/__e2e__/drag-drop.test.ts
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { test, expect } from "./utils";
+import { test, expect } from "@nn/test";
import { AppModel } from "./models/app.model";
import { NotesViewModel } from "./models/notes-view.model";
diff --git a/apps/web/__e2e__/editor.test.ts b/apps/web/__e2e__/editor.test.ts
index dbf10bf6a..bdcc4d7e9 100644
--- a/apps/web/__e2e__/editor.test.ts
+++ b/apps/web/__e2e__/editor.test.ts
@@ -18,7 +18,8 @@ along with this program. If not, see .
*/
import { AppModel } from "./models/app.model";
-import { test, expect, NOTE, TITLE_ONLY_NOTE } from "./utils";
+import { NOTE, TITLE_ONLY_NOTE } from "./utils";
+import { test, expect } from "@nn/test";
test("focus mode", async ({ page }) => {
const app = new AppModel(page);
diff --git a/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-1-electron.txt b/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-1-electron.txt
new file mode 100644
index 000000000..793ea9eb3
--- /dev/null
+++ b/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-1-electron.txt
@@ -0,0 +1 @@
+53ad8e4e40ebebd0f400498dTest note 1 (0) Test note 1 (1) Test note 1 (2) Test note 1 (3) Test note 1 (4) Test note 1 (5) Test note 1 (6) Test note 1 (7) Test note 1 (8) Test note 1 (9)
\ No newline at end of file
diff --git a/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-2-electron.txt b/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-2-electron.txt
new file mode 100644
index 000000000..bcc66667f
--- /dev/null
+++ b/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-2-electron.txt
@@ -0,0 +1 @@
+f054d19e9a2f46eff7b9bb25Test note 2 (0)Test note 2 (1)Test note 2 (2)Test note 2 (3)Test note 2 (4)Test note 2 (5)Test note 2 (6)Test note 2 (7)Test note 2 (8)Test note 2 (9)
\ No newline at end of file
diff --git a/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-title-1-electron.txt b/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-title-1-electron.txt
new file mode 100644
index 000000000..1b6dba491
--- /dev/null
+++ b/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-title-1-electron.txt
@@ -0,0 +1 @@
+Test note 1Test note 1 (0) Test note 1 (1) Test note 1 (2) Test note 1 (3) Test note 1 (4) Test note 1 (5) Test note 1 (6) Test note 1 (7) Test note 1 (8) Test note 1 (9)
\ No newline at end of file
diff --git a/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-title-2-electron.txt b/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-title-2-electron.txt
new file mode 100644
index 000000000..92705d03c
--- /dev/null
+++ b/apps/web/__e2e__/editor.test.ts-snapshots/fast-switch-and-edit-note-title-2-electron.txt
@@ -0,0 +1 @@
+Test note 2Test note 2 (0)Test note 2 (1)Test note 2 (2)Test note 2 (3)Test note 2 (4)Test note 2 (5)Test note 2 (6)Test note 2 (7)Test note 2 (8)Test note 2 (9)
\ No newline at end of file
diff --git a/apps/web/__e2e__/editor.test.ts-snapshots/focus-mode-electron.jpg b/apps/web/__e2e__/editor.test.ts-snapshots/focus-mode-electron.jpg
new file mode 100644
index 000000000..1557a9a5e
Binary files /dev/null and b/apps/web/__e2e__/editor.test.ts-snapshots/focus-mode-electron.jpg differ
diff --git a/apps/web/__e2e__/editor.test.ts-snapshots/normal-mode-from-focus-mode-electron.jpg b/apps/web/__e2e__/editor.test.ts-snapshots/normal-mode-from-focus-mode-electron.jpg
new file mode 100644
index 000000000..0223a9210
Binary files /dev/null and b/apps/web/__e2e__/editor.test.ts-snapshots/normal-mode-from-focus-mode-electron.jpg differ
diff --git a/apps/web/__e2e__/editor.test.ts-snapshots/readonly-edited-note-electron.txt b/apps/web/__e2e__/editor.test.ts-snapshots/readonly-edited-note-electron.txt
new file mode 100644
index 000000000..8fdeb8f1c
--- /dev/null
+++ b/apps/web/__e2e__/editor.test.ts-snapshots/readonly-edited-note-electron.txt
@@ -0,0 +1 @@
+An edit I madeThis is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1
\ No newline at end of file
diff --git a/apps/web/__e2e__/keyboard-list-navigation.test.ts b/apps/web/__e2e__/keyboard-list-navigation.test.ts
index 40f2e0004..121abc5be 100644
--- a/apps/web/__e2e__/keyboard-list-navigation.test.ts
+++ b/apps/web/__e2e__/keyboard-list-navigation.test.ts
@@ -17,8 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Page } from "@playwright/test";
-import { test, expect } from "./utils";
+import { test, expect, Browser, Page } from "@nn/test";
import { AppModel } from "./models/app.model";
import { NoteItemModel } from "./models/note-item.model";
diff --git a/apps/web/__e2e__/models/app.model.ts b/apps/web/__e2e__/models/app.model.ts
index af61ccf77..4510b16db 100644
--- a/apps/web/__e2e__/models/app.model.ts
+++ b/apps/web/__e2e__/models/app.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { AuthModel } from "./auth.model";
import { CheckoutModel } from "./checkout.model";
diff --git a/apps/web/__e2e__/models/auth.model.ts b/apps/web/__e2e__/models/auth.model.ts
index dec3c800c..a07558b6b 100644
--- a/apps/web/__e2e__/models/auth.model.ts
+++ b/apps/web/__e2e__/models/auth.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { generateSync } from "otplib";
@@ -77,7 +77,7 @@ export class AuthModel {
await this.page.locator(getTestId("close-plans")).click();
await this.page
- .locator(getTestId("sync-status-synced"))
+ .locator(getTestId("sync-status-syncing"))
.waitFor({ state: "visible" });
}
}
diff --git a/apps/web/__e2e__/models/base-item.model.ts b/apps/web/__e2e__/models/base-item.model.ts
index c719df9d3..4e8e1faad 100644
--- a/apps/web/__e2e__/models/base-item.model.ts
+++ b/apps/web/__e2e__/models/base-item.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
export class BaseItemModel {
diff --git a/apps/web/__e2e__/models/base-view.model.ts b/apps/web/__e2e__/models/base-view.model.ts
index 43be533a0..09fbeec05 100644
--- a/apps/web/__e2e__/models/base-view.model.ts
+++ b/apps/web/__e2e__/models/base-view.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { iterateList } from "./utils";
import { ContextMenuModel } from "./context-menu.model";
@@ -73,8 +73,10 @@ export class BaseViewModel {
return undefined;
}
- async waitForItem(title: string) {
- await this.list.locator(getTestId("title"), { hasText: title }).waitFor();
+ async waitForItem(title: string, timeout = 10000) {
+ await this.list
+ .locator(getTestId("title"), { hasText: title })
+ .waitFor({ timeout });
}
async waitForList() {
diff --git a/apps/web/__e2e__/models/checkout.model.ts b/apps/web/__e2e__/models/checkout.model.ts
index bec91cfcb..205906165 100644
--- a/apps/web/__e2e__/models/checkout.model.ts
+++ b/apps/web/__e2e__/models/checkout.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { PriceItem } from "./types";
import { iterateList } from "./utils";
diff --git a/apps/web/__e2e__/models/context-menu.model.ts b/apps/web/__e2e__/models/context-menu.model.ts
index f54600708..9068c2d0f 100644
--- a/apps/web/__e2e__/models/context-menu.model.ts
+++ b/apps/web/__e2e__/models/context-menu.model.ts
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Page, Locator } from "@playwright/test";
+import type { Page, Locator } from "@playwright/test";
import { getTestId } from "../utils";
export class ContextMenuModel {
diff --git a/apps/web/__e2e__/models/editor.model.ts b/apps/web/__e2e__/models/editor.model.ts
index f3460773c..b18b10a97 100644
--- a/apps/web/__e2e__/models/editor.model.ts
+++ b/apps/web/__e2e__/models/editor.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { TabItemModel } from "./tab-item.model";
import { iterateList } from "./utils";
@@ -110,7 +110,7 @@ export class EditorModel {
await this.wordCountText.waitFor();
}
- async waitForContent(text: string) {
+ async waitForContent(text: string, timeout = 10000) {
await this.page.waitForFunction(
({ expected }) => {
const contentElement = document.querySelector(
@@ -122,7 +122,27 @@ export class EditorModel {
.includes(expected.replace(/\s+/g, ""));
return false;
},
- { expected: text }
+ { expected: text },
+ { timeout }
+ );
+ }
+
+ async waitForTitle(title: string, timeout = 10000) {
+ await this.page.waitForFunction(
+ ({ expected }) => {
+ const titleInput = document.querySelector(
+ `.active [data-test-id="editor-title"]`
+ ) as HTMLInputElement | null;
+ if (titleInput)
+ return expected !== undefined
+ ? titleInput.value === expected
+ : titleInput.value.length > 0;
+ return false;
+ },
+ { expected: title },
+ {
+ timeout
+ }
);
}
diff --git a/apps/web/__e2e__/models/item.model.ts b/apps/web/__e2e__/models/item.model.ts
index a9bfe2a36..2981790bf 100644
--- a/apps/web/__e2e__/models/item.model.ts
+++ b/apps/web/__e2e__/models/item.model.ts
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Locator } from "@playwright/test";
+import type { Locator } from "@playwright/test";
import { BaseItemModel } from "./base-item.model";
import { ContextMenuModel } from "./context-menu.model";
import { NotesViewModel } from "./notes-view.model";
diff --git a/apps/web/__e2e__/models/items-view.model.ts b/apps/web/__e2e__/models/items-view.model.ts
index a35ed719a..04710fd4e 100644
--- a/apps/web/__e2e__/models/items-view.model.ts
+++ b/apps/web/__e2e__/models/items-view.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { BaseViewModel } from "./base-view.model";
import { ItemModel } from "./item.model";
diff --git a/apps/web/__e2e__/models/navigation-menu.model.ts b/apps/web/__e2e__/models/navigation-menu.model.ts
index 189cd1c44..54022f7ba 100644
--- a/apps/web/__e2e__/models/navigation-menu.model.ts
+++ b/apps/web/__e2e__/models/navigation-menu.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { ContextMenuModel } from "./context-menu.model";
import { confirmDialog, fillItemDialog, iterateList } from "./utils";
@@ -31,6 +31,10 @@ export class NavigationMenuModel {
this.menu = page.locator(getTestId(id));
}
+ async waitFor() {
+ await this.menu.waitFor();
+ }
+
async findItem(title: string) {
for await (const item of this.iterateList()) {
const menuItem = new NavigationItemModel(item);
diff --git a/apps/web/__e2e__/models/note-item.model.ts b/apps/web/__e2e__/models/note-item.model.ts
index 79c580852..eccf90bf5 100644
--- a/apps/web/__e2e__/models/note-item.model.ts
+++ b/apps/web/__e2e__/models/note-item.model.ts
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Locator } from "@playwright/test";
+import type { Locator } from "@playwright/test";
import { getTestId } from "../utils";
import { BaseItemModel } from "./base-item.model";
import { EditorModel } from "./editor.model";
diff --git a/apps/web/__e2e__/models/note-properties.model.ts b/apps/web/__e2e__/models/note-properties.model.ts
index ce9d1325a..028c3c24a 100644
--- a/apps/web/__e2e__/models/note-properties.model.ts
+++ b/apps/web/__e2e__/models/note-properties.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { downloadAndReadFile, getTestId } from "../utils";
import { ContextMenuModel } from "./context-menu.model";
import { ToggleModel } from "./toggle.model";
diff --git a/apps/web/__e2e__/models/notebook-item.model.ts b/apps/web/__e2e__/models/notebook-item.model.ts
index 06cb82e6a..d75df6a7a 100644
--- a/apps/web/__e2e__/models/notebook-item.model.ts
+++ b/apps/web/__e2e__/models/notebook-item.model.ts
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Locator } from "@playwright/test";
+import type { Locator } from "@playwright/test";
import { BaseItemModel } from "./base-item.model";
import { ContextMenuModel } from "./context-menu.model";
import { ToggleModel } from "./toggle.model";
diff --git a/apps/web/__e2e__/models/notebooks-view.model.ts b/apps/web/__e2e__/models/notebooks-view.model.ts
index 5d3a4ad74..1fdecff01 100644
--- a/apps/web/__e2e__/models/notebooks-view.model.ts
+++ b/apps/web/__e2e__/models/notebooks-view.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { BaseViewModel } from "./base-view.model";
import { NotebookItemModel } from "./notebook-item.model";
diff --git a/apps/web/__e2e__/models/notes-view.model.ts b/apps/web/__e2e__/models/notes-view.model.ts
index 0968a8b07..58c25e94c 100644
--- a/apps/web/__e2e__/models/notes-view.model.ts
+++ b/apps/web/__e2e__/models/notes-view.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { BaseViewModel } from "./base-view.model";
import { EditorModel } from "./editor.model";
diff --git a/apps/web/__e2e__/models/reminder-item.model.ts b/apps/web/__e2e__/models/reminder-item.model.ts
index a4714e0ce..02b977352 100644
--- a/apps/web/__e2e__/models/reminder-item.model.ts
+++ b/apps/web/__e2e__/models/reminder-item.model.ts
@@ -18,7 +18,7 @@ along with this program. If not, see .
*/
import { Reminder } from "@notesnook/core";
-import { Locator } from "@playwright/test";
+import type { Locator } from "@playwright/test";
import { getTestId } from "../utils";
import { BaseItemModel } from "./base-item.model";
import { ContextMenuModel } from "./context-menu.model";
diff --git a/apps/web/__e2e__/models/reminders-view.model.ts b/apps/web/__e2e__/models/reminders-view.model.ts
index 6748cff84..c36ae7887 100644
--- a/apps/web/__e2e__/models/reminders-view.model.ts
+++ b/apps/web/__e2e__/models/reminders-view.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { BaseViewModel } from "./base-view.model";
import { ReminderItemModel } from "./reminder-item.model";
diff --git a/apps/web/__e2e__/models/search-view-model.ts b/apps/web/__e2e__/models/search-view-model.ts
index 53f4e896a..9d96ef41d 100644
--- a/apps/web/__e2e__/models/search-view-model.ts
+++ b/apps/web/__e2e__/models/search-view-model.ts
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Page } from "@playwright/test";
+import type { Page } from "@playwright/test";
import { BaseViewModel } from "./base-view.model";
import { ItemModel } from "./item.model";
import { Item } from "./types";
diff --git a/apps/web/__e2e__/models/session-history-item-model.ts b/apps/web/__e2e__/models/session-history-item-model.ts
index 7b6e83d64..a6cc29754 100644
--- a/apps/web/__e2e__/models/session-history-item-model.ts
+++ b/apps/web/__e2e__/models/session-history-item-model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { NotePropertiesModel } from "./note-properties.model";
import { SessionHistoryPreviewModel } from "./session-history-preview-model";
diff --git a/apps/web/__e2e__/models/session-history-preview-model.ts b/apps/web/__e2e__/models/session-history-preview-model.ts
index f93f8642c..318f7745d 100644
--- a/apps/web/__e2e__/models/session-history-preview-model.ts
+++ b/apps/web/__e2e__/models/session-history-preview-model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { NotePropertiesModel } from "./note-properties.model";
diff --git a/apps/web/__e2e__/models/settings-view.model.ts b/apps/web/__e2e__/models/settings-view.model.ts
index 690e42be3..85960a637 100644
--- a/apps/web/__e2e__/models/settings-view.model.ts
+++ b/apps/web/__e2e__/models/settings-view.model.ts
@@ -17,8 +17,13 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Page } from "@playwright/test";
-import { downloadAndReadFile, getTestId, uploadFile } from "../utils";
+import type { Page } from "@playwright/test";
+import {
+ downloadAndReadFile,
+ getTestId,
+ IS_DESKTOP_TESTS,
+ uploadFile
+} from "../utils";
import {
confirmDialog,
fillConfirmPasswordDialog,
@@ -27,6 +32,9 @@ import {
waitToHaveText
} from "./utils";
import { NavigationMenuModel } from "./navigation-menu.model";
+import { AppModel } from "./app.model";
+import { getAppFromPage } from "../../../desktop/__tests__/electron-test/utils";
+import { readFile } from "node:fs/promises";
export class SettingsViewModel {
private readonly page: Page;
@@ -118,17 +126,35 @@ export class SettingsViewModel {
const item = await this.navigation.findItem("Backup & export");
await item?.click();
- return await downloadAndReadFile(
- this.page,
- async () => {
- const backupData = this.page
- .locator(getTestId("setting-create-backup"))
- .locator("select");
- await backupData.selectOption({ value: "partial", label: "Backup" });
- if (password) await fillPasswordDialog(this.page, password);
- },
- "utf-8"
- );
+ const saveBackup = async () => {
+ const backupData = this.page
+ .locator(getTestId("setting-create-backup"))
+ .locator("select");
+ await backupData.selectOption({ value: "partial", label: "Backup" });
+ if (password) await fillPasswordDialog(this.page, password);
+ await waitForDialog(this.page, "Creating a backup");
+ };
+
+ if (IS_DESKTOP_TESTS) {
+ await saveBackup();
+ const toast = new AppModel(this.page).toasts.toasts.locator(
+ getTestId("toast-message")
+ );
+ await toast.waitFor();
+ const backupPath = (await toast.textContent())
+ ?.replace("Backup saved at", "")
+ .trim();
+ if (!backupPath)
+ throw new Error("Backup path not found in toast message");
+ const app = getAppFromPage(this.page);
+ const resolvedDocumentPath = await app.evaluate(({ app }) =>
+ app.getPath("documents")
+ );
+ const resolvedBackupPath = backupPath.startsWith("documents")
+ ? backupPath.replace("documents", resolvedDocumentPath)
+ : backupPath;
+ return await readFile(resolvedBackupPath, "utf-8");
+ } else return await downloadAndReadFile(this.page, saveBackup, "utf-8");
}
async restoreData(filename: string, password?: string) {
@@ -191,4 +217,16 @@ export class SettingsViewModel {
.locator("input");
await titleFormatInput.fill(format);
}
+
+ async checkForUpdates() {
+ await this.navigation.waitFor();
+
+ const item = await this.navigation.findItem("About");
+ await item?.click();
+
+ const button = this.page
+ .locator(getTestId("setting-version"))
+ .locator("button", { hasText: "Check for updates" });
+ await button.click();
+ }
}
diff --git a/apps/web/__e2e__/models/subnotebooks-view.model.ts b/apps/web/__e2e__/models/subnotebooks-view.model.ts
index d4064107a..d02ad7e87 100644
--- a/apps/web/__e2e__/models/subnotebooks-view.model.ts
+++ b/apps/web/__e2e__/models/subnotebooks-view.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { BaseViewModel } from "./base-view.model";
import { NotebookItemModel } from "./notebook-item.model";
diff --git a/apps/web/__e2e__/models/tab-item.model.ts b/apps/web/__e2e__/models/tab-item.model.ts
index 3e0849bb5..002f72ca6 100644
--- a/apps/web/__e2e__/models/tab-item.model.ts
+++ b/apps/web/__e2e__/models/tab-item.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { ContextMenuModel } from "./context-menu.model";
diff --git a/apps/web/__e2e__/models/toasts.model.ts b/apps/web/__e2e__/models/toasts.model.ts
index ac06832ac..70a77cf60 100644
--- a/apps/web/__e2e__/models/toasts.model.ts
+++ b/apps/web/__e2e__/models/toasts.model.ts
@@ -17,11 +17,11 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
export class ToastsModel {
- private readonly toasts: Locator;
+ readonly toasts: Locator;
constructor(page: Page) {
this.toasts = page.locator(".toasts-container > div");
diff --git a/apps/web/__e2e__/models/toggle.model.ts b/apps/web/__e2e__/models/toggle.model.ts
index c8d5ce9ee..4de30b229 100644
--- a/apps/web/__e2e__/models/toggle.model.ts
+++ b/apps/web/__e2e__/models/toggle.model.ts
@@ -17,7 +17,7 @@ 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 type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
export enum TOGGLE_STATES {
diff --git a/apps/web/__e2e__/models/trash-item.model.ts b/apps/web/__e2e__/models/trash-item.model.ts
index 10b51e59d..0c4911d5b 100644
--- a/apps/web/__e2e__/models/trash-item.model.ts
+++ b/apps/web/__e2e__/models/trash-item.model.ts
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Locator } from "@playwright/test";
+import type { Locator } from "@playwright/test";
import { getTestId } from "../utils";
import { BaseItemModel } from "./base-item.model";
import { ContextMenuModel } from "./context-menu.model";
diff --git a/apps/web/__e2e__/models/trash-view.model.ts b/apps/web/__e2e__/models/trash-view.model.ts
index 06388e0a8..3dd91a9cf 100644
--- a/apps/web/__e2e__/models/trash-view.model.ts
+++ b/apps/web/__e2e__/models/trash-view.model.ts
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { Page } from "@playwright/test";
+import type { Page } from "@playwright/test";
import { getTestId } from "../utils";
import { BaseViewModel } from "./base-view.model";
import { TrashItemModel } from "./trash-item.model";
diff --git a/apps/web/__e2e__/models/utils.ts b/apps/web/__e2e__/models/utils.ts
index 1906c4afa..cab0c4e46 100644
--- a/apps/web/__e2e__/models/utils.ts
+++ b/apps/web/__e2e__/models/utils.ts
@@ -18,7 +18,7 @@ along with this program. If not, see .
*/
import { Reminder } from "@notesnook/core";
-import { Locator, Page } from "@playwright/test";
+import type { Locator, Page } from "@playwright/test";
import { getTestId } from "../utils";
import { Color, Item, Notebook } from "./types";
import dayjs from "dayjs";
diff --git a/apps/web/__e2e__/monographs.test.ts b/apps/web/__e2e__/monographs.test.ts
index 0122a73d3..646520f73 100644
--- a/apps/web/__e2e__/monographs.test.ts
+++ b/apps/web/__e2e__/monographs.test.ts
@@ -18,7 +18,8 @@ along with this program. If not, see .
*/
import { AppModel } from "./models/app.model";
-import { test, expect, USER } from "./utils";
+import { USER } from "./utils";
+import { test, expect } from "@nn/test";
test("don't show monographs list to unauthenticated user", async ({ page }) => {
const app = new AppModel(page);
diff --git a/apps/web/__e2e__/navigation.test.ts b/apps/web/__e2e__/navigation.test.ts
index 172928fd5..d5b8c3255 100644
--- a/apps/web/__e2e__/navigation.test.ts
+++ b/apps/web/__e2e__/navigation.test.ts
@@ -18,7 +18,7 @@ along with this program. If not, see .
*/
/* eslint-disable no-undef */
-import { test, expect } from "./utils";
+import { test, expect } from "@nn/test";
import { AppModel } from "./models/app.model";
function createRoute(key: string, header: string) {
diff --git a/apps/web/__e2e__/notebooks.issues.test.ts b/apps/web/__e2e__/notebooks.issues.test.ts
index f9e4928c0..053bf0752 100644
--- a/apps/web/__e2e__/notebooks.issues.test.ts
+++ b/apps/web/__e2e__/notebooks.issues.test.ts
@@ -17,6 +17,6 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { test } from "./utils";
+import { test } from "@nn/test";
test.skip("TODO: pin 3 notebooks & make sure they can be unpinned", () => {});
test.skip("TODO: pin 4 notebooks & make sure 4th pin shows an error", () => {});
diff --git a/apps/web/__e2e__/notebooks.test.ts b/apps/web/__e2e__/notebooks.test.ts
index 452a9edcf..a127a5a8b 100644
--- a/apps/web/__e2e__/notebooks.test.ts
+++ b/apps/web/__e2e__/notebooks.test.ts
@@ -20,14 +20,13 @@ along with this program. If not, see .
import { AppModel } from "./models/app.model";
import { Notebook } from "./models/types";
import {
- test,
- expect,
getTestId,
NOTE,
NOTEBOOK,
orderByOptions,
sortByOptions
} from "./utils";
+import { test, expect } from "@nn/test";
test("create a notebook", async ({ page }) => {
const app = new AppModel(page);
diff --git a/apps/web/__e2e__/notehistory.test.ts b/apps/web/__e2e__/notehistory.test.ts
index 02ae08591..6fd2d09a2 100644
--- a/apps/web/__e2e__/notehistory.test.ts
+++ b/apps/web/__e2e__/notehistory.test.ts
@@ -17,7 +17,8 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { test, expect, createHistorySession, PASSWORD } from "./utils";
+import { createHistorySession, PASSWORD } from "./utils";
+import { test, expect } from "@nn/test";
test.setTimeout(60 * 1000);
diff --git a/apps/web/__e2e__/notes.issues.test.ts b/apps/web/__e2e__/notes.issues.test.ts
index 01ab1b92d..4c8c34232 100644
--- a/apps/web/__e2e__/notes.issues.test.ts
+++ b/apps/web/__e2e__/notes.issues.test.ts
@@ -18,7 +18,9 @@ along with this program. If not, see .
*/
import { AppModel } from "./models/app.model";
-import { expect, test, NOTE } from "./utils";
+import { NOTE } from "./utils";
+import { test, expect } from "@nn/test";
+
test.skip("TODO: make sure jump to group works", () => {});
test("#1002 Can't add a tag that's a substring of an existing tag", async ({
diff --git a/apps/web/__e2e__/notes.test.ts b/apps/web/__e2e__/notes.test.ts
index 28e2df5a2..19e940125 100644
--- a/apps/web/__e2e__/notes.test.ts
+++ b/apps/web/__e2e__/notes.test.ts
@@ -20,14 +20,13 @@ along with this program. If not, see .
import dayjs from "dayjs";
import { AppModel } from "./models/app.model";
import {
- test,
- expect,
groupByOptions,
NOTE,
orderByOptions,
PASSWORD,
sortByOptions
} from "./utils";
+import { test, expect } from "@nn/test";
test("create a note", async ({ page }) => {
const app = new AppModel(page);
diff --git a/apps/web/__e2e__/notes.test.ts-snapshots/export-html-electron.txt b/apps/web/__e2e__/notes.test.ts-snapshots/export-html-electron.txt
new file mode 100644
index 000000000..d8eee9000
--- /dev/null
+++ b/apps/web/__e2e__/notes.test.ts-snapshots/export-html-electron.txt
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+ Test 1
+
+
+
+
+
+
+
+
+
+
+
+
+ Test 1
+ This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1
+
+
diff --git a/apps/web/__e2e__/notes.test.ts-snapshots/export-md-electron.txt b/apps/web/__e2e__/notes.test.ts-snapshots/export-md-electron.txt
new file mode 100644
index 000000000..2dcd8f1ab
--- /dev/null
+++ b/apps/web/__e2e__/notes.test.ts-snapshots/export-md-electron.txt
@@ -0,0 +1,4 @@
+# Test 1
+
+This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1
+
diff --git a/apps/web/__e2e__/notes.test.ts-snapshots/export-txt-electron.txt b/apps/web/__e2e__/notes.test.ts-snapshots/export-txt-electron.txt
new file mode 100644
index 000000000..a35ec00b8
--- /dev/null
+++ b/apps/web/__e2e__/notes.test.ts-snapshots/export-txt-electron.txt
@@ -0,0 +1,3 @@
+Test 1
+
+This is Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1Test 1
\ No newline at end of file
diff --git a/apps/web/__e2e__/reminders.test.ts b/apps/web/__e2e__/reminders.test.ts
index 647d24e9f..ba9e5dac8 100644
--- a/apps/web/__e2e__/reminders.test.ts
+++ b/apps/web/__e2e__/reminders.test.ts
@@ -19,7 +19,8 @@ along with this program. If not, see .
import { Reminder } from "@notesnook/core";
import { AppModel } from "./models/app.model";
-import { test, expect, getTestId } from "./utils";
+import { getTestId } from "./utils";
+import { test, expect } from "@nn/test";
const ONE_TIME_REMINDER: Partial = {
title: "Test reminder 1",
@@ -41,7 +42,7 @@ test("add a one-time reminder", async ({ page }, info) => {
expect(reminder).toBeDefined();
page.on("dialog", (dialog) => dialog.accept());
- await page.waitForEvent("dialog");
+ await page.waitForEvent("dialog", { timeout: 0 });
});
test("adding a one-time reminder before current time should not be possible", async ({
diff --git a/apps/web/__e2e__/search.test.ts b/apps/web/__e2e__/search.test.ts
index f26c7f1af..9aea17793 100644
--- a/apps/web/__e2e__/search.test.ts
+++ b/apps/web/__e2e__/search.test.ts
@@ -18,7 +18,8 @@ along with this program. If not, see .
*/
import { AppModel } from "./models/app.model";
-import { test, expect, getTestId } from "./utils";
+import { getTestId } from "./utils";
+import { test, expect } from "@nn/test";
test("closing search via close button should clear query", async ({ page }) => {
const app = new AppModel(page);
diff --git a/apps/web/__e2e__/settings.test.ts b/apps/web/__e2e__/settings.test.ts
index f5c444cc4..22a6c7f69 100644
--- a/apps/web/__e2e__/settings.test.ts
+++ b/apps/web/__e2e__/settings.test.ts
@@ -18,7 +18,8 @@ along with this program. If not, see .
*/
import { AppModel } from "./models/app.model";
-import { test, expect, NOTE } from "./utils";
+import { NOTE } from "./utils";
+import { test, expect } from "@nn/test";
test("ask for image compression during image upload when 'Image Compression' setting is 'Ask every time'", async ({
page
diff --git a/apps/web/__e2e__/sync.test.ts b/apps/web/__e2e__/sync.test.ts
index 54d5315cc..20973f3bb 100644
--- a/apps/web/__e2e__/sync.test.ts
+++ b/apps/web/__e2e__/sync.test.ts
@@ -16,18 +16,17 @@ 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 { Browser } from "@playwright/test";
+
import { AppModel } from "./models/app.model";
-import { test, expect, USER } from "./utils";
+import { USER } from "./utils";
+import { test, expect, TestArgs } from "@nn/test";
// run this test file sequentially
test.describe.configure({ mode: "serial" });
-async function createDevice(browser: Browser) {
- // Create two isolated browser contexts
- const context = await browser.newContext();
- const page = await context.newPage();
-
+async function createDevice(args: { newPage: TestArgs["newPage"] }) {
+ const page = await args.newPage?.();
+ if (!page) throw new Error("Page not found");
const app = new AppModel(page);
await app.auth.goto();
await app.auth.login(USER.CURRENT);
@@ -36,56 +35,44 @@ async function createDevice(browser: Browser) {
return app;
}
-async function actAndSync(
- devices: AppModel[],
- ...actions: (Promise | undefined)[]
-) {
- const results = await Promise.all([
- ...actions.filter((a) => !!a),
- ...devices.map((d) =>
- d.waitForSync("syncing").then(() => d.waitForSync("synced"))
- )
- ]);
-
- await Promise.all(devices.map((d) => d.page.waitForTimeout(2000)));
- return results.slice(0, actions.length) as T[];
-}
-
+test.setTimeout(120 * 1000);
test(`content edits in a note opened on 2 devices in multiple tabs should sync in real-time`, async ({
- browser
+ newPage
}, info) => {
const NOTE = {
title: `Note ${makeid(20)}`
};
- info.setTimeout(70 * 1000);
+ info.setTimeout(120 * 1000);
const newContent = makeid(24).repeat(2);
const [deviceA, deviceB] = await Promise.all([
- createDevice(browser),
- createDevice(browser)
+ createDevice({ newPage }),
+ createDevice({ newPage })
]);
const [notesA, notesB] = await Promise.all(
[deviceA, deviceB].map((d) => d.goToNotes())
);
- const noteB = (
- await actAndSync([deviceA, deviceB], notesB.createNote(NOTE))
- )[0];
- const noteA = await notesA.findNote(NOTE);
- await Promise.all([noteA, noteB].map((note) => note?.openNote()));
- await noteA?.openNote(true);
-
- if ((await notesB.editor.getContent("text")) !== "")
- await actAndSync([deviceA, deviceB], notesB.editor.clear());
- await expect(notesA.editor.content).toBeEmpty();
- await expect(notesB.editor.content).toBeEmpty();
- await actAndSync([deviceA, deviceB], notesB.editor.setContent(newContent));
+ const noteB = await notesB.createNote(NOTE);
+ await notesA.waitForItem(NOTE.title, 0);
+ const noteA = await notesA.findNote({ title: NOTE.title });
expect(noteA).toBeDefined();
expect(noteB).toBeDefined();
+
+ await noteA?.openNote();
+ await noteA?.openNote(true);
+
+ await expect(notesA.editor.content).toBeEmpty();
+ await expect(notesB.editor.content).toBeEmpty();
+
+ await notesA.editor.setContent(newContent);
+ await notesB.editor.waitForContent(newContent, 0);
+
await expect(notesA.editor.content).toHaveText(newContent);
await expect(notesB.editor.content).toHaveText(newContent);
+
const tabsA = await notesA.editor.getTabs();
await tabsA[0].click();
await expect(notesA.editor.content).toHaveText(newContent);
@@ -95,40 +82,47 @@ test(`content edits in a note opened on 2 devices in multiple tabs should sync i
});
test(`title edits in a note opened on 2 devices in multiple tabs should sync in real-time`, async ({
- browser
+ newPage
}, info) => {
const NOTE = {
title: `Note ${makeid(20)}`
};
- info.setTimeout(70 * 1000);
+ info.setTimeout(120 * 1000);
const newContent = makeid(24).repeat(2);
const [deviceA, deviceB] = await Promise.all([
- createDevice(browser),
- createDevice(browser)
+ createDevice({
+ newPage
+ }),
+ createDevice({
+ newPage
+ })
]);
const [notesA, notesB] = await Promise.all(
[deviceA, deviceB].map((d) => d.goToNotes())
);
- const noteB = (
- await actAndSync([deviceA, deviceB], notesB.createNote(NOTE))
- )[0];
- const noteA = await notesA.findNote(NOTE);
- await Promise.all([noteA, noteB].map((note) => note?.openNote()));
- await noteA?.openNote(true);
- if ((await notesB.editor.getContent("text")) !== "")
- await actAndSync([deviceA, deviceB], notesB.editor.clear());
- await expect(notesA.editor.content).toBeEmpty();
- await expect(notesB.editor.content).toBeEmpty();
- await actAndSync([deviceA, deviceB], notesB.editor.setTitle(newContent));
+ const noteB = await notesB.createNote(NOTE);
+ await notesA.waitForItem(NOTE.title, 0);
+ const noteA = await notesA.findNote({ title: NOTE.title });
expect(noteA).toBeDefined();
expect(noteB).toBeDefined();
+
+ await noteA?.openNote();
+ await noteA?.openNote(true);
+
+ await expect(notesA.editor.content).toBeEmpty();
+ await expect(notesB.editor.content).toBeEmpty();
+
+ await notesA.editor.setTitle(newContent);
+ await notesB.editor.waitForTitle(newContent, 0);
+
expect(await notesA.editor.getTitle()).toBe(newContent);
expect(await notesB.editor.getTitle()).toBe(newContent);
+
const tabsA = await notesA.editor.getTabs();
await tabsA[0].click();
expect(await notesA.editor.getTitle()).toBe(newContent);
diff --git a/apps/web/__e2e__/tabs.test.ts b/apps/web/__e2e__/tabs.test.ts
index ab8810e50..25d2192d4 100644
--- a/apps/web/__e2e__/tabs.test.ts
+++ b/apps/web/__e2e__/tabs.test.ts
@@ -18,7 +18,8 @@ along with this program. If not, see .
*/
import { AppModel } from "./models/app.model";
-import { test, expect, createHistorySession, NOTE } from "./utils";
+import { createHistorySession, NOTE } from "./utils";
+import { test, expect } from "@nn/test";
test("notes should open in the same tab", async ({ page }) => {
const app = new AppModel(page);
diff --git a/apps/web/__e2e__/tags.test.ts b/apps/web/__e2e__/tags.test.ts
index ddfead5ff..dbdffb7b2 100644
--- a/apps/web/__e2e__/tags.test.ts
+++ b/apps/web/__e2e__/tags.test.ts
@@ -19,14 +19,8 @@ along with this program. If not, see .
import { AppModel } from "./models/app.model";
import { Item } from "./models/types";
-import {
- test,
- expect,
- getTestId,
- NOTE,
- orderByOptions,
- sortByOptions
-} from "./utils";
+import { getTestId, NOTE, orderByOptions, sortByOptions } from "./utils";
+import { test, expect } from "@nn/test";
const TAG: Item = { title: "hello-world" };
const EDITED_TAG: Item = { title: "hello-world-2" };
diff --git a/apps/web/__e2e__/user.test.ts b/apps/web/__e2e__/user.test.ts
index 003d2000f..f32923059 100644
--- a/apps/web/__e2e__/user.test.ts
+++ b/apps/web/__e2e__/user.test.ts
@@ -18,7 +18,8 @@ along with this program. If not, see .
*/
import { AppModel } from "./models/app.model";
-import { test, expect, USER } from "./utils";
+import { USER } from "./utils";
+import { test, expect } from "@nn/test";
// async function forceExpireSession() {
// await page.evaluate(() => {
diff --git a/apps/web/__e2e__/utils/index.ts b/apps/web/__e2e__/utils/index.ts
index 9f6baa34e..2ce9014b9 100644
--- a/apps/web/__e2e__/utils/index.ts
+++ b/apps/web/__e2e__/utils/index.ts
@@ -20,7 +20,7 @@ along with this program. If not, see .
import fs from "fs";
import dotenv from "dotenv";
import path, { join } from "path";
-import { test as base, Locator, Page } from "@playwright/test";
+import type { Locator, Page } from "@playwright/test";
import {
GroupByOptions,
Notebook,
@@ -30,18 +30,7 @@ import {
import { tmpdir } from "os";
import { AppModel } from "../models/app.model";
-export type { Browser, Page } from "@playwright/test";
-export { expect } from "@playwright/test";
-
-const test = base.extend>({
- page: async ({ page }, use) => {
- const client = await page.context().newCDPSession(page);
- await client.send("Emulation.setCPUThrottlingRate", {
- rate: 1
- });
- await use(page);
- }
-});
+export const IS_DESKTOP_TESTS = process.env.TEST_DESKTOP === "true";
type Note = {
title: string;
@@ -124,7 +113,7 @@ async function downloadAndReadFile(
page: Page,
action: () => Promise,
encoding: BufferEncoding | null | undefined = "utf-8"
-) {
+): Promise {
const [download] = await Promise.all([
page.waitForEvent("download"),
action()
@@ -206,7 +195,6 @@ export async function createHistorySession(page: Page, locked = false) {
}
export {
- test,
USER,
NOTE,
TITLE_ONLY_NOTE,
diff --git a/apps/web/__e2e__/utils/test.ts b/apps/web/__e2e__/utils/test.ts
new file mode 100644
index 000000000..ab4afd46f
--- /dev/null
+++ b/apps/web/__e2e__/utils/test.ts
@@ -0,0 +1,53 @@
+/*
+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 as base,
+ PlaywrightTestArgs,
+ PlaywrightTestOptions,
+ PlaywrightWorkerArgs,
+ PlaywrightWorkerOptions
+} from "@playwright/test";
+import { ElectronTestFixtures } from "../../../desktop/__tests__/electron-test";
+
+export { expect } from "@playwright/test";
+export type { Page, Browser } from "@playwright/test";
+export type TestArgs = PlaywrightTestArgs &
+ PlaywrightTestOptions &
+ PlaywrightWorkerArgs &
+ PlaywrightWorkerOptions &
+ Partial;
+export const test = base.extend<
+ NonNullable & Partial
+>({
+ page: async ({ page }, use) => {
+ const client = await page.context().newCDPSession(page);
+ await client.send("Emulation.setCPUThrottlingRate", {
+ rate: 1
+ });
+ await use(page);
+ },
+ newPage: async ({ browser }, use) => {
+ await use(async () => {
+ const context = await browser.newContext();
+ const page = await context.newPage();
+ return page;
+ });
+ }
+});
diff --git a/apps/web/__e2e__/vault.test.ts b/apps/web/__e2e__/vault.test.ts
index 9f234d496..54cfac639 100644
--- a/apps/web/__e2e__/vault.test.ts
+++ b/apps/web/__e2e__/vault.test.ts
@@ -18,7 +18,8 @@ along with this program. If not, see .
*/
import { AppModel } from "./models/app.model";
-import { test, expect, getTestId, NOTE, PASSWORD } from "./utils";
+import { getTestId, NOTE, PASSWORD } from "./utils";
+import { test, expect } from "@nn/test";
test("locking a note should show vault unlocked status", async ({ page }) => {
const app = new AppModel(page);
diff --git a/apps/web/package.json b/apps/web/package.json
index 39d3964b0..1b6ce9a9c 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -87,7 +87,7 @@
"devDependencies": {
"@babel/core": "7.22.5",
"@cloudflare/workers-types": "^4.20250224.0",
- "@playwright/test": "^1.58.2",
+ "@playwright/test": "1.58.2",
"@swc/plugin-react-remove-properties": "^6.0.2",
"@trpc/server": "10.45.2",
"@types/babel__core": "^7.20.1",
@@ -136,6 +136,7 @@
"build:test": "cross-env PLATFORM=web TEST=true vite build",
"build:beta": "cross-env PLATFORM=web BETA=true vite build",
"build:desktop": "cross-env PLATFORM=desktop vite build",
+ "build:test:desktop": "cross-env PLATFORM=desktop TEST=true vite build",
"analyze": "cross-env ANALYZING=true PLATFORM=web vite build",
"test": "playwright test -u",
"postinstall": "patch-package"
diff --git a/apps/web/src/common/index.ts b/apps/web/src/common/index.ts
index 3d17d28d5..14c5c89e2 100644
--- a/apps/web/src/common/index.ts
+++ b/apps/web/src/common/index.ts
@@ -62,6 +62,7 @@ import { UpgradeDialog } from "../dialogs/buy-dialog/upgrade-dialog";
import { setToolbarPreset } from "./toolbar-config";
import { useKeyStore } from "../interfaces/key-store";
import { TaskScheduler } from "../utils/task-scheduler";
+import { path } from "@notesnook-importer/core/dist/src/utils/path";
export const CREATE_BUTTON_MAP = {
notes: {
@@ -187,7 +188,13 @@ export async function createBackup(
);
console.error(error);
} else {
- showToast("success", `${strings.backupSavedAt(filePath)}`);
+ const backupDirectory = useSettingStore.getState().backupStorageLocation;
+ showToast(
+ "success",
+ IS_DESKTOP_APP
+ ? `${strings.backupSavedAt(path.join(backupDirectory, filePath))}`
+ : strings.backupSuccess()
+ );
return true;
}
return false;
diff --git a/apps/web/src/hooks/use-menu.ts b/apps/web/src/hooks/use-menu.ts
index f553b64e4..a4ff94bcb 100644
--- a/apps/web/src/hooks/use-menu.ts
+++ b/apps/web/src/hooks/use-menu.ts
@@ -137,5 +137,6 @@ function findAndCallAction(items: MenuItem[], ids: string[]) {
}
function canShowNativeMenu(items: MenuItem[]) {
+ if (IS_TESTING) return false;
return items.every((item) => item.type !== "popup");
}
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
index d04d7fd36..a9a85d366 100644
--- a/apps/web/tsconfig.json
+++ b/apps/web/tsconfig.json
@@ -1,11 +1,14 @@
{
- "extends": "../../tsconfig",
+ "extends": "../../tsconfig.json",
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"jsx": "react-jsx",
"moduleResolution": "Bundler",
- "noEmit": true
+ "noEmit": true,
+ "paths": {
+ "@nn/test": ["./__e2e__/utils/test.ts"]
+ }
},
"include": ["src", "global.d.ts", "__tests__", "__e2e__"]
}
diff --git a/package.json b/package.json
index 942587903..2fc901a07 100644
--- a/package.json
+++ b/package.json
@@ -96,6 +96,7 @@
"build",
"release",
"build:test",
+ "build:test:desktop",
"build:beta",
"build:desktop",
"start",