package com.door43.translationstudio.newui; import org.apache.commons.io.FileUtils; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.support.test.InstrumentationRegistry; import android.support.test.espresso.ViewInteraction; import android.support.test.espresso.matcher.BoundedMatcher; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.support.v7.widget.Toolbar; import android.test.suitebuilder.annotation.LargeTest; import android.view.View; import com.door43.translationstudio.AppContext; import com.door43.translationstudio.R; import com.door43.translationstudio.core.Profile; import com.door43.translationstudio.tasks.UploadCrashReportTask; import java.io.File; import java.io.InputStream; import static android.support.test.espresso.Espresso.onView; 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.isAssignableFrom; import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static junit.framework.Assert.assertTrue; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @RunWith(AndroidJUnit4.class) @LargeTest public class ImportUsfmActivityUiTest { private File mTestFile; private File mTempDir; private Context mTestContext; @Rule public ActivityTestRule<ImportUsfmActivity> mActivityRule = new ActivityTestRule<>( ImportUsfmActivity.class, true, // initialTouchMode false); // don't launchActivity yet @Before public void setUp() { mTestContext = InstrumentationRegistry.getContext(); UploadCrashReportTask.archiveErrorLogs(); if(AppContext.getProfile() == null) { // make sure this is initialized AppContext.setProfile(new Profile("testing")); } } @After public void tearDown() { if(mTempDir != null) { FileUtils.deleteQuietly(mTempDir); mTempDir = null; mTestFile = null; } finishActivity(); } @Test public void markNoChapter() throws Exception { //given String testFile = "usfm/mrk_no_chapter.usfm"; Intent intent = getIntentForTestFile(testFile); mActivityRule.launchActivity(intent); checkDisplayState(R.string.title_activity_import_usfm_language, true); onView(withText("aa")).perform(click()); boolean seen = waitWhileDisplayed(R.string.reading_usfm); //when matchSummaryDialog(R.string.title_import_usfm_error, "mrk", false); rotateScreen(); //then matchSummaryDialog(R.string.title_import_usfm_error, "mrk", false); rotateScreen(); } @Test public void markNoId() throws Exception { //given String testFile = "usfm/mrk_no_id.usfm"; Intent intent = getIntentForTestFile(testFile); mActivityRule.launchActivity(intent); checkDisplayState(R.string.title_activity_import_usfm_language, true); onView(withText("aa")).perform(click()); boolean seen = waitWhileDisplayed(R.string.reading_usfm); thenShouldShowMissingBookNameDialog(); rotateScreen(); //when thenShouldShowMissingBookNameDialog(); onView(withId(R.id.positiveButton)).perform(click()); clickOnViewText("Bible: NT"); clickOnViewText("Mark"); //then boolean seen2 = waitWhileDisplayed(R.string.reading_usfm); matchSummaryDialog(R.string.title_processing_usfm_summary, "mrk", true); rotateScreen(); matchSummaryDialog(R.string.title_processing_usfm_summary, "mrk", true); } @Test public void markValid() throws Exception { //given String testFile = "usfm/66-JUD.usfm"; Intent intent = getIntentForTestFile(testFile); mActivityRule.launchActivity(intent); checkDisplayState(R.string.title_activity_import_usfm_language, true); onView(withText("aa")).perform(click()); boolean seen = waitWhileDisplayed(R.string.reading_usfm); matchSummaryDialog(R.string.title_processing_usfm_summary, "jud", true); rotateScreen(); matchSummaryDialog(R.string.title_processing_usfm_summary, "jud", true); //when onView(withId(R.id.positiveButton)).perform(click()); //then boolean seen2 = waitWhileDisplayed(R.string.importing_usfm); matchImportResultsDialog(true); rotateScreen(); matchImportResultsDialog(true); } @Test public void judeNoVerses() throws Exception { //given String testFile = "usfm/jude.no_verses.usfm"; Intent intent = getIntentForTestFile(testFile); mActivityRule.launchActivity(intent); checkDisplayState(R.string.title_activity_import_usfm_language, true); //when onView(withText("aa")).perform(click()); boolean seen = waitWhileDisplayed(R.string.reading_usfm); //then matchSummaryDialog(R.string.title_import_usfm_error, "jud", false); rotateScreen(); matchSummaryDialog(R.string.title_import_usfm_error, "jud", false); rotateScreen(); } /** * match expected values on summary dialog * @param title * @param book * @param noErrors */ protected void matchSummaryDialog(int title, String book, boolean noErrors) { thenShouldHaveDialogTitle(title); if(book != null) { shouldHaveFoundBook(book); } checkForImportErrors(noErrors); } /** * wait until view with text is shown * @param matchText */ protected void clickOnViewText(String matchText) { for(int i = 0; i < 20; i++) { // wait until dialog is displayed try { onView(withText(matchText)).check(matches(withText(matchText))); break; } catch (Exception e) { } } onView(withText(matchText)).check(matches(withText(matchText))); onView(withText(matchText)).perform(click()); } /** * match expected values on summary dialog * @param success */ protected void matchImportResultsDialog(boolean success) { int matchTitle = success ? R.string.title_import_usfm_results : R.string.title_import_usfm_error; int matchText = success ? R.string.import_usfm_success : R.string.import_usfm_failed; thenShouldHaveDialogTitle( matchTitle ); onView(withId(R.id.dialog_content)).check(matches(withText(matchText))); } /** * check if dialog content shows no errors * @param noErrors */ private void checkForImportErrors(boolean noErrors) { String matchText = AppContext.context().getResources().getString(R.string.no_error); Matcher<View> viewMatcher = withText(containsString(matchText)); if(!noErrors) { viewMatcher = not(viewMatcher); } onView(withId(R.id.dialog_content)).check(matches(viewMatcher)); } /** * make sure we found the book * @param book */ private void shouldHaveFoundBook(String book) { String format = AppContext.context().getResources().getString(R.string.found_book); String matchText = String.format(format, book); onView(withId(R.id.dialog_content)).check(matches(withText(containsString(matchText)))); } protected void thenShouldShowMissingBookNameDialog() { thenShouldHaveDialogTitle(R.string.title_activity_import_usfm_language); } protected void thenShouldHaveDialogTitle(int title) { String titleStr = AppContext.context().getResources().getString(title); for(int i = 0; i < 40; i++) { // wait until displayed try { onView(withId(R.id.dialog_title)).check(matches(withText(titleStr))); break; } catch (Exception e) { } } onView(withId(R.id.dialog_title)).check(matches(withText(titleStr))); } /** * since progress dialog is async, then we idle while it is shown * @param resource * @return */ private boolean waitWhileDisplayed(int resource) { String text = AppContext.context().getResources().getString(resource); return waitWhileDisplayed(text); } /** * since progress dialog is async, then we idle while it is shown * @param text * @return */ private boolean waitWhileDisplayed(String text) { boolean done = false; boolean viewSeen = false; int maxCount = 1000; // sanity limit for (int i = 0; (i < maxCount) && !done ; i++) { try { onView(withText(text)).check(matches(isCompletelyDisplayed())); viewSeen = true; } catch (Exception e) { done = true; } } if(!done) { String msg = "Max count reached"; } return viewSeen; } /** * make sure text is displayed or not displayed in view * @param resource * @param displayed */ private void checkDisplayState(int resource, boolean displayed) { String text = AppContext.context().getResources().getString(resource); checkDisplayState(text, displayed); } /** * make sure text is displayed or not displayed in view * @param text * @param displayed */ private void checkDisplayState(String text, boolean displayed) { ViewInteraction interaction = onView(withText(text)); Matcher<View> displayState = isCompletelyDisplayed(); if(!displayed) { displayState = not(displayState); } interaction.check(matches(displayState)); } /** * generate intent for ImportUsfmActivity * @param fileName * @return * @throws Exception */ protected Intent getIntentForTestFile(String fileName) throws Exception { Intent intent = new Intent(); Resources testRes = mTestContext.getResources(); mTempDir = new File(AppContext.context().getCacheDir(), System.currentTimeMillis() + ""); mTempDir.mkdirs(); InputStream usfmStream = mTestContext.getAssets().open(fileName); mTestFile = new File(mTempDir, "testFile.usfm"); FileUtils.copyInputStreamToFile(usfmStream, mTestFile); intent.putExtra(ImportUsfmActivity.EXTRA_USFM_IMPORT_FILE, mTestFile); return intent; } /** * force page orientation change */ protected void rotateScreen() { Context context = InstrumentationRegistry.getTargetContext(); int orientation = context.getResources().getConfiguration().orientation; Activity activity = mActivityRule.getActivity(); activity.setRequestedOrientation( (orientation == Configuration.ORIENTATION_PORTRAIT) ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } /** * finsih activity if still running */ private void finishActivity() { Activity activity = mActivityRule.getActivity(); if(activity != null) { activity.finish(); } } /** * get interaction for toolbar with title * @param resource * @return */ private static ViewInteraction matchToolbarTitle( int resource) { String title = AppContext.context().getResources().getString(resource); return matchToolbarTitle(title); } /** * get interaction for toolbar with title * @param title * @return */ private static ViewInteraction matchToolbarTitle( CharSequence title) { return onView(allOf(isAssignableFrom(Toolbar.class))) .check(matches(withToolbarTitle(is(title)))); } /** * get interaction for toolbar with title * @param title * @return */ private static ViewInteraction notMatchToolbarTitle( CharSequence title) { return onView(isAssignableFrom(Toolbar.class)) .check(matches(not(withToolbarTitle(is(title))))); } /** * match toolbar * @param textMatcher * @return */ private static Matcher<Object> withToolbarTitle( final Matcher<CharSequence> textMatcher) { return new BoundedMatcher<Object, Toolbar>(Toolbar.class) { @Override public boolean matchesSafely(Toolbar toolbar) { return textMatcher.matches(toolbar.getTitle()); } @Override public void describeTo(Description description) { description.appendText("with toolbar title: "); textMatcher.describeTo(description); } }; } }