mobile: fix e2e tests

This commit is contained in:
Ammar Ahmed
2024-11-11 18:44:28 +05:00
committed by Abdullah Atta
parent e36bc816ea
commit e0a704d022
33 changed files with 4137 additions and 4090 deletions

View File

@@ -39,6 +39,7 @@ DerivedData
*.xcuserstate *.xcuserstate
*.hprof *.hprof
**/.xcode.env.local **/.xcode.env.local
native/cache
# Android/IntelliJ # Android/IntelliJ
# #
rn-build-deps/ rn-build-deps/

View File

@@ -311,8 +311,10 @@ export class VaultDialog extends Component {
loading: true loading: true
}); });
try { try {
let verified = await db.user.verifyPassword(this.password); let verified = true;
if (!(await db.user.getUser())) verified = true; if (await db.user.getUser()) {
verified = await db.user.verifyPassword(this.password);
}
if (verified) { if (verified) {
let noteIds = []; let noteIds = [];
if (this.state.deleteAll) { if (this.state.deleteAll) {
@@ -320,20 +322,21 @@ export class VaultDialog extends Component {
const relations = await db.relations.from(vault, "note").get(); const relations = await db.relations.from(vault, "note").get();
noteIds = relations.map((item) => item.toId); noteIds = relations.map((item) => item.toId);
} }
await db.vault.delete(this.state.deleteAll); await db.vault.delete(this.state.deleteAll);
noteIds.forEach((id) => { if (this.state.deleteAll) {
eSendEvent( noteIds.forEach((id) => {
eUpdateNoteInEditor, eSendEvent(
{ eUpdateNoteInEditor,
id: id, {
deleted: true id: id,
}, deleted: true
true },
); true
}); );
});
}
console.log("VAULT UPDATED EVENT");
eSendEvent("vaultUpdated"); eSendEvent("vaultUpdated");
this.setState({ this.setState({
loading: false loading: false
@@ -517,7 +520,6 @@ export class VaultDialog extends Component {
}); });
this.close(); this.close();
} else { } else {
eSendEvent("vaultUpdated");
ToastManager.show({ ToastManager.show({
heading: strings.vaultCreated(), heading: strings.vaultCreated(),
type: "success", type: "success",
@@ -525,6 +527,7 @@ export class VaultDialog extends Component {
}); });
this.close(); this.close();
} }
eSendEvent("vaultUpdated");
} }
_permanantUnlock() { _permanantUnlock() {

View File

@@ -206,7 +206,7 @@ export const NotebookSheet = () => {
backgroundInteractionEnabled backgroundInteractionEnabled
gestureEnabled gestureEnabled
> >
<View {/* <View
style={{ style={{
position: "absolute", position: "absolute",
right: 24 + normalize(50), right: 24 + normalize(50),
@@ -214,17 +214,11 @@ export const NotebookSheet = () => {
}} }}
> >
<Pressable <Pressable
testID={notesnook.buttons.add} testID="add-notebook-button"
type="secondary" type="secondary"
onPress={() => { onPress={() => {
if (!notebook) return; if (!notebook) return;
AddNotebookSheet.present(
undefined,
notebook,
undefined,
undefined,
false
);
}} }}
style={{ style={{
borderRadius: 100 borderRadius: 100
@@ -245,7 +239,7 @@ export const NotebookSheet = () => {
/> />
</View> </View>
</Pressable> </Pressable>
</View> </View> */}
<View <View
style={{ style={{
@@ -404,6 +398,27 @@ export const NotebookSheet = () => {
height: 40 * fontScale height: 40 * fontScale
}} }}
/> />
<IconButton
testID="add-notebook-button"
name="notebook-plus"
onPress={() => {
if (!notebook) return;
AddNotebookSheet.present(
undefined,
notebook,
undefined,
undefined,
false
);
}}
color={colors.primary.icon}
size={22}
style={{
width: 40 * fontScale,
height: 40 * fontScale
}}
/>
</> </>
)} )}
</View> </View>

View File

@@ -958,7 +958,9 @@ export const useActions = ({
title: title:
item.type !== "notebook" && item.type !== "note" item.type !== "notebook" && item.type !== "note"
? strings.doActions.delete[ ? strings.doActions.delete[
item.type as keyof typeof strings.doActions.delete item.type === "trash"
? item.itemType
: (item.type as keyof typeof strings.doActions.delete)
](1) ](1)
: strings.moveToTrash(), : strings.moveToTrash(),
icon: "delete-outline", icon: "delete-outline",

View File

@@ -18,11 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import React, { useCallback, useEffect } from "react"; import React, { useCallback, useEffect } from "react";
import { db } from "../common/database";
import BiometricService from "../services/biometrics"; import BiometricService from "../services/biometrics";
import { eSubscribeEvent, eUnSubscribeEvent } from "../services/event-manager"; import { eSubscribeEvent, eUnSubscribeEvent } from "../services/event-manager";
import { db } from "../common/database";
const VaultStatusCache = { const VaultStatusDefaults = {
exists: false, exists: false,
biometryEnrolled: false, biometryEnrolled: false,
isBiometryAvailable: false isBiometryAvailable: false
@@ -35,18 +35,12 @@ export type VaultStatusType = {
}; };
export const useVaultStatus = () => { export const useVaultStatus = () => {
const [vaultStatus, setVaultStatus] = React.useState(VaultStatusCache); const [vaultStatus, setVaultStatus] = React.useState(VaultStatusDefaults);
const checkVaultStatus = useCallback(() => { const checkVaultStatus = useCallback(() => {
db.vault?.exists().then(async (exists) => { db.vault?.exists().then(async (exists) => {
const available = await BiometricService.isBiometryAvailable(); const available = await BiometricService.isBiometryAvailable();
const fingerprint = await BiometricService.hasInternetCredentials(); const fingerprint = await BiometricService.hasInternetCredentials();
if (
VaultStatusCache.exists === exists &&
VaultStatusCache.biometryEnrolled === fingerprint &&
VaultStatusCache.isBiometryAvailable === available
)
return;
setVaultStatus({ setVaultStatus({
exists: exists, exists: exists,
biometryEnrolled: fingerprint, biometryEnrolled: fingerprint,

View File

@@ -0,0 +1,29 @@
import { execSync } from "child_process";
import { pathExists, ensureDir } from "fs-extra";
import { resolveConfig } from "detox/internals";
import { globalSetup } from "detox/runners/jest";
export default async function customGlobalSetup() {
const config = await resolveConfig();
if (config.device.type === "android.emulator") {
await downloadTestButlerAPK();
}
await globalSetup();
}
async function downloadTestButlerAPK() {
const version = "2.2.1";
const artifactUrl = `https://repo1.maven.org/maven2/com/linkedin/testbutler/test-butler-app/${version}/test-butler-app-${version}.apk`;
const filePath = `cache/test-butler-app.apk`;
await ensureDir("cache");
if (!(await pathExists(filePath))) {
console.log(`\nDownloading Test-Butler APK v${version}...`);
execSync(`curl -f -o ${filePath} ${artifactUrl}`);
}
}
module.exports = customGlobalSetup;

View File

@@ -1,20 +1,21 @@
/** @type {import('@jest/types').Config.InitialOptions} */ /** @type {import('@jest/types').Config.InitialOptions} */
module.exports = { module.exports = {
rootDir: "..", rootDir: "..",
testMatch: ["<rootDir>/e2e/**/*.e2e.js"],
testTimeout: 120000,
maxWorkers: 1, maxWorkers: 1,
setupFilesAfterEnv: ["./e2e/setup.js"], testTimeout: 120000,
globalSetup: "detox/runners/jest/globalSetup", globalSetup: "./e2e/globalSetup.ts",
globalTeardown: "detox/runners/jest/globalTeardown", globalTeardown: "detox/runners/jest/globalTeardown",
reporters: ["detox/runners/jest/reporter"], setupFilesAfterEnv: ["./e2e/setup.ts"],
testEnvironment: "detox/runners/jest/testEnvironment", testEnvironment: "detox/runners/jest/testEnvironment",
verbose: true, reporters: ["detox/runners/jest/reporter"],
testRunner: "jest-circus/runner",
testMatch: ["<rootDir>/e2e/**/*.e2e.(js|ts)"],
transform: { transform: {
"^.+\\.(js|jsx|ts|tsx)$": [ "\\.tsx?$": "ts-jest",
"^.+\\.(js|jsx)$": [
"babel-jest", "babel-jest",
{ configFile: "../native/babel.config.js" } { configFile: "../native/babel.config.js" }
] ]
}, },
transformIgnorePatterns: ["<rootDir>/../node_modules/"] verbose: true
}; };

View File

@@ -74,7 +74,7 @@ async function login() {
// } // }
describe("AUTH", () => { describe("AUTH", () => {
it("Login", async () => { it.skip("Login", async () => {
await prepare(); await prepare();
await openSideMenu(); await openSideMenu();
await login(); await login();

View File

@@ -17,23 +17,22 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { navigate, tapByText, prepare, sleep } from "./utils"; import { Tests } from "./utils";
describe("APP LAUNCH AND NAVIGATION", () => { describe("APP LAUNCH AND NAVIGATION", () => {
it("App should launch successfully & hide welcome screen", async () => { it("App should launch successfully & hide welcome screen", async () => {
await prepare(); await Tests.prepare();
}); });
it("Basic navigation should work", async () => { it("Basic navigation should work", async () => {
await prepare(); await Tests.prepare();
await navigate("Notebooks"); await Tests.navigate("Notebooks");
await sleep(500); await Tests.navigate("Favorites");
await navigate("Favorites"); await Tests.navigate("Trash");
await navigate("Trash"); await Tests.navigate("Tags");
await navigate("Tags"); await Tests.navigate("Settings");
await navigate("Settings"); await Tests.navigate("Monographs");
await navigate("Monographs"); await Tests.navigate("Reminders");
await navigate("Reminders"); await Tests.navigate("Notes");
await navigate("Notes");
}); });
}); });

View File

@@ -1,140 +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 <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import {
navigate,
tapById,
visibleByText,
createNote,
prepare,
visibleById,
notVisibleById,
sleep,
exitEditor,
tapByText,
elementByText,
elementById
} from "./utils";
describe("NOTE TESTS", () => {
it("Create a note in editor", async () => {
await prepare();
await createNote();
});
it("Open and close a note", async () => {
await prepare();
await createNote();
await tapById(notesnook.ids.note.get(0));
await exitEditor();
});
it.only("Notes properties should show", async () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await waitFor(elementByText("Created at:")).toBeVisible().withTimeout(500);
});
it("Favorite and unfavorite a note", async () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-favorite");
await visibleById("icon-star");
await navigate("Favorites");
await visibleByText(note.body);
await sleep(500);
await tapById(notesnook.listitem.menu);
await tapById("icon-favorite");
await expect(element(by.text(note.body))).not.toBeVisible();
await navigate("Notes");
});
it("Pin a note to top", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-pin");
await visibleByText("Pinned");
await visibleById("icon-pinned");
await tapById(notesnook.listitem.menu);
await tapById("icon-pin");
expect(element(by.id("icon-pinned"))).not.toBeVisible();
});
it("Pin a note in notifications", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-pin-to-notifications");
await visibleByText("Unpin from notifications");
await sleep(500);
await tapById("icon-pin-to-notifications");
await sleep(500);
await visibleByText("Pin to notifications");
});
it("Copy note", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await sleep(1000);
await waitFor(elementById("icon-copy")).toBeVisible().withTimeout(500);
await tapById("icon-copy");
});
it("Assign colors to a note", async () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await sleep(1000);
await tapByText("Add color");
await sleep(500);
await elementById("color-title-input").typeText("Test color");
await tapByText("Add color");
await sleep(3000);
await visibleById("icon-check");
await tapById("icon-color-#efefef");
await notVisibleById("icon-check");
await tapById("icon-color-#efefef");
await device.pressBack();
await navigate("Test color");
await visibleByText(note.body);
});
it("Delete & restore a note", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await sleep(500);
await tapById("icon-trash");
await navigate("Trash");
await sleep(500);
await tapById(notesnook.listitem.menu);
await sleep(500);
await tapByText("Restore note");
await device.pressBack();
await sleep(500);
await visibleByText(
"Test note description that is very long and should not fit in text."
);
});
});

View File

@@ -0,0 +1,116 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import { Tests } from "./utils";
describe("NOTE TESTS", () => {
it("Create a note in editor", async () => {
await Tests.prepare();
await Tests.createNote();
});
it("Open and close a note", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.ids.note.get(0)).waitAndTap();
await Tests.exitEditor();
});
it("Notes properties should show", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromText("Created at").isVisible();
});
it("Favorite and unfavorite a note", async () => {
await Tests.prepare();
let note = await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-favorite").waitAndTap();
await Tests.fromId("icon-star").isVisible();
await Tests.navigate("Favorites");
await Tests.fromText(note.body).isVisible();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-favorite").waitAndTap();
await Tests.fromText(note.body).isNotVisible();
await Tests.navigate("Notes");
});
it("Pin a note to top", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-pin").waitAndTap();
await Tests.fromText("Pinned").isVisible();
await Tests.fromId("icon-pinned").isVisible();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-pin").waitAndTap();
await Tests.fromText("icon-pinned").isNotVisible();
});
it("Pin a note in notifications", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-pin-to-notifications").waitAndTap();
await Tests.fromText("Unpin from notifications").isVisible();
await Tests.fromId("icon-pin-to-notifications").waitAndTap();
await Tests.fromText("Pin to notifications").isVisible();
});
it("Copy note", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-copy").isVisible();
await Tests.fromId("icon-copy").waitAndTap();
});
it("Assign colors to a note", async () => {
await Tests.prepare();
let note = await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromText("Add color").waitAndTap();
await Tests.fromId("color-title-input").element.typeText("Test color");
await Tests.fromText("Add color").waitAndTap();
await Tests.fromId("icon-check").isVisible();
await Tests.fromId("icon-color-#efefef").waitAndTap();
await Tests.fromId("icon-check").isNotVisible();
await Tests.fromId("icon-color-#efefef").waitAndTap();
await device.pressBack();
await Tests.navigate("Test color");
await Tests.fromText(note.body).isVisible();
});
it.only("Delete & restore a note", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromId("icon-trash").waitAndTap();
await Tests.navigate("Trash");
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.fromText("Restore note").waitAndTap();
await device.pressBack();
await Tests.fromText(
"Test note description that is very long and should not fit in text."
).isVisible();
});
});

View File

@@ -1,309 +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 <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import {
tapById,
elementById,
visibleByText,
tapByText,
createNote,
prepare,
notVisibleById,
navigate,
elementByText,
sleep,
notVisibleByText,
visibleById,
createNotebook
} from "./utils";
// async function addTopic(title = "Topic") {
// await tapById(notesnook.buttons.add);
// await elementById("input-title").typeText(title);
// await tapByText("Add");
// await sleep(500);
// }
describe("NOTEBOOKS", () => {
it("Create a notebook with title only", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", false, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await visibleByText("Notebook 1");
});
it("Create a notebook title & description", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await visibleByText("Notebook 1");
});
it("Create a notebook, move notes", async () => {
await prepare();
let note = await createNote();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, true);
await sleep(500);
await tapById("listitem.select");
await tapByText("Move selected notes");
await sleep(500);
await tapByText("Notebook 1");
await sleep(500);
await visibleByText(note.body);
});
it("Add a sub notebook to a notebook", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await tapByText("Notebook 1");
await sleep(500);
await tapById("add-notebook-button");
await createNotebook("Sub notebook", true, true);
await sleep(500);
await device.pressBack();
await visibleByText("Sub notebook");
await tapById(notesnook.ids.notebook.menu);
await tapByText("Move to trash");
await sleep(300);
await tapByText("Delete");
await notVisibleByText("Sub notebook");
});
it("Remove a sub notebook from notebook", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await tapByText("Notebook 1");
await sleep(500);
await tapById("add-notebook-button");
await createNotebook("Sub notebook", true, true);
await sleep(500);
await device.pressBack();
await visibleByText("Sub notebook");
});
it("Edit notebook", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapById(notesnook.buttons.add);
await createNotebook("Notebook 1", true);
await sleep(500);
await device.pressBack();
await tapById(notesnook.ids.notebook.menu);
await tapByText("Edit notebook");
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
" (edited)"
);
await tapByText("Save");
await visibleByText("Notebook 1 (edited)");
});
it("Edit a sub notebook", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapById(notesnook.buttons.add);
await createNotebook("Notebook 1", true);
await sleep(500);
await device.pressBack();
await sleep(500);
await tapByText("Notebook 1");
await sleep(500);
await tapById("add-notebook-button");
await createNotebook("Sub notebook", true, true);
await device.pressBack();
await sleep(500);
await tapById(notesnook.ids.notebook.menu);
await tapByText("Edit notebook");
await sleep(500);
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
" (edited)"
);
await tapByText("Save");
await sleep(500);
await visibleByText("Sub notebook (edited)");
});
it("Add a note to notebook", async () => {
await prepare();
await navigate("Notebooks");
await tapByText("Add your first notebook");
await sleep(500);
await createNotebook("Notebook 1", true, true);
await sleep(500);
await device.pressBack();
await sleep(500);
await tapByText("Notebook 1");
await createNote();
});
it("Remove note from Notebook", async () => {
await prepare();
await navigate("Notebooks");
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, true);
await sleep(500);
await device.pressBack();
await sleep(500);
await tapByText("Notebook 1");
await sleep(500);
let note = await createNote();
await elementByText(note.body).longPress();
await tapById("select-minus");
await notVisibleById(note.title);
});
it("Add/Remove note to notebook from home", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", true, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await navigate("Notes");
await createNote();
await tapById(notesnook.listitem.menu);
await tapById("icon-notebooks");
await sleep(500);
await tapByText("Notebook 1");
await tapByText("Save");
await sleep(300);
await visibleByText("Notebook 1");
});
it("Edit notebook title, description", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook();
await sleep(500);
await device.pressBack();
await sleep(500);
await visibleByText("Notebook 1");
await tapById(notesnook.ids.notebook.menu);
await tapByText("Edit notebook");
await sleep(500);
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
" (Edited)"
);
await elementById(
notesnook.ids.dialogs.notebook.inputs.description
).clearText();
await elementById(
notesnook.ids.dialogs.notebook.inputs.description
).typeText("Description of Notebook 1 (Edited)");
await tapByText("Save");
await sleep(500);
await visibleByText("Notebook 1 (Edited)");
await visibleByText("Description of Notebook 1 (Edited)");
});
it.skip("Move notebook to trash", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", false, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await visibleByText("Notebook 1");
await tapById(notesnook.ids.notebook.menu);
await tapByText("Move to trash");
await sleep(2000);
await tapByText("Delete");
await sleep(4000);
await navigate("Trash");
await visibleByText("Notebook 1");
});
it("Move notebook to trash with notes", async () => {
await prepare();
let note = await createNote();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", false, true);
await sleep(500);
await tapById("listitem.select");
await tapByText("Move selected notes");
await sleep(500);
await visibleByText("Notebook 1");
await tapById(notesnook.ids.notebook.menu);
await tapByText("Move to trash");
await sleep(2000);
await tapByText("Move all notes in this notebook to trash");
await sleep(500);
await tapByText("Delete");
await sleep(4000);
await navigate("Trash");
await visibleByText("Notebook 1");
await visibleByText(note.body);
});
it("Pin notebook to side menu", async () => {
await prepare();
await navigate("Notebooks");
await sleep(500);
await tapByText("Add your first notebook");
await createNotebook("Notebook 1", false, false);
await sleep(500);
await device.pressBack();
await sleep(500);
await visibleByText("Notebook 1");
await tapById(notesnook.ids.notebook.menu);
await tapByText("Add Shortcut");
let menu = elementById(notesnook.ids.default.header.buttons.left);
await menu.tap();
await visibleByText("Notebook 1");
});
});

View File

@@ -0,0 +1,233 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import { Tests } from "./utils";
describe("NOTEBOOKS", () => {
it("Create a notebook with title only", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", false);
await device.pressBack();
await Tests.fromText("Notebook 1").isVisible();
});
it("Create a notebook title & description", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").isVisible();
});
it("Create a notebook, move notes", async () => {
await Tests.prepare();
let note = await Tests.createNote();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await Tests.fromId("listitem.select").waitAndTap();
await Tests.fromText("Move selected notes").waitAndTap();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.fromText(note.body).isVisible();
});
it("Add a sub notebook to a notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.sleep(500);
await Tests.fromId("add-notebook-button").waitAndTap();
await Tests.createNotebook("Sub notebook", true);
await Tests.fromText("Sub notebook").isVisible();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Move to trash").waitAndTap();
await Tests.fromText("Delete").waitAndTap();
await Tests.fromText("Sub notebook").isNotVisible();
});
it("Remove a sub notebook from notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.sleep(500);
await Tests.fromId("add-notebook-button").waitAndTap();
await Tests.createNotebook("Sub notebook", true);
await Tests.fromText("Sub notebook").isVisible();
});
it("Edit notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromId(notesnook.buttons.add).waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.fromText("Edit notebook").waitAndTap();
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.title
).element.typeText(" (edited)");
await Tests.fromText("Save").waitAndTap();
await Tests.fromText("Notebook 1 (edited)").isVisible();
});
it("Edit a sub notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromId(notesnook.buttons.add).waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.sleep(500);
await Tests.fromId("add-notebook-button").waitAndTap();
await Tests.createNotebook("Sub notebook", true);
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Edit notebook").waitAndTap();
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.title
).element.typeText(" (edited)");
await Tests.fromText("Save").waitAndTap();
await Tests.fromText("Sub notebook (edited)").isVisible();
});
it("Add a note to notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.createNote();
});
it.skip("Remove note from Notebook", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.sleep(500);
let note = await Tests.createNote();
await Tests.fromText(note.body).element.longPress();
await Tests.fromId("select-minus").waitAndTap();
await Tests.fromId(note.title).isNotVisible();
});
it("Add/Remove note to notebook from home", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.navigate("Notes");
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-notebooks").waitAndTap();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.fromText("Save").waitAndTap();
await Tests.fromText("Notebook 1").isVisible();
});
it("Edit notebook title, description", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook();
await device.pressBack();
await Tests.fromText("Notebook 1").isVisible();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Edit notebook").waitAndTap();
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.title
).element.typeText(" (Edited)");
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.description
).element.clearText();
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.description
).element.typeText("Description of Notebook 1 (Edited)");
await Tests.fromText("Save").waitAndTap();
await Tests.fromText("Notebook 1 (Edited)").isVisible();
await Tests.fromText("Description of Notebook 1 (Edited)").isVisible();
});
it.skip("Move notebook to trash", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", false);
await device.pressBack();
await Tests.fromText("Notebook 1").isVisible();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Move to trash").waitAndTap();
await Tests.fromText("Delete").waitAndTap();
await Tests.navigate("Trash");
await Tests.fromText("Notebook 1").isVisible();
});
it("Move notebook to trash with notes", async () => {
await Tests.prepare();
let note = await Tests.createNote();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", false);
await Tests.fromId("listitem.select").waitAndTap();
await Tests.fromText("Move selected notes").waitAndTap();
await Tests.fromText("Notebook 1").isVisible();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Move to trash").waitAndTap();
await Tests.fromText(
"Move all notes in this notebook to trash"
).waitAndTap();
await Tests.fromText("Delete").waitAndTap();
await Tests.navigate("Trash");
await Tests.fromText("Notebook 1").isVisible();
await Tests.fromText(note.body).isVisible();
});
it.skip("Pin notebook to side menu", async () => {
await Tests.prepare();
await Tests.navigate("Notebooks");
await Tests.fromText("Add your first notebook").waitAndTap();
await Tests.createNotebook("Notebook 1", false);
await device.pressBack();
await Tests.fromText("Notebook 1").isVisible();
await Tests.fromId(notesnook.ids.notebook.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add Shortcut").waitAndTap();
await Tests.fromId(notesnook.ids.default.header.buttons.left).waitAndTap();
await Tests.fromText("Notebook 1").isVisible();
});
});

