diff --git a/android/BOINC/app/build.gradle b/android/BOINC/app/build.gradle index ee0cabe49cf..77ba92b8a41 100644 --- a/android/BOINC/app/build.gradle +++ b/android/BOINC/app/build.gradle @@ -79,6 +79,8 @@ android { // Required when setting minSdkVersion to 20 or lower multiDexEnabled true vectorDrawables.useSupportLibrary = true + + testInstrumentationRunner = 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { @@ -167,6 +169,10 @@ dependencies { testImplementation 'org.robolectric:robolectric:4.3.1' testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit5_version" testRuntimeOnly "org.junit.vintage:junit-vintage-engine:$junit5_version" + + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test:rules:1.3.0' } repositories { mavenCentral() diff --git a/android/BOINC/app/src/androidTest/java/edu/berkeley/boinc/BOINCActivityTest.kt b/android/BOINC/app/src/androidTest/java/edu/berkeley/boinc/BOINCActivityTest.kt new file mode 100644 index 00000000000..369549b193d --- /dev/null +++ b/android/BOINC/app/src/androidTest/java/edu/berkeley/boinc/BOINCActivityTest.kt @@ -0,0 +1,144 @@ +package edu.berkeley.boinc + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import org.hamcrest.Matchers.allOf +import org.hamcrest.core.IsInstanceOf +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +// Disable BOINC notifications (or just the main channel on Oreo and higher) before running this test, as +// the client notification can cover the hamburger button during test execution and cause the test to fail. +@LargeTest +@RunWith(AndroidJUnit4::class) +class BOINCActivityTest { + @Rule + @JvmField + var mActivityScenarioRule = ActivityScenarioRule(BOINCActivity::class.java) + + @Test + fun boincActivityTest() { + val textView = onView( + allOf(withText(R.string.tab_tasks), + withParent(allOf(withId(R.id.action_bar), + withParent(withId(R.id.action_bar_container)))), + isDisplayed())) + textView.check(matches(withText(R.string.tab_tasks))) + + val appCompatImageButton = onView( + allOf(withContentDescription("BOINC"), + childAtPosition( + allOf(withId(R.id.action_bar), + childAtPosition( + withId(R.id.action_bar_container), + 0)), + 1), + isDisplayed())) + appCompatImageButton.perform(click()) + + val viewInteraction = onView(allOf(withText(R.string.tab_notices), isDisplayed())) + viewInteraction.perform(click()) + + val textView2 = onView( + allOf(withText(R.string.tab_notices), + withParent(allOf(withId(R.id.action_bar), + withParent(withId(R.id.action_bar_container)))), + isDisplayed())) + textView2.check(matches(withText(R.string.tab_notices))) + + appCompatImageButton.perform(click()) + + val viewInteraction2 = onView(allOf(withText(R.string.tab_projects), isDisplayed())) + viewInteraction2.perform(click()) + + val textView3 = onView( + allOf(withText(R.string.tab_projects), + withParent(allOf(withId(R.id.action_bar), + withParent(withId(R.id.action_bar_container)))), + isDisplayed())) + textView3.check(matches(withText(R.string.tab_projects))) + + appCompatImageButton.perform(click()) + + val viewInteraction3 = onView(allOf(withText(R.string.tab_preferences), isDisplayed())) + viewInteraction3.perform(click()) + + val textView4 = onView( + allOf(withText(R.string.tab_preferences), + withParent(allOf(withId(R.id.action_bar), + withParent(withId(R.id.action_bar_container)))), + isDisplayed())) + textView4.check(matches(withText(R.string.tab_preferences))) + + appCompatImageButton.perform(click()) + + val viewInteraction4 = onView(allOf(withText(R.string.menu_about), isDisplayed())) + viewInteraction4.perform(click()) + + val textView5 = onView( + allOf(withId(R.id.title), withText(R.string.menu_about), + withParent(withParent(withId(android.R.id.content))), + isDisplayed())) + textView5.check(matches(withText(R.string.menu_about))) + + val appCompatButton = onView( + allOf(withId(R.id.returnB), withText(R.string.about_button), + childAtPosition( + childAtPosition( + withId(android.R.id.content), + 0), + 6), + isDisplayed())) + appCompatButton.perform(click()) + + appCompatImageButton.perform(click()) + + val viewInteraction5 = onView(allOf(withText(R.string.menu_eventlog), isDisplayed())) + viewInteraction5.perform(click()) + + val textView6 = onView( + allOf(withText(R.string.menu_eventlog), + withParent(allOf(withId(R.id.action_bar), + withParent(withId(R.id.action_bar_container)))), + isDisplayed())) + textView6.check(matches(withText(R.string.menu_eventlog))) + + val textView7 = onView( + allOf(withText(R.string.eventlog_client_header), + withParent(allOf(withContentDescription(R.string.eventlog_client_header), + withParent(IsInstanceOf.instanceOf(android.widget.LinearLayout::class.java)))), + isDisplayed())) + textView7.check(matches(withText(R.string.eventlog_client_header))) + + val textView8 = onView( + allOf(withText(R.string.eventlog_gui_header), + withParent(allOf(withContentDescription(R.string.eventlog_gui_header), + withParent(IsInstanceOf.instanceOf(android.widget.LinearLayout::class.java)))), + isDisplayed())) + textView8.check(matches(withText(R.string.eventlog_gui_header))) + + val appCompatImageButton2 = onView( + allOf(withContentDescription("Navigate up"), + childAtPosition( + allOf(withId(R.id.action_bar), + childAtPosition( + withId(R.id.action_bar_container), + 0)), + 1), + isDisplayed())) + appCompatImageButton2.perform(click()) + + val textView9 = onView( + allOf(withText(R.string.tab_tasks), + withParent(allOf(withId(R.id.action_bar), + withParent(withId(R.id.action_bar_container)))), + isDisplayed())) + textView9.check(matches(withText(R.string.tab_tasks))) + } +} diff --git a/android/BOINC/app/src/androidTest/java/edu/berkeley/boinc/SplashActivityToEventLogTest.kt b/android/BOINC/app/src/androidTest/java/edu/berkeley/boinc/SplashActivityToEventLogTest.kt new file mode 100644 index 00000000000..8718d3d9476 --- /dev/null +++ b/android/BOINC/app/src/androidTest/java/edu/berkeley/boinc/SplashActivityToEventLogTest.kt @@ -0,0 +1,85 @@ +package edu.berkeley.boinc + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.longClick +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import org.hamcrest.Matchers.allOf +import org.hamcrest.core.IsInstanceOf +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@LargeTest +@RunWith(AndroidJUnit4::class) +class SplashActivityToEventLogTest { + @Rule + @JvmField + var mActivityScenarioRule = ActivityScenarioRule(SplashActivity::class.java) + + @Test + fun splashActivityToEventLogTest() { + val imageView = onView( + allOf(withId(R.id.logo), withContentDescription("Starting…"), + childAtPosition( + childAtPosition( + withId(android.R.id.content), + 0), + 0), + isDisplayed())) + imageView.perform(longClick()) + + val textView = onView( + allOf(withId(R.id.refresh), withContentDescription("Refresh"), + childAtPosition( + childAtPosition( + withId(R.id.action_bar), + 2), + 0), + isDisplayed())) + textView.check(matches(isDisplayed())) + + val textView2 = onView( + allOf(withId(R.id.email_to), withContentDescription("Send as Email"), + childAtPosition( + childAtPosition( + withId(R.id.action_bar), + 2), + 1), + isDisplayed())) + textView2.check(matches(isDisplayed())) + + val textView3 = onView( + allOf(withId(R.id.copy), withContentDescription("Copy to Clipboard"), + childAtPosition( + childAtPosition( + withId(R.id.action_bar), + 2), + 2), + isDisplayed())) + textView3.check(matches(isDisplayed())) + + val textView4 = onView( + allOf(withText("CLIENT MESSAGES"), + childAtPosition( + childAtPosition( + IsInstanceOf.instanceOf(androidx.appcompat.widget.LinearLayoutCompat::class.java), + 0), + 0), + isDisplayed())) + textView4.check(matches(withText("CLIENT MESSAGES"))) + + val textView5 = onView( + allOf(withText("GUI MESSAGES"), + childAtPosition( + childAtPosition( + IsInstanceOf.instanceOf(androidx.appcompat.widget.LinearLayoutCompat::class.java), + 1), + 0), + isDisplayed())) + textView5.check(matches(withText("GUI MESSAGES"))) + } +} diff --git a/android/BOINC/app/src/androidTest/java/edu/berkeley/boinc/TestUtils.kt b/android/BOINC/app/src/androidTest/java/edu/berkeley/boinc/TestUtils.kt new file mode 100644 index 00000000000..710cb83880c --- /dev/null +++ b/android/BOINC/app/src/androidTest/java/edu/berkeley/boinc/TestUtils.kt @@ -0,0 +1,22 @@ +package edu.berkeley.boinc + +import android.view.View +import android.view.ViewGroup +import org.hamcrest.Description +import org.hamcrest.Matcher +import org.hamcrest.TypeSafeMatcher + +fun childAtPosition(parentMatcher: Matcher, position: Int): Matcher { + return object : TypeSafeMatcher() { + override fun describeTo(description: Description) { + description.appendText("Child at position $position in parent ") + parentMatcher.describeTo(description) + } + + public override fun matchesSafely(view: View): Boolean { + val parent = view.parent + return parent is ViewGroup && parentMatcher.matches(parent) + && view == parent.getChildAt(position) + } + } +}