From fdd38d179ee806906e555cc23e2be3dee93b7574 Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Thu, 3 Dec 2020 16:58:01 +0530 Subject: [PATCH 1/8] fix for format tests --- .../AdministratorControlsActivityTest.kt | 407 +++++------------- .../android/app/home/HomeActivityTest.kt | 369 ++++++++-------- .../exploration/ExplorationActivityTest.kt | 376 ++++++++-------- .../ProfileProgressFragmentTest.kt | 402 ++++++----------- .../domain/topic/TopicListControllerTest.kt | 59 +-- 5 files changed, 626 insertions(+), 987 deletions(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt index dc68dcef73d..55fafd2554e 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt @@ -3,18 +3,11 @@ package org.oppia.android.app.administratorcontrols import android.app.Application import android.content.Context import android.content.Intent -import android.view.View -import android.view.ViewParent -import android.widget.FrameLayout import androidx.appcompat.app.AppCompatActivity -import androidx.core.widget.NestedScrollView import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ActivityScenario.launch import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.PerformException -import androidx.test.espresso.UiController -import androidx.test.espresso.ViewAction import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition @@ -22,22 +15,16 @@ import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.matcher.RootMatchers.isDialog -import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.isChecked import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.espresso.util.HumanReadables import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.rule.ActivityTestRule -import com.google.firebase.FirebaseApp import dagger.Component -import org.hamcrest.Matchers import org.hamcrest.Matchers.not import org.junit.After import org.junit.Before -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.oppia.android.R @@ -54,7 +41,6 @@ import org.oppia.android.app.profile.ProfileChooserActivity import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPositionOnView import org.oppia.android.app.settings.profile.ProfileListActivity import org.oppia.android.app.shim.ViewBindingShimModule -import org.oppia.android.app.testing.NavigationDrawerTestActivity import org.oppia.android.app.utility.OrientationChangeAction.Companion.orientationLandscape import org.oppia.android.app.utility.OrientationChangeAction.Companion.orientationPortrait import org.oppia.android.domain.classify.InteractionsModule @@ -100,13 +86,6 @@ import javax.inject.Singleton ) class AdministratorControlsActivityTest { - @get:Rule - var activityTestRule: ActivityTestRule = ActivityTestRule( - AdministratorControlsActivity::class.java, /* initialTouchMode= */ - true, /* launchActivity= */ - false - ) - @Inject lateinit var profileTestHelper: ProfileTestHelper @@ -122,7 +101,6 @@ class AdministratorControlsActivityTest { setUpTestApplicationComponent() testCoroutineDispatchers.registerIdlingResource() profileTestHelper.initializeProfiles() - FirebaseApp.initializeApp(context) } @After @@ -143,51 +121,24 @@ class AdministratorControlsActivityTest { ) ).use { testCoroutineDispatchers.runCurrent() - onView( - atPositionOnView( - R.id.administrator_controls_list, - 0, R.id.general_text_view - ) + verifyItemDisplayedOnAdministratorControlListItem( + itemPosition = 0, + targetView = R.id.general_text_view ) - .check(matches(isDisplayed())) - onView( - atPositionOnView( - R.id.administrator_controls_list, - 0, R.id.edit_account_text_view - ) + verifyTextOnAdministratorListItemAtPosition( + itemPosition = 0, + targetViewId = R.id.edit_account_text_view, + stringToMatch = context.getString(R.string.administrator_controls_edit_account) ) - .check( - matches( - withText( - context.resources.getString( - R.string.administrator_controls_edit_account - ) - ) - ) - ) - onView( - atPositionOnView( - R.id.administrator_controls_list, - 1, - R.id.profile_management_text_view - ) + verifyItemDisplayedOnAdministratorControlListItem( + itemPosition = 1, + targetView = R.id.profile_management_text_view ) - .check(matches(isDisplayed())) - onView( - atPositionOnView( - R.id.administrator_controls_list, - 1, R.id.edit_profiles_text_view - ) + verifyTextOnAdministratorListItemAtPosition( + itemPosition = 1, + targetViewId = R.id.edit_profiles_text_view, + stringToMatch = context.getString(R.string.administrator_controls_edit_profiles) ) - .check( - matches( - withText( - context.resources.getString( - R.string.administrator_controls_edit_profiles - ) - ) - ) - ) } } @@ -199,43 +150,22 @@ class AdministratorControlsActivityTest { ) ).use { testCoroutineDispatchers.runCurrent() - onView( - atPositionOnView( - R.id.administrator_controls_list, - 2, - R.id.download_permissions_text_view - ) - ) - .check( - matches( - withText( - context.resources.getString( - R.string.administrator_controls_download_permissions_label - ) - ) - ) - ) - onView( - atPositionOnView( - R.id.administrator_controls_list, - 2, - R.id.topic_update_on_wifi_constraint_layout + verifyTextOnAdministratorListItemAtPosition( + itemPosition = 2, + targetViewId = R.id.download_permissions_text_view, + stringToMatch = context.getString( + R.string.administrator_controls_download_permissions_label ) ) - .check(matches(isDisplayed())) - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 2 - ) + verifyItemDisplayedOnAdministratorControlListItem( + itemPosition = 2, + targetView = R.id.topic_update_on_wifi_constraint_layout ) - onView( - atPositionOnView( - R.id.administrator_controls_list, - 2, - R.id.auto_update_topic_constraint_layout - ) + scrollToPosition(position = 2) + verifyItemDisplayedOnAdministratorControlListItem( + itemPosition = 2, + targetView = R.id.auto_update_topic_constraint_layout ) - .check(matches(isDisplayed())) } } @@ -247,55 +177,25 @@ class AdministratorControlsActivityTest { ) ).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 3 - ) - ) - onView( - atPositionOnView( - R.id.administrator_controls_list, - 3, R.id.app_information_text_view - ) + scrollToPosition(position = 3) + verifyItemDisplayedOnAdministratorControlListItem( + itemPosition = 3, + targetView = R.id.app_information_text_view ) - .check(matches(isDisplayed())) - onView( - atPositionOnView( - R.id.administrator_controls_list, - 3, R.id.app_version_text_view - ) + verifyTextOnAdministratorListItemAtPosition( + itemPosition = 3, + targetViewId = R.id.app_version_text_view, + stringToMatch = context.getString(R.string.administrator_controls_app_version) ) - .check( - matches( - withText( - context.resources.getString( - R.string.administrator_controls_app_version - ) - ) - ) - ) - onView( - atPositionOnView( - R.id.administrator_controls_list, - 4, R.id.account_actions_text_view - ) + verifyItemDisplayedOnAdministratorControlListItem( + itemPosition = 4, + targetView = R.id.account_actions_text_view ) - .check(matches(isDisplayed())) - onView( - atPositionOnView( - R.id.administrator_controls_list, - 4, R.id.log_out_text_view - ) + verifyTextOnAdministratorListItemAtPosition( + itemPosition = 4, + targetViewId = R.id.log_out_text_view, + stringToMatch = context.getString(R.string.administrator_controls_log_out) ) - .check( - matches( - withText( - context.resources.getString( - R.string.administrator_controls_log_out - ) - ) - ) - ) } } @@ -313,20 +213,15 @@ class AdministratorControlsActivityTest { 2, R.id.topic_update_on_wifi_switch ) - ) - .check(matches(not(isChecked()))) - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 2 - ) - ) + ).check(matches(not(isChecked()))) + scrollToPosition(position = 2) onView( atPositionOnView( R.id.administrator_controls_list, - 2, R.id.auto_update_topic_switch + 2, + R.id.auto_update_topic_switch ) - ) - .check(matches(not(isChecked()))) + ).check(matches(not(isChecked()))) } } @@ -338,72 +233,60 @@ class AdministratorControlsActivityTest { ) ).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 2 - ) - ) + scrollToPosition(position = 2) onView( atPositionOnView( R.id.administrator_controls_list, - 2, R.id.topic_update_on_wifi_switch + 2, + R.id.topic_update_on_wifi_switch ) - ) - .check(matches(not(isChecked()))) + ).check(matches(not(isChecked()))) onView( atPositionOnView( R.id.administrator_controls_list, - 2, R.id.auto_update_topic_switch + 2, + R.id.auto_update_topic_switch ) - ) - .check(matches(not(isChecked()))) + ).check(matches(not(isChecked()))) onView( atPositionOnView( R.id.administrator_controls_list, - 2, R.id.topic_update_on_wifi_switch + 2, + R.id.topic_update_on_wifi_switch ) - ) - .perform(click()) + ).perform(click()) onView(isRoot()).perform(orientationLandscape()) - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 2 - ) - ) + scrollToPosition(position = 2) onView( atPositionOnView( R.id.administrator_controls_list, - 2, R.id.topic_update_on_wifi_switch + 2, + R.id.topic_update_on_wifi_switch ) - ) - .check(matches(isChecked())) + ).check(matches(isChecked())) onView( atPositionOnView( R.id.administrator_controls_list, - 2, R.id.auto_update_topic_switch + 2, + R.id.auto_update_topic_switch ) - ) - .check(matches(not(isChecked()))) + ).check(matches(not(isChecked()))) onView(isRoot()).perform(orientationPortrait()) - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 2 - ) - ) + scrollToPosition(position = 2) onView( atPositionOnView( R.id.administrator_controls_list, - 2, R.id.topic_update_on_wifi_switch + 2, + R.id.topic_update_on_wifi_switch ) - ) - .check(matches(isChecked())) + ).check(matches(isChecked())) onView( atPositionOnView( R.id.administrator_controls_list, - 2, R.id.auto_update_topic_switch + 2, + R.id.auto_update_topic_switch ) - ) - .check(matches(not(isChecked()))) + ).check(matches(not(isChecked()))) } } @@ -411,7 +294,7 @@ class AdministratorControlsActivityTest { fun testAdministratorControlsFragment_loadFragment_clickEditProfile_checkOpensProfileListActivity() { // ktlint-disable max-line-length launch( createAdministratorControlsActivityIntent( - 0 + profileId = 0 ) ).use { testCoroutineDispatchers.runCurrent() @@ -428,18 +311,11 @@ class AdministratorControlsActivityTest { ) ).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 4 - ) - ) + scrollToPosition(position = 4) onView(withId(R.id.log_out_text_view)).perform(click()) - onView(withText(R.string.log_out_dialog_message)).inRoot(isDialog()) - .check(matches(isDisplayed())) - onView(withText(R.string.log_out_dialog_okay_button)).inRoot(isDialog()) - .check(matches(isDisplayed())) - onView(withText(R.string.log_out_dialog_cancel_button)).inRoot(isDialog()) - .check(matches(isDisplayed())) + verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_message)) + verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_okay_button)) + verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_cancel_button)) } } @@ -451,24 +327,13 @@ class AdministratorControlsActivityTest { ) ).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 4 - ) - ) + scrollToPosition(position = 4) onView(isRoot()).perform(orientationLandscape()) - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 4 - ) - ) + scrollToPosition(position = 4) onView(withId(R.id.log_out_text_view)).perform(click()) - onView(withText(R.string.log_out_dialog_message)).inRoot(isDialog()) - .check(matches(isDisplayed())) - onView(withText(R.string.log_out_dialog_okay_button)).inRoot(isDialog()) - .check(matches(isDisplayed())) - onView(withText(R.string.log_out_dialog_cancel_button)).inRoot(isDialog()) - .check(matches(isDisplayed())) + verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_message)) + verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_okay_button)) + verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_cancel_button)) } } @@ -481,14 +346,9 @@ class AdministratorControlsActivityTest { ) ).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 4 - ) - ) + scrollToPosition(position = 4) onView(withId(R.id.log_out_text_view)).perform(click()) - onView(withText(R.string.log_out_dialog_message)).inRoot(isDialog()) - .check(matches(isDisplayed())) + verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_message)) onView(withText(R.string.log_out_dialog_okay_button)).perform(click()) intended(hasComponent(ProfileChooserActivity::class.java.name)) } @@ -502,14 +362,9 @@ class AdministratorControlsActivityTest { ) ).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 4 - ) - ) + scrollToPosition(position = 4) onView(withId(R.id.log_out_text_view)).perform(click()) - onView(withText(R.string.log_out_dialog_message)).inRoot(isDialog()) - .check(matches(isDisplayed())) + verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_message)) onView(withText(R.string.log_out_dialog_cancel_button)).perform(click()) onView(withId(R.id.log_out_text_view)).check(matches(isDisplayed())) } @@ -519,15 +374,11 @@ class AdministratorControlsActivityTest { fun testAdministratorControlsFragment_clickAppVersion_opensAppVersionActivity() { launch( createAdministratorControlsActivityIntent( - 0 + profileId = 0 ) ).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.administrator_controls_list)).perform( - scrollToPosition( - 3 - ) - ) + scrollToPosition(position = 3) onView(withId(R.id.app_version_text_view)).perform(click()) intended(hasComponent(AppVersionActivity::class.java.name)) } @@ -540,65 +391,45 @@ class AdministratorControlsActivityTest { ) } - private fun createNavigationDrawerActivityIntent(profileId: Int): Intent { - return NavigationDrawerTestActivity.createNavigationDrawerTestActivity( - ApplicationProvider.getApplicationContext(), - profileId - ) + private fun verifyItemDisplayedOnAdministratorControlListItem( + itemPosition: Int, + targetView: Int + ) { + onView( + atPositionOnView( + R.id.administrator_controls_list, + itemPosition, + targetView + ) + ).check(matches(isDisplayed())) } - /** Functions nestedScrollTo() and findFirstParentLayoutOfClass() taken from: https://stackoverflow.com/a/46037284/8860848 */ - private fun nestedScrollTo(): ViewAction { - return object : ViewAction { - override fun getDescription(): String { - return "View is not NestedScrollView" - } - - override fun getConstraints(): org.hamcrest.Matcher { - return Matchers.allOf( - ViewMatchers.isDescendantOfA(ViewMatchers.isAssignableFrom(NestedScrollView::class.java)) - ) - } - - override fun perform(uiController: UiController, view: View) { - try { - val nestedScrollView = - findFirstParentLayoutOfClass(view, NestedScrollView::class.java) as NestedScrollView - nestedScrollView.scrollTo(0, view.getTop()) - } catch (e: Exception) { - throw PerformException.Builder() - .withActionDescription(this.description) - .withViewDescription(HumanReadables.describe(view)) - .withCause(e) - .build() - } - uiController.loopMainThreadUntilIdle() - } - } - } - - private fun findFirstParentLayoutOfClass(view: View, parentClass: Class): View { - var parent: ViewParent = FrameLayout(view.getContext()) - lateinit var incrementView: ViewParent - var i = 0 - while (!(parent.javaClass === parentClass)) { - if (i == 0) { - parent = findParent(view) - } else { - parent = findParent(incrementView) - } - incrementView = parent - i++ - } - return parent as View + private fun verifyTextOnAdministratorListItemAtPosition( + itemPosition: Int, + targetViewId: Int, + stringToMatch: String + ) { + onView( + atPositionOnView( + R.id.administrator_controls_list, + itemPosition, + targetViewId + ) + ).check(matches(withText(stringToMatch))) } - private fun findParent(view: View): ViewParent { - return view.getParent() + private fun scrollToPosition(position: Int) { + onView(withId(R.id.administrator_controls_list)).perform( + scrollToPosition( + position + ) + ) } - private fun findParent(view: ViewParent): ViewParent { - return view.getParent() + private fun verifyTextInDialog(textInDialog: String) { + onView(withText(textInDialog)) + .inRoot(isDialog()) + .check(matches(isDisplayed())) } // TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them. diff --git a/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt index d8093e8b87f..9c31b37d6bc 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt @@ -24,13 +24,11 @@ import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withParent import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.google.firebase.FirebaseApp import dagger.Component import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.containsString import org.junit.After import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.oppia.android.R @@ -115,7 +113,8 @@ class HomeActivityTest { @Inject lateinit var testCoroutineDispatchers: TestCoroutineDispatchers - private val internalProfileId: Int = 1 + private val internalProfileId: Int = 0 + private val internalProfileId1: Int = 1 private lateinit var oppiaClock: OppiaClock @Before @@ -124,7 +123,6 @@ class HomeActivityTest { setUpTestApplicationComponent() testCoroutineDispatchers.registerIdlingResource() profileTestHelper.initializeProfiles() - FirebaseApp.initializeApp(context) } @After @@ -146,140 +144,110 @@ class HomeActivityTest { } @Test - @Ignore( - "This test case is incorrect as it depends on internalProfileId " + - "which is not guaranteed to be 0 for admin." - ) - fun testHomeActivity_recyclerViewIndex0_withProfileId0_displayProfileName_profileNameDisplayedSuccessfully() { // ktlint-disable max-line-length + fun testHomeActivity_withAdminProfile_displayProfileName_profileNameDisplayedSuccessfully() { launch(createHomeActivityIntent(internalProfileId)).use { - onView( - atPositionOnView( - R.id.home_recycler_view, - 0, - R.id.profile_name_textview - ) - ).check(matches(withText("Admin!"))) + testCoroutineDispatchers.runCurrent() + scrollToPosition(position = 0) + verifyExactTextOnHomeListItemAtPosition( + itemPosition = 0, + targetViewId = R.id.profile_name_textview, + stringToMatch = "Admin!" + ) } } @Test - fun testHomeActivity_recyclerViewIndex0_displayGreetingMessageBasedOnTime_goodMorningMessageDisplayedSuccessful() { // ktlint-disable max-line-length - getApplicationDependencies() - oppiaClock.setCurrentTimeMs(MORNING_TIMESTAMP) + fun testHomeActivity_withAdminProfile_configurationChange_profileNameDisplayedSuccessfully() { launch(createHomeActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(0) + onView(isRoot()).perform(orientationLandscape()) + scrollToPosition(position = 0) + verifyExactTextOnHomeListItemAtPosition( + itemPosition = 0, + targetViewId = R.id.profile_name_textview, + stringToMatch = "Admin!" ) - onView( - atPositionOnView( - R.id.home_recycler_view, - 0, - R.id.welcome_text_view - ) - ).check(matches(withText("Good morning,"))) } } @Test - fun testHomeActivity_recyclerViewIndex0_displayGreetingMessageBasedOnTime_goodAfternoonMessageDisplayedSuccessful() { // ktlint-disable max-line-length + fun testHomeActivity_displayGreetingMessageBasedOnTime_goodMorningMessageDisplayedSuccessful() { getApplicationDependencies() - oppiaClock.setCurrentTimeMs(AFTERNOON_TIMESTAMP) - launch(createHomeActivityIntent(internalProfileId)).use { + oppiaClock.setCurrentTimeMs(MORNING_TIMESTAMP) + launch(createHomeActivityIntent(internalProfileId1)).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(0) + scrollToPosition(position = 0) + verifyExactTextOnHomeListItemAtPosition( + itemPosition = 0, + targetViewId = R.id.welcome_text_view, + stringToMatch = "Good morning," ) - onView( - atPositionOnView( - R.id.home_recycler_view, - 0, - R.id.welcome_text_view - ) - ).check(matches(withText("Good afternoon,"))) } } @Test - fun testHomeActivity_recyclerViewIndex0_displayGreetingMessageBasedOnTime_goodEveningMessageDisplayedSuccessful() { // ktlint-disable max-line-length + fun testHomeActivity_displayGreetingMessageBasedOnTime_goodAfternoonMessageDisplayedSuccessful() { getApplicationDependencies() - oppiaClock.setCurrentTimeMs(EVENING_TIMESTAMP) - launch(createHomeActivityIntent(internalProfileId)).use { + oppiaClock.setCurrentTimeMs(AFTERNOON_TIMESTAMP) + launch(createHomeActivityIntent(internalProfileId1)).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(0) + scrollToPosition(position = 0) + verifyExactTextOnHomeListItemAtPosition( + itemPosition = 0, + targetViewId = R.id.welcome_text_view, + stringToMatch = "Good afternoon," ) - onView( - atPositionOnView( - R.id.home_recycler_view, - 0, - R.id.welcome_text_view - ) - ).check(matches(withText("Good evening,"))) } } @Test - fun testHomeActivity_recyclerViewIndex0_configurationChange_displaysWelcomeMessageCorrectly() { - launch(createHomeActivityIntent(0)).use { + fun testHomeActivity_displayGreetingMessageBasedOnTime_goodEveningMessageDisplayedSuccessful() { + getApplicationDependencies() + oppiaClock.setCurrentTimeMs(EVENING_TIMESTAMP) + launch(createHomeActivityIntent(internalProfileId1)).use { testCoroutineDispatchers.runCurrent() - onView(isRoot()).perform(orientationLandscape()) - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(0) + scrollToPosition(position = 0) + verifyExactTextOnHomeListItemAtPosition( + itemPosition = 0, + targetViewId = R.id.welcome_text_view, + stringToMatch = "Good evening," ) - onView( - atPositionOnView( - R.id.home_recycler_view, - 0, - R.id.profile_name_textview - ) - ).check(matches(withText("Admin!"))) } } @Test fun testHomeActivity_recyclerViewIndex1_displaysRecentlyPlayedStoriesText() { - launch(createHomeActivityIntent(internalProfileId)).use { - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(1) - ) - onView( - atPositionOnView( - R.id.home_recycler_view, - 1, - R.id.recently_played_stories_text_view - ) - ).check( - matches( - withText(R.string.recently_played_stories) - ) + launch(createHomeActivityIntent(internalProfileId1)).use { + scrollToPosition(position = 1) + verifyExactTextOnHomeListItemAtPosition( + itemPosition = 1, + targetViewId = R.id.recently_played_stories_text_view, + stringToMatch = context.getString(R.string.recently_played_stories) ) } } @Test - fun testHomeActivity_recyclerViewIndex1_displaysViewAllText() { - launch(createHomeActivityIntent(internalProfileId)).use { - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(1) - ) - onView(atPositionOnView(R.id.home_recycler_view, 1, R.id.view_all_text_view)).check( - matches( - withText(R.string.view_all) - ) + fun testHomeActivity_userProfile_displaysViewAllText() { + launch(createHomeActivityIntent(internalProfileId1)).use { + scrollToPosition(position = 1) + verifyExactTextOnHomeListItemAtPosition( + itemPosition = 1, + targetViewId = R.id.view_all_text_view, + stringToMatch = context.getString(R.string.view_all) ) } } @Test - fun testHomeActivity_recyclerViewIndex1_clickViewAll_opensRecentlyPlayedActivity() { - launch(createHomeActivityIntent(internalProfileId)).use { - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(1) - ) + fun testHomeActivity_clickViewAll_opensRecentlyPlayedActivity() { + launch(createHomeActivityIntent(internalProfileId1)).use { + scrollToPosition(position = 1) onView( atPositionOnView( - R.id.home_recycler_view, 1, R.id.view_all_text_view + R.id.home_recycler_view, + 1, + R.id.view_all_text_view ) ).perform(click()) intended(hasComponent(RecentlyPlayedActivity::class.java.name)) @@ -287,8 +255,8 @@ class HomeActivityTest { } @Test - fun testHomeActivity_recyclerViewIndex1_promotedCard_chapterNameIsCorrect() { - launch(createHomeActivityIntent(internalProfileId)).use { + fun testHomeActivity_promotedCard_chapterNameIsCorrect() { + launch(createHomeActivityIntent(internalProfileId1)).use { testCoroutineDispatchers.runCurrent() onView( allOf( @@ -302,43 +270,37 @@ class HomeActivityTest { } @Test - fun testHomeActivity_recyclerViewIndex1_promotedCard_storyNameIsCorrect() { - launch(createHomeActivityIntent(internalProfileId)).use { + fun testHomeActivity_promotedCard_storyNameIsCorrect() { + launch(createHomeActivityIntent(internalProfileId1)).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(1) - ) - onView(atPositionOnView(R.id.home_recycler_view, 1, R.id.story_name_text_view)).check( - matches( - withText(containsString("First Story")) - ) + scrollToPosition(position = 1) + verifyTextOnHomeListItemAtPosition( + itemPosition = 1, + targetViewId = R.id.story_name_text_view, + stringToMatch = "First Story" ) } } @Test - fun testHomeActivity_recyclerViewIndex1_configurationChange_promotedCard_storyNameIsCorrect() { - launch(createHomeActivityIntent(internalProfileId)).use { + fun testHomeActivity_configurationChange_promotedCard_storyNameIsCorrect() { + launch(createHomeActivityIntent(internalProfileId1)).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(1) - ) + scrollToPosition(position = 1) onView(isRoot()).perform(orientationLandscape()) - onView(atPositionOnView(R.id.home_recycler_view, 1, R.id.story_name_text_view)).check( - matches( - withText(containsString("First Story")) - ) + verifyTextOnHomeListItemAtPosition( + itemPosition = 1, + targetViewId = R.id.story_name_text_view, + stringToMatch = "First Story" ) } } @Test - fun testHomeActivity_recyclerViewIndex1_clickPromotedStory_opensTopicActivity() { - launch(createHomeActivityIntent(internalProfileId)).use { + fun testHomeActivity_clickPromotedStory_opensTopicActivity() { + launch(createHomeActivityIntent(internalProfileId1)).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(1) - ) + scrollToPosition(position = 1) onView( allOf( withId(R.id.promoted_story_list_recycler_view), @@ -348,119 +310,90 @@ class HomeActivityTest { ) ).perform(click()) intended(hasComponent(TopicActivity::class.java.name)) - intended(hasExtra(TopicActivity.getProfileIdKey(), internalProfileId)) + intended(hasExtra(TopicActivity.getProfileIdKey(), internalProfileId1)) intended(hasExtra(TopicActivity.getTopicIdKey(), TEST_TOPIC_ID_0)) intended(hasExtra(TopicActivity.getStoryIdKey(), TEST_STORY_ID_0)) } } @Test - fun testHomeActivity_recyclerViewIndex1_promotedCard_topicNameIsCorrect() { - launch(createHomeActivityIntent(internalProfileId)).use { + fun testHomeActivity_promotedCard_topicNameIsCorrect() { + launch(createHomeActivityIntent(internalProfileId1)).use { testCoroutineDispatchers.runCurrent() - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(1) - ) - onView(atPositionOnView(R.id.home_recycler_view, 1, R.id.topic_name_text_view)).check( - matches( - withText(containsString("FIRST TEST TOPIC")) - ) + scrollToPosition(position = 1) + verifyTextOnHomeListItemAtPosition( + itemPosition = 1, + targetViewId = R.id.topic_name_text_view, + stringToMatch = "FIRST TEST TOPIC" ) } } @Test - fun testHomeActivity_recyclerViewIndex3_topicSummary_topicNameIsCorrect() { - launch(createHomeActivityIntent(internalProfileId)).use { - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(3) - ) - onView(atPositionOnView(R.id.home_recycler_view, 3, R.id.topic_name_text_view)).check( - matches( - withText(containsString("First Test Topic")) - ) + fun testHomeActivity_topicSummaryCard_topicNameIsCorrect() { + launch(createHomeActivityIntent(internalProfileId1)).use { + scrollToPosition(position = 3) + verifyTextOnHomeListItemAtPosition( + itemPosition = 3, + targetViewId = R.id.topic_name_text_view, + stringToMatch = "First Test Topic" ) } } @Test - fun testHomeActivity_recyclerViewIndex3_topicSummary_lessonCountIsCorrect() { - launch(createHomeActivityIntent(internalProfileId)).use { - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(3) - ) - onView( - atPositionOnView( - R.id.home_recycler_view, - 3, R.id.lesson_count_text_view - ) - ).check( - matches( - withText(containsString("5 Lessons")) - ) + fun testHomeActivity_topicSummaryCard_lessonCountIsCorrect() { + launch(createHomeActivityIntent(internalProfileId1)).use { + scrollToPosition(position = 3) + verifyTextOnHomeListItemAtPosition( + itemPosition = 3, + targetViewId = R.id.lesson_count_text_view, + stringToMatch = "5 Lessons" ) } } @Test - fun testHomeActivity_recyclerViewIndex4_topicSummary_topicNameIsCorrect() { - launch(createHomeActivityIntent(internalProfileId)).use { - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(4) - ) - onView(atPositionOnView(R.id.home_recycler_view, 4, R.id.topic_name_text_view)).check( - matches( - withText(containsString("Second Test Topic")) - ) + fun testHomeActivity_topicSummarySecondCard_topicNameIsCorrect() { + launch(createHomeActivityIntent(internalProfileId1)).use { + scrollToPosition(position = 4) + verifyTextOnHomeListItemAtPosition( + itemPosition = 4, + targetViewId = R.id.topic_name_text_view, + stringToMatch = "Second Test Topic" ) } } @Test - fun testHomeActivity_recyclerViewIndex4_topicSummary_lessonCountIsCorrect() { - launch(createHomeActivityIntent(internalProfileId)).use { - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(4) - ) - onView( - atPositionOnView( - R.id.home_recycler_view, - 4, R.id.lesson_count_text_view - ) - ).check( - matches( - withText(containsString("1 Lesson")) - ) + fun testHomeActivity_topicSummarySecondCard_lessonCountIsCorrect() { + launch(createHomeActivityIntent(internalProfileId1)).use { + scrollToPosition(position = 4) + verifyTextOnHomeListItemAtPosition( + itemPosition = 4, + targetViewId = R.id.lesson_count_text_view, + stringToMatch = "1 Lesson" ) } } @Test - fun testHomeActivity_recyclerViewIndex4_topicSummary_configurationChange_lessonCountIsCorrect() { - launch(createHomeActivityIntent(internalProfileId)).use { + fun testHomeActivity_topicSummary_configurationChange_lessonCountIsCorrect() { + launch(createHomeActivityIntent(internalProfileId1)).use { onView(isRoot()).perform(orientationLandscape()) - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(4) - ) - onView( - atPositionOnView( - R.id.home_recycler_view, - 4, R.id.lesson_count_text_view - ) - ).check( - matches( - withText(containsString("1 Lesson")) - ) + scrollToPosition(position = 4) + verifyTextOnHomeListItemAtPosition( + itemPosition = 4, + targetViewId = R.id.lesson_count_text_view, + stringToMatch = "1 Lesson" ) } } @Test - fun testHomeActivity_recyclerViewIndex3_clickTopicSummary_opensTopicActivity() { - launch(createHomeActivityIntent(internalProfileId)).use { - onView(withId(R.id.home_recycler_view)).perform( - scrollToPosition(3) - ) + fun testHomeActivity_clickTopicSummary_opensTopicActivity() { + launch(createHomeActivityIntent(internalProfileId1)).use { + scrollToPosition(position = 3) onView(atPosition(R.id.home_recycler_view, 3)).perform(click()) intended(hasComponent(TopicActivity::class.java.name)) intended(hasExtra(TopicActivity.getTopicIdKey(), TEST_TOPIC_ID_0)) @@ -469,7 +402,7 @@ class HomeActivityTest { @Test fun testHomeActivity_onBackPressed_showsExitToProfileChooserDialog() { - launch(createHomeActivityIntent(internalProfileId)).use { + launch(createHomeActivityIntent(internalProfileId1)).use { pressBack() onView(withText(R.string.home_activity_back_dialog_message)) .inRoot(isDialog()) @@ -479,7 +412,7 @@ class HomeActivityTest { @Test fun testHomeActivity_onBackPressed_orientationChange_showsExitToProfileChooserDialog() { - launch(createHomeActivityIntent(internalProfileId)).use { + launch(createHomeActivityIntent(internalProfileId1)).use { pressBack() onView(isRoot()).perform(orientationLandscape()) onView(withText(R.string.home_activity_back_dialog_message)) @@ -490,7 +423,7 @@ class HomeActivityTest { @Test fun testHomeActivity_onBackPressed_clickExit_checkOpensProfileActivity() { - launch(createHomeActivityIntent(internalProfileId)).use { + launch(createHomeActivityIntent(internalProfileId1)).use { pressBack() onView(withText(R.string.home_activity_back_dialog_exit)) .inRoot(isDialog()) @@ -501,7 +434,7 @@ class HomeActivityTest { @Test fun testHomeActivity_checkSpanForItem0_spanSizeIsTwoOrThree() { - launch(createHomeActivityIntent(internalProfileId)).use { + launch(createHomeActivityIntent(internalProfileId1)).use { if (context.resources.getBoolean(R.bool.isTablet)) { onView(withId(R.id.home_recycler_view)).check(hasGridItemCount(3, 0)) } else { @@ -512,21 +445,57 @@ class HomeActivityTest { @Test fun testHomeActivity_checkSpanForItem4_spanSizeIsOne() { - launch(createHomeActivityIntent(internalProfileId)).use { + launch(createHomeActivityIntent(internalProfileId1)).use { onView(withId(R.id.home_recycler_view)).check(hasGridItemCount(1, 4)) } } @Test fun testHomeActivity_configurationChange_checkSpanForItem4_spanSizeIsOne() { - launch(createHomeActivityIntent(internalProfileId)).use { + launch(createHomeActivityIntent(internalProfileId1)).use { onView(isRoot()).perform(orientationLandscape()) onView(withId(R.id.home_recycler_view)).check(hasGridItemCount(1, 4)) } } private fun createHomeActivityIntent(profileId: Int): Intent { - return HomeActivity.createHomeActivity(ApplicationProvider.getApplicationContext(), profileId) + return HomeActivity.createHomeActivity(context, profileId) + } + + private fun scrollToPosition(position: Int) { + onView(withId(R.id.home_recycler_view)).perform( + scrollToPosition( + position + ) + ) + } + + private fun verifyTextOnHomeListItemAtPosition( + itemPosition: Int, + targetViewId: Int, + stringToMatch: String + ) { + onView( + atPositionOnView( + R.id.home_recycler_view, + itemPosition, + targetViewId + ) + ).check(matches(withText(containsString(stringToMatch)))) + } + + private fun verifyExactTextOnHomeListItemAtPosition( + itemPosition: Int, + targetViewId: Int, + stringToMatch: String + ) { + onView( + atPositionOnView( + R.id.home_recycler_view, + itemPosition, + targetViewId + ) + ).check(matches(withText(stringToMatch))) } // TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them. diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt index 6513a05c578..c56bb3f958b 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt @@ -3,7 +3,6 @@ package org.oppia.android.app.player.exploration import android.app.Application import android.content.Context import android.content.Intent -import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ActivityScenario.launch @@ -11,13 +10,9 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import androidx.test.espresso.Espresso.pressBack -import androidx.test.espresso.PerformException -import androidx.test.espresso.UiController -import androidx.test.espresso.ViewAction -import androidx.test.espresso.ViewInteraction -import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.action.ViewActions.typeText import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition @@ -33,15 +28,12 @@ import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.espresso.util.HumanReadables -import androidx.test.espresso.util.TreeIterables import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule import com.google.common.truth.Truth.assertThat import com.google.firebase.FirebaseApp import dagger.Component import org.hamcrest.CoreMatchers.allOf -import org.hamcrest.Matcher import org.hamcrest.Matchers.not import org.junit.After import org.junit.Before @@ -92,10 +84,13 @@ import org.oppia.android.domain.topic.TEST_EXPLORATION_ID_2 import org.oppia.android.domain.topic.TEST_STORY_ID_0 import org.oppia.android.domain.topic.TEST_TOPIC_ID_0 import org.oppia.android.testing.IsOnRobolectric +import org.oppia.android.testing.OppiaTestRule +import org.oppia.android.testing.RunOn import org.oppia.android.testing.TestAccessibilityModule import org.oppia.android.testing.TestCoroutineDispatchers import org.oppia.android.testing.TestDispatcherModule import org.oppia.android.testing.TestLogReportingModule +import org.oppia.android.testing.TestPlatform import org.oppia.android.util.caching.testing.CachingTestModule import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.logging.LoggerModule @@ -107,7 +102,6 @@ import org.oppia.android.util.parser.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import java.io.IOException -import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton @@ -120,6 +114,17 @@ import javax.inject.Singleton ) class ExplorationActivityTest { + // TODO(#163): Fill in remaining tests for this activity. + @get:Rule + var explorationActivityTestRule: ActivityTestRule = ActivityTestRule( + ExplorationActivity::class.java, + /* initialTouchMode= */ true, + /* launchActivity= */ false + ) + + @get:Rule + val oppiaTestRule = OppiaTestRule() + @Inject lateinit var explorationDataController: ExplorationDataController @@ -138,7 +143,6 @@ class ExplorationActivityTest { fun setUp() { Intents.init() setUpTestApplicationComponent() - context = ApplicationProvider.getApplicationContext() testCoroutineDispatchers.registerIdlingResource() FirebaseApp.initializeApp(context) } @@ -163,12 +167,6 @@ class ExplorationActivityTest { } } - // TODO(#163): Fill in remaining tests for this activity. - @get:Rule - var explorationActivityTestRule: ActivityTestRule = ActivityTestRule( - ExplorationActivity::class.java, /* initialTouchMode= */ true, /* launchActivity= */ false - ) - @Test fun testExploration_toolbarTitle_isDisplayedSuccessfully() { launch( @@ -198,7 +196,7 @@ class ExplorationActivityTest { ) ).use { explorationDataController.startPlayingExploration(TEST_EXPLORATION_ID_2) - onView(isRoot()).perform(orientationLandscape()) + changeConfiguration() testCoroutineDispatchers.runCurrent() onView(withId(R.id.exploration_toolbar_title)) .check(matches(withText("Prototype Exploration"))) @@ -295,7 +293,7 @@ class ExplorationActivityTest { ) ).use { explorationDataController.startPlayingExploration(TEST_EXPLORATION_ID_2) - onView(isRoot()).perform(orientationLandscape()) + changeConfiguration() onView(withId(R.id.action_audio_player)).check(matches(not(isDisplayed()))) } explorationDataController.stopPlayingExploration() @@ -312,13 +310,12 @@ class ExplorationActivityTest { RATIOS_EXPLORATION_ID_0 ) ).use { - explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) - networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.NONE) - testCoroutineDispatchers.runCurrent() - onView(withId(R.id.action_audio_player)).perform(click()) - onView(withText(context.getString(R.string.audio_dialog_offline_message))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) + startPlayingExplorationWithConnectionType( + explorationId = RATIOS_EXPLORATION_ID_0, + connectionStatus = NetworkConnectionUtil.ConnectionStatus.NONE + ) + clickAudioPlayerIcon() + verifyTextInDialog(context.getString(R.string.audio_dialog_offline_message)) } explorationDataController.stopPlayingExploration() } @@ -328,19 +325,18 @@ class ExplorationActivityTest { setupAudio() launch( createExplorationActivityIntent( - internalProfileId, RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 + internalProfileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, + RATIOS_EXPLORATION_ID_0 ) ).use { - explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) - networkConnectionUtil.setCurrentConnectionStatus( - NetworkConnectionUtil.ConnectionStatus.CELLULAR + startPlayingExplorationWithConnectionType( + explorationId = RATIOS_EXPLORATION_ID_0, + connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR ) - testCoroutineDispatchers.runCurrent() - onView(withId(R.id.action_audio_player)).perform(click()) - onView(withText(context.getString(R.string.cellular_data_alert_dialog_title))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) + clickAudioPlayerIcon() + verifyTextInDialog(context.getString(R.string.cellular_data_alert_dialog_title)) } explorationDataController.stopPlayingExploration() } @@ -351,20 +347,19 @@ class ExplorationActivityTest { setupAudio() launch( createExplorationActivityIntent( - internalProfileId, RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 + internalProfileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, + RATIOS_EXPLORATION_ID_0 ) ).use { - explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) - networkConnectionUtil.setCurrentConnectionStatus( - NetworkConnectionUtil.ConnectionStatus.CELLULAR + startPlayingExplorationWithConnectionType( + explorationId = RATIOS_EXPLORATION_ID_0, + connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR ) - testCoroutineDispatchers.runCurrent() - onView(withId(R.id.action_audio_player)).perform(click()) - onView(isRoot()).perform(orientationLandscape()) - onView(withText(context.getString(R.string.cellular_data_alert_dialog_title))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) + clickAudioPlayerIcon() + changeConfiguration() + verifyTextInDialog(context.getString(R.string.cellular_data_alert_dialog_title)) } explorationDataController.stopPlayingExploration() } @@ -375,22 +370,17 @@ class ExplorationActivityTest { setupAudio() launch( createExplorationActivityIntent( - internalProfileId, RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 + internalProfileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, + RATIOS_EXPLORATION_ID_0 ) ).use { - explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) - networkConnectionUtil.setCurrentConnectionStatus( - NetworkConnectionUtil.ConnectionStatus.CELLULAR - ) - testCoroutineDispatchers.runCurrent() - onView(withId(R.id.action_audio_player)).perform(click()) - onView( - allOf( - withText(context.getString(R.string.cellular_data_alert_dialog_title)), - withEffectiveVisibility(Visibility.VISIBLE) - ) + startPlayingExplorationWithConnectionType( + explorationId = RATIOS_EXPLORATION_ID_0, + connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR ) + clickAudioPlayerIcon() onView( allOf( withText(context.getString(R.string.audio_language_select_dialog_cancel_button)), @@ -414,26 +404,17 @@ class ExplorationActivityTest { RATIOS_EXPLORATION_ID_0 ) ).use { - explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) - networkConnectionUtil.setCurrentConnectionStatus( - NetworkConnectionUtil.ConnectionStatus.CELLULAR - ) - testCoroutineDispatchers.runCurrent() - onView(withId(R.id.action_audio_player)).perform(click()) - onView( - allOf( - withText(context.getString(R.string.cellular_data_alert_dialog_title)), - withEffectiveVisibility(Visibility.VISIBLE) - ) + startPlayingExplorationWithConnectionType( + explorationId = RATIOS_EXPLORATION_ID_0, + connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR ) - + clickAudioPlayerIcon() onView( allOf( withText(context.getString(R.string.cellular_data_alert_dialog_okay_button)), withEffectiveVisibility(Visibility.VISIBLE) ) ).inRoot(isDialog()).perform(click()) - onView( allOf( withId(R.id.ivPlayPauseAudio), @@ -450,28 +431,23 @@ class ExplorationActivityTest { setupAudio() launch( createExplorationActivityIntent( - internalProfileId, RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 + internalProfileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, + RATIOS_EXPLORATION_ID_0 ) ).use { - explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) - networkConnectionUtil.setCurrentConnectionStatus( - NetworkConnectionUtil.ConnectionStatus.CELLULAR + startPlayingExplorationWithConnectionType( + explorationId = RATIOS_EXPLORATION_ID_0, + connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR ) - testCoroutineDispatchers.runCurrent() - onView(withId(R.id.action_audio_player)).perform(click()) - onView(withText(context.getString(R.string.cellular_data_alert_dialog_title))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - onView(withId(R.id.cellular_data_dialog_checkbox)) - .inRoot(isDialog()) - .perform(click()) - onView(withText(context.getString(R.string.audio_language_select_dialog_cancel_button))) - .inRoot(isDialog()) - .perform(click()) + clickAudioPlayerIcon() + verifyTextInDialog(context.getString(R.string.cellular_data_alert_dialog_title)) + clickOnDialogItemView(R.id.cellular_data_dialog_checkbox) + clickOnDialogItemText(context.getString(R.string.audio_language_select_dialog_cancel_button)) testCoroutineDispatchers.runCurrent() - onView(withId(R.id.action_audio_player)).perform(click()) + clickAudioPlayerIcon() testCoroutineDispatchers.runCurrent() onView(withId(R.id.ivPlayPauseAudio)).check(matches(not(isDisplayed()))) @@ -487,29 +463,24 @@ class ExplorationActivityTest { setupAudio() launch( createExplorationActivityIntent( - internalProfileId, RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 + internalProfileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, + RATIOS_EXPLORATION_ID_0 ) ).use { - explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) - networkConnectionUtil.setCurrentConnectionStatus( - NetworkConnectionUtil.ConnectionStatus.CELLULAR + startPlayingExplorationWithConnectionType( + explorationId = RATIOS_EXPLORATION_ID_0, + connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR ) - testCoroutineDispatchers.runCurrent() - onView(withId(R.id.action_audio_player)).perform(click()) - onView(withText(context.getString(R.string.cellular_data_alert_dialog_title))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - onView(withId(R.id.cellular_data_dialog_checkbox)) - .inRoot(isDialog()) - .perform(click()) - onView(withText(context.getString(R.string.audio_language_select_dialog_okay_button))) - .inRoot(isDialog()) - .perform(click()) + clickAudioPlayerIcon() + verifyTextInDialog(context.getString(R.string.cellular_data_alert_dialog_title)) + clickOnDialogItemView(R.id.cellular_data_dialog_checkbox) + clickOnDialogItemText(context.getString(R.string.audio_language_select_dialog_okay_button)) testCoroutineDispatchers.runCurrent() - onView(withId(R.id.action_audio_player)).perform(click()) - onView(withId(R.id.action_audio_player)).perform(click()) + clickAudioPlayerIcon() + clickAudioPlayerIcon() testCoroutineDispatchers.runCurrent() onView(withId(R.id.ivPlayPauseAudio)).check(matches(isDisplayed())) @@ -520,12 +491,10 @@ class ExplorationActivityTest { } // TODO (#1855): Resolve ktlint max line in app module test - // TODO(#89): The ExplorationActivity takes time to finish. This test case is failing currently. + @RunOn(TestPlatform.ROBOLECTRIC) @Test - @Ignore("The ExplorationActivity takes time to finish, needs to fixed in #89.") fun testAudioWithWifi_openRatioExploration_clickAudioIcon_checkAudioFragmentHasDefaultLanguageAndAutoPlays() { // ktlint-disable max-line-length - getApplicationDependencies(RATIOS_EXPLORATION_ID_0) - networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) + setupAudio() launch( createExplorationActivityIntent( internalProfileId, @@ -534,16 +503,20 @@ class ExplorationActivityTest { RATIOS_EXPLORATION_ID_0 ) ).use { - waitForTheView(withText("What is a Ratio?")) - onView(withId(R.id.action_audio_player)).perform(click()) - onView( - allOf( - withId(R.id.ivPlayPauseAudio), - withEffectiveVisibility(Visibility.VISIBLE) - ) + startPlayingExplorationWithConnectionType( + explorationId = RATIOS_EXPLORATION_ID_0, + connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR ) - onView(allOf(withText("EN"), withEffectiveVisibility(Visibility.VISIBLE))) - waitForTheView(withDrawable(R.drawable.ic_pause_circle_filled_white_24dp)) + testCoroutineDispatchers.runCurrent() + clickAudioPlayerIcon() + clickOnDialogItemText(context.getString(R.string.audio_language_select_dialog_okay_button)) + + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.ivPlayPauseAudio)) + .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) + onView(withText("EN")) + .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) + testCoroutineDispatchers.advanceUntilIdle() onView(withId(R.id.ivPlayPauseAudio)).check( matches( withDrawable( @@ -567,29 +540,26 @@ class ExplorationActivityTest { FRACTIONS_EXPLORATION_ID_0 ) ).use { - explorationDataController.startPlayingExploration(FRACTIONS_EXPLORATION_ID_0) - networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) - testCoroutineDispatchers.runCurrent() + startPlayingExplorationWithConnectionType( + explorationId = FRACTIONS_EXPLORATION_ID_0, + connectionStatus = NetworkConnectionUtil.ConnectionStatus.LOCAL + ) onView(withId(R.id.state_recycler_view)).perform( scrollToPosition( 1 ) ) onView(withId(R.id.continue_button)).perform(click()) - onView(withId(R.id.action_audio_player)).perform(click()) + clickAudioPlayerIcon() onView( allOf( withText("EN"), withEffectiveVisibility(Visibility.VISIBLE) ) ).perform(click()) - onView(withText("Hinglish")) - .inRoot(isDialog()) - .perform(click()) + clickOnDialogItemText("Hinglish") - onView(withText(context.getString(R.string.audio_language_select_dialog_okay_button))) - .inRoot(isDialog()) - .perform(click()) + clickOnDialogItemText(context.getString(R.string.audio_language_select_dialog_okay_button)) onView(withId(R.id.state_recycler_view)).perform( scrollToPosition( @@ -603,19 +573,27 @@ class ExplorationActivityTest { } // TODO (#1855): Resolve ktlint max line in app module test - // TODO(#89): The ExplorationActivity takes time to finish. This test case is failing currently. + @RunOn(TestPlatform.ROBOLECTRIC) @Test - @Ignore("The ExplorationActivity takes time to finish, needs to fixed in #89.") fun testAudioWithWifi_openRatioExploration_continueToInteraction_clickAudioButton_submitAnswer_checkFeedbackAudioPlays() { // ktlint-disable max-line-length - getApplicationDependencies(RATIOS_EXPLORATION_ID_0) - networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) + setupAudio() launch( createExplorationActivityIntent( - internalProfileId, RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 + internalProfileId, + RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, + RATIOS_EXPLORATION_ID_0 ) ).use { - waitForTheView(withText("What is a Ratio?")) + startPlayingExplorationWithConnectionType( + explorationId = RATIOS_EXPLORATION_ID_0, + connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR + ) + onView(withId(R.id.state_recycler_view)).perform( + scrollToPosition( + 1 + ) + ) // Clicks continue until we reach the first interaction. onView(withId(R.id.continue_button)).perform(click()) onView(withId(R.id.continue_button)).perform(click()) @@ -623,16 +601,29 @@ class ExplorationActivityTest { onView(withId(R.id.continue_button)).perform(click()) onView(withId(R.id.continue_button)).perform(click()) - onView(withId(R.id.action_audio_player)).perform(click()) + clickAudioPlayerIcon() + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.state_recycler_view)).perform( + scrollToPosition( + 1 + ) + ) onView(withId(R.id.text_input_interaction_view)).perform( - ViewActions.typeText("123"), + typeText("123"), closeSoftKeyboard() ) + testCoroutineDispatchers.advanceUntilIdle() + onView(withId(R.id.submit_answer_button)).perform(click()) - Thread.sleep(1000) + testCoroutineDispatchers.runCurrent() - onView(withId(R.id.ivPlayPauseAudio)) - .check(matches(withContentDescription(context.getString(R.string.audio_pause_description)))) + onView(withId(R.id.ivPlayPauseAudio)).check( + matches( + withDrawable( + R.drawable.ic_pause_circle_filled_white_24dp + ) + ) + ) } explorationDataController.stopPlayingExploration() } @@ -662,8 +653,7 @@ class ExplorationActivityTest { ) ).use { pressBack() - onView(withText(R.string.stop_exploration_dialog_title)).inRoot(isDialog()) - .check(matches(isDisplayed())) + verifyTextInDialog(context.getString(R.string.stop_exploration_dialog_title)) } } @@ -678,13 +668,11 @@ class ExplorationActivityTest { ) ).use { onView(withContentDescription(R.string.nav_app_bar_navigate_up_description)).perform(click()) - onView(withText(R.string.stop_exploration_dialog_title)).inRoot(isDialog()) - .check(matches(isDisplayed())) + verifyTextInDialog(context.getString(R.string.stop_exploration_dialog_title)) } } // TODO (#1855): Resolve ktlint max line in app module test - // TODO(#89): Check this test case too. It works in pair with below test case. @Test fun testExplorationActivity_onBackPressed_showsStopExplorationDialog_clickCancel_dismissesDialog() { // ktlint-disable max-line-length explorationActivityTestRule.launchActivity( @@ -696,8 +684,8 @@ class ExplorationActivityTest { ) ) pressBack() - onView(withText(R.string.stop_exploration_dialog_cancel_button)).inRoot(isDialog()) - .perform(click()) + clickOnDialogItemText(context.getString(R.string.stop_exploration_dialog_cancel_button)) + testCoroutineDispatchers.runCurrent() assertThat(explorationActivityTestRule.activity.isFinishing).isFalse() } @@ -715,8 +703,8 @@ class ExplorationActivityTest { ) ) pressBack() - onView(withText(R.string.stop_exploration_dialog_leave_button)).inRoot(isDialog()) - .perform(click()) + clickOnDialogItemText(context.getString(R.string.stop_exploration_dialog_leave_button)) + testCoroutineDispatchers.runCurrent() assertThat(explorationActivityTestRule.activity.isFinishing).isTrue() } @@ -732,10 +720,45 @@ class ExplorationActivityTest { topicId, storyId, explorationId, - /* backflowScreen= */ null + backflowScreen = null ) } + private fun changeConfiguration() { + onView(isRoot()).perform(orientationLandscape()) + } + + private fun clickAudioPlayerIcon() { + onView(withId(R.id.action_audio_player)).perform(click()) + } + + private fun startPlayingExplorationWithConnectionType( + explorationId: String, + connectionStatus: NetworkConnectionUtil.ConnectionStatus + ) { + explorationDataController.startPlayingExploration(explorationId) + networkConnectionUtil.setCurrentConnectionStatus(connectionStatus) + testCoroutineDispatchers.runCurrent() + } + + private fun verifyTextInDialog(textInDialog: String) { + onView(withText(textInDialog)) + .inRoot(isDialog()) + .check(matches(isDisplayed())) + } + + private fun clickOnDialogItemText(string: String) { + onView(withText(string)) + .inRoot(isDialog()) + .perform(click()) + } + + private fun clickOnDialogItemView(viewId: Int) { + onView(withId(viewId)) + .inRoot(isDialog()) + .perform(click()) + } + private fun setupAudio() { // Only initialize the Robolectric shadows when running on Robolectric (and use reflection since // Espresso can't load Robolectric into its classpath). @@ -793,53 +816,6 @@ class ExplorationActivityTest { "exploration/$explorationId/assets/audio/$audioFileName" } - private fun waitForTheView(viewMatcher: Matcher): ViewInteraction { - return onView(isRoot()).perform(waitForMatch(viewMatcher, 30000L)) - } - -// TODO(#59): Remove these waits once we can ensure that the production executors are not depended on in tests. -// Sleeping is really bad practice in Espresso tests, and can lead to test flakiness. It shouldn't be necessary if we -// use a test executor service with a counting idle resource, but right now Gradle mixes dependencies such that both -// the test and production blocking executors are being used. The latter cannot be updated to notify Espresso of any -// active coroutines, so the test attempts to assert state before it's ready. This artificial delay in the Espresso -// thread helps to counter that. - /** - * Perform action of waiting for a specific matcher to finish. Adapted from: - * https://stackoverflow.com/a/22563297/3689782. - */ - private fun waitForMatch(viewMatcher: Matcher, millis: Long): ViewAction { - return object : ViewAction { - override fun getDescription(): String { - return "wait for a specific view with matcher <$viewMatcher> during $millis millis." - } - - override fun getConstraints(): Matcher { - return isRoot() - } - - override fun perform(uiController: UiController?, view: View?) { - checkNotNull(uiController) - uiController.loopMainThreadUntilIdle() - val startTime = System.currentTimeMillis() - val endTime = startTime + millis - - do { - if (TreeIterables.breadthFirstViewTraversal(view).any { viewMatcher.matches(it) }) { - return - } - uiController.loopMainThreadForAtLeast(50) - } while (System.currentTimeMillis() < endTime) - - // Couldn't match in time. - throw PerformException.Builder() - .withActionDescription(description) - .withViewDescription(HumanReadables.describe(view)) - .withCause(TimeoutException()) - .build() - } - } - } - // TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them. // TODO(#1675): Add NetworkModule once data module is migrated off of Moshi. @Singleton diff --git a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt index f87efc4e20c..f2237387735 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt @@ -9,16 +9,11 @@ import android.content.Intent import android.content.res.Resources import android.net.Uri import android.provider.MediaStore -import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ActivityScenario.launch import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.PerformException -import androidx.test.espresso.UiController -import androidx.test.espresso.ViewAction -import androidx.test.espresso.ViewInteraction import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition @@ -35,15 +30,12 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.espresso.util.HumanReadables -import androidx.test.espresso.util.TreeIterables import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.firebase.FirebaseApp import dagger.Component import dagger.Module import dagger.Provides import org.hamcrest.CoreMatchers.allOf -import org.hamcrest.CoreMatchers.containsString import org.hamcrest.CoreMatchers.not import org.hamcrest.Matcher import org.junit.After @@ -104,7 +96,6 @@ import org.oppia.android.util.parser.HtmlParserEntityTypeModule import org.oppia.android.util.parser.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode -import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton @@ -149,6 +140,10 @@ class ProfileProgressFragmentTest { Intents.release() } + private fun setUpTestApplicationComponent() { + ApplicationProvider.getApplicationContext().inject(this) + } + private fun createProfileProgressActivityIntent(profileId: Int): Intent { return ProfileProgressActivity.createProfileProgressActivityIntent( ApplicationProvider.getApplicationContext(), @@ -160,11 +155,11 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragment_checkProfileName_profileNameIsCorrect() { launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText("Admin")) - onView( - atPositionOnView(R.id.profile_progress_list, 0, R.id.profile_name_text_view) - ).check( - matches(withText("Admin")) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.profile_name_text_view, + stringToMatch = "Admin" ) } } @@ -174,11 +169,11 @@ class ProfileProgressFragmentTest { launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() onView(isRoot()).perform(orientationLandscape()) - waitForTheView(withText("Admin")) - onView( - atPositionOnView(R.id.profile_progress_list, 0, R.id.profile_name_text_view) - ).check( - matches(withText("Admin")) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.profile_name_text_view, + stringToMatch = "Admin" ) } } @@ -187,17 +182,9 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragment_openProfilePictureEditDialog() { launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText("Admin")) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, - R.id.profile_edit_image - ) - ).perform(click()) + clickProfileProgressItem(itemPosition = 0, targetViewId = R.id.profile_edit_image) testCoroutineDispatchers.runCurrent() - onView(withText(R.string.profile_progress_edit_dialog_title)).inRoot(isDialog()) - .check(matches(isDisplayed())) + verifyTextInDialog(context.getString(R.string.profile_progress_edit_dialog_title)) } } @@ -205,17 +192,8 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragment_openProfilePictureEditDialog_configurationChange_dialogIsStillOpen() { // ktlint-disable max-line-length launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText("Admin")) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, - R.id.profile_edit_image - ) - ).perform(click()) - testCoroutineDispatchers.runCurrent() - onView(withText(R.string.profile_progress_edit_dialog_title)).inRoot(isDialog()) - .check(matches(isDisplayed())) + clickProfileProgressItem(itemPosition = 0, targetViewId = R.id.profile_edit_image) + verifyTextInDialog(context.getString(R.string.profile_progress_edit_dialog_title)) onView(isRoot()).perform(orientationLandscape()) onView(withText(R.string.profile_progress_edit_dialog_title)).check(matches(isDisplayed())) } @@ -231,17 +209,8 @@ class ProfileProgressFragmentTest { intending(expectedIntent).respondWith(activityResult) launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText("Admin")) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, - R.id.profile_edit_image - ) - ).perform(click()) - testCoroutineDispatchers.runCurrent() - onView(withText(R.string.profile_progress_edit_dialog_title)).inRoot(isDialog()) - .check(matches(isDisplayed())) + clickProfileProgressItem(itemPosition = 0, targetViewId = R.id.profile_edit_image) + verifyTextInDialog(context.getString(R.string.profile_progress_edit_dialog_title)) onView(withText(R.string.profile_picture_edit_alert_dialog_choose_from_library)) .perform(click()) intended(expectedIntent) @@ -258,17 +227,8 @@ class ProfileProgressFragmentTest { intending(expectedIntent).respondWith(activityResult) launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText("Admin")) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, - R.id.profile_edit_image - ) - ).perform(click()) - testCoroutineDispatchers.runCurrent() - onView(withText(R.string.profile_progress_edit_dialog_title)).inRoot(isDialog()) - .check(matches(isDisplayed())) + clickProfileProgressItem(itemPosition = 0, targetViewId = R.id.profile_edit_image) + verifyTextInDialog(context.getString(R.string.profile_progress_edit_dialog_title)) onView(withText(R.string.profile_picture_edit_alert_dialog_choose_from_library)) .perform(click()) testCoroutineDispatchers.runCurrent() @@ -276,17 +236,9 @@ class ProfileProgressFragmentTest { onView(isRoot()).perform(orientationLandscape()) testCoroutineDispatchers.runCurrent() intended(expectedIntent) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, - R.id.profile_edit_image - ) - ).perform(click()) - testCoroutineDispatchers.runCurrent() + clickProfileProgressItem(itemPosition = 0, targetViewId = R.id.profile_edit_image) // The dialog should still be open after a configuration change. - onView(withText(R.string.profile_progress_edit_dialog_title)).inRoot(isDialog()) - .check(matches(isDisplayed())) + verifyTextInDialog(context.getString(R.string.profile_progress_edit_dialog_title)) } } @@ -294,11 +246,11 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragmentNoProgress_recyclerViewItem0_checkOngoingTopicsCount_countIsZero() { // ktlint-disable max-line-length launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText("0")) - onView( - atPositionOnView(R.id.profile_progress_list, 0, R.id.ongoing_topics_count) - ).check( - matches(withText("0")) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.ongoing_topics_count, + stringToMatch = "0" ) } } @@ -316,11 +268,11 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText("2")) - onView( - atPositionOnView(R.id.profile_progress_list, 0, R.id.ongoing_topics_count) - ).check( - matches(withText("2")) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.ongoing_topics_count, + stringToMatch = "2" ) } } @@ -340,11 +292,11 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() onView(isRoot()).perform(orientationLandscape()) testCoroutineDispatchers.runCurrent() - waitForTheView(withText("2")) - onView( - atPositionOnView(R.id.profile_progress_list, 0, R.id.ongoing_topics_count) - ).check( - matches(withText("2")) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.ongoing_topics_count, + stringToMatch = "2" ) } } @@ -353,14 +305,11 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragmentNoProgress_recyclerViewItem0_checkOngoingTopicsString_descriptionIsCorrect() { // ktlint-disable max-line-length launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText(R.string.topics_in_progress)) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, R.id.ongoing_topics_description_text_view - ) - ).check( - matches(withText(R.string.topics_in_progress)) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.ongoing_topics_description_text_view, + stringToMatch = context.getString(R.string.topics_in_progress) ) } } @@ -378,14 +327,11 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText(R.string.topics_in_progress)) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, R.id.ongoing_topics_description_text_view - ) - ).check( - matches(withText(R.string.topics_in_progress)) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.ongoing_topics_description_text_view, + stringToMatch = context.getString(R.string.topics_in_progress) ) } } @@ -405,14 +351,11 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() onView(isRoot()).perform(orientationLandscape()) testCoroutineDispatchers.runCurrent() - waitForTheView(withText(R.string.topics_in_progress)) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, R.id.ongoing_topics_description_text_view - ) - ).check( - matches(withText(R.string.topics_in_progress)) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.ongoing_topics_description_text_view, + stringToMatch = context.getString(R.string.topics_in_progress) ) } } @@ -421,11 +364,11 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragmentNoProgress_recyclerViewItem0_checkCompletedStoriesCount_countIsZero() { // ktlint-disable max-line-length launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText("0")) - onView( - atPositionOnView(R.id.profile_progress_list, 0, R.id.completed_stories_count) - ).check( - matches(withText("0")) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.completed_stories_count, + stringToMatch = "0" ) } } @@ -443,11 +386,11 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText("2")) - onView( - atPositionOnView(R.id.profile_progress_list, 0, R.id.completed_stories_count) - ).check( - matches(withText("2")) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.completed_stories_count, + stringToMatch = "2" ) } } @@ -456,15 +399,11 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragmentNoProgress_recyclerViewItem0_checkCompletedStoriesString_descriptionIsCorrect() { // ktlint-disable max-line-length launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText(R.string.stories_completed)) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, - R.id.completed_stories_description_text_view - ) - ).check( - matches(withText(R.string.stories_completed)) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.completed_stories_description_text_view, + stringToMatch = context.getString(R.string.stories_completed) ) } } @@ -482,15 +421,11 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText(R.string.stories_completed)) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, - R.id.completed_stories_description_text_view - ) - ).check( - matches(withText(R.string.stories_completed)) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.completed_stories_description_text_view, + stringToMatch = context.getString(R.string.stories_completed) ) } } @@ -503,11 +438,11 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() onView(withId(R.id.profile_progress_list)) .perform(scrollToPosition(1)) - waitForTheView(withText("First Story")) - onView( - atPositionOnView(R.id.profile_progress_list, 1, R.id.story_name_text_view) - ).check( - matches(withText(containsString("First Story"))) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 1, + targetViewId = R.id.story_name_text_view, + stringToMatch = "First Story" ) } } @@ -521,14 +456,11 @@ class ProfileProgressFragmentTest { 1 ) ) - waitForTheView(withText("First Story")) - onView( - atPositionOnView( - R.id.profile_progress_list, - 1, R.id.story_name_text_view - ) - ).check( - matches(withText(containsString("First Story"))) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 1, + targetViewId = R.id.story_name_text_view, + stringToMatch = "First Story" ) } } @@ -542,14 +474,11 @@ class ProfileProgressFragmentTest { 1 ) ) - waitForTheView(withText("FIRST TEST TOPIC")) - onView( - atPositionOnView( - R.id.profile_progress_list, - 1, R.id.topic_name_text_view - ) - ).check( - matches(withText(containsString("FIRST TEST TOPIC"))) + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 1, + targetViewId = R.id.topic_name_text_view, + stringToMatch = "FIRST TEST TOPIC" ) } } @@ -563,14 +492,8 @@ class ProfileProgressFragmentTest { 1 ) ) - waitForTheView(withText("FIRST TEST TOPIC")) - onView( - atPositionOnView( - R.id.profile_progress_list, - 1, R.id.topic_name_text_view - ) - ).perform(click()) testCoroutineDispatchers.runCurrent() + clickProfileProgressItem(itemPosition = 1, targetViewId = R.id.topic_name_text_view) intended(hasComponent(TopicActivity::class.java.name)) intended(hasExtra(TopicActivity.getProfileIdKey(), internalProfileId)) intended(hasExtra(TopicActivity.getTopicIdKey(), TEST_TOPIC_ID_0)) @@ -582,13 +505,13 @@ class ProfileProgressFragmentTest { fun testProfileProgressActivity_recyclerViewIndex0_clickViewAll_opensRecentlyPlayedActivity() { launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText("Admin")) - onView(atPositionOnView(R.id.profile_progress_list, 0, R.id.view_all_text_view)) - .check( - matches(withText("View All")) - ) - .perform(click()) - testCoroutineDispatchers.runCurrent() + matchTextOnListItem( + recyclerView = R.id.profile_progress_list, + itemPosition = 0, + targetViewId = R.id.view_all_text_view, + stringToMatch = "View All" + ) + clickProfileProgressItem(itemPosition = 0, targetViewId = R.id.view_all_text_view) intended(hasComponent(RecentlyPlayedActivity::class.java.name)) intended( hasExtra( @@ -603,15 +526,13 @@ class ProfileProgressFragmentTest { fun testProfileProgressActivityNoProgress_recyclerViewIndex0_clickTopicCount_isNotClickable() { launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText(R.string.topics_in_progress)) onView( atPositionOnView( R.id.profile_progress_list, - 0, R.id.ongoing_topics_container + 0, + R.id.ongoing_topics_container ) - ).check( - matches(not(isClickable())) - ) + ).check(matches(not(isClickable()))) } } @@ -619,15 +540,13 @@ class ProfileProgressFragmentTest { fun testProfileProgressActivityNoProgress_recyclerViewIndex0_clickStoryCount_isNotClickable() { launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText(R.string.stories_completed)) onView( atPositionOnView( R.id.profile_progress_list, - 0, R.id.completed_stories_container + 0, + R.id.completed_stories_container ) - ).check( - matches(not(isClickable())) - ) + ).check(matches(not(isClickable()))) } } @@ -637,39 +556,30 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() onView(isRoot()).perform(orientationLandscape()) testCoroutineDispatchers.runCurrent() - waitForTheView(withText(R.string.stories_completed)) onView( atPositionOnView( R.id.profile_progress_list, - 0, R.id.completed_stories_container + 0, + R.id.completed_stories_container ) - ).check( - matches(not(isClickable())) - ) + ).check(matches(not(isClickable()))) } } @Test fun testProfileProgressActivityWithProgress_recyclerViewIndex0_clickTopicCount_opensOngoingTopicListActivity() { // ktlint-disable max-line-length storyProgressTestHelper.markPartialTopicProgressForFractions( - profileId, + profileId = profileId, timestampOlderThanAWeek = false ) storyProgressTestHelper.markTwoPartialStoryProgressForRatios( - profileId, + profileId = profileId, timestampOlderThanAWeek = false ) testCoroutineDispatchers.runCurrent() launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText(R.string.topics_in_progress)) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, - R.id.ongoing_topics_container - ) - ).perform(click()) + clickProfileProgressItem(itemPosition = 0, targetViewId = R.id.ongoing_topics_container) testCoroutineDispatchers.runCurrent() intended(hasComponent(OngoingTopicListActivity::class.java.name)) intended( @@ -684,25 +594,17 @@ class ProfileProgressFragmentTest { @Test fun testProfileProgressActivityWithProgress_recyclerViewIndex0_clickStoryCount_opensCompletedStoryListActivity() { // ktlint-disable max-line-length storyProgressTestHelper.markFullStoryPartialTopicProgressForRatios( - profileId, + profileId = profileId, timestampOlderThanAWeek = false ) storyProgressTestHelper.markFullStoryProgressForFractions( - profileId, + profileId = profileId, timestampOlderThanAWeek = false ) testCoroutineDispatchers.runCurrent() launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - waitForTheView(withText(R.string.stories_completed)) - onView( - atPositionOnView( - R.id.profile_progress_list, - 0, - R.id.completed_stories_container - ) - ).perform(click()) - testCoroutineDispatchers.runCurrent() + clickProfileProgressItem(itemPosition = 0, targetViewId = R.id.completed_stories_container) intended(hasComponent(CompletedStoryListActivity::class.java.name)) intended( hasExtra( @@ -726,51 +628,37 @@ class ProfileProgressFragmentTest { return Instrumentation.ActivityResult(RESULT_OK, resultIntent) } - private fun waitForTheView(viewMatcher: Matcher): ViewInteraction { - return onView(isRoot()).perform(waitForMatch(viewMatcher, 30000)) - } - - // TODO(#59): Remove these waits once we can ensure that the production executors are not depended on in tests. - // Sleeping is really bad practice in Espresso tests, and can lead to test flakiness. It shouldn't be necessary if we - // use a test executor service with a counting idle resource, but right now Gradle mixes dependencies such that both - // the test and production blocking executors are being used. The latter cannot be updated to notify Espresso of any - // active coroutines, so the test attempts to assert state before it's ready. This artificial delay in the Espresso - // thread helps to counter that. - /** - * Perform action of waiting for a specific matcher to finish. Adapted from: - * https://stackoverflow.com/a/22563297/3689782. - */ - private fun waitForMatch(viewMatcher: Matcher, millis: Long): ViewAction { - return object : ViewAction { - override fun getDescription(): String { - return "wait for a specific view with matcher <$viewMatcher> during $millis millis." - } - - override fun getConstraints(): Matcher { - return isRoot() - } - - override fun perform(uiController: UiController?, view: View?) { - checkNotNull(uiController) - uiController.loopMainThreadUntilIdle() - val startTime = System.currentTimeMillis() - val endTime = startTime + millis - - do { - if (TreeIterables.breadthFirstViewTraversal(view).any { viewMatcher.matches(it) }) { - return - } - uiController.loopMainThreadForAtLeast(50) - } while (System.currentTimeMillis() < endTime) - - // Couldn't match in time. - throw PerformException.Builder() - .withActionDescription(description) - .withViewDescription(HumanReadables.describe(view)) - .withCause(TimeoutException()) - .build() - } - } + // TODO(#2208): Create helper function in Test for RecyclerView + private fun matchTextOnListItem( + recyclerView: Int, + itemPosition: Int, + targetViewId: Int, + stringToMatch: String + ) { + onView( + atPositionOnView( + recyclerView, + itemPosition, + targetViewId + ) + ).check(matches(withText(stringToMatch))) + } + + private fun clickProfileProgressItem(itemPosition: Int, targetViewId: Int) { + onView( + atPositionOnView( + R.id.profile_progress_list, + itemPosition, + targetViewId + ) + ).perform(click()) + testCoroutineDispatchers.runCurrent() + } + + private fun verifyTextInDialog(textInDialog: String) { + onView(withText(textInDialog)) + .inRoot(isDialog()) + .check(matches(isDisplayed())) } @Module @@ -790,10 +678,6 @@ class ProfileProgressFragmentTest { fun provideGlobalLogLevel(): LogLevel = LogLevel.VERBOSE } - private fun setUpTestApplicationComponent() { - ApplicationProvider.getApplicationContext().inject(this) - } - // TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them. // TODO(#1675): Add NetworkModule once data module is migrated off of Moshi. @Singleton diff --git a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt index 84aaa8da12b..c904c354bfd 100644 --- a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt @@ -11,7 +11,6 @@ import dagger.Component import dagger.Module import dagger.Provides import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -26,6 +25,7 @@ import org.oppia.android.app.model.LessonThumbnailGraphic import org.oppia.android.app.model.OngoingStoryList import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.PromotedStory +import org.oppia.android.app.model.TopicSummary import org.oppia.android.domain.oppialogger.LogStorageModule import org.oppia.android.testing.TestCoroutineDispatchers import org.oppia.android.testing.TestDispatcherModule @@ -113,117 +113,93 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_firstTopic_hasCorrectTopicInfo() { - val topicListLiveData = topicListController.getTopicList() + val firstTopic = retrieveTopicSummaryFromTopicList(index = 0) - val topicList = topicListLiveData.value!!.getOrThrow() - val firstTopic = topicList.getTopicSummary(0) assertThat(firstTopic.topicId).isEqualTo(TEST_TOPIC_ID_0) assertThat(firstTopic.name).isEqualTo("First Test Topic") } @Test fun testRetrieveTopicList_firstTopic_hasCorrectThumbnail() { - val topicListLiveData = topicListController.getTopicList() + val firstTopic = retrieveTopicSummaryFromTopicList(index = 0) - val topicList = topicListLiveData.value!!.getOrThrow() - val firstTopic = topicList.getTopicSummary(0) assertThat(firstTopic.topicThumbnail.thumbnailGraphic) .isEqualTo(LessonThumbnailGraphic.ADDING_AND_SUBTRACTING_FRACTIONS) } @Test fun testRetrieveTopicList_firstTopic_hasCorrectLessonCount() { - val topicListLiveData = topicListController.getTopicList() + val firstTopic = retrieveTopicSummaryFromTopicList(index = 0) - val topicList = topicListLiveData.value!!.getOrThrow() - val firstTopic = topicList.getTopicSummary(0) assertThat(firstTopic.totalChapterCount).isEqualTo(5) } @Test fun testRetrieveTopicList_secondTopic_hasCorrectTopicInfo() { - val topicListLiveData = topicListController.getTopicList() + val secondTopic = retrieveTopicSummaryFromTopicList(index = 1) - val topicList = topicListLiveData.value!!.getOrThrow() - val secondTopic = topicList.getTopicSummary(1) assertThat(secondTopic.topicId).isEqualTo(TEST_TOPIC_ID_1) assertThat(secondTopic.name).isEqualTo("Second Test Topic") } @Test fun testRetrieveTopicList_secondTopic_hasCorrectThumbnail() { - val topicListLiveData = topicListController.getTopicList() + val secondTopic = retrieveTopicSummaryFromTopicList(index = 1) - val topicList = topicListLiveData.value!!.getOrThrow() - val secondTopic = topicList.getTopicSummary(1) assertThat(secondTopic.topicThumbnail.thumbnailGraphic) .isEqualTo(LessonThumbnailGraphic.BAKER) } @Test fun testRetrieveTopicList_secondTopic_hasCorrectLessonCount() { - val topicListLiveData = topicListController.getTopicList() + val secondTopic = retrieveTopicSummaryFromTopicList(index = 1) - val topicList = topicListLiveData.value!!.getOrThrow() - val secondTopic = topicList.getTopicSummary(1) assertThat(secondTopic.totalChapterCount).isEqualTo(1) } @Test fun testRetrieveTopicList_fractionsTopic_hasCorrectTopicInfo() { - val topicListLiveData = topicListController.getTopicList() + val fractionsTopic = retrieveTopicSummaryFromTopicList(index = 2) - val topicList = topicListLiveData.value!!.getOrThrow() - val fractionsTopic = topicList.getTopicSummary(2) assertThat(fractionsTopic.topicId).isEqualTo(FRACTIONS_TOPIC_ID) assertThat(fractionsTopic.name).isEqualTo("Fractions") } @Test fun testRetrieveTopicList_fractionsTopic_hasCorrectThumbnail() { - val topicListLiveData = topicListController.getTopicList() + val fractionsTopic = retrieveTopicSummaryFromTopicList(index = 2) - val topicList = topicListLiveData.value!!.getOrThrow() - val fractionsTopic = topicList.getTopicSummary(2) assertThat(fractionsTopic.topicThumbnail.thumbnailGraphic) .isEqualTo(LessonThumbnailGraphic.CHILD_WITH_FRACTIONS_HOMEWORK) } @Test fun testRetrieveTopicList_fractionsTopic_hasCorrectLessonCount() { - val topicListLiveData = topicListController.getTopicList() + val fractionsTopic = retrieveTopicSummaryFromTopicList(index = 2) - val topicList = topicListLiveData.value!!.getOrThrow() - val fractionsTopic = topicList.getTopicSummary(2) assertThat(fractionsTopic.totalChapterCount).isEqualTo(2) } @Test fun testRetrieveTopicList_ratiosTopic_hasCorrectTopicInfo() { - val topicListLiveData = topicListController.getTopicList() + val ratiosTopic = retrieveTopicSummaryFromTopicList(index = 3) - val topicList = topicListLiveData.value!!.getOrThrow() - val ratiosTopic = topicList.getTopicSummary(3) assertThat(ratiosTopic.topicId).isEqualTo(RATIOS_TOPIC_ID) assertThat(ratiosTopic.name).isEqualTo("Ratios and Proportional Reasoning") } @Test fun testRetrieveTopicList_ratiosTopic_hasCorrectThumbnail() { - val topicListLiveData = topicListController.getTopicList() + val ratiosTopic = retrieveTopicSummaryFromTopicList(index = 3) - val topicList = topicListLiveData.value!!.getOrThrow() - val ratiosTopic = topicList.getTopicSummary(3) assertThat(ratiosTopic.topicThumbnail.thumbnailGraphic) .isEqualTo(LessonThumbnailGraphic.DUCK_AND_CHICKEN) } @Test fun testRetrieveTopicList_ratiosTopic_hasCorrectLessonCount() { - val topicListLiveData = topicListController.getTopicList() + val ratiosTopic = retrieveTopicSummaryFromTopicList(index = 3) - val topicList = topicListLiveData.value!!.getOrThrow() - val ratiosTopic = topicList.getTopicSummary(3) assertThat(ratiosTopic.totalChapterCount).isEqualTo(4) } @@ -238,7 +214,7 @@ class TopicListControllerTest { } @Test - @Ignore("Failing on Circle CI.") + fun testRetrieveOngoingStoryList_markRecentlyPlayedFracStory0Exp0_ongoingStoryListIsCorrect() { storyProgressController.recordRecentlyPlayedChapter( profileId0, @@ -314,7 +290,6 @@ class TopicListControllerTest { } @Test - @Ignore("Failing on Circle CI.") fun testRetrieveOngoingStoryList_markAllChaptersCompletedInFractions_ongoingStoryListIsCorrect() { storyProgressController.recordCompletedChapter( profileId0, @@ -341,7 +316,7 @@ class TopicListControllerTest { verifyGetOngoingStoryListSucceeded() val ongoingTopicList = ongoingStoryListResultCaptor.value.getOrThrow() - assertThat(ongoingTopicList.recentStoryCount).isEqualTo(2) + assertThat(ongoingTopicList.recentStoryCount).isEqualTo(4) verifyDefaultOngoingStoryListSucceeded() } @@ -616,6 +591,10 @@ class TopicListControllerTest { return Date().time - NINE_DAYS_IN_MS } + private fun retrieveTopicSummaryFromTopicList(index: Int): TopicSummary { + return topicListController.getTopicList().value!!.getOrThrow().getTopicSummary(index) + } + // TODO(#89): Move this to a common test application component. @Module class TestModule { From b85c119c14cfdec0a4e91041805fdb4a78a4fb91 Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Mon, 7 Dec 2020 17:07:38 +0530 Subject: [PATCH 2/8] nit fix --- .../app/player/exploration/ExplorationActivityTest.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt index c56bb3f958b..2f996c2d0e2 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt @@ -491,6 +491,7 @@ class ExplorationActivityTest { } // TODO (#1855): Resolve ktlint max line in app module test + // TODO(#89): The ExplorationActivity takes time to finish. This test case is failing currently. @RunOn(TestPlatform.ROBOLECTRIC) @Test fun testAudioWithWifi_openRatioExploration_clickAudioIcon_checkAudioFragmentHasDefaultLanguageAndAutoPlays() { // ktlint-disable max-line-length @@ -505,11 +506,10 @@ class ExplorationActivityTest { ).use { startPlayingExplorationWithConnectionType( explorationId = RATIOS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR + connectionStatus = NetworkConnectionUtil.ConnectionStatus.LOCAL ) testCoroutineDispatchers.runCurrent() clickAudioPlayerIcon() - clickOnDialogItemText(context.getString(R.string.audio_language_select_dialog_okay_button)) testCoroutineDispatchers.runCurrent() onView(withId(R.id.ivPlayPauseAudio)) @@ -573,8 +573,9 @@ class ExplorationActivityTest { } // TODO (#1855): Resolve ktlint max line in app module test - @RunOn(TestPlatform.ROBOLECTRIC) + // TODO(#89): The ExplorationActivity takes time to finish. This test case is failing currently. @Test + @Ignore fun testAudioWithWifi_openRatioExploration_continueToInteraction_clickAudioButton_submitAnswer_checkFeedbackAudioPlays() { // ktlint-disable max-line-length setupAudio() launch( @@ -587,7 +588,7 @@ class ExplorationActivityTest { ).use { startPlayingExplorationWithConnectionType( explorationId = RATIOS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR + connectionStatus = NetworkConnectionUtil.ConnectionStatus.LOCAL ) onView(withId(R.id.state_recycler_view)).perform( scrollToPosition( @@ -602,7 +603,6 @@ class ExplorationActivityTest { onView(withId(R.id.continue_button)).perform(click()) clickAudioPlayerIcon() - testCoroutineDispatchers.runCurrent() onView(withId(R.id.state_recycler_view)).perform( scrollToPosition( 1 From 320b93a0ef8ef3ff605e0816f26ed70910221584 Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Sun, 13 Dec 2020 09:03:33 +0530 Subject: [PATCH 3/8] remove exploration test from this pr --- .../exploration/ExplorationActivityTest.kt | 372 ++++++++++-------- 1 file changed, 198 insertions(+), 174 deletions(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt index 2f996c2d0e2..6513a05c578 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt @@ -3,6 +3,7 @@ package org.oppia.android.app.player.exploration import android.app.Application import android.content.Context import android.content.Intent +import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ActivityScenario.launch @@ -10,9 +11,13 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import androidx.test.espresso.Espresso.pressBack +import androidx.test.espresso.PerformException +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.closeSoftKeyboard -import androidx.test.espresso.action.ViewActions.typeText import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition @@ -28,12 +33,15 @@ import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.espresso.util.HumanReadables +import androidx.test.espresso.util.TreeIterables import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.ActivityTestRule import com.google.common.truth.Truth.assertThat import com.google.firebase.FirebaseApp import dagger.Component import org.hamcrest.CoreMatchers.allOf +import org.hamcrest.Matcher import org.hamcrest.Matchers.not import org.junit.After import org.junit.Before @@ -84,13 +92,10 @@ import org.oppia.android.domain.topic.TEST_EXPLORATION_ID_2 import org.oppia.android.domain.topic.TEST_STORY_ID_0 import org.oppia.android.domain.topic.TEST_TOPIC_ID_0 import org.oppia.android.testing.IsOnRobolectric -import org.oppia.android.testing.OppiaTestRule -import org.oppia.android.testing.RunOn import org.oppia.android.testing.TestAccessibilityModule import org.oppia.android.testing.TestCoroutineDispatchers import org.oppia.android.testing.TestDispatcherModule import org.oppia.android.testing.TestLogReportingModule -import org.oppia.android.testing.TestPlatform import org.oppia.android.util.caching.testing.CachingTestModule import org.oppia.android.util.gcsresource.GcsResourceModule import org.oppia.android.util.logging.LoggerModule @@ -102,6 +107,7 @@ import org.oppia.android.util.parser.ImageParsingModule import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode import java.io.IOException +import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton @@ -114,17 +120,6 @@ import javax.inject.Singleton ) class ExplorationActivityTest { - // TODO(#163): Fill in remaining tests for this activity. - @get:Rule - var explorationActivityTestRule: ActivityTestRule = ActivityTestRule( - ExplorationActivity::class.java, - /* initialTouchMode= */ true, - /* launchActivity= */ false - ) - - @get:Rule - val oppiaTestRule = OppiaTestRule() - @Inject lateinit var explorationDataController: ExplorationDataController @@ -143,6 +138,7 @@ class ExplorationActivityTest { fun setUp() { Intents.init() setUpTestApplicationComponent() + context = ApplicationProvider.getApplicationContext() testCoroutineDispatchers.registerIdlingResource() FirebaseApp.initializeApp(context) } @@ -167,6 +163,12 @@ class ExplorationActivityTest { } } + // TODO(#163): Fill in remaining tests for this activity. + @get:Rule + var explorationActivityTestRule: ActivityTestRule = ActivityTestRule( + ExplorationActivity::class.java, /* initialTouchMode= */ true, /* launchActivity= */ false + ) + @Test fun testExploration_toolbarTitle_isDisplayedSuccessfully() { launch( @@ -196,7 +198,7 @@ class ExplorationActivityTest { ) ).use { explorationDataController.startPlayingExploration(TEST_EXPLORATION_ID_2) - changeConfiguration() + onView(isRoot()).perform(orientationLandscape()) testCoroutineDispatchers.runCurrent() onView(withId(R.id.exploration_toolbar_title)) .check(matches(withText("Prototype Exploration"))) @@ -293,7 +295,7 @@ class ExplorationActivityTest { ) ).use { explorationDataController.startPlayingExploration(TEST_EXPLORATION_ID_2) - changeConfiguration() + onView(isRoot()).perform(orientationLandscape()) onView(withId(R.id.action_audio_player)).check(matches(not(isDisplayed()))) } explorationDataController.stopPlayingExploration() @@ -310,12 +312,13 @@ class ExplorationActivityTest { RATIOS_EXPLORATION_ID_0 ) ).use { - startPlayingExplorationWithConnectionType( - explorationId = RATIOS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.NONE - ) - clickAudioPlayerIcon() - verifyTextInDialog(context.getString(R.string.audio_dialog_offline_message)) + explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) + networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.NONE) + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.action_audio_player)).perform(click()) + onView(withText(context.getString(R.string.audio_dialog_offline_message))) + .inRoot(isDialog()) + .check(matches(isDisplayed())) } explorationDataController.stopPlayingExploration() } @@ -325,18 +328,19 @@ class ExplorationActivityTest { setupAudio() launch( createExplorationActivityIntent( - internalProfileId, - RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, - RATIOS_EXPLORATION_ID_0 + internalProfileId, RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 ) ).use { - startPlayingExplorationWithConnectionType( - explorationId = RATIOS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR + explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) + networkConnectionUtil.setCurrentConnectionStatus( + NetworkConnectionUtil.ConnectionStatus.CELLULAR ) - clickAudioPlayerIcon() - verifyTextInDialog(context.getString(R.string.cellular_data_alert_dialog_title)) + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.action_audio_player)).perform(click()) + onView(withText(context.getString(R.string.cellular_data_alert_dialog_title))) + .inRoot(isDialog()) + .check(matches(isDisplayed())) } explorationDataController.stopPlayingExploration() } @@ -347,19 +351,20 @@ class ExplorationActivityTest { setupAudio() launch( createExplorationActivityIntent( - internalProfileId, - RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, - RATIOS_EXPLORATION_ID_0 + internalProfileId, RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 ) ).use { - startPlayingExplorationWithConnectionType( - explorationId = RATIOS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR + explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) + networkConnectionUtil.setCurrentConnectionStatus( + NetworkConnectionUtil.ConnectionStatus.CELLULAR ) - clickAudioPlayerIcon() - changeConfiguration() - verifyTextInDialog(context.getString(R.string.cellular_data_alert_dialog_title)) + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.action_audio_player)).perform(click()) + onView(isRoot()).perform(orientationLandscape()) + onView(withText(context.getString(R.string.cellular_data_alert_dialog_title))) + .inRoot(isDialog()) + .check(matches(isDisplayed())) } explorationDataController.stopPlayingExploration() } @@ -370,17 +375,22 @@ class ExplorationActivityTest { setupAudio() launch( createExplorationActivityIntent( - internalProfileId, - RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, - RATIOS_EXPLORATION_ID_0 + internalProfileId, RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 ) ).use { - startPlayingExplorationWithConnectionType( - explorationId = RATIOS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR + explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) + networkConnectionUtil.setCurrentConnectionStatus( + NetworkConnectionUtil.ConnectionStatus.CELLULAR + ) + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.action_audio_player)).perform(click()) + onView( + allOf( + withText(context.getString(R.string.cellular_data_alert_dialog_title)), + withEffectiveVisibility(Visibility.VISIBLE) + ) ) - clickAudioPlayerIcon() onView( allOf( withText(context.getString(R.string.audio_language_select_dialog_cancel_button)), @@ -404,17 +414,26 @@ class ExplorationActivityTest { RATIOS_EXPLORATION_ID_0 ) ).use { - startPlayingExplorationWithConnectionType( - explorationId = RATIOS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR + explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) + networkConnectionUtil.setCurrentConnectionStatus( + NetworkConnectionUtil.ConnectionStatus.CELLULAR ) - clickAudioPlayerIcon() + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.action_audio_player)).perform(click()) + onView( + allOf( + withText(context.getString(R.string.cellular_data_alert_dialog_title)), + withEffectiveVisibility(Visibility.VISIBLE) + ) + ) + onView( allOf( withText(context.getString(R.string.cellular_data_alert_dialog_okay_button)), withEffectiveVisibility(Visibility.VISIBLE) ) ).inRoot(isDialog()).perform(click()) + onView( allOf( withId(R.id.ivPlayPauseAudio), @@ -431,23 +450,28 @@ class ExplorationActivityTest { setupAudio() launch( createExplorationActivityIntent( - internalProfileId, - RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, - RATIOS_EXPLORATION_ID_0 + internalProfileId, RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 ) ).use { - startPlayingExplorationWithConnectionType( - explorationId = RATIOS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR + explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) + networkConnectionUtil.setCurrentConnectionStatus( + NetworkConnectionUtil.ConnectionStatus.CELLULAR ) - clickAudioPlayerIcon() - verifyTextInDialog(context.getString(R.string.cellular_data_alert_dialog_title)) - clickOnDialogItemView(R.id.cellular_data_dialog_checkbox) - clickOnDialogItemText(context.getString(R.string.audio_language_select_dialog_cancel_button)) + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.action_audio_player)).perform(click()) + onView(withText(context.getString(R.string.cellular_data_alert_dialog_title))) + .inRoot(isDialog()) + .check(matches(isDisplayed())) + onView(withId(R.id.cellular_data_dialog_checkbox)) + .inRoot(isDialog()) + .perform(click()) + onView(withText(context.getString(R.string.audio_language_select_dialog_cancel_button))) + .inRoot(isDialog()) + .perform(click()) testCoroutineDispatchers.runCurrent() - clickAudioPlayerIcon() + onView(withId(R.id.action_audio_player)).perform(click()) testCoroutineDispatchers.runCurrent() onView(withId(R.id.ivPlayPauseAudio)).check(matches(not(isDisplayed()))) @@ -463,24 +487,29 @@ class ExplorationActivityTest { setupAudio() launch( createExplorationActivityIntent( - internalProfileId, - RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, - RATIOS_EXPLORATION_ID_0 + internalProfileId, RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 ) ).use { - startPlayingExplorationWithConnectionType( - explorationId = RATIOS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.CELLULAR + explorationDataController.startPlayingExploration(RATIOS_EXPLORATION_ID_0) + networkConnectionUtil.setCurrentConnectionStatus( + NetworkConnectionUtil.ConnectionStatus.CELLULAR ) - clickAudioPlayerIcon() - verifyTextInDialog(context.getString(R.string.cellular_data_alert_dialog_title)) - clickOnDialogItemView(R.id.cellular_data_dialog_checkbox) - clickOnDialogItemText(context.getString(R.string.audio_language_select_dialog_okay_button)) + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.action_audio_player)).perform(click()) + onView(withText(context.getString(R.string.cellular_data_alert_dialog_title))) + .inRoot(isDialog()) + .check(matches(isDisplayed())) + onView(withId(R.id.cellular_data_dialog_checkbox)) + .inRoot(isDialog()) + .perform(click()) + onView(withText(context.getString(R.string.audio_language_select_dialog_okay_button))) + .inRoot(isDialog()) + .perform(click()) testCoroutineDispatchers.runCurrent() - clickAudioPlayerIcon() - clickAudioPlayerIcon() + onView(withId(R.id.action_audio_player)).perform(click()) + onView(withId(R.id.action_audio_player)).perform(click()) testCoroutineDispatchers.runCurrent() onView(withId(R.id.ivPlayPauseAudio)).check(matches(isDisplayed())) @@ -492,10 +521,11 @@ class ExplorationActivityTest { // TODO (#1855): Resolve ktlint max line in app module test // TODO(#89): The ExplorationActivity takes time to finish. This test case is failing currently. - @RunOn(TestPlatform.ROBOLECTRIC) @Test + @Ignore("The ExplorationActivity takes time to finish, needs to fixed in #89.") fun testAudioWithWifi_openRatioExploration_clickAudioIcon_checkAudioFragmentHasDefaultLanguageAndAutoPlays() { // ktlint-disable max-line-length - setupAudio() + getApplicationDependencies(RATIOS_EXPLORATION_ID_0) + networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) launch( createExplorationActivityIntent( internalProfileId, @@ -504,19 +534,16 @@ class ExplorationActivityTest { RATIOS_EXPLORATION_ID_0 ) ).use { - startPlayingExplorationWithConnectionType( - explorationId = RATIOS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.LOCAL + waitForTheView(withText("What is a Ratio?")) + onView(withId(R.id.action_audio_player)).perform(click()) + onView( + allOf( + withId(R.id.ivPlayPauseAudio), + withEffectiveVisibility(Visibility.VISIBLE) + ) ) - testCoroutineDispatchers.runCurrent() - clickAudioPlayerIcon() - - testCoroutineDispatchers.runCurrent() - onView(withId(R.id.ivPlayPauseAudio)) - .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) - onView(withText("EN")) - .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) - testCoroutineDispatchers.advanceUntilIdle() + onView(allOf(withText("EN"), withEffectiveVisibility(Visibility.VISIBLE))) + waitForTheView(withDrawable(R.drawable.ic_pause_circle_filled_white_24dp)) onView(withId(R.id.ivPlayPauseAudio)).check( matches( withDrawable( @@ -540,26 +567,29 @@ class ExplorationActivityTest { FRACTIONS_EXPLORATION_ID_0 ) ).use { - startPlayingExplorationWithConnectionType( - explorationId = FRACTIONS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.LOCAL - ) + explorationDataController.startPlayingExploration(FRACTIONS_EXPLORATION_ID_0) + networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) + testCoroutineDispatchers.runCurrent() onView(withId(R.id.state_recycler_view)).perform( scrollToPosition( 1 ) ) onView(withId(R.id.continue_button)).perform(click()) - clickAudioPlayerIcon() + onView(withId(R.id.action_audio_player)).perform(click()) onView( allOf( withText("EN"), withEffectiveVisibility(Visibility.VISIBLE) ) ).perform(click()) - clickOnDialogItemText("Hinglish") + onView(withText("Hinglish")) + .inRoot(isDialog()) + .perform(click()) - clickOnDialogItemText(context.getString(R.string.audio_language_select_dialog_okay_button)) + onView(withText(context.getString(R.string.audio_language_select_dialog_okay_button))) + .inRoot(isDialog()) + .perform(click()) onView(withId(R.id.state_recycler_view)).perform( scrollToPosition( @@ -575,26 +605,17 @@ class ExplorationActivityTest { // TODO (#1855): Resolve ktlint max line in app module test // TODO(#89): The ExplorationActivity takes time to finish. This test case is failing currently. @Test - @Ignore + @Ignore("The ExplorationActivity takes time to finish, needs to fixed in #89.") fun testAudioWithWifi_openRatioExploration_continueToInteraction_clickAudioButton_submitAnswer_checkFeedbackAudioPlays() { // ktlint-disable max-line-length - setupAudio() + getApplicationDependencies(RATIOS_EXPLORATION_ID_0) + networkConnectionUtil.setCurrentConnectionStatus(NetworkConnectionUtil.ConnectionStatus.LOCAL) launch( createExplorationActivityIntent( - internalProfileId, - RATIOS_TOPIC_ID, - RATIOS_STORY_ID_0, - RATIOS_EXPLORATION_ID_0 + internalProfileId, RATIOS_TOPIC_ID, + RATIOS_STORY_ID_0, RATIOS_EXPLORATION_ID_0 ) ).use { - startPlayingExplorationWithConnectionType( - explorationId = RATIOS_EXPLORATION_ID_0, - connectionStatus = NetworkConnectionUtil.ConnectionStatus.LOCAL - ) - onView(withId(R.id.state_recycler_view)).perform( - scrollToPosition( - 1 - ) - ) + waitForTheView(withText("What is a Ratio?")) // Clicks continue until we reach the first interaction. onView(withId(R.id.continue_button)).perform(click()) onView(withId(R.id.continue_button)).perform(click()) @@ -602,28 +623,16 @@ class ExplorationActivityTest { onView(withId(R.id.continue_button)).perform(click()) onView(withId(R.id.continue_button)).perform(click()) - clickAudioPlayerIcon() - onView(withId(R.id.state_recycler_view)).perform( - scrollToPosition( - 1 - ) - ) + onView(withId(R.id.action_audio_player)).perform(click()) onView(withId(R.id.text_input_interaction_view)).perform( - typeText("123"), + ViewActions.typeText("123"), closeSoftKeyboard() ) - testCoroutineDispatchers.advanceUntilIdle() - onView(withId(R.id.submit_answer_button)).perform(click()) - testCoroutineDispatchers.runCurrent() + Thread.sleep(1000) - onView(withId(R.id.ivPlayPauseAudio)).check( - matches( - withDrawable( - R.drawable.ic_pause_circle_filled_white_24dp - ) - ) - ) + onView(withId(R.id.ivPlayPauseAudio)) + .check(matches(withContentDescription(context.getString(R.string.audio_pause_description)))) } explorationDataController.stopPlayingExploration() } @@ -653,7 +662,8 @@ class ExplorationActivityTest { ) ).use { pressBack() - verifyTextInDialog(context.getString(R.string.stop_exploration_dialog_title)) + onView(withText(R.string.stop_exploration_dialog_title)).inRoot(isDialog()) + .check(matches(isDisplayed())) } } @@ -668,11 +678,13 @@ class ExplorationActivityTest { ) ).use { onView(withContentDescription(R.string.nav_app_bar_navigate_up_description)).perform(click()) - verifyTextInDialog(context.getString(R.string.stop_exploration_dialog_title)) + onView(withText(R.string.stop_exploration_dialog_title)).inRoot(isDialog()) + .check(matches(isDisplayed())) } } // TODO (#1855): Resolve ktlint max line in app module test + // TODO(#89): Check this test case too. It works in pair with below test case. @Test fun testExplorationActivity_onBackPressed_showsStopExplorationDialog_clickCancel_dismissesDialog() { // ktlint-disable max-line-length explorationActivityTestRule.launchActivity( @@ -684,8 +696,8 @@ class ExplorationActivityTest { ) ) pressBack() - clickOnDialogItemText(context.getString(R.string.stop_exploration_dialog_cancel_button)) - testCoroutineDispatchers.runCurrent() + onView(withText(R.string.stop_exploration_dialog_cancel_button)).inRoot(isDialog()) + .perform(click()) assertThat(explorationActivityTestRule.activity.isFinishing).isFalse() } @@ -703,8 +715,8 @@ class ExplorationActivityTest { ) ) pressBack() - clickOnDialogItemText(context.getString(R.string.stop_exploration_dialog_leave_button)) - testCoroutineDispatchers.runCurrent() + onView(withText(R.string.stop_exploration_dialog_leave_button)).inRoot(isDialog()) + .perform(click()) assertThat(explorationActivityTestRule.activity.isFinishing).isTrue() } @@ -720,45 +732,10 @@ class ExplorationActivityTest { topicId, storyId, explorationId, - backflowScreen = null + /* backflowScreen= */ null ) } - private fun changeConfiguration() { - onView(isRoot()).perform(orientationLandscape()) - } - - private fun clickAudioPlayerIcon() { - onView(withId(R.id.action_audio_player)).perform(click()) - } - - private fun startPlayingExplorationWithConnectionType( - explorationId: String, - connectionStatus: NetworkConnectionUtil.ConnectionStatus - ) { - explorationDataController.startPlayingExploration(explorationId) - networkConnectionUtil.setCurrentConnectionStatus(connectionStatus) - testCoroutineDispatchers.runCurrent() - } - - private fun verifyTextInDialog(textInDialog: String) { - onView(withText(textInDialog)) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - } - - private fun clickOnDialogItemText(string: String) { - onView(withText(string)) - .inRoot(isDialog()) - .perform(click()) - } - - private fun clickOnDialogItemView(viewId: Int) { - onView(withId(viewId)) - .inRoot(isDialog()) - .perform(click()) - } - private fun setupAudio() { // Only initialize the Robolectric shadows when running on Robolectric (and use reflection since // Espresso can't load Robolectric into its classpath). @@ -816,6 +793,53 @@ class ExplorationActivityTest { "exploration/$explorationId/assets/audio/$audioFileName" } + private fun waitForTheView(viewMatcher: Matcher): ViewInteraction { + return onView(isRoot()).perform(waitForMatch(viewMatcher, 30000L)) + } + +// TODO(#59): Remove these waits once we can ensure that the production executors are not depended on in tests. +// Sleeping is really bad practice in Espresso tests, and can lead to test flakiness. It shouldn't be necessary if we +// use a test executor service with a counting idle resource, but right now Gradle mixes dependencies such that both +// the test and production blocking executors are being used. The latter cannot be updated to notify Espresso of any +// active coroutines, so the test attempts to assert state before it's ready. This artificial delay in the Espresso +// thread helps to counter that. + /** + * Perform action of waiting for a specific matcher to finish. Adapted from: + * https://stackoverflow.com/a/22563297/3689782. + */ + private fun waitForMatch(viewMatcher: Matcher, millis: Long): ViewAction { + return object : ViewAction { + override fun getDescription(): String { + return "wait for a specific view with matcher <$viewMatcher> during $millis millis." + } + + override fun getConstraints(): Matcher { + return isRoot() + } + + override fun perform(uiController: UiController?, view: View?) { + checkNotNull(uiController) + uiController.loopMainThreadUntilIdle() + val startTime = System.currentTimeMillis() + val endTime = startTime + millis + + do { + if (TreeIterables.breadthFirstViewTraversal(view).any { viewMatcher.matches(it) }) { + return + } + uiController.loopMainThreadForAtLeast(50) + } while (System.currentTimeMillis() < endTime) + + // Couldn't match in time. + throw PerformException.Builder() + .withActionDescription(description) + .withViewDescription(HumanReadables.describe(view)) + .withCause(TimeoutException()) + .build() + } + } + } + // TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them. // TODO(#1675): Add NetworkModule once data module is migrated off of Moshi. @Singleton From 22aed090f11bad5148295adcb94552a375d539fc Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Fri, 18 Dec 2020 12:27:13 +0530 Subject: [PATCH 4/8] nit fix --- .../AdministratorControlsActivityTest.kt | 37 ++++--- .../ProfileProgressFragmentTest.kt | 54 ++++------ .../domain/topic/TopicListControllerTest.kt | 98 +++++-------------- 3 files changed, 59 insertions(+), 130 deletions(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt index 55fafd2554e..465fd34a534 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt @@ -3,6 +3,7 @@ package org.oppia.android.app.administratorcontrols import android.app.Application import android.content.Context import android.content.Intent +import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ActivityScenario.launch @@ -128,7 +129,7 @@ class AdministratorControlsActivityTest { verifyTextOnAdministratorListItemAtPosition( itemPosition = 0, targetViewId = R.id.edit_account_text_view, - stringToMatch = context.getString(R.string.administrator_controls_edit_account) + stringIdToMatch = R.string.administrator_controls_edit_account ) verifyItemDisplayedOnAdministratorControlListItem( itemPosition = 1, @@ -137,7 +138,7 @@ class AdministratorControlsActivityTest { verifyTextOnAdministratorListItemAtPosition( itemPosition = 1, targetViewId = R.id.edit_profiles_text_view, - stringToMatch = context.getString(R.string.administrator_controls_edit_profiles) + stringIdToMatch = R.string.administrator_controls_edit_profiles ) } } @@ -153,9 +154,7 @@ class AdministratorControlsActivityTest { verifyTextOnAdministratorListItemAtPosition( itemPosition = 2, targetViewId = R.id.download_permissions_text_view, - stringToMatch = context.getString( - R.string.administrator_controls_download_permissions_label - ) + stringIdToMatch = R.string.administrator_controls_download_permissions_label ) verifyItemDisplayedOnAdministratorControlListItem( itemPosition = 2, @@ -185,7 +184,7 @@ class AdministratorControlsActivityTest { verifyTextOnAdministratorListItemAtPosition( itemPosition = 3, targetViewId = R.id.app_version_text_view, - stringToMatch = context.getString(R.string.administrator_controls_app_version) + stringIdToMatch = R.string.administrator_controls_app_version ) verifyItemDisplayedOnAdministratorControlListItem( itemPosition = 4, @@ -194,7 +193,7 @@ class AdministratorControlsActivityTest { verifyTextOnAdministratorListItemAtPosition( itemPosition = 4, targetViewId = R.id.log_out_text_view, - stringToMatch = context.getString(R.string.administrator_controls_log_out) + stringIdToMatch = R.string.administrator_controls_log_out ) } } @@ -313,9 +312,9 @@ class AdministratorControlsActivityTest { testCoroutineDispatchers.runCurrent() scrollToPosition(position = 4) onView(withId(R.id.log_out_text_view)).perform(click()) - verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_message)) - verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_okay_button)) - verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_cancel_button)) + verifyTextInDialog(textInDialogId = R.string.log_out_dialog_message) + verifyTextInDialog(textInDialogId = R.string.log_out_dialog_okay_button) + verifyTextInDialog(textInDialogId = R.string.log_out_dialog_cancel_button) } } @@ -331,9 +330,9 @@ class AdministratorControlsActivityTest { onView(isRoot()).perform(orientationLandscape()) scrollToPosition(position = 4) onView(withId(R.id.log_out_text_view)).perform(click()) - verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_message)) - verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_okay_button)) - verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_cancel_button)) + verifyTextInDialog(textInDialogId = R.string.log_out_dialog_message) + verifyTextInDialog(textInDialogId = R.string.log_out_dialog_okay_button) + verifyTextInDialog(textInDialogId = R.string.log_out_dialog_cancel_button) } } @@ -348,7 +347,7 @@ class AdministratorControlsActivityTest { testCoroutineDispatchers.runCurrent() scrollToPosition(position = 4) onView(withId(R.id.log_out_text_view)).perform(click()) - verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_message)) + verifyTextInDialog(textInDialogId = R.string.log_out_dialog_message) onView(withText(R.string.log_out_dialog_okay_button)).perform(click()) intended(hasComponent(ProfileChooserActivity::class.java.name)) } @@ -364,7 +363,7 @@ class AdministratorControlsActivityTest { testCoroutineDispatchers.runCurrent() scrollToPosition(position = 4) onView(withId(R.id.log_out_text_view)).perform(click()) - verifyTextInDialog(textInDialog = context.getString(R.string.log_out_dialog_message)) + verifyTextInDialog(textInDialogId = R.string.log_out_dialog_message) onView(withText(R.string.log_out_dialog_cancel_button)).perform(click()) onView(withId(R.id.log_out_text_view)).check(matches(isDisplayed())) } @@ -407,7 +406,7 @@ class AdministratorControlsActivityTest { private fun verifyTextOnAdministratorListItemAtPosition( itemPosition: Int, targetViewId: Int, - stringToMatch: String + @StringRes stringIdToMatch: Int ) { onView( atPositionOnView( @@ -415,7 +414,7 @@ class AdministratorControlsActivityTest { itemPosition, targetViewId ) - ).check(matches(withText(stringToMatch))) + ).check(matches(withText(context.getString(stringIdToMatch)))) } private fun scrollToPosition(position: Int) { @@ -426,8 +425,8 @@ class AdministratorControlsActivityTest { ) } - private fun verifyTextInDialog(textInDialog: String) { - onView(withText(textInDialog)) + private fun verifyTextInDialog(@StringRes textInDialogId: Int) { + onView(withText(context.getString(textInDialogId))) .inRoot(isDialog()) .check(matches(isDisplayed())) } diff --git a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt index f2237387735..30d6c957ba2 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/profileprogress/ProfileProgressFragmentTest.kt @@ -155,8 +155,7 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragment_checkProfileName_profileNameIsCorrect() { launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.profile_name_text_view, stringToMatch = "Admin" @@ -169,8 +168,7 @@ class ProfileProgressFragmentTest { launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() onView(isRoot()).perform(orientationLandscape()) - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.profile_name_text_view, stringToMatch = "Admin" @@ -246,8 +244,7 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragmentNoProgress_recyclerViewItem0_checkOngoingTopicsCount_countIsZero() { // ktlint-disable max-line-length launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.ongoing_topics_count, stringToMatch = "0" @@ -268,8 +265,7 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.ongoing_topics_count, stringToMatch = "2" @@ -292,8 +288,7 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() onView(isRoot()).perform(orientationLandscape()) testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.ongoing_topics_count, stringToMatch = "2" @@ -305,8 +300,7 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragmentNoProgress_recyclerViewItem0_checkOngoingTopicsString_descriptionIsCorrect() { // ktlint-disable max-line-length launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.ongoing_topics_description_text_view, stringToMatch = context.getString(R.string.topics_in_progress) @@ -327,8 +321,7 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.ongoing_topics_description_text_view, stringToMatch = context.getString(R.string.topics_in_progress) @@ -351,8 +344,7 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() onView(isRoot()).perform(orientationLandscape()) testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.ongoing_topics_description_text_view, stringToMatch = context.getString(R.string.topics_in_progress) @@ -364,8 +356,7 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragmentNoProgress_recyclerViewItem0_checkCompletedStoriesCount_countIsZero() { // ktlint-disable max-line-length launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.completed_stories_count, stringToMatch = "0" @@ -386,8 +377,7 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.completed_stories_count, stringToMatch = "2" @@ -399,8 +389,7 @@ class ProfileProgressFragmentTest { fun testProfileProgressFragmentNoProgress_recyclerViewItem0_checkCompletedStoriesString_descriptionIsCorrect() { // ktlint-disable max-line-length launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.completed_stories_description_text_view, stringToMatch = context.getString(R.string.stories_completed) @@ -421,8 +410,7 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.completed_stories_description_text_view, stringToMatch = context.getString(R.string.stories_completed) @@ -438,8 +426,7 @@ class ProfileProgressFragmentTest { testCoroutineDispatchers.runCurrent() onView(withId(R.id.profile_progress_list)) .perform(scrollToPosition(1)) - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 1, targetViewId = R.id.story_name_text_view, stringToMatch = "First Story" @@ -456,8 +443,7 @@ class ProfileProgressFragmentTest { 1 ) ) - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 1, targetViewId = R.id.story_name_text_view, stringToMatch = "First Story" @@ -474,8 +460,7 @@ class ProfileProgressFragmentTest { 1 ) ) - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 1, targetViewId = R.id.topic_name_text_view, stringToMatch = "FIRST TEST TOPIC" @@ -505,8 +490,7 @@ class ProfileProgressFragmentTest { fun testProfileProgressActivity_recyclerViewIndex0_clickViewAll_opensRecentlyPlayedActivity() { launch(createProfileProgressActivityIntent(internalProfileId)).use { testCoroutineDispatchers.runCurrent() - matchTextOnListItem( - recyclerView = R.id.profile_progress_list, + verifyItemDisplayedOnProfileProgressListItem( itemPosition = 0, targetViewId = R.id.view_all_text_view, stringToMatch = "View All" @@ -628,16 +612,14 @@ class ProfileProgressFragmentTest { return Instrumentation.ActivityResult(RESULT_OK, resultIntent) } - // TODO(#2208): Create helper function in Test for RecyclerView - private fun matchTextOnListItem( - recyclerView: Int, + private fun verifyItemDisplayedOnProfileProgressListItem( itemPosition: Int, targetViewId: Int, stringToMatch: String ) { onView( atPositionOnView( - recyclerView, + R.id.profile_progress_list, itemPosition, targetViewId ) diff --git a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt index 732ee6c8571..60062a75bac 100644 --- a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt @@ -11,7 +11,6 @@ import dagger.Component import dagger.Module import dagger.Provides import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -45,7 +44,7 @@ import org.oppia.android.util.parser.DefaultGcsPrefix import org.oppia.android.util.parser.ImageDownloadUrlTemplate import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode -import java.util.Date +import java.util.* import javax.inject.Inject import javax.inject.Singleton @@ -103,36 +102,24 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_isSuccessful() { - val topicListLiveData = topicListController.getTopicList().toLiveData() + verifyObserverOfTopicListLiveData() - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() - - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicListResult = topicListResultCaptor.value assertThat(topicListResult!!.isSuccess()).isTrue() } @Test fun testRetrieveTopicList_providesListOfMultipleTopics() { - val topicListLiveData = topicListController.getTopicList().toLiveData() + verifyObserverOfTopicListLiveData() - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() - - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() assertThat(topicList.topicSummaryCount).isGreaterThan(1) } @Test fun testRetrieveTopicList_firstTopic_hasCorrectTopicInfo() { - val topicListLiveData = topicListController.getTopicList().toLiveData() + verifyObserverOfTopicListLiveData() - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() - - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val firstTopic = topicList.getTopicSummary(0) assertThat(firstTopic.topicId).isEqualTo(TEST_TOPIC_ID_0) @@ -141,12 +128,8 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_firstTopic_hasCorrectThumbnail() { - val topicListLiveData = topicListController.getTopicList().toLiveData() + verifyObserverOfTopicListLiveData() - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() - - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val firstTopic = topicList.getTopicSummary(0) assertThat(firstTopic.topicThumbnail.thumbnailGraphic) @@ -155,12 +138,8 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_firstTopic_hasCorrectLessonCount() { - val topicListLiveData = topicListController.getTopicList().toLiveData() + verifyObserverOfTopicListLiveData() - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() - - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val firstTopic = topicList.getTopicSummary(0) assertThat(firstTopic.totalChapterCount).isEqualTo(5) @@ -168,12 +147,8 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_secondTopic_hasCorrectTopicInfo() { - val topicListLiveData = topicListController.getTopicList().toLiveData() + verifyObserverOfTopicListLiveData() - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() - - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val secondTopic = topicList.getTopicSummary(1) assertThat(secondTopic.topicId).isEqualTo(TEST_TOPIC_ID_1) @@ -182,12 +157,8 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_secondTopic_hasCorrectThumbnail() { - val topicListLiveData = topicListController.getTopicList().toLiveData() + verifyObserverOfTopicListLiveData() - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() - - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val secondTopic = topicList.getTopicSummary(1) assertThat(secondTopic.topicThumbnail.thumbnailGraphic) @@ -196,12 +167,8 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_secondTopic_hasCorrectLessonCount() { - val topicListLiveData = topicListController.getTopicList().toLiveData() - - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() + verifyObserverOfTopicListLiveData() - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val secondTopic = topicList.getTopicSummary(1) assertThat(secondTopic.totalChapterCount).isEqualTo(1) @@ -209,12 +176,8 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_fractionsTopic_hasCorrectTopicInfo() { - val topicListLiveData = topicListController.getTopicList().toLiveData() - - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() + verifyObserverOfTopicListLiveData() - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val fractionsTopic = topicList.getTopicSummary(2) assertThat(fractionsTopic.topicId).isEqualTo(FRACTIONS_TOPIC_ID) @@ -223,12 +186,8 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_fractionsTopic_hasCorrectThumbnail() { - val topicListLiveData = topicListController.getTopicList().toLiveData() - - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() + verifyObserverOfTopicListLiveData() - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val fractionsTopic = topicList.getTopicSummary(2) assertThat(fractionsTopic.topicThumbnail.thumbnailGraphic) @@ -237,12 +196,8 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_fractionsTopic_hasCorrectLessonCount() { - val topicListLiveData = topicListController.getTopicList().toLiveData() - - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() + verifyObserverOfTopicListLiveData() - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val fractionsTopic = topicList.getTopicSummary(2) assertThat(fractionsTopic.totalChapterCount).isEqualTo(2) @@ -250,12 +205,8 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_ratiosTopic_hasCorrectTopicInfo() { - val topicListLiveData = topicListController.getTopicList().toLiveData() - - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() + verifyObserverOfTopicListLiveData() - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val ratiosTopic = topicList.getTopicSummary(3) assertThat(ratiosTopic.topicId).isEqualTo(RATIOS_TOPIC_ID) @@ -264,12 +215,8 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_ratiosTopic_hasCorrectThumbnail() { - val topicListLiveData = topicListController.getTopicList().toLiveData() - - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() + verifyObserverOfTopicListLiveData() - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val ratiosTopic = topicList.getTopicSummary(3) assertThat(ratiosTopic.topicThumbnail.thumbnailGraphic) @@ -278,12 +225,8 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_ratiosTopic_hasCorrectLessonCount() { - val topicListLiveData = topicListController.getTopicList().toLiveData() - - topicListLiveData.observeForever(mockTopicListObserver) - testCoroutineDispatchers.runCurrent() + verifyObserverOfTopicListLiveData() - verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicList = topicListResultCaptor.value.getOrThrow() val ratiosTopic = topicList.getTopicSummary(3) assertThat(ratiosTopic.totalChapterCount).isEqualTo(4) @@ -300,7 +243,6 @@ class TopicListControllerTest { } @Test - @Ignore("Failing on Circle CI.") fun testRetrieveOngoingStoryList_markRecentlyPlayedFracStory0Exp0_ongoingStoryListIsCorrect() { storyProgressController.recordRecentlyPlayedChapter( profileId0, @@ -376,7 +318,6 @@ class TopicListControllerTest { } @Test - @Ignore("Failing on Circle CI.") fun testRetrieveOngoingStoryList_markAllChaptersCompletedInFractions_ongoingStoryListIsCorrect() { storyProgressController.recordCompletedChapter( profileId0, @@ -403,7 +344,7 @@ class TopicListControllerTest { verifyGetOngoingStoryListSucceeded() val ongoingTopicList = ongoingStoryListResultCaptor.value.getOrThrow() - assertThat(ongoingTopicList.recentStoryCount).isEqualTo(2) + assertThat(ongoingTopicList.recentStoryCount).isEqualTo(4) verifyDefaultOngoingStoryListSucceeded() } @@ -678,6 +619,13 @@ class TopicListControllerTest { return Date().time - NINE_DAYS_IN_MS } + private fun verifyObserverOfTopicListLiveData() { + val topicListLiveData = topicListController.getTopicList().toLiveData() + topicListLiveData.observeForever(mockTopicListObserver) + testCoroutineDispatchers.runCurrent() + verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) + } + // TODO(#89): Move this to a common test application component. @Module class TestModule { From c83ac5a628fd2801fa7d245c47e39acbdf49ebeb Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Fri, 18 Dec 2020 12:30:06 +0530 Subject: [PATCH 5/8] ktlint fix --- .../org/oppia/android/domain/topic/TopicListControllerTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt index 60062a75bac..057347a8dc0 100644 --- a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt @@ -44,7 +44,7 @@ import org.oppia.android.util.parser.DefaultGcsPrefix import org.oppia.android.util.parser.ImageDownloadUrlTemplate import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode -import java.util.* +import java.util.Date import javax.inject.Inject import javax.inject.Singleton From ba4be989e2e26a153c80b902b12d3db1c4599dea Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Tue, 5 Jan 2021 04:20:10 +0530 Subject: [PATCH 6/8] nit fix --- .../domain/topic/TopicListControllerTest.kt | 144 +++++------------- 1 file changed, 38 insertions(+), 106 deletions(-) diff --git a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt index 057347a8dc0..e9bfa16670b 100644 --- a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt @@ -44,7 +44,7 @@ import org.oppia.android.util.parser.DefaultGcsPrefix import org.oppia.android.util.parser.ImageDownloadUrlTemplate import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode -import java.util.Date +import java.util.* import javax.inject.Inject import javax.inject.Singleton @@ -102,25 +102,25 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_isSuccessful() { - verifyObserverOfTopicListLiveData() + val topicListLiveData = topicListController.getTopicList().toLiveData() + + topicListLiveData.observeForever(mockTopicListObserver) + testCoroutineDispatchers.runCurrent() + verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) val topicListResult = topicListResultCaptor.value assertThat(topicListResult!!.isSuccess()).isTrue() } @Test fun testRetrieveTopicList_providesListOfMultipleTopics() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() assertThat(topicList.topicSummaryCount).isGreaterThan(1) } @Test fun testRetrieveTopicList_firstTopic_hasCorrectTopicInfo() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val firstTopic = topicList.getTopicSummary(0) assertThat(firstTopic.topicId).isEqualTo(TEST_TOPIC_ID_0) assertThat(firstTopic.name).isEqualTo("First Test Topic") @@ -128,9 +128,7 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_firstTopic_hasCorrectThumbnail() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val firstTopic = topicList.getTopicSummary(0) assertThat(firstTopic.topicThumbnail.thumbnailGraphic) .isEqualTo(LessonThumbnailGraphic.ADDING_AND_SUBTRACTING_FRACTIONS) @@ -138,18 +136,14 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_firstTopic_hasCorrectLessonCount() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val firstTopic = topicList.getTopicSummary(0) assertThat(firstTopic.totalChapterCount).isEqualTo(5) } @Test fun testRetrieveTopicList_secondTopic_hasCorrectTopicInfo() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val secondTopic = topicList.getTopicSummary(1) assertThat(secondTopic.topicId).isEqualTo(TEST_TOPIC_ID_1) assertThat(secondTopic.name).isEqualTo("Second Test Topic") @@ -157,9 +151,7 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_secondTopic_hasCorrectThumbnail() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val secondTopic = topicList.getTopicSummary(1) assertThat(secondTopic.topicThumbnail.thumbnailGraphic) .isEqualTo(LessonThumbnailGraphic.BAKER) @@ -167,18 +159,14 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_secondTopic_hasCorrectLessonCount() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val secondTopic = topicList.getTopicSummary(1) assertThat(secondTopic.totalChapterCount).isEqualTo(1) } @Test fun testRetrieveTopicList_fractionsTopic_hasCorrectTopicInfo() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val fractionsTopic = topicList.getTopicSummary(2) assertThat(fractionsTopic.topicId).isEqualTo(FRACTIONS_TOPIC_ID) assertThat(fractionsTopic.name).isEqualTo("Fractions") @@ -186,9 +174,7 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_fractionsTopic_hasCorrectThumbnail() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val fractionsTopic = topicList.getTopicSummary(2) assertThat(fractionsTopic.topicThumbnail.thumbnailGraphic) .isEqualTo(LessonThumbnailGraphic.CHILD_WITH_FRACTIONS_HOMEWORK) @@ -196,18 +182,14 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_fractionsTopic_hasCorrectLessonCount() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val fractionsTopic = topicList.getTopicSummary(2) assertThat(fractionsTopic.totalChapterCount).isEqualTo(2) } @Test fun testRetrieveTopicList_ratiosTopic_hasCorrectTopicInfo() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val ratiosTopic = topicList.getTopicSummary(3) assertThat(ratiosTopic.topicId).isEqualTo(RATIOS_TOPIC_ID) assertThat(ratiosTopic.name).isEqualTo("Ratios and Proportional Reasoning") @@ -215,9 +197,7 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_ratiosTopic_hasCorrectThumbnail() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val ratiosTopic = topicList.getTopicSummary(3) assertThat(ratiosTopic.topicThumbnail.thumbnailGraphic) .isEqualTo(LessonThumbnailGraphic.DUCK_AND_CHICKEN) @@ -225,9 +205,7 @@ class TopicListControllerTest { @Test fun testRetrieveTopicList_ratiosTopic_hasCorrectLessonCount() { - verifyObserverOfTopicListLiveData() - - val topicList = topicListResultCaptor.value.getOrThrow() + val topicList = retrieveTopicList() val ratiosTopic = topicList.getTopicSummary(3) assertThat(ratiosTopic.totalChapterCount).isEqualTo(4) } @@ -251,15 +229,8 @@ class TopicListControllerTest { FRACTIONS_EXPLORATION_ID_0, getCurrentTimestamp() ) - testCoroutineDispatchers.runCurrent() - - topicListController.getOngoingStoryList(profileId0).toLiveData() - .observeForever(mockOngoingStoryListObserver) - testCoroutineDispatchers.runCurrent() - verifyGetOngoingStoryListSucceeded() - - val ongoingTopicList = ongoingStoryListResultCaptor.value.getOrThrow() + val ongoingTopicList = retrieveOngoingStoryList() assertThat(ongoingTopicList.recentStoryCount).isEqualTo(1) verifyOngoingStoryAsFractionStory0Exploration0(ongoingTopicList.recentStoryList[0]) } @@ -273,15 +244,8 @@ class TopicListControllerTest { FRACTIONS_EXPLORATION_ID_0, getCurrentTimestamp() ) - testCoroutineDispatchers.runCurrent() - topicListController.getOngoingStoryList(profileId0).toLiveData() - .observeForever(mockOngoingStoryListObserver) - testCoroutineDispatchers.runCurrent() - - verifyGetOngoingStoryListSucceeded() - - val ongoingTopicList = ongoingStoryListResultCaptor.value.getOrThrow() + val ongoingTopicList = retrieveOngoingStoryList() assertThat(ongoingTopicList.recentStoryCount).isEqualTo(1) verifyOngoingStoryAsFractionStory0Exploration1(ongoingTopicList.recentStoryList[0]) } @@ -304,15 +268,8 @@ class TopicListControllerTest { FRACTIONS_EXPLORATION_ID_1, getCurrentTimestamp() ) - testCoroutineDispatchers.runCurrent() - - topicListController.getOngoingStoryList(profileId0).toLiveData() - .observeForever(mockOngoingStoryListObserver) - testCoroutineDispatchers.runCurrent() - verifyGetOngoingStoryListSucceeded() - - val ongoingTopicList = ongoingStoryListResultCaptor.value.getOrThrow() + val ongoingTopicList = retrieveOngoingStoryList() assertThat(ongoingTopicList.recentStoryCount).isEqualTo(1) verifyOngoingStoryAsFractionStory0Exploration1(ongoingTopicList.recentStoryList[0]) } @@ -335,15 +292,8 @@ class TopicListControllerTest { FRACTIONS_EXPLORATION_ID_1, getCurrentTimestamp() ) - testCoroutineDispatchers.runCurrent() - topicListController.getOngoingStoryList(profileId0).toLiveData() - .observeForever(mockOngoingStoryListObserver) - testCoroutineDispatchers.runCurrent() - - verifyGetOngoingStoryListSucceeded() - - val ongoingTopicList = ongoingStoryListResultCaptor.value.getOrThrow() + val ongoingTopicList = retrieveOngoingStoryList() assertThat(ongoingTopicList.recentStoryCount).isEqualTo(4) verifyDefaultOngoingStoryListSucceeded() } @@ -366,15 +316,8 @@ class TopicListControllerTest { RATIOS_EXPLORATION_ID_2, getCurrentTimestamp() ) - testCoroutineDispatchers.runCurrent() - - topicListController.getOngoingStoryList(profileId0).toLiveData() - .observeForever(mockOngoingStoryListObserver) - testCoroutineDispatchers.runCurrent() - - verifyGetOngoingStoryListSucceeded() - val ongoingTopicList = ongoingStoryListResultCaptor.value.getOrThrow() + val ongoingTopicList = retrieveOngoingStoryList() assertThat(ongoingTopicList.recentStoryCount).isEqualTo(2) verifyOngoingStoryAsRatioStory0Exploration0(ongoingTopicList.recentStoryList[0]) verifyOngoingStoryAsRatioStory1Exploration2(ongoingTopicList.recentStoryList[1]) @@ -398,15 +341,8 @@ class TopicListControllerTest { RATIOS_EXPLORATION_ID_2, getCurrentTimestamp() ) - testCoroutineDispatchers.runCurrent() - topicListController.getOngoingStoryList(profileId0).toLiveData() - .observeForever(mockOngoingStoryListObserver) - testCoroutineDispatchers.runCurrent() - - verifyGetOngoingStoryListSucceeded() - - val ongoingTopicList = ongoingStoryListResultCaptor.value.getOrThrow() + val ongoingTopicList = retrieveOngoingStoryList() assertThat(ongoingTopicList.recentStoryCount).isEqualTo(2) verifyOngoingStoryAsRatioStory0Exploration1(ongoingTopicList.recentStoryList[0]) verifyOngoingStoryAsRatioStory1Exploration2(ongoingTopicList.recentStoryList[1]) @@ -439,15 +375,8 @@ class TopicListControllerTest { RATIOS_EXPLORATION_ID_2, getCurrentTimestamp() ) - testCoroutineDispatchers.runCurrent() - topicListController.getOngoingStoryList(profileId0).toLiveData() - .observeForever(mockOngoingStoryListObserver) - testCoroutineDispatchers.runCurrent() - - verifyGetOngoingStoryListSucceeded() - - val ongoingTopicList = ongoingStoryListResultCaptor.value.getOrThrow() + val ongoingTopicList = retrieveOngoingStoryList() assertThat(ongoingTopicList.recentStoryCount).isEqualTo(3) verifyOngoingStoryAsFractionStory0Exploration1(ongoingTopicList.recentStoryList[0]) verifyOngoingStoryAsRatioStory0Exploration1(ongoingTopicList.recentStoryList[1]) @@ -481,15 +410,8 @@ class TopicListControllerTest { RATIOS_EXPLORATION_ID_2, getCurrentTimestamp() ) - testCoroutineDispatchers.runCurrent() - - topicListController.getOngoingStoryList(profileId0).toLiveData() - .observeForever(mockOngoingStoryListObserver) - testCoroutineDispatchers.runCurrent() - - verifyGetOngoingStoryListSucceeded() - val ongoingTopicList = ongoingStoryListResultCaptor.value.getOrThrow() + val ongoingTopicList = retrieveOngoingStoryList() assertThat(ongoingTopicList.recentStoryCount).isEqualTo(1) assertThat(ongoingTopicList.olderStoryCount).isEqualTo(2) verifyOngoingStoryAsFractionStory0Exploration1(ongoingTopicList.olderStoryList[0]) @@ -619,11 +541,21 @@ class TopicListControllerTest { return Date().time - NINE_DAYS_IN_MS } - private fun verifyObserverOfTopicListLiveData() { + private fun retrieveTopicList(): TopicList { val topicListLiveData = topicListController.getTopicList().toLiveData() topicListLiveData.observeForever(mockTopicListObserver) testCoroutineDispatchers.runCurrent() verify(mockTopicListObserver).onChanged(topicListResultCaptor.capture()) + return topicListResultCaptor.value.getOrThrow() + } + + private fun retrieveOngoingStoryList(): OngoingStoryList { + testCoroutineDispatchers.runCurrent() + topicListController.getOngoingStoryList(profileId0).toLiveData() + .observeForever(mockOngoingStoryListObserver) + testCoroutineDispatchers.runCurrent() + verifyGetOngoingStoryListSucceeded() + return ongoingStoryListResultCaptor.value.getOrThrow() } // TODO(#89): Move this to a common test application component. From c8ddbd2c97d6196a2901ad947e115b924156ffba Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Tue, 5 Jan 2021 04:39:33 +0530 Subject: [PATCH 7/8] comment added back --- .../administratorcontrols/AdministratorControlsActivityTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt index e7a2b8a403a..eecb6cb7928 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/administratorcontrols/AdministratorControlsActivityTest.kt @@ -447,6 +447,7 @@ class AdministratorControlsActivityTest { ) } + /** Functions nestedScrollTo() and findFirstParentLayoutOfClass() taken from: https://stackoverflow.com/a/46037284/8860848 */ private fun nestedScrollTo(): ViewAction { return object : ViewAction { override fun getDescription(): String { From 9081a2684febe51021a71e10d79ead4bdb48b39d Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Tue, 5 Jan 2021 04:41:04 +0530 Subject: [PATCH 8/8] wildcard import fix --- .../org/oppia/android/domain/topic/TopicListControllerTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt index c1b300db8bc..6bc4a850d37 100644 --- a/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt +++ b/domain/src/test/java/org/oppia/android/domain/topic/TopicListControllerTest.kt @@ -46,7 +46,7 @@ import org.oppia.android.util.parser.DefaultGcsPrefix import org.oppia.android.util.parser.ImageDownloadUrlTemplate import org.robolectric.annotation.Config import org.robolectric.annotation.LooperMode -import java.util.* +import java.util.Date import javax.inject.Inject import javax.inject.Singleton