View File

@@ -17,29 +17,17 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { notesnook } from "../test.ids"; import { Tests } from "./utils";
import {
tapById,
visibleByText,
createNote,
prepare,
elementById,
sleep,
tapByText
} from "./utils";
describe("Search", () => { describe("Search", () => {
it("Search for a note", async () => { it("Search for a note", async () => {
await prepare(); await Tests.prepare();
let note = await createNote(); let note = await Tests.createNote();
await tapById("icon-search"); await Tests.fromId("icon-search").waitAndTap();
await sleep(300); await Tests.fromId("search-input").element.typeText("Test");
await elementById("search-input").typeText("Test"); await Tests.fromText(note.body).waitAndTap();
await sleep(1000);
await tapByText(note.body);
await sleep(1000);
await device.pressBack(); await device.pressBack();
await device.pressBack(); await device.pressBack();
await visibleByText(note.body); await Tests.fromText(note.body).isVisible();
}); });
}); });

View File

@@ -1,121 +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 <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import {
tapById,
visibleByText,
createNote,
prepare,
tapByText,
notVisibleByText,
sleep,
navigate,
createNotebook
} from "./utils";
async function sortBy(sorting, elementText = "Default") {
await tapByText(elementText);
await tapByText(sorting);
await device.pressBack();
}
describe("Sort & filter", () => {
it("Sort by date-edited/date-created", async () => {
await prepare();
let webview = web(by.id(notesnook.editor.id));
await createNote("Note 1", "Note 1");
await sleep(300);
await createNote("Note 2", "Note 2");
await sleep(300);
await tapByText("Note 1");
await sleep(500);
await expect(webview.element(by.web.className("ProseMirror"))).toExist();
await webview.element(by.web.className("ProseMirror")).tap();
await webview
.element(by.web.className("ProseMirror"))
.typeText("Edited ", true);
await device.pressBack();
await device.pressBack();
await sortBy("Date created");
await tapById(notesnook.listitem.menu);
//await visibleByText("Note 2");
await device.pressBack();
await sortBy("Date edited");
await tapById(notesnook.listitem.menu);
//await visibleByText("Edited Note 1");
await device.pressBack();
});
it("Disable grouping", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await sortBy("None");
await sleep(300);
await visibleByText("None");
});
it("Group by Abc", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await sortBy("Abc");
await visibleByText("N");
});
it("Group by Year", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await sortBy("Year");
await sleep(300);
await visibleByText("Year");
});
it("Group by Week", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await sortBy("Week");
await sleep(300);
await visibleByText("Week");
});
it("Group by Month", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await sortBy("Month");
await sleep(300);
await visibleByText("Month");
});
it("Compact mode", async () => {
await prepare();
await createNote("Note 1", "Note 1");
await sleep(300);
await tapById("icon-compact-mode");
await sleep(300);
await notVisibleByText("Note 1");
await tapById("icon-compact-mode");
await sleep(300);
await visibleByText("Note 1");
});
});

