diff --git a/app/src/androidTest/java/com/orgzly/android/espresso/BooksTest.java b/app/src/androidTest/java/com/orgzly/android/espresso/BooksTest.java
index 04d1b75b6..3f85cb5d5 100644
--- a/app/src/androidTest/java/com/orgzly/android/espresso/BooksTest.java
+++ b/app/src/androidTest/java/com/orgzly/android/espresso/BooksTest.java
@@ -11,8 +11,12 @@
import static androidx.test.espresso.intent.Intents.intending;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static androidx.test.espresso.matcher.ViewMatchers.hasChildCount;
+import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
+import static androidx.test.espresso.matcher.ViewMatchers.isNotChecked;
+import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.orgzly.android.espresso.util.EspressoUtils.contextualToolbarOverflowMenu;
@@ -21,7 +25,9 @@
import static com.orgzly.android.espresso.util.EspressoUtils.onNoteInBook;
import static com.orgzly.android.espresso.util.EspressoUtils.onSnackbar;
import static com.orgzly.android.espresso.util.EspressoUtils.replaceTextCloseKeyboard;
+import static com.orgzly.android.espresso.util.EspressoUtils.sync;
import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
@@ -41,6 +47,7 @@
import com.orgzly.R;
import com.orgzly.android.BookFormat;
import com.orgzly.android.OrgzlyTest;
+import com.orgzly.android.repos.RepoType;
import com.orgzly.android.ui.main.MainActivity;
import org.junit.Before;
@@ -299,4 +306,106 @@ public void testBackPressClosesSelectionMenu() {
// Make sure we're still in the app
onBook(0, R.id.item_book_title).check(matches(withText("book-1")));
}
+
+ @Test
+ public void testSetLinkOnSingleBookCurrentRepoIsSelected() {
+ testUtils.setupRepo(RepoType.MOCK, "mock://repo");
+ sync();
+ onBook(0, R.id.item_book_link_repo).check(matches(withText("mock://repo")));
+ onBook(0).perform(longClick());
+ contextualToolbarOverflowMenu().perform(click());
+ onView(withText(R.string.books_context_menu_item_set_link)).perform(click());
+ onView(withText("mock://repo")).check(matches(isChecked()));
+ }
+
+ /**
+ * When setting the link of multiple books, no repo should be pre-selected, no matter how
+ * many repos there are, and no matter whether the books already have a link or not. The
+ * reason for this is that we have intuitive way of handling selected books being linked to
+ * different repos.
+ */
+ @Test
+ public void testSetLinkOnMultipleBooksNoRepoIsSelected() {
+ testUtils.setupRepo(RepoType.MOCK, "mock://repo");
+ sync();
+ onBook(0, R.id.item_book_link_repo).check(matches(withText("mock://repo")));
+ onBook(1, R.id.item_book_link_repo).check(matches(withText("mock://repo")));
+ onBook(0).perform(longClick());
+ onBook(1).perform(click());
+ contextualToolbarOverflowMenu().perform(click());
+ onView(withText(R.string.books_context_menu_item_set_link)).perform(click());
+ onView(withText("mock://repo")).check(matches(isNotChecked()));
+ }
+
+ @Test
+ public void testDeleteSingleBookLinkedUrlIsShown() {
+ testUtils.setupRepo(RepoType.MOCK, "mock://repo");
+ sync();
+ onBook(0, R.id.item_book_link_repo).check(matches(withText("mock://repo")));
+ onBook(0).perform(longClick());
+ contextualToolbarOverflowMenu().perform(click());
+ onView(withText(R.string.delete)).perform(click());
+ onView(withText(R.string.also_delete_linked_book)).check(matches(isDisplayed()));
+ onView(withId(R.id.delete_linked_url)).check(matches(withText("mock://repo/book-1.org")));
+ }
+
+ @Test
+ public void testDeleteMultipleBooksLinkedUrlIsNotShown() {
+ testUtils.setupRepo(RepoType.MOCK, "mock://repo");
+ sync();
+ onBook(0, R.id.item_book_link_repo).check(matches(withText("mock://repo")));
+ onBook(1, R.id.item_book_link_repo).check(matches(withText("mock://repo")));
+ onBook(0).perform(longClick());
+ onBook(1).perform(click());
+ contextualToolbarOverflowMenu().perform(click());
+ onView(withText(R.string.delete)).perform(click());
+ onView(withText(R.string.also_delete_linked_books)).check(matches(isDisplayed()));
+ onView(withId(R.id.delete_linked_url)).check(matches(withText("")));
+ }
+
+ @Test
+ public void testDeleteMultipleBooksWithNoLinks() {
+ onBook(0).perform(longClick());
+ onBook(1).perform(click());
+ contextualToolbarOverflowMenu().perform(click());
+ onView(withText(R.string.delete)).perform(click());
+ onView(withText(R.string.delete)).perform(click());
+ assert dataRepository.getBooks().size() == 1;
+ }
+
+ @Test
+ public void testDeleteMultipleBooksAndRooks() {
+ testUtils.setupRepo(RepoType.MOCK, "mock://repo");
+ sync();
+ onBook(0, R.id.item_book_link_repo).check(matches(withText("mock://repo")));
+ onBook(1, R.id.item_book_link_repo).check(matches(withText("mock://repo")));
+ onBook(0).perform(longClick());
+ onBook(1).perform(click());
+ contextualToolbarOverflowMenu().perform(click());
+ onView(withText(R.string.delete)).perform(click());
+ onView(withId(R.id.delete_linked_checkbox)).perform(click());
+ onView(withText(R.string.delete)).perform(click());
+ assert dataRepository.getBooks().size() == 1;
+ }
+
+ /**
+ * When multiple books are selected, the "rename" and "export" actions should be removed from
+ * the context menu. By also testing that only the expected number of actions are shown, we
+ * protect against someone later adding actions to the menu without fully considering the support for
+ * multiple selected books. When such support is added, this test will need to be updated.
+ */
+ @Test
+ public void testMultipleBooksSelectedContextMenuShowsSupportedActionsOnly() {
+ onBook(0).perform(longClick());
+ contextualToolbarOverflowMenu().perform(click());
+ onView(withText(R.string.rename)).check(matches(isDisplayed()));
+ onView(withText(R.string.export)).check(matches(isDisplayed()));
+ onView(withClassName(containsString("MenuDropDownListView"))).check(matches(hasChildCount(4)));
+ pressBack();
+ onBook(1).perform(click());
+ contextualToolbarOverflowMenu().perform(click());
+ onView(withText(R.string.rename)).check(doesNotExist());
+ onView(withText(R.string.export)).check(doesNotExist());
+ onView(withClassName(containsString("MenuDropDownListView"))).check(matches(hasChildCount(2)));
+ }
}
diff --git a/app/src/androidTest/java/com/orgzly/android/espresso/SyncingTest.java b/app/src/androidTest/java/com/orgzly/android/espresso/SyncingTest.java
index eecbd1d24..d8ad9b7db 100644
--- a/app/src/androidTest/java/com/orgzly/android/espresso/SyncingTest.java
+++ b/app/src/androidTest/java/com/orgzly/android/espresso/SyncingTest.java
@@ -5,7 +5,6 @@
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.contrib.DrawerActions.close;
import static androidx.test.espresso.contrib.DrawerActions.open;
import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
@@ -14,7 +13,6 @@
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.orgzly.android.espresso.util.EspressoUtils.clickSetting;
import static com.orgzly.android.espresso.util.EspressoUtils.contextualToolbarOverflowMenu;
-import static com.orgzly.android.espresso.util.EspressoUtils.grantAlarmsAndRemindersPermission;
import static com.orgzly.android.espresso.util.EspressoUtils.onActionItemClick;
import static com.orgzly.android.espresso.util.EspressoUtils.onBook;
import static com.orgzly.android.espresso.util.EspressoUtils.onListItem;
@@ -24,6 +22,7 @@
import static com.orgzly.android.espresso.util.EspressoUtils.recyclerViewItemCount;
import static com.orgzly.android.espresso.util.EspressoUtils.replaceTextCloseKeyboard;
import static com.orgzly.android.espresso.util.EspressoUtils.settingsSetTodoKeywords;
+import static com.orgzly.android.espresso.util.EspressoUtils.sync;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
@@ -69,16 +68,6 @@ public void tearDown() throws Exception {
}
}
- /**
- * Utility method for starting sync using drawer button.
- */
- private void sync() {
- grantAlarmsAndRemindersPermission();
- onView(withId(R.id.drawer_layout)).perform(open());
- onView(withId(R.id.sync_button_container)).perform(click());
- onView(withId(R.id.drawer_layout)).perform(close());
- }
-
@Test
public void testRunSync() {
scenario = ActivityScenario.launch(MainActivity.class);
diff --git a/app/src/androidTest/java/com/orgzly/android/espresso/util/EspressoUtils.java b/app/src/androidTest/java/com/orgzly/android/espresso/util/EspressoUtils.java
index 66141e453..cc401eaba 100644
--- a/app/src/androidTest/java/com/orgzly/android/espresso/util/EspressoUtils.java
+++ b/app/src/androidTest/java/com/orgzly/android/espresso/util/EspressoUtils.java
@@ -7,6 +7,8 @@
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.pressKey;
import static androidx.test.espresso.action.ViewActions.replaceText;
+import static androidx.test.espresso.contrib.DrawerActions.close;
+import static androidx.test.espresso.contrib.DrawerActions.open;
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
@@ -512,4 +514,14 @@ public static void grantAlarmsAndRemindersPermission() {
getInstrumentation().getUiAutomation().executeShellCommand(shellCmd);
}
}
+
+ /**
+ * Utility method for starting sync using drawer button.
+ */
+ public static void sync() {
+ grantAlarmsAndRemindersPermission();
+ onView(withId(R.id.drawer_layout)).perform(open());
+ onView(withId(R.id.sync_button_container)).perform(click());
+ onView(withId(R.id.drawer_layout)).perform(close());
+ }
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2a796190e..eaf761f93 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -291,7 +291,7 @@
Renamed from ā%sā
Delete linked remote notebook
- Delete all linked remote notebooks
+ Delete any linked remote notebooks
Deleting notebook(s) failed: %s
Notebook(s) deleted