mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 19:57:52 +01:00
web: add tests for backups (#1542)
Signed-off-by: Muhammad Ali <alihamuh@gmail.com> Co-authored-by: Abdullah Atta <abdullahatta@streetwriters.co>
This commit is contained in:
74
apps/web/__e2e__/backups.test.ts
Normal file
74
apps/web/__e2e__/backups.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import { test, Browser, expect } from "@playwright/test";
|
||||
import { AppModel } from "./models/app.model";
|
||||
import { NOTE, USER } from "./utils";
|
||||
|
||||
test("create a backup", async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
const notes = await app.goToNotes();
|
||||
await notes.createNote(NOTE);
|
||||
const settings = await app.goToSettings();
|
||||
const backup = await settings.createBackup();
|
||||
expect(backup.length > 0).toBeTruthy();
|
||||
});
|
||||
|
||||
test.setTimeout(45 * 1000);
|
||||
test("restore a backup", async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
|
||||
const settings = await app.goToSettings();
|
||||
await settings.restoreData("backup.nnbackup", USER.CURRENT.password);
|
||||
const notes = await app.goToNotes();
|
||||
expect(await notes.isEmpty()).toBeTruthy();
|
||||
const notebooks = await app.goToNotebooks();
|
||||
expect(await notebooks.isEmpty()).toBeTruthy();
|
||||
const favotites = await app.goToFavorites();
|
||||
expect(await favotites.isEmpty()).toBeTruthy();
|
||||
const tags = await app.goToTags();
|
||||
expect(await tags.isEmpty()).toBeTruthy();
|
||||
});
|
||||
|
||||
test("create an encrypted backup", async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.auth.goto();
|
||||
await app.auth.login(USER.CURRENT);
|
||||
const settings = await app.goToSettings();
|
||||
const backup = await settings.createBackup(USER.CURRENT.password);
|
||||
|
||||
expect(backup.length > 0).toBeTruthy();
|
||||
});
|
||||
|
||||
test("restore an encrypted backup", async ({ page }) => {
|
||||
const app = new AppModel(page);
|
||||
await app.goto();
|
||||
|
||||
const settings = await app.goToSettings();
|
||||
await settings.restoreData("encrypted.nnbackup", USER.CURRENT.password);
|
||||
const notes = await app.goToNotes();
|
||||
expect(await notes.isEmpty()).toBeTruthy();
|
||||
const notebooks = await app.goToNotebooks();
|
||||
expect(await notebooks.isEmpty()).toBeTruthy();
|
||||
const favotites = await app.goToFavorites();
|
||||
expect(await favotites.isEmpty()).toBeTruthy();
|
||||
const tags = await app.goToTags();
|
||||
expect(await tags.isEmpty()).toBeTruthy();
|
||||
});
|
||||
1
apps/web/__e2e__/data/backup.nnbackup
Normal file
1
apps/web/__e2e__/data/backup.nnbackup
Normal file
File diff suppressed because one or more lines are too long
1
apps/web/__e2e__/data/encrypted.nnbackup
Normal file
1
apps/web/__e2e__/data/encrypted.nnbackup
Normal file
File diff suppressed because one or more lines are too long
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Locator, Page } from "@playwright/test";
|
||||
import { getTestId } from "../utils";
|
||||
import { downloadAndReadFile, getTestId, uploadFile } from "../utils";
|
||||
import { confirmDialog, fillPasswordDialog, waitToHaveText } from "./utils";
|
||||
|
||||
export class SettingsViewModel {
|
||||
@@ -26,6 +26,10 @@ export class SettingsViewModel {
|
||||
private readonly logoutButton: Locator;
|
||||
private readonly accountStatusContainer: Locator;
|
||||
private readonly backupRecoveryKeyButton: Locator;
|
||||
private readonly backupRestoreContainer: Locator;
|
||||
private readonly backupData: Locator;
|
||||
private readonly restoreBackup: Locator;
|
||||
private readonly encyptBackups: Locator;
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.logoutButton = page.locator(getTestId("settings-logout"));
|
||||
@@ -33,6 +37,10 @@ export class SettingsViewModel {
|
||||
this.backupRecoveryKeyButton = page.locator(
|
||||
getTestId("backup-recovery-key")
|
||||
);
|
||||
this.backupRestoreContainer = page.locator(getTestId("backup-restore"));
|
||||
this.backupData = page.locator(getTestId("backup-data"));
|
||||
this.restoreBackup = page.locator(getTestId("restore-backup"));
|
||||
this.encyptBackups = page.locator(getTestId("encrypt-backups"));
|
||||
}
|
||||
|
||||
async logout() {
|
||||
@@ -59,4 +67,19 @@ export class SettingsViewModel {
|
||||
async isLoggedIn() {
|
||||
return await this.accountStatusContainer.isVisible();
|
||||
}
|
||||
|
||||
async createBackup(password?: string) {
|
||||
await this.backupRestoreContainer.click();
|
||||
if (password) await this.encyptBackups.click();
|
||||
await this.backupData.click();
|
||||
if (password) await fillPasswordDialog(this.page, password);
|
||||
return await downloadAndReadFile(this.page, this.backupData, "utf-8");
|
||||
}
|
||||
|
||||
async restoreData(filename: string, password: string | undefined) {
|
||||
await this.backupRestoreContainer.click();
|
||||
await this.restoreBackup.click();
|
||||
await uploadFile(this.page, this.restoreBackup, filename);
|
||||
if (password) await fillPasswordDialog(this.page, password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,15 @@ async function downloadAndReadFile(
|
||||
return fs.readFileSync(path, { encoding });
|
||||
}
|
||||
|
||||
async function uploadFile(page: Page, action: Locator, filename: string) {
|
||||
const [fileChooser] = await Promise.all([
|
||||
page.waitForEvent("filechooser"),
|
||||
await action.click()
|
||||
]);
|
||||
|
||||
await fileChooser.setFiles(path.join(__dirname, "../data", filename));
|
||||
}
|
||||
|
||||
function isTestAll() {
|
||||
return process.env.TEST_ALL === "true";
|
||||
}
|
||||
@@ -135,6 +144,7 @@ export {
|
||||
createNote,
|
||||
editNote,
|
||||
downloadAndReadFile,
|
||||
uploadFile,
|
||||
isTestAll,
|
||||
orderByOptions,
|
||||
sortByOptions,
|
||||
|
||||
@@ -25,7 +25,16 @@ import { Flex } from "@theme-ui/components";
|
||||
import Switch from "../switch";
|
||||
|
||||
function Toggle(props) {
|
||||
const { title, onTip, offTip, isToggled, onToggled, onlyIf, premium } = props;
|
||||
const {
|
||||
title,
|
||||
onTip,
|
||||
offTip,
|
||||
isToggled,
|
||||
onToggled,
|
||||
onlyIf,
|
||||
premium,
|
||||
testId
|
||||
} = props;
|
||||
const onClick = useCallback(async () => {
|
||||
if (isUserPremium() || !premium || isToggled) onToggled();
|
||||
else {
|
||||
@@ -37,6 +46,7 @@ function Toggle(props) {
|
||||
return (
|
||||
<Flex
|
||||
onClick={onClick}
|
||||
data-test-id={testId}
|
||||
py={2}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
|
||||
@@ -54,7 +54,7 @@ import { appVersion } from "../utils/version";
|
||||
import { CHECK_IDS } from "@notesnook/core/common";
|
||||
import Tip from "../components/tip";
|
||||
import Toggle from "../components/toggle";
|
||||
import { isDesktop, isMacStoreApp } from "../utils/platform";
|
||||
import { isDesktop, isMacStoreApp, isTesting } from "../utils/platform";
|
||||
import Vault from "../common/vault";
|
||||
import { isUserPremium } from "../hooks/use-is-user-premium";
|
||||
import { Slider } from "@theme-ui/components";
|
||||
@@ -529,6 +529,7 @@ function Settings() {
|
||||
</>
|
||||
)}
|
||||
<Header
|
||||
testId={"backup-restore"}
|
||||
title="Backup & restore"
|
||||
isOpen={groups.backup}
|
||||
onClick={() => {
|
||||
@@ -539,6 +540,7 @@ function Settings() {
|
||||
{groups.backup && (
|
||||
<>
|
||||
<Button
|
||||
data-test-id={"backup-data"}
|
||||
variant="list"
|
||||
onClick={async () => {
|
||||
if (!isUserPremium() && encryptBackups) toggleEncryptBackups();
|
||||
@@ -571,9 +573,10 @@ function Settings() {
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{isLoggedIn && (
|
||||
{(isLoggedIn || isTesting()) && (
|
||||
<>
|
||||
<Button
|
||||
data-test-id="restore-backup"
|
||||
variant="list"
|
||||
onClick={async () => {
|
||||
await importBackup();
|
||||
@@ -587,6 +590,7 @@ function Settings() {
|
||||
</Button>
|
||||
<Toggle
|
||||
title="Encrypt backups"
|
||||
testId="encrypt-backups"
|
||||
onTip="All backup files will be encrypted"
|
||||
offTip="Backup files will not be encrypted"
|
||||
onToggled={toggleEncryptBackups}
|
||||
|
||||
Reference in New Issue
Block a user