View File

@@ -0,0 +1,93 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import { Tests } from "./utils";
async function sortBy(sorting: string, elementText = "Default") {
await Tests.fromText(elementText).waitAndTap();
await Tests.fromText(sorting).waitAndTap();
await device.pressBack();
}
describe("Sort & filter", () => {
it("Sort by date-edited/date-created", async () => {
await Tests.prepare();
let webview = web();
await Tests.createNote("Note 1", "Note 1");
await Tests.createNote("Note 2", "Note 2");
await Tests.fromText("Note 1").waitAndTap();
await webview.element(by.web.className("ProseMirror")).tap();
await webview
.element(by.web.className("ProseMirror"))
.typeText("Edited ", true);
await device.pressBack();
await device.pressBack();
await sortBy("Date created");
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await device.pressBack();
await sortBy("Date edited");
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await device.pressBack();
});
it("Disable grouping", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await sortBy("None");
await Tests.fromText("Default").isNotVisible();
});
it("Group by Abc", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await sortBy("Abc");
await Tests.fromText("N").isVisible();
});
it("Group by Year", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await sortBy("Year");
await Tests.fromText("Year").isVisible();
});
it("Group by Week", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await sortBy("Week");
await Tests.fromText("Week").isVisible();
});
it("Group by Month", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await sortBy("Month");
await Tests.fromText("Month").isVisible();
});
it("Compact mode", async () => {
await Tests.prepare();
await Tests.createNote("Note 1", "Note 1");
await Tests.fromId("icon-compact-mode").waitAndTap();
await Tests.fromText("Note 1").isNotVisible();
await Tests.fromId("icon-compact-mode").waitAndTap();
await Tests.fromText("Note 1").isVisible();
});
});

