Skip to content

Commit

Permalink
Merge pull request #288 from amberin/select-multiple-notebooks-pr
Browse files Browse the repository at this point in the history
Allow selecting multiple notebooks
  • Loading branch information
amberin committed Jul 25, 2024
2 parents f762a4c + e405b16 commit e1e4df3
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 69 deletions.
109 changes: 109 additions & 0 deletions app/src/androidTest/java/com/orgzly/android/espresso/BooksTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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 no
* intuitive way of displaying links to multiple 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)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,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;
Expand Down Expand Up @@ -69,15 +69,6 @@ public void tearDown() throws Exception {
}
}

/**
* Utility method for starting sync using drawer button.
*/
private void sync() {
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);
Expand Down Expand Up @@ -268,6 +259,44 @@ public void testForceLoadingAfterModification() {
onView(allOf(withId(R.id.item_book_sync_needed_icon))).check(matches(not(isDisplayed())));
}

@Test
public void testForceLoadingMultipleBooks() {
testUtils.setupRepo(RepoType.MOCK, "mock://repo-a");
testUtils.setupBook("book-one", "First book used for testing\n* Note A");
testUtils.setupBook("book-two", "Second book used for testing\n* Note 1\n* Note 2");
scenario = ActivityScenario.launch(MainActivity.class);

sync(); // To ensure that all books have repo links
onBook(0).perform(click());
// Check that the content of book 1 is unchanged
onNoteInBook(1, R.id.item_head_title_view).check(matches(withText("Note A")));
// Modify the content of book 1
onNoteInBook(1).perform(longClick());
onView(withId(R.id.toggle_state)).perform(click());
// Check that the content of book 1 is changed.
onNoteInBook(1, R.id.item_head_title_view).check(matches(not(withText("Note A"))));
pressBack();
pressBack();
// Change the content of book 2
onBook(1).perform(click());
onNoteInBook(1).perform(longClick());
onView(withId(R.id.toggle_state)).perform(click());
pressBack();
pressBack();
// Select both books
onBook(0).perform(longClick());
onBook(1).perform(click());
onView(withId(R.id.books_context_menu_force_load)).perform(click());
onView(withText(R.string.overwrite)).perform(click());
// Check that the content of book 1 was restored
onBook(0).perform(click());
onNoteInBook(1, R.id.item_head_title_view).check(matches(withText("Note A")));
pressBack();
// Check that the content of book 2 was restored
onBook(1).perform(click());
onNoteInBook(1, R.id.item_head_title_view).check(matches(withText("Note 1")));
}

@Test
public void testForceSavingBookWithNoLinkAndMultipleRepos() {
testUtils.setupRepo(RepoType.MOCK, "mock://repo-a");
Expand Down Expand Up @@ -331,6 +360,44 @@ public void testForceSavingBookWithLink() {
context.getString(R.string.force_saved_to_uri, "mock://repo-a/booky.org")))));
}

@Test
public void testForceSavingMultipleBooks() {
Repo repo = testUtils.setupRepo(RepoType.MOCK, "mock://repo-a");
testUtils.setupRook(repo, "mock://repo-a/book-one.org", "Content from repo", "abc",
1234567890000L);
testUtils.setupRook(repo, "mock://repo-a/book-two.org", "Content from repo", "abc",
1234567890000L);
testUtils.setupBook("book-one", "First book used for testing\n* Note A", repo);
testUtils.setupBook("book-two", "Second book used for testing\n* Note A", repo);
scenario = ActivityScenario.launch(MainActivity.class);

onBook(0).perform(longClick());
onBook(1).perform(click());
onView(withId(R.id.books_context_menu_force_save)).perform(click());
onView(withText(R.string.overwrite)).perform(click());

onBook(0, R.id.item_book_last_action)
.check(matches(withText(endsWith(
context.getString(R.string.force_saved_to_uri,
"mock://repo-a/book-one.org")))));
onBook(1, R.id.item_book_last_action)
.check(matches(withText(endsWith(
context.getString(R.string.force_saved_to_uri,
"mock://repo-a/book-two.org")))));
// Check that a subsequent sync changes nothing
sync();
onBook(0, R.id.item_book_last_action).check(matches(withText(endsWith(
context.getString(R.string.sync_status_no_change)))));
onBook(1, R.id.item_book_last_action).check(matches(withText(endsWith(
context.getString(R.string.sync_status_no_change)))));
// Check contents
onBook(0).perform(click());
onNoteInBook(1, R.id.item_head_title_view).check(matches(withText("Note A")));
pressBack();
onBook(1).perform(click());
onNoteInBook(1, R.id.item_head_title_view).check(matches(withText("Note A")));
}

@Test
public void testSyncButton() {
testUtils.setupRepo(RepoType.MOCK, "mock://repo-a");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -513,4 +515,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());
}
}
Loading

0 comments on commit e1e4df3

Please sign in to comment.