From 5296736d951bf8a89ce4a39d0dce69718e2bcf5a Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Tue, 3 Nov 2020 12:17:49 +0530 Subject: [PATCH 1/2] Fix few TopicRevisionFragmentTest for robolectric --- .../app/recyclerview/RecyclerViewMatcher.kt | 30 ++++ .../revision/TopicRevisionFragmentTest.kt | 170 ++++++++---------- 2 files changed, 100 insertions(+), 100 deletions(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt b/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt index ca60e585223..0bf14a90bee 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt @@ -74,6 +74,10 @@ class RecyclerViewMatcher { fun hasGridItemCount(spanCount: Int, position: Int): ViewAssertion { return RecyclerViewGridItemCountAssertion(spanCount, position) } + + fun hasGridColumnCount(expectedColumnCount: Int): ViewAssertion { + return GridLayoutManagerColumnCountAssertion(expectedColumnCount) + } } private class RecyclerViewItemCountAssertion(private val count: Int) : ViewAssertion { @@ -102,4 +106,30 @@ class RecyclerViewMatcher { assertThat("RecyclerViewGrid span count", spanCount, equalTo(count)) } } + + private class GridLayoutManagerColumnCountAssertion(expectedColumnCount: Int) : ViewAssertion { + private var expectedColumnCount: Int = 0 + + init { + this.expectedColumnCount = expectedColumnCount + } + + override fun check(view: View, noViewFoundException: NoMatchingViewException?) { + if (noViewFoundException != null) { + throw noViewFoundException + } + val recyclerView = view as RecyclerView + if (recyclerView.layoutManager is GridLayoutManager) { + val gridLayoutManager = recyclerView.layoutManager as GridLayoutManager + val spanCount = gridLayoutManager.spanCount + if (spanCount != expectedColumnCount) { + val errorMessage = + ("expected column count " + expectedColumnCount + " but was " + spanCount) + throw AssertionError(errorMessage) + } + } else { + throw IllegalStateException("no grid layout manager") + } + } + } } diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/revision/TopicRevisionFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/revision/TopicRevisionFragmentTest.kt index 5a3fda66504..aeb5ef54a9a 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/topic/revision/TopicRevisionFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/topic/revision/TopicRevisionFragmentTest.kt @@ -1,30 +1,32 @@ package org.oppia.android.app.topic.revision import android.app.Application -import android.view.View +import android.content.Context +import android.content.Intent import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.ActivityScenario.launch import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.NoMatchingViewException -import androidx.test.espresso.ViewAssertion import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.RecyclerViewActions +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.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.rule.ActivityTestRule import com.google.firebase.FirebaseApp import dagger.Component import org.hamcrest.Matchers.allOf +import org.junit.After import org.junit.Before import org.junit.Ignore -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.oppia.android.R @@ -37,9 +39,12 @@ import org.oppia.android.app.application.ApplicationModule import org.oppia.android.app.application.ApplicationStartupListenerModule import org.oppia.android.app.player.state.hintsandsolution.HintsAndSolutionConfigModule import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPosition +import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPositionOnView +import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.hasGridColumnCount import org.oppia.android.app.shim.ViewBindingShimModule import org.oppia.android.app.topic.TopicActivity import org.oppia.android.app.topic.TopicTab +import org.oppia.android.app.topic.revisioncard.RevisionCardActivity import org.oppia.android.app.utility.EspressoTestsMatchers.withDrawable import org.oppia.android.app.utility.OrientationChangeAction.Companion.orientationLandscape import org.oppia.android.domain.classify.InteractionsModule @@ -61,6 +66,7 @@ import org.oppia.android.domain.question.QuestionModule import org.oppia.android.domain.topic.FRACTIONS_TOPIC_ID import org.oppia.android.domain.topic.PrimeTopicAssetsControllerModule 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.util.caching.testing.CachingTestModule @@ -72,6 +78,7 @@ 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 javax.inject.Inject import javax.inject.Singleton /** Tests for [TopicRevisionFragment]. */ @@ -82,33 +89,45 @@ import javax.inject.Singleton qualifiers = "port-xxhdpi" ) class TopicRevisionFragmentTest { + + @Inject + lateinit var context: Context + + @Inject + lateinit var testCoroutineDispatchers: TestCoroutineDispatchers + private val subtopicThumbnail = R.drawable.topic_fractions_01 private val internalProfileId = 0 - @get:Rule - var topicActivityTestRule: ActivityTestRule = ActivityTestRule( - TopicActivity::class.java, - /* initialTouchMode= */ true, - /* launchActivity= */ false - ) - @Before fun setUp() { - FirebaseApp.initializeApp(ApplicationProvider.getApplicationContext()) + Intents.init() + setUpTestApplicationComponent() + FirebaseApp.initializeApp(context) + testCoroutineDispatchers.registerIdlingResource() + } + + @After + fun tearDown() { + testCoroutineDispatchers.unregisterIdlingResource() + Intents.release() + } + + private fun setUpTestApplicationComponent() { ApplicationProvider.getApplicationContext().inject(this) } @Test - // TODO(#973): Fix TopicRevisionFragmentTest - @Ignore - fun testTopicRevisionFragment_loadFragment_displayReviewTopics_isSuccessful() { + fun testTopicRevisionFragment_loadFragment_displayRevisionTopics_isSuccessful() { launchTopicActivityIntent(internalProfileId, FRACTIONS_TOPIC_ID).use { + testCoroutineDispatchers.runCurrent() onView( allOf( withText(TopicTab.getTabForPosition(3).name), isDescendantOfA(withId(R.id.topic_tabs_container)) ) ).perform(click()) + testCoroutineDispatchers.runCurrent() onView(atPosition(R.id.revision_recycler_view, 0)) .check(matches(hasDescendant(withId(R.id.subtopic_title)))) } @@ -117,63 +136,41 @@ class TopicRevisionFragmentTest { @Test // TODO(#973): Fix TopicRevisionFragmentTest @Ignore - fun testTopicRevisionFragment_loadFragment_selectReviewTopics_opensReviewActivity() { - topicActivityTestRule.launchActivity( - TopicActivity.createTopicActivityIntent( - ApplicationProvider.getApplicationContext(), - internalProfileId, - FRACTIONS_TOPIC_ID - ) - ) - onView( - allOf( - withText(TopicTab.getTabForPosition(3).name), - isDescendantOfA(withId(R.id.topic_tabs_container)) - ) - ).perform(click()) - onView(atPosition(R.id.revision_recycler_view, 0)).perform(click()) - } - - @Test - // TODO(#973): Fix TopicRevisionFragmentTest - @Ignore - fun testTopicRevisionFragment_loadFragment_selectReviewTopics_reviewCardDisplaysCorrectExplanation() { // ktlint-disable max-line-length + fun testTopicRevisionFragment_loadFragment_selectRevisionTopics_opensRevisionCardActivity() { launchTopicActivityIntent(internalProfileId, FRACTIONS_TOPIC_ID).use { + testCoroutineDispatchers.runCurrent() onView( allOf( withText(TopicTab.getTabForPosition(3).name), isDescendantOfA(withId(R.id.topic_tabs_container)) ) ).perform(click()) - onView(atPosition(R.id.revision_recycler_view, 1)).perform(click()) - onView(withId(R.id.revision_card_explanation_text)) - .check( - matches( - withText( - "Description of subtopic is here." - ) - ) + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.revision_recycler_view)).perform( + RecyclerViewActions.scrollToPosition( + 0 ) + ) + onView(atPosition(R.id.revision_recycler_view, 0)).perform(click()) + intended(hasComponent(RevisionCardActivity::class.java.name)) } } @Test - // TODO(#973): Fix TopicRevisionFragmentTest - @Ignore fun testTopicRevisionFragment_loadFragment_checkTopicThumbnail_isCorrect() { launchTopicActivityIntent(internalProfileId, FRACTIONS_TOPIC_ID).use { + testCoroutineDispatchers.runCurrent() onView( allOf( withText(TopicTab.getTabForPosition(3).name), isDescendantOfA(withId(R.id.topic_tabs_container)) ) ).perform(click()) - onView(withId(R.id.revision_recycler_view)).check( + testCoroutineDispatchers.runCurrent() + onView(atPositionOnView(R.id.revision_recycler_view, 0, R.id.subtopic_image_view)).check( matches( - hasDescendant( - withDrawable( - subtopicThumbnail - ) + withDrawable( + subtopicThumbnail ) ) ) @@ -191,18 +188,17 @@ class TopicRevisionFragmentTest { ).perform(click()) onView(withId(R.id.revision_recycler_view)) .check( - GridLayoutManagerColumnCountAssertion( - 2 + hasGridColumnCount( + expectedColumnCount = 2 ) ) } } @Test - // TODO(#973): Fix TopicRevisionFragmentTest - @Ignore - fun testTopicPracticeFragment_loadFragment_configurationChange_reviewSubtopicsAreDisplayed() { + fun testTopicPracticeFragment_loadFragment_configurationChange_revisionSubtopicsAreDisplayed() { launchTopicActivityIntent(internalProfileId, FRACTIONS_TOPIC_ID).use { + testCoroutineDispatchers.runCurrent() onView(isRoot()).perform(orientationLandscape()) onView( allOf( @@ -210,16 +206,16 @@ class TopicRevisionFragmentTest { isDescendantOfA(withId(R.id.topic_tabs_container)) ) ).perform(click()) + testCoroutineDispatchers.runCurrent() onView(atPosition(R.id.revision_recycler_view, 0)) .check(matches(hasDescendant(withId(R.id.subtopic_title)))) } } @Test - // TODO(#973): Fix TopicRevisionFragmentTest - @Ignore fun testTopicRevisionFragment_loadFragment_configurationChange_checkTopicThumbnail_isCorrect() { launchTopicActivityIntent(internalProfileId, FRACTIONS_TOPIC_ID).use { + testCoroutineDispatchers.runCurrent() onView(isRoot()).perform(orientationLandscape()) onView( allOf( @@ -227,12 +223,11 @@ class TopicRevisionFragmentTest { isDescendantOfA(withId(R.id.topic_tabs_container)) ) ).perform(click()) - onView(withId(R.id.revision_recycler_view)).check( + testCoroutineDispatchers.runCurrent() + onView(atPositionOnView(R.id.revision_recycler_view, 0, R.id.subtopic_image_view)).check( matches( - hasDescendant( - withDrawable( - subtopicThumbnail - ) + withDrawable( + subtopicThumbnail ) ) ) @@ -244,6 +239,7 @@ class TopicRevisionFragmentTest { @Ignore fun testTopicRevisionFragment_loadFragment_configurationChange_checkSpanCount_isThree() { launchTopicActivityIntent(internalProfileId, FRACTIONS_TOPIC_ID).use { + testCoroutineDispatchers.runCurrent() onView(isRoot()).perform(orientationLandscape()) onView( allOf( @@ -253,50 +249,24 @@ class TopicRevisionFragmentTest { ).perform(click()) onView(withId(R.id.revision_recycler_view)) .check( - GridLayoutManagerColumnCountAssertion( - 3 + hasGridColumnCount( + expectedColumnCount = 3 ) ) } } + private fun createTopicActivityIntent(internalProfileId: Int, topicId: String): Intent { + return TopicActivity.createTopicActivityIntent( + ApplicationProvider.getApplicationContext(), internalProfileId, topicId + ) + } + private fun launchTopicActivityIntent( internalProfileId: Int, topicId: String ): ActivityScenario { - val intent = - TopicActivity.createTopicActivityIntent( - ApplicationProvider.getApplicationContext(), - internalProfileId, - topicId - ) - return ActivityScenario.launch(intent) - } - - class GridLayoutManagerColumnCountAssertion(expectedColumnCount: Int) : ViewAssertion { - private var expectedColumnCount: Int = 0 - - init { - this.expectedColumnCount = expectedColumnCount - } - - override fun check(view: View, noViewFoundException: NoMatchingViewException?) { - if (noViewFoundException != null) { - throw noViewFoundException - } - val recyclerView = view as RecyclerView - if (recyclerView.layoutManager is GridLayoutManager) { - val gridLayoutManager = recyclerView.layoutManager as GridLayoutManager - val spanCount = gridLayoutManager.spanCount - if (spanCount != expectedColumnCount) { - val errorMessage = - ("expected column count " + expectedColumnCount + " but was " + spanCount) - throw AssertionError(errorMessage) - } - } else { - throw IllegalStateException("no grid layout manager") - } - } + return launch(createTopicActivityIntent(internalProfileId, topicId)) } // TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them. From 90e69fd58b88d97eb46c274f5c6d806825de1c5b Mon Sep 17 00:00:00 2001 From: anandwana001 Date: Wed, 4 Nov 2020 10:28:08 +0530 Subject: [PATCH 2/2] nit fix --- .../org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt b/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt index 0bf14a90bee..2b4f16f78e4 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt @@ -124,7 +124,7 @@ class RecyclerViewMatcher { val spanCount = gridLayoutManager.spanCount if (spanCount != expectedColumnCount) { val errorMessage = - ("expected column count " + expectedColumnCount + " but was " + spanCount) + ("expected column count $expectedColumnCount but was $spanCount") throw AssertionError(errorMessage) } } else {