View File

@@ -1,103 +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 <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import {
navigate,
tapById,
visibleByText,
createNote,
prepare,
elementById,
tapByText,
notVisibleByText,
sleep
} from "./utils";
describe("Tags", () => {
it("Tag a note", async () => {
await prepare();
let note = await createNote();
await tapById(notesnook.listitem.menu);
await tapByText("Add tags");
await elementById("tag-input").typeText("testtag");
await tapByText('Add "#testtag"');
await visibleByText("#testtag");
await device.pressBack();
await device.pressBack();
await navigate("Tags");
await tapByText("#testtag");
await visibleByText(note.body);
});
it("Untag a note", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapByText("Add tags");
await elementById("tag-input").typeText("testtag");
await tapByText('Add "#testtag"');
await visibleByText("#testtag");
await tapByText("#testtag");
await device.pressBack();
await device.pressBack();
await notVisibleByText("#testtag");
});
it("Creat shortcut of a tag", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapByText("Add tags");
await elementById("tag-input").typeText("testtag");
await tapByText('Add "#testtag"');
await visibleByText("#testtag");
await device.pressBack();
await device.pressBack();
await navigate("Tags");
await sleep(500);
await tapById(notesnook.ids.tag.menu);
await sleep(500);
await tapByText("Add Shortcut");
let menu = elementById(notesnook.ids.default.header.buttons.left);
await menu.tap();
await visibleByText("#testtag");
});
it("Rename a tag", async () => {
await prepare();
await createNote();
await tapById(notesnook.listitem.menu);
await tapByText("Add tags");
await elementById("tag-input").typeText("testtag");
await tapByText('Add "#testtag"');
await visibleByText("#testtag");
await device.pressBack();
await device.pressBack();
await navigate("Tags");
await tapById(notesnook.ids.tag.menu);
await sleep(500);
await tapByText("Rename tag");
await sleep(500);
await elementById("input-value").clearText();
await elementById("input-value").typeText("testtag_edited");
await tapByText("Save");
await visibleByText("#testtag_edited");
});
});

