/* * Copyright 2016 Square Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package flow.sample.tree; import android.support.test.espresso.action.ViewActions; import android.support.test.rule.ActivityTestRule; import flow.sample.tree.model.Contact; import flow.sample.tree.ui.welcome.WelcomeScreen; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; 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.pressBack; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withParent; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static flow.sample.tree.FlowServices.CONTACTS_STORAGE; import static flow.sample.tree.FlowServices.CONTACT_EDITOR; import static flow.sample.tree.FlowViewAssertions.doesNotHaveFlowService; import static flow.sample.tree.FlowViewAssertions.hasFlowService; import static flow.sample.tree.model.ContactsStorage.HOMER; public class TreeSampleTest { @Rule public ActivityTestRule rule = new ActivityTestRule<>(TreeSampleActivity.class); @Test public void editsAreSaved() { // We start on the welcome screen. // WelcomeScreen does not have ContactsUiKey as an ancestor, so it shouldn't have contacts. onView(withParent(withId(android.R.id.content))) // .check(doesNotHaveFlowService(CONTACTS_STORAGE)) // .check(doesNotHaveFlowService(CONTACT_EDITOR)); // Click through. onView(withText(new WelcomeScreen().toString())).perform(click()); // We should now be on the ListContactsScreen, which has ContactsUiKey as its parent. // Since we've entered ContactsUiKey, ContactsStorage should be initialized and available. onView(withParent(withId(android.R.id.content))) // .check(hasFlowService(CONTACTS_STORAGE)) // // We shouldn't have a contact editor yet though .check(doesNotHaveFlowService(CONTACT_EDITOR)); // We should be looking at our list of contacts. Click on Homer. onData(contactWithId(HOMER.id)).perform(click()); // Now we should be on the EditNameScreen onView(withParent(withId(android.R.id.content))) // // Since EditNameScreen is an EditContactKey, it should have a contact editor. .check(hasFlowService(CONTACT_EDITOR)) // // Its parent is ContactsUiKey, so we should still have the storage service, too. .check(hasFlowService(CONTACTS_STORAGE)); String originalName = HOMER.name; String originalEmail = HOMER.email; String editedName = originalName + " Edited"; String editedEmail = originalEmail + " Edited"; // Homer's name and email should be populated. onView(withId(R.id.email)).check(matches(withText(originalEmail))); onView(withId(R.id.edit_name)) // .check(matches(withText(originalName))) // Edit his name. .perform(ViewActions.typeText(" Edited")) // .check(matches(withText(editedName))) // Continue to the next screen. .perform(ViewActions.pressImeActionButton()); // Now we're on the EditEmailScreen. It's also an EditContactKey, so it should share the same // ancestor scopes as EditNameScreen. We should still see the same editor and storage. onView(withParent(withId(android.R.id.content))) // .check(hasFlowService(CONTACT_EDITOR)) // .check(hasFlowService(CONTACTS_STORAGE)); // We can verify that we have the same editor instance by checking that we see the name change // we just made on the previous screen. onView(withId(R.id.name)).check(matches(withText(editedName))); // Email should also be populated. onView(withId(R.id.edit_email)) // .check(matches(withText(originalEmail))) // Edit the email field. .perform(ViewActions.typeText(" Edited")) // .check(matches(withText(editedEmail))); // Save changes. This should take us back to the list. onView(withId(R.id.save)).perform(click()); // The ContactsEditor should no longer be available, but we should still have storage. // TODO see if it's in the view's context onView(withParent(withId(android.R.id.content))) // .check(doesNotHaveFlowService(CONTACT_EDITOR)) // .check(hasFlowService(CONTACTS_STORAGE)); // We can verify that we always had the same storage instance by checking that our // edits are reflected in the list. onData(contactWithId(HOMER.id)) // .onChildView(withId(R.id.contact_name)) // .check(matches(withText(editedName))); onData(contactWithId(HOMER.id)) // .onChildView(withId(R.id.contact_email)) // .check(matches(withText(editedEmail))); // Press back to go to the welcome screen. pressBack(); // By going back to the WelcomeScreen, we leave the ContactsUiKey. This should cause the only // reference to the ContactsStorage to be dropped. onView(withParent(withId(android.R.id.content))) // .check(doesNotHaveFlowService(CONTACTS_STORAGE)) // .check(doesNotHaveFlowService(CONTACT_EDITOR)); // Click through to go to the list again. onView(withText(new WelcomeScreen().toString())).perform(click()); // Since we're re-entering the ContactsKey, we should get a brand // new ContactsStorage instance, and our edits should be lost. onView(withParent(withId(android.R.id.content))) // .check(hasFlowService(CONTACTS_STORAGE)); onData(contactWithId(HOMER.id)) // .onChildView(withId(R.id.contact_name)) // .check(matches(withText(originalName))); onData(contactWithId(HOMER.id)) // .onChildView(withId(R.id.contact_email)) // .check(matches(withText(originalEmail))); } private static Matcher<Contact> contactWithId(String contactId) { final String contactId1 = contactId; return new BaseMatcher<Contact>() { private final String contactId = contactId1; @Override public boolean matches(Object o) { return o instanceof Contact && contactId.equals(((Contact) o).id); } @Override public void describeTo(Description description) { description.appendText("contact with id " + contactId); } }; } }