package com.orgzly.android.espresso;
import android.support.test.rule.ActivityTestRule;
import android.widget.DatePicker;
import com.orgzly.R;
import com.orgzly.android.OrgzlyTest;
import com.orgzly.android.ui.MainActivity;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.Espresso.openContextualActionModeOverflowMenu;
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.longClick;
import static android.support.test.espresso.action.ViewActions.replaceText;
import static android.support.test.espresso.action.ViewActions.scrollTo;
import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.contrib.DrawerActions.open;
import static android.support.test.espresso.contrib.PickerActions.setDate;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static com.orgzly.android.espresso.EspressoUtils.closeSoftKeyboardWithDelay;
import static com.orgzly.android.espresso.EspressoUtils.onActionItemClick;
import static com.orgzly.android.espresso.EspressoUtils.onListItem;
import static com.orgzly.android.espresso.EspressoUtils.toLandscape;
import static com.orgzly.android.espresso.EspressoUtils.toPortrait;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasToString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
public class BookTest extends OrgzlyTest {
@Rule
public ActivityTestRule activityRule = new ActivityTestRule<>(MainActivity.class, true, false);
@Before
public void setUp() throws Exception {
super.setUp();
/* Create book with enough notes to get a scrollable list on every device. */
shelfTestUtils.setupBook("book-name",
"Sample book used for tests\n" +
"* Note #1.\n" +
"* Note #2.\n" +
"** TODO Note #3.\n" +
"** Note #4.\n" +
"*** DONE Note #5.\n" +
"CLOSED: [2014-06-03 Tue 13:34]\n" +
"**** Note #6.\n" +
"** Note #7.\n" +
"* DONE Note #8.\n" +
"CLOSED: [2014-06-03 Tue 3:34]\n" +
"**** Note #9.\n" +
"SCHEDULED: <2014-05-26 Mon>\n" +
"** Note #10.\n" +
"** Note #11.\n" +
"** Note #12.\n" +
"** Note #13.\n" +
"** Note #14.\n" +
"** Note #15.\n" +
"** Note #16.\n" +
"** Note #17.\n" +
"** Note #18.\n" +
"** Note #19.\n" +
"** Note #20.\n" +
"** Note #21.\n" +
"** Note #22.\n" +
"** Note #23.\n" +
"** Note #24.\n" +
"** Note #25.\n" +
"** Note #26.\n" +
"** Note #27.\n" +
"** Note #28.\n" +
"** Note #29.\n" +
"** Note #30.\n" +
"** Note #31.\n" +
"** Note #32.\n" +
"** Note #33.\n" +
"** Note #34.\n" +
"** Note #35.\n" +
"** Note #36.\n" +
"** Note #37.\n" +
"** Note #38.\n" +
"** Note #39.\n" +
"** Note #40.\n" +
"");
activityRule.launchActivity(null);
onView(allOf(withText("book-name"), isDisplayed())).perform(click());
}
@Test
public void testNoteExists() {
onListItem(7).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #7."))));
}
@Test
public void testBookHasNewNoteIconDisplayed() {
onView(withId(R.id.fab)).check(matches(isDisplayed()));
}
@Test
public void testOpensBookDescription() {
onListItem(0).perform(click());
onView(withId(R.id.fragment_book_preface_container)).check(matches(isDisplayed()));
}
@Test
public void testOpensNoteFromBook() {
onListItem(2).perform(click());
onView(withId(R.id.fragment_note_view_flipper)).check(matches(isDisplayed()));
}
@Test
public void testScheduledNoteTimeStaysTheSameAfterSetting() {
onListItem(9).onChildView(withId(R.id.item_head_scheduled_text)).check(matches(allOf(withText(userDateTime("<2014-05-26 Mon>")), isDisplayed())));
onListItem(9).perform(longClick());
onView(withId(R.id.book_cab_edit)).perform(click());
onView(withText(R.string.schedule)).perform(click());
onView(withText(R.string.set)).perform(click());
onListItem(9).onChildView(withId(R.id.item_head_scheduled_text)).check(matches(allOf(withText(userDateTime("<2014-05-26 Mon>")), isDisplayed())));
}
@Test
public void testRemovingScheduledTimeFromMultipleNotes() {
onListItem(8).onChildView(withId(R.id.item_head_scheduled)).check(matches(not(isDisplayed())));
onListItem(9).onChildView(withId(R.id.item_head_scheduled)).check(matches(isDisplayed()));
onListItem(8).perform(longClick());
onListItem(9).perform(click());
onView(withId(R.id.book_cab_edit)).perform(click());
onView(withText(R.string.schedule)).perform(click());
onView(withText(R.string.clear)).perform(click());
onListItem(8).onChildView(withId(R.id.item_head_scheduled)).check(matches(not(isDisplayed())));
onListItem(9).onChildView(withId(R.id.item_head_scheduled)).check(matches(not(isDisplayed())));
}
@Test
public void testRemovingDoneState() {
onListItem(5).onChildView(withId(R.id.item_head_title)).check(matches(withText(startsWith("DONE"))));
onListItem(8).onChildView(withId(R.id.item_head_title)).check(matches(withText(startsWith("DONE"))));
onListItem(5).perform(longClick());
onListItem(8).perform(click());
onView(withId(R.id.book_cab_edit)).perform(click());
onView(withText(R.string.state)).perform(click());
onView(withText("TODO")).perform(click());
onListItem(5).onChildView(withId(R.id.item_head_title)).check(matches(withText(startsWith("TODO"))));
onListItem(8).onChildView(withId(R.id.item_head_title)).check(matches(withText(startsWith("TODO"))));
}
@Test
public void testUpdatingBookPreface() {
onListItem(0).perform(click());
onView(withId(R.id.fragment_book_preface_content)).perform(replaceText("New content"));
onView(withId(R.id.done)).perform(click());
onListItem(0).perform(click());
onView(withId(R.id.fragment_book_preface_content)).check(matches(withText("New content")));
}
@Test
public void testScrollPositionKeptOnRotation() {
toLandscape(activityRule);
onListItem(40).onChildView(withId(R.id.item_head_title)).check(matches(withText("Note #40."))); // Scroll
toPortrait(activityRule);
onView(withText("Note #40.")).check(matches(isDisplayed()));
}
@Test
public void testCreateNewNoteUsingFabWhenBookIsEmpty() {
// Create new empty notebook
pressBack();
onView(withId(R.id.fab)).perform(click());
onView(withId(R.id.dialog_input)).perform(replaceText("book-created-from-scratch"));
onView(withText("Create")).perform(click());
onView(allOf(withText("book-created-from-scratch"), isDisplayed())).perform(click());
onView(withId(R.id.fab)).perform(click());
onView(withId(R.id.fragment_note_container)).check(matches(isDisplayed()));
}
@Test
public void testFoldUnfoldAllButtonWhenBookIsEmpty() {
// Create new empty notebook
pressBack();
onView(withId(R.id.fab)).perform(click());
onView(withId(R.id.dialog_input)).perform(replaceText("book-created-from-scratch"));
onView(withText("Create")).perform(click());
onView(allOf(withText("book-created-from-scratch"), isDisplayed())).perform(click());
onView(withId(R.id.books_options_menu_item_cycle_visibility)).check(doesNotExist());
}
@Test
public void testBackFromSettingsShouldReturnToPreviousFragment() {
onActionItemClick(R.id.activity_action_settings, R.string.settings);
pressBack();
onView(withId(R.id.fragment_book_view_flipper)).check(matches(isDisplayed()));
}
@Test
public void testCutThenOpenNoteAtThePosition() {
onListItem(2).perform(longClick());
openContextualActionModeOverflowMenu();
onView(withText(R.string.cut)).perform(click());
/* Open note at the same position as the cut one. */
onListItem(2).perform(click());
}
@Test
public void testCabForMovingNotesDisplayed() {
onListItem(1).perform(longClick());
openContextualActionModeOverflowMenu();
onView(withText(R.string.move)).perform(click());
onView(withId(R.id.notes_action_move_down)).check(matches(isDisplayed()));
}
@Test
public void testOrderOfMovedNote() {
onListItem(3).perform(longClick());
openContextualActionModeOverflowMenu();
onView(withText(R.string.move)).perform(click());
onView(withId(R.id.notes_action_move_down)).perform(click());
onListItem(1).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #1."))));
onListItem(2).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #2."))));
onListItem(3).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #4."))));
onListItem(4).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #5."))));
onListItem(5).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #6."))));
onListItem(6).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #3."))));
}
@Test
public void testActionModeMovingStaysOpenAfterRotation() {
toPortrait(activityRule);
onView(withId(R.id.notes_action_move_down)).check(doesNotExist());
onListItem(2).perform(longClick());
openContextualActionModeOverflowMenu();
onView(withText(R.string.move)).perform(click());
onView(withId(R.id.notes_action_move_down)).check(matches(isDisplayed()));
toLandscape(activityRule);
onView(withId(R.id.notes_action_move_down)).check(matches(isDisplayed()));
}
@Test
public void testPromoting() {
onListItem(2).perform(longClick());
openContextualActionModeOverflowMenu();
onView(withText(R.string.move)).perform(click());
onView(withId(R.id.notes_action_move_left)).perform(click());
}
@Test
public void testPasteAbove() {
onListItem(2).perform(longClick());
openContextualActionModeOverflowMenu();
onView(withText(R.string.cut)).perform(click());
onListItem(1).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #1."))));
onListItem(2).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #8."))));
onListItem(3).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #9."))));
onListItem(1).perform(longClick());
openContextualActionModeOverflowMenu();
onView(withText(R.string.paste)).perform(click());
onView(withText(R.string.heads_action_menu_item_paste_above)).perform(click());
onListItem(1).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #2."))));
onListItem(2).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #3."))));
onListItem(3).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #4."))));
onListItem(4).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #5."))));
onListItem(5).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #6."))));
onListItem(6).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #7."))));
onListItem(7).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #1."))));
onListItem(8).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #8."))));
onListItem(9).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #9."))));
}
@Test
public void testPasteUnder() {
onListItem(2).perform(longClick());
openContextualActionModeOverflowMenu();
onView(withText(R.string.cut)).perform(click());
onListItem(1).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #1."))));
onListItem(2).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #8."))));
onListItem(3).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #9."))));
onListItem(2).perform(longClick());
openContextualActionModeOverflowMenu();
onView(withText(R.string.paste)).perform(click());
onView(withText(R.string.heads_action_menu_item_paste_under)).perform(click());
onListItem(1).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #1."))));
onListItem(2).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #8."))));
onListItem(3).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #9."))));
onListItem(4).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #10."))));
onListItem(35).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #2."))));
onListItem(36).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #3."))));
}
@Test
public void testFoldNotes() {
onListItem(2).onChildView(withId(R.id.item_head_fold_button)).perform(click());
onListItem(1).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #1."))));
onListItem(2).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #2."))));
onListItem(3).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #8."))));
}
@Test
public void testCreateNewNoteUnderFolded() {
/* Fold. */
onListItem(2).onChildView(withId(R.id.item_head_fold_button)).perform(click());
/* Create new note under folded. */
onListItem(2).perform(longClick());
onView(withId(R.id.book_cab_new)).perform(click());
onView(withText(R.string.heads_action_menu_item_add_under)).perform(click());
onView(withId(R.id.fragment_note_title)).perform(replaceText("Created"));
onView(withId(R.id.done)).perform(click());
/* New note should not be visible. */
onListItem(1).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #1."))));
onListItem(2).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #2."))));
onListItem(3).onChildView(withId(R.id.item_head_title)).check(matches(withText(endsWith("Note #8."))));
}
@Test
public void testNewNoteWithCreatedTimestamp() {
/* Enable "Created at" in settings. */
onActionItemClick(R.id.activity_action_settings, R.string.settings);
onListItem(EspressoUtils.SETTINGS_CREATED_AT).perform(click());
pressBack();
onView(withId(R.id.fab)).perform(click());
onView(withId(R.id.fragment_note_title)).perform(replaceText("Title"));
onView(withId(R.id.done)).perform(click());
onListItem(41).perform(click());
onView(withId(R.id.name)).check(matches(allOf(isDisplayed(), withText(R.string.created_property_name))));
}
@Test
public void testReturnToNonExistentNoteByPressingBack() {
onListItem(1).perform(click());
onView(withId(R.id.drawer_layout)).perform(open());
onView(withText(R.string.notebooks)).perform(click());
onView(allOf(withText("book-name"), isDisplayed())).perform(longClick());
onData(hasToString(containsString(context.getString(R.string.delete)))).perform(click());
onView(withText(R.string.ok)).perform(click());
pressBack();
onView(withId(R.id.fragment_note_view_flipper)).check(matches(isDisplayed()));
onView(withText(R.string.fragment_note_does_not_exist)).check(matches(isDisplayed()));
onView(withId(R.id.done)).check(doesNotExist());
onView(withId(R.id.close)).check(doesNotExist());
pressBack(); // Leave the note
}
@Test
public void testScrollPositionKeptInBackStack() {
onListItem(40).check(matches(isDisplayed())); // Scroll to note
onView(withText("Note #40.")).check(matches(isDisplayed())); // Check it's displayed
onView(withText("Note #40.")).perform(click());
pressBack();
onView(withText("Note #40.")).check(matches(isDisplayed())); // Check it's displayed
}
@Test
public void testSetDeadlineTimeForNewNote() {
onView(withId(R.id.fab)).perform(click());
onView(withId(R.id.fragment_note_deadline_button)).perform(closeSoftKeyboardWithDelay(), scrollTo(), click());
onView(withId(R.id.dialog_timestamp_date_picker)).perform(click());
onView(withClassName(equalTo(DatePicker.class.getName()))).perform(setDate(2014, 4, 1));
onView(withText(R.string.ok)).perform(click());
onView(withText(R.string.set)).perform(click());
onView(withId(R.id.fragment_note_deadline_button)).check(matches(withText(userDateTime("<2014-04-01 Tue>"))));
}
@Test
public void testDeleteBookPreface() {
// Preface exists
onListItem(0).perform(click());
onView(withId(R.id.fragment_book_preface_container)).check(matches(isDisplayed()));
// Enter and delete it
openContextualActionModeOverflowMenu();
onView(withText(R.string.delete)).perform(click());
// First list item is now a note
onView(withId(R.id.fragment_book_view_flipper)).check(matches(isDisplayed()));
onListItem(0).onChildView(withId(R.id.item_head_title)).check(matches(withText("Note #1.")));
}
}