View File

@@ -0,0 +1,95 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import { Tests } from "./utils";
describe("Tags", () => {
it("Tag a note", async () => {
await Tests.prepare();
let note = await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add tag").waitAndTap();
await Tests.fromId("tag-input").element.typeText("testtag");
await Tests.fromText('Add "#testtag"').waitAndTap();
await Tests.fromText("#testtag").isVisible();
await device.pressBack();
await device.pressBack();
await Tests.navigate("Tags");
await Tests.fromText("#testtag").waitAndTap();
await Tests.fromText(note.body).isVisible();
});
it("Untag a note", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add tag").waitAndTap();
await Tests.fromId("tag-input").element.typeText("testtag");
await Tests.fromText('Add "#testtag"').waitAndTap();
await Tests.fromText("#testtag").isVisible();
await Tests.fromText("#testtag").waitAndTap();
await device.pressBack();
await device.pressBack();
await Tests.fromText("#testtag").isNotVisible();
});
it("Create shortcut of a tag", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add tag").waitAndTap();
await Tests.fromId("tag-input").element.typeText("testtag");
await Tests.fromText('Add "#testtag"').waitAndTap();
await Tests.fromText("#testtag").isVisible();
await device.pressBack();
await device.pressBack();
await Tests.navigate("Tags");
await Tests.fromId(notesnook.ids.tag.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add shortcut").waitAndTap();
await Tests.fromId(notesnook.ids.default.header.buttons.left).waitAndTap();
await Tests.fromText("testtag").isVisible();
});
it("Rename a tag", async () => {
await Tests.prepare();
await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add tag").waitAndTap();
await Tests.fromId("tag-input").element.typeText("testtag");
await Tests.fromText('Add "#testtag"').waitAndTap();
await Tests.fromText("#testtag").isVisible();
await device.pressBack();
await device.pressBack();
await Tests.navigate("Tags");
await Tests.fromId(notesnook.ids.tag.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Rename").waitAndTap();
await Tests.sleep(100);
await Tests.fromId("input-value").element.clearText();
await Tests.fromId("input-value").element.typeText("testtag_edited");
await Tests.fromText("Save").waitAndTap();
await Tests.fromText("#testtag_edited").isVisible();
});
});

View File

@@ -1,166 +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 <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import { readFileSync } from "fs";
import { expect as jestExpect } from "@jest/globals";
import { toMatchImageSnapshot } from "jest-image-snapshot";
import { device as _device } from "detox";
jestExpect.extend({ toMatchImageSnapshot });
const sleep = (duration) =>
new Promise((resolve) =>
setTimeout(() => {
resolve();
}, duration)
);
async function LaunchApp() {
await waitFor(element(by.id(notesnook.ids.default.root)))
.toBeVisible()
.withTimeout(500);
}
function elementById(id) {
return element(by.id(id)).atIndex(0);
}
function elementByText(text) {
return element(by.text(text)).atIndex(0);
}
async function tapById(id) {
await elementById(id).tap();
}
async function tapByText(text) {
await elementByText(text).tap();
}
async function visibleByText(text) {
await expect(elementByText(text)).toBeVisible();
}
async function visibleById(id) {
await expect(elementById(id)).toBeVisible();
}
async function notVisibleById(id) {
await expect(elementById(id)).not.toBeVisible();
}
async function notVisibleByText(text) {
await expect(elementByText(text)).not.toBeVisible();
}
async function exitEditor() {
await _device.pressBack();
await _device.pressBack();
}
async function createNote(_title, _body) {
let title = _title || "Test note description that ";
let body =
_body ||
"Test note description that is very long and should not fit in text.";
await tapById(notesnook.buttons.add);
let webview = web(by.id(notesnook.editor.id));
await expect(webview.element(by.web.className("ProseMirror"))).toExist();
await webview.element(by.web.className("ProseMirror")).tap();
await webview.element(by.web.className("ProseMirror")).typeText(body, true);
await exitEditor();
await waitFor(element(by.text(body)))
.toBeVisible()
.withTimeout(500);
return { title, body };
}
async function openSideMenu() {
let menu = elementById(notesnook.ids.default.header.buttons.left);
await menu.tap();
}
async function navigate(screen) {
let menu = elementById(notesnook.ids.default.header.buttons.left);
await waitFor(menu).toBeVisible().withTimeout(300);
await menu.tap();
await waitFor(elementByText(screen)).toBeVisible().withTimeout(300);
await elementByText(screen).tap();
}
const testvars = {
isFirstTest: true
};
async function prepare() {
await device.disableSynchronization();
if (testvars.isFirstTest) {
testvars.isFirstTest = false;
return await LaunchApp();
}
await device.reverseTcpPort(8081);
await device.uninstallApp();
await device.installApp();
await device.launchApp({ newInstance: true });
await LaunchApp();
}
async function matchSnapshot(element, name) {
let path = await element.takeScreenshot(name);
const bitmapBuffer = readFileSync(path);
jestExpect(bitmapBuffer).toMatchImageSnapshot({
failureThreshold: 200,
failureThresholdType: "pixel"
});
}
async function createNotebook(title = "Notebook 1", description = true) {
await elementById(notesnook.ids.dialogs.notebook.inputs.title).typeText(
title
);
if (description) {
await elementById(
notesnook.ids.dialogs.notebook.inputs.description
).typeText(`Description of ${title}`);
}
await tapByText("Add");
await sleep(300);
}
export {
matchSnapshot,
createNotebook,
prepare,
LaunchApp,
createNote,
navigate,
openSideMenu,
notVisibleById,
notVisibleByText,
visibleById,
visibleByText,
tapById,
tapByText,
elementByText,
elementById,
sleep,
exitEditor
};

View File

@@ -0,0 +1,149 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { expect as jestExpect } from "@jest/globals";
import { device as _device, expect } from "detox";
import { readFileSync } from "fs";
import { toMatchImageSnapshot } from "jest-image-snapshot";
import type { RouteName } from "../../app/stores/use-navigation-store";
import { notesnook } from "../test.ids";
jestExpect.extend({ toMatchImageSnapshot });
const testvars = {
isFirstTest: true
};
class Element {
element: Detox.NativeElement;
constructor(public type: "id" | "text", public value: string) {
if (type == "id") {
this.element = element(by.id(value)).atIndex(0);
} else {
this.element = element(by.text(value)).atIndex(0);
}
}
isVisible(timeout?: number) {
return waitFor(this.element)
.toBeVisible()
.withTimeout(timeout || 500);
}
isNotVisible(timeout?: number) {
return waitFor(this.element)
.not.toBeVisible()
.withTimeout(timeout || 500);
}
async waitAndTap(timeout?: number) {
await waitFor(this.element)
.toBeVisible()
.withTimeout(timeout || 500);
await this.element.tap();
}
tap(point?: Detox.Point2D): Promise<void> {
return this.element.tap(point);
}
static fromId(id: string) {
return new Element("id", id);
}
static fromText(text: string) {
return new Element("text", text);
}
}
const Tests = {
awaitLaunch: async () => {
await waitFor(element(by.id(notesnook.ids.default.root)))
.toBeVisible()
.withTimeout(500);
},
sleep: (duration: number) => {
return new Promise((resolve) =>
setTimeout(() => {
resolve(undefined);
}, duration)
);
},
fromId: Element.fromId,
fromText: Element.fromText,
async exitEditor() {
await _device.pressBack();
await _device.pressBack();
},
async createNote(_title?: string, _body?: string) {
let title = _title || "Test note description that ";
let body =
_body ||
"Test note description that is very long and should not fit in text.";
await Tests.fromId(notesnook.buttons.add).tap();
await expect(web().element(by.web.className("ProseMirror"))).toExist();
// await web().element(by.web.className("ProseMirror")).tap();
await web().element(by.web.className("ProseMirror")).typeText(body, true);
await Tests.exitEditor();
await Tests.fromText(body).isVisible();
return { title, body };
},
async navigate(screen: RouteName | ({} & string)) {
let menu = Tests.fromId(notesnook.ids.default.header.buttons.left);
await menu.waitAndTap();
await Tests.fromText(screen).waitAndTap();
},
async openSideMenu() {
await Tests.fromId(notesnook.ids.default.header.buttons.left).waitAndTap();
},
async prepare() {
await device.disableSynchronization();
if (testvars.isFirstTest) {
testvars.isFirstTest = false;
return await Tests.awaitLaunch();
}
await device.reverseTcpPort(8081);
await device.uninstallApp();
await device.installApp();
await device.launchApp({ newInstance: true });
await Tests.awaitLaunch();
},
async createNotebook(title = "Notebook 1", description = true) {
await Tests.sleep(1000);
const titleInput = Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.title
);
await titleInput.isVisible();
await titleInput.element.typeText(title);
if (description) {
await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.description
).element.typeText(`Description of ${title}`);
}
await Tests.fromText("Add").waitAndTap();
},
async matchSnapshot(element: Element, name: string) {
let path = await element.element.takeScreenshot(name);
const bitmapBuffer = readFileSync(path);
(jestExpect(bitmapBuffer) as any).toMatchImageSnapshot({
failureThreshold: 200,
failureThresholdType: "pixel"
});
}
};
export { Element, Tests };

View File

@@ -1,145 +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 <http://www.gnu.org/licenses/>.
*/
import { notesnook } from "../test.ids";
import {
tapById,
elementById,
visibleByText,
tapByText,
createNote,
prepare,
visibleById,
matchSnapshot,
notVisibleById,
navigate
} from "./utils";
import { sleep } from "./utils";
async function lockNote() {
await tapById(notesnook.listitem.menu);
await tapById("icon-lock-unlock");
await sleep(1000);
await visibleByText("Lock");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await elementById(notesnook.ids.dialogs.vault.pwdAlt).typeText("1234");
await tapByText("Lock");
await sleep(500);
await visibleById("note-locked-icon");
}
async function removeFromVault() {
await tapById(notesnook.listitem.menu);
await tapById("icon-lock-unlock");
await sleep(1000);
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await tapByText("Unlock");
await sleep(1000);
await notVisibleById("note-locked-icon");
}
async function openLockedNote(pwd) {
await tapById(notesnook.ids.note.get(1));
await sleep(1000);
await visibleByText("Open");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText(pwd || "1234");
await tapByText("Open");
await sleep(3000);
await matchSnapshot(elementById("editor-wrapper"), "note-after-vault-unlock");
}
async function goToPrivacySecuritySettings() {
await navigate("Settings");
await tapByText("Vault");
await sleep(500);
}
describe("VAULT", () => {
it("Create vault from settings", async () => {
await prepare();
await goToPrivacySecuritySettings();
await tapByText("Create vault");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await elementById(notesnook.ids.dialogs.vault.pwdAlt).typeText("1234");
await tapByText("Create");
await sleep(500);
await visibleByText("Clear vault");
});
it("Change vault password", async () => {
await prepare();
await createNote();
await lockNote();
await goToPrivacySecuritySettings();
await tapByText("Change vault password");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await elementById(notesnook.ids.dialogs.vault.changePwd).typeText("2362");
await tapByText("Change");
await sleep(2000);
await device.pressBack();
await device.pressBack();
await device.pressBack();
await openLockedNote("2362");
});
it("Delete vault", async () => {
await prepare();
await createNote();
await lockNote();
await goToPrivacySecuritySettings();
await tapByText("Delete vault");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await tapByText("Delete");
await sleep(500);
await visibleByText("Create vault");
await device.pressBack();
await device.pressBack();
await visibleById(notesnook.listitem.menu);
});
it("Delete vault with locked notes", async () => {
await prepare();
await createNote();
await lockNote();
await goToPrivacySecuritySettings();
await tapByText("Delete vault");
await elementById(notesnook.ids.dialogs.vault.pwd).typeText("1234");
await tapByText("Delete all notes");
await tapByText("Delete");
await sleep(500);
await visibleByText("Create vault");
await device.pressBack();
await device.pressBack();
await notVisibleById(notesnook.listitem.menu);
});
it("Add a note to vault", async () => {
await prepare();
await createNote();
await lockNote();
await openLockedNote();
});
it("Remove note from vault", async () => {
await prepare();
await createNote();
await lockNote();
await removeFromVault();
});
});

View File

@@ -0,0 +1,143 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { expect } from "detox";
import { notesnook } from "../test.ids";
import { Tests } from "./utils";
async function lockNote() {
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-lock-unlock").waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Lock").isVisible();
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText("1234");
await Tests.fromId(notesnook.ids.dialogs.vault.pwdAlt).element.typeText(
"1234"
);
await Tests.fromText("Lock").waitAndTap();
await Tests.fromId("note-locked-icon").isVisible();
}
async function removeFromVault() {
await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-lock-unlock").waitAndTap();
await Tests.sleep(500);
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText("1234");
await Tests.fromText("Unlock").waitAndTap();
await Tests.fromId("note-locked-icon").isNotVisible();
}
async function openLockedNote(pwd?: string) {
await Tests.fromId(notesnook.ids.note.get(0)).waitAndTap();
await Tests.sleep(500);
await web()
.element(by.web.name("password"))
.typeText(pwd || "1234", false);
await web().element(by.web.className("unlock-note")).tap();
await Tests.sleep(500);
await expect(web().element(by.web.className("unlock-note"))).not.toExist();
}
async function goToPrivacySecuritySettings() {
await Tests.navigate("Settings");
await Tests.fromText("Vault").waitAndTap();
}
describe("VAULT", () => {
it("Create vault from settings", async () => {
await Tests.prepare();
await goToPrivacySecuritySettings();
await Tests.fromText("Create vault").waitAndTap();
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
"1234"
);
await Tests.fromId(notesnook.ids.dialogs.vault.pwdAlt).element.typeText(
"1234"
);
await Tests.fromText("Create").waitAndTap();
await Tests.fromText("Clear vault").isVisible();
});
it("Change vault password", async () => {
await Tests.prepare();
await Tests.createNote();
await lockNote();
await goToPrivacySecuritySettings();
await Tests.fromText("Change vault password").waitAndTap();
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
"1234"
);
await Tests.fromId(notesnook.ids.dialogs.vault.changePwd).element.typeText(
"2362"
);
await Tests.fromText("Change").waitAndTap();
await device.pressBack();
await device.pressBack();
await Tests.sleep(500);
await openLockedNote("2362");
});
it("Delete vault", async () => {
await Tests.prepare();
await Tests.createNote();
await lockNote();
await goToPrivacySecuritySettings();
await Tests.fromText("Delete vault").waitAndTap();
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
"1234"
);
await Tests.fromText("Delete").waitAndTap();
await Tests.fromText("Create vault").isVisible();
await device.pressBack();
await device.pressBack();
await Tests.fromId(notesnook.listitem.menu).isVisible();
});
it("Delete vault with locked notes", async () => {
await Tests.prepare();
await Tests.createNote();
await lockNote();
await goToPrivacySecuritySettings();
await Tests.fromText("Delete vault").waitAndTap();
await Tests.fromId(notesnook.ids.dialogs.vault.pwd).element.typeText(
"1234"
);
await Tests.fromText("Delete notes in this vault").waitAndTap();
await Tests.fromText("Delete").waitAndTap();
await Tests.fromText("Create vault").isVisible();
await device.pressBack();
await device.pressBack();
await Tests.fromId(notesnook.listitem.menu).isNotVisible();
});
it("Add a note to vault", async () => {
await Tests.prepare();
await Tests.createNote();
await lockNote();
await openLockedNote();
});
it.only("Remove note from vault", async () => {
await Tests.prepare();
await Tests.createNote();
await lockNote();
await removeFromVault();
});
});

View File

@@ -52,7 +52,7 @@ module.exports = {
emulator: { emulator: {
type: 'android.emulator', type: 'android.emulator',
device: { device: {
avdName: 'Pixel_5_API_31' avdName: 'Pixel_5_API_34'
} }
} }
}, },

View File

@@ -10,8 +10,6 @@
}, },
"dependencies": { "dependencies": {
"@ammarahmed/notifee-react-native": "7.4.7", "@ammarahmed/notifee-react-native": "7.4.7",
"@ammarahmed/react-native-share-extension": "^3.0.0",
"@ammarahmed/react-native-sodium": "1.5.4",
"@bam.tech/react-native-image-resizer": "3.0.5", "@bam.tech/react-native-image-resizer": "3.0.5",
"@callstack/repack": "^4.1.1", "@callstack/repack": "^4.1.1",
"@react-native-clipboard/clipboard": "^1.9.0", "@react-native-clipboard/clipboard": "^1.9.0",
@@ -73,7 +71,10 @@
"react-native-url-polyfill": "^2.0.0", "react-native-url-polyfill": "^2.0.0",
"react-native-screenguard": "^1.0.0", "react-native-screenguard": "^1.0.0",
"@formatjs/intl-locale": "4.0.0", "@formatjs/intl-locale": "4.0.0",
"@formatjs/intl-pluralrules": "5.2.14" "@formatjs/intl-pluralrules": "5.2.14",
"detox": "^20.27.6",
"@ammarahmed/react-native-share-extension": "^2.6.0",
"@ammarahmed/react-native-sodium": "1.5.6"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.0", "@babel/core": "^7.20.0",
@@ -86,7 +87,6 @@
"@react-native/metro-config": "0.74.86", "@react-native/metro-config": "0.74.86",
"@tsconfig/react-native": "^3.0.2", "@tsconfig/react-native": "^3.0.2",
"@types/html-to-text": "^8.0.1", "@types/html-to-text": "^8.0.1",
"@types/jest": "^29.2.1",
"@types/metro-config": "^0.76.3", "@types/metro-config": "^0.76.3",
"@types/react": "^18.2.6", "@types/react": "^18.2.6",
"@types/react-native": "^0.69.1", "@types/react-native": "^0.69.1",
@@ -98,19 +98,13 @@
"babel-loader": "^8.2.5", "babel-loader": "^8.2.5",
"babel-plugin-module-resolver": "^4.1.0", "babel-plugin-module-resolver": "^4.1.0",
"babel-plugin-transform-remove-console": "6.9.4", "babel-plugin-transform-remove-console": "6.9.4",
"detox": "^20.11.2",
"eslint": "^8.19.0", "eslint": "^8.19.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.28.0", "eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-native": "^4.0.0", "eslint-plugin-react-native": "^4.0.0",
"eslint-plugin-unused-imports": "^2.0.0", "eslint-plugin-unused-imports": "^2.0.0",
"expect": "^29.6.2",
"jest": "^29.6.3",
"jest-circus": "^29.6.2",
"jest-image-snapshot": "^6.2.0",
"@react-native/babel-preset": "0.74.86", "@react-native/babel-preset": "0.74.86",
"pixelmatch": "^5.3.0",
"prettier": "2.8.8", "prettier": "2.8.8",
"react-native-actions-shortcuts": "^1.0.1", "react-native-actions-shortcuts": "^1.0.1",
"react-native-bundle-visualizer": "^3.1.1", "react-native-bundle-visualizer": "^3.1.1",
@@ -118,10 +112,16 @@
"react-refresh": "0.14.0", "react-refresh": "0.14.0",
"react-test-renderer": "18.2.0", "react-test-renderer": "18.2.0",
"terser-webpack-plugin": "^5.3.5", "terser-webpack-plugin": "^5.3.5",
"ts-jest": "^29.1.1",
"webpack-cli": "^5.1.4", "webpack-cli": "^5.1.4",
"webpack": "^5.88.2", "webpack": "^5.88.2",
"metro-react-native-babel-preset": "0.77.0", "metro-react-native-babel-preset": "0.77.0",
"acorn-import-attributes": "1.9.5" "acorn-import-attributes": "1.9.5",
"expect": "^29.7.0",
"jest": "^29.7.0",
"jest-circus": "^29.7.0",
"jest-image-snapshot": "^6.4.0",
"ts-jest": "^29.2.5",
"pixelmatch": "^6.0.0",
"@types/jest": "^29.5.14"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
diff --git a/node_modules/react-native-actions-shortcuts/android/build.gradle b/node_modules/react-native-actions-shortcuts/android/build.gradle diff --git a/node_modules/react-native-actions-shortcuts/android/build.gradle b/node_modules/react-native-actions-shortcuts/android/build.gradle
index 19bd311..d15cdaf 100644 index 19bd311..bc37238 100644
--- a/node_modules/react-native-actions-shortcuts/android/build.gradle --- a/node_modules/react-native-actions-shortcuts/android/build.gradle
+++ b/node_modules/react-native-actions-shortcuts/android/build.gradle +++ b/node_modules/react-native-actions-shortcuts/android/build.gradle
@@ -29,7 +29,7 @@ android { @@ -29,7 +29,7 @@ android {
@@ -7,7 +7,7 @@ index 19bd311..d15cdaf 100644
buildToolsVersion getExtOrDefault('buildToolsVersion') buildToolsVersion getExtOrDefault('buildToolsVersion')
defaultConfig { defaultConfig {
- minSdkVersion 16 - minSdkVersion 16
+ minSdkVersion 21 + minSdkVersion getExtOrDefault('minSdkVersion')
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion') targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"

View File

@@ -0,0 +1,11 @@
diff --git a/node_modules/react-native-fingerprint-scanner/android/build.gradle b/node_modules/react-native-fingerprint-scanner/android/build.gradle
index eaf3829..6c392ba 100644
--- a/node_modules/react-native-fingerprint-scanner/android/build.gradle
+++ b/node_modules/react-native-fingerprint-scanner/android/build.gradle
@@ -49,5 +49,5 @@ dependencies {
// 1.2.3 is the minimum version compatible with androidx.
// See https://github.com/uccmawei/FingerprintIdentify/issues/74
// (translation https://translate.google.com/translate?sl=zh-CN&tl=en&u=https://github.com/uccmawei/FingerprintIdentify/issues/74)
- implementation "com.wei.android.lib:fingerprintidentify:${safeExtGet("fingerprintidentify", "1.2.6")}"
+ implementation "com.github.uccmawei:FingerprintIdentify:1.2.6"
}

View File

@@ -39,7 +39,7 @@ index 0ccfd23..7771e6f 100644
s.preserve_paths = 'LICENSE', 'package.json' s.preserve_paths = 'LICENSE', 'package.json'
s.source_files = '**/*.{h,m}' s.source_files = '**/*.{h,m}'
diff --git a/node_modules/react-native-tooltips/android/build.gradle b/node_modules/react-native-tooltips/android/build.gradle diff --git a/node_modules/react-native-tooltips/android/build.gradle b/node_modules/react-native-tooltips/android/build.gradle
index 978045f..401b458 100644 index 978045f..59c64f1 100644
--- a/node_modules/react-native-tooltips/android/build.gradle --- a/node_modules/react-native-tooltips/android/build.gradle
+++ b/node_modules/react-native-tooltips/android/build.gradle +++ b/node_modules/react-native-tooltips/android/build.gradle
@@ -3,8 +3,9 @@ buildscript { @@ -3,8 +3,9 @@ buildscript {
@@ -53,24 +53,36 @@ index 978045f..401b458 100644
} }
dependencies { dependencies {
@@ -15,12 +16,12 @@ buildscript { @@ -14,18 +15,19 @@ buildscript {
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
android { -android {
- compileSdkVersion 29 - compileSdkVersion 29
- buildToolsVersion '29.0.2' - buildToolsVersion '29.0.2'
+ compileSdkVersion 31
+ buildToolsVersion '31.0.0'
+def safeExtGet(prop, fallback) {
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
+}
+
+android {
+ compileSdkVersion safeExtGet('compileSdkVersion', 29)
+ buildToolsVersion safeExtGet('buildToolsVersion', '29.0.2')
defaultConfig { defaultConfig {
- minSdkVersion 16 - minSdkVersion 16
- targetSdkVersion 29 - targetSdkVersion 29
+ minSdkVersion 21 + minSdkVersion safeExtGet('minSdkVersion', 21)
+ targetSdkVersion 31 + targetSdkVersion safeExtGet('targetSdkVersion', 29)
versionCode 1 versionCode 1
versionName "1.0" - versionName "1.0"
- }
- lintOptions {
- abortOnError false
+ versionName "0.1"
} }
@@ -36,5 +37,5 @@ repositories { }
@@ -36,5 +38,5 @@ repositories {
dependencies { dependencies {
implementation 'com.facebook.react:react-native:+' implementation 'com.facebook.react:react-native:+'
@@ -104,7 +116,7 @@ index fb96466..75a6fdb 100644
@interface TooltipDelegate : NSObject <SexyTooltipDelegate> @interface TooltipDelegate : NSObject <SexyTooltipDelegate>
@@ -7,9 +9,15 @@ @interface TooltipDelegate : NSObject <SexyTooltipDelegate> @@ -7,9 +9,15 @@
@end @end
@implementation TooltipDelegate @implementation TooltipDelegate
@@ -120,7 +132,7 @@ index fb96466..75a6fdb 100644
} }
@end @end
@@ -17,77 +25,120 @@ - (void)tooltipDidDismiss:(SexyTooltip *)tooltip { @@ -17,77 +25,120 @@
@implementation RNTooltips @implementation RNTooltips

View File

@@ -646,6 +646,7 @@ const Tiptap = ({
/> />
<button <button
className="unlock-note"
style={{ style={{
backgroundColor: colors.primary.accent, backgroundColor: colors.primary.accent,
borderRadius: 5, borderRadius: 5,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -424,12 +424,11 @@ $headline$: Use starting line of the note as title.`,
other: "" other: ""
}), }),
createVault: () => t`Create Vault`, createVault: () => t`Create vault`,
createVaultDesc: () => createVaultDesc: () => t`A vault stores your notes in a encrypted storage.`,
t`A vault stores your notes in a password-encrypted storage.`, vaultFingerprintUnlock: () => t`Vault fingerprint unlock`,
vaultFingerprintUnlock: () => t`Vault Fingerprint Unlock`, revokeVaultFingerprintUnlock: () => t`Revoke vault fingerprint unlock`,
revokeVaultFingerprintUnlock: () => t`Revoke Vault Fingerprint Unlock`, changeVaultPassword: () => t`Change vault password`,
changeVaultPassword: () => t`Change Vault Password`,
deleteNote: () => doActions.delete.note(1), deleteNote: () => doActions.delete.note(1),
shareNote: () => t`Share note`, shareNote: () => t`Share note`,
copyNote: () => t`Copy note`, copyNote: () => t`Copy note`,