diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
index 0f7bc519db6..80e519c3aa2 100644
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -1,5 +1,6 @@
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0958158687b..e6248d90d9f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -12,6 +12,7 @@
android:supportsRtl="true"
android:theme="@style/OppiaTheme">
+
diff --git a/app/src/main/java/org/oppia/app/activity/ActivityComponent.kt b/app/src/main/java/org/oppia/app/activity/ActivityComponent.kt
index 7102ac5af84..d4d73e0e1c8 100644
--- a/app/src/main/java/org/oppia/app/activity/ActivityComponent.kt
+++ b/app/src/main/java/org/oppia/app/activity/ActivityComponent.kt
@@ -6,6 +6,7 @@ import dagger.Subcomponent
import org.oppia.app.fragment.FragmentComponent
import org.oppia.app.home.HomeActivity
import org.oppia.app.player.exploration.ExplorationActivity
+import org.oppia.app.topic.TopicActivity
import javax.inject.Provider
/** Root subcomponent for all activities. */
@@ -22,4 +23,5 @@ interface ActivityComponent {
fun inject(explorationActivity: ExplorationActivity)
fun inject(homeActivity: HomeActivity)
+ fun inject(topicActivity: TopicActivity)
}
diff --git a/app/src/main/java/org/oppia/app/fragment/FragmentComponent.kt b/app/src/main/java/org/oppia/app/fragment/FragmentComponent.kt
index 7e48d0893d5..4f04b36fae8 100644
--- a/app/src/main/java/org/oppia/app/fragment/FragmentComponent.kt
+++ b/app/src/main/java/org/oppia/app/fragment/FragmentComponent.kt
@@ -7,7 +7,12 @@ import org.oppia.app.home.HomeFragment
import org.oppia.app.player.exploration.ExplorationFragment
import org.oppia.app.player.state.StateFragment
import org.oppia.app.player.audio.AudioFragment
+import org.oppia.app.topic.TopicFragment
import org.oppia.app.topic.conceptcard.ConceptCardFragment
+import org.oppia.app.topic.overview.TopicOverviewFragment
+import org.oppia.app.topic.play.TopicPlayFragment
+import org.oppia.app.topic.review.TopicReviewFragment
+import org.oppia.app.topic.train.TopicTrainFragment
/** Root subcomponent for all fragments. */
@Subcomponent
@@ -20,8 +25,13 @@ interface FragmentComponent {
}
fun inject(audioFragment: AudioFragment)
+ fun inject(conceptCardFragment: ConceptCardFragment)
fun inject(explorationFragment: ExplorationFragment)
fun inject(homeFragment: HomeFragment)
fun inject(stateFragment: StateFragment)
- fun inject(conceptCardFragment: ConceptCardFragment)
+ fun inject(topicFragment: TopicFragment)
+ fun inject(topicOverviewFragment: TopicOverviewFragment)
+ fun inject(topicPlayFragment: TopicPlayFragment)
+ fun inject(topicReviewFragment: TopicReviewFragment)
+ fun inject(topicTrainFragment: TopicTrainFragment)
}
diff --git a/app/src/main/java/org/oppia/app/fragment/InjectableDialogFragment.kt b/app/src/main/java/org/oppia/app/fragment/InjectableDialogFragment.kt
index 601c8c6737e..2f0f96f3dde 100644
--- a/app/src/main/java/org/oppia/app/fragment/InjectableDialogFragment.kt
+++ b/app/src/main/java/org/oppia/app/fragment/InjectableDialogFragment.kt
@@ -2,7 +2,6 @@ package org.oppia.app.fragment
import android.content.Context
import androidx.fragment.app.DialogFragment
-import androidx.fragment.app.Fragment
import org.oppia.app.activity.InjectableAppCompatActivity
/**
diff --git a/app/src/main/java/org/oppia/app/topic/TopicActivity.kt b/app/src/main/java/org/oppia/app/topic/TopicActivity.kt
new file mode 100644
index 00000000000..31772522639
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/TopicActivity.kt
@@ -0,0 +1,16 @@
+package org.oppia.app.topic
+
+import android.os.Bundle
+import org.oppia.app.activity.InjectableAppCompatActivity
+import javax.inject.Inject
+
+/** The activity for tabs in Topic. */
+class TopicActivity : InjectableAppCompatActivity() {
+ @Inject lateinit var topicActivityPresenter: TopicActivityPresenter
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ activityComponent.inject(this)
+ topicActivityPresenter.handleOnCreate()
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/topic/TopicActivityPresenter.kt b/app/src/main/java/org/oppia/app/topic/TopicActivityPresenter.kt
new file mode 100644
index 00000000000..2e807be8c74
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/TopicActivityPresenter.kt
@@ -0,0 +1,24 @@
+package org.oppia.app.topic
+
+import androidx.appcompat.app.AppCompatActivity
+import org.oppia.app.R
+import org.oppia.app.activity.ActivityScope
+import javax.inject.Inject
+
+/** The presenter for [TopicActivity]. */
+@ActivityScope
+class TopicActivityPresenter @Inject constructor(private val activity: AppCompatActivity) {
+ fun handleOnCreate() {
+ activity.setContentView(R.layout.topic_activity)
+ if (getTopicFragment() == null) {
+ activity.supportFragmentManager.beginTransaction().add(
+ R.id.topic_fragment_placeholder,
+ TopicFragment()
+ ).commitNow()
+ }
+ }
+
+ private fun getTopicFragment(): TopicFragment? {
+ return activity.supportFragmentManager.findFragmentById(R.id.topic_fragment_placeholder) as TopicFragment?
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/topic/TopicFragment.kt b/app/src/main/java/org/oppia/app/topic/TopicFragment.kt
new file mode 100644
index 00000000000..b414fcacd64
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/TopicFragment.kt
@@ -0,0 +1,23 @@
+package org.oppia.app.topic
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import org.oppia.app.fragment.InjectableFragment
+import javax.inject.Inject
+
+/** Fragment that contains tabs for Topic. */
+class TopicFragment : InjectableFragment() {
+ @Inject lateinit var topicFragmentPresenter: TopicFragmentPresenter
+
+ override fun onAttach(context: Context?) {
+ super.onAttach(context)
+ fragmentComponent.inject(this)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return topicFragmentPresenter.handleCreateView(inflater, container)
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/topic/TopicFragmentPresenter.kt b/app/src/main/java/org/oppia/app/topic/TopicFragmentPresenter.kt
new file mode 100644
index 00000000000..24e7b205070
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/TopicFragmentPresenter.kt
@@ -0,0 +1,23 @@
+package org.oppia.app.topic
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import org.oppia.app.databinding.TopicFragmentBinding
+import org.oppia.app.fragment.FragmentScope
+import javax.inject.Inject
+
+/** The controller for [TopicFragment]. */
+@FragmentScope
+class TopicFragmentPresenter @Inject constructor(
+ private val fragment: Fragment
+) {
+ fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
+ val binding = TopicFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
+ binding.let {
+ it.lifecycleOwner = fragment
+ }
+ return binding.root
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/topic/overview/TopicOverviewFragment.kt b/app/src/main/java/org/oppia/app/topic/overview/TopicOverviewFragment.kt
new file mode 100644
index 00000000000..589b41f92d0
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/overview/TopicOverviewFragment.kt
@@ -0,0 +1,24 @@
+package org.oppia.app.topic.overview
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import org.oppia.app.fragment.InjectableFragment
+import javax.inject.Inject
+
+/** Fragment that contains overview of Topic. */
+class TopicOverviewFragment : InjectableFragment() {
+ @Inject
+ lateinit var topicOverviewFragmentPresenter: TopicOverviewFragmentPresenter
+
+ override fun onAttach(context: Context?) {
+ super.onAttach(context)
+ fragmentComponent.inject(this)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return topicOverviewFragmentPresenter.handleCreateView(inflater, container)
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/topic/overview/TopicOverviewFragmentPresenter.kt b/app/src/main/java/org/oppia/app/topic/overview/TopicOverviewFragmentPresenter.kt
new file mode 100644
index 00000000000..25842b77bc4
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/overview/TopicOverviewFragmentPresenter.kt
@@ -0,0 +1,23 @@
+package org.oppia.app.topic.overview
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import org.oppia.app.databinding.TopicOverviewFragmentBinding
+import org.oppia.app.fragment.FragmentScope
+import javax.inject.Inject
+
+/** The presenter for [TopicOverviewFragment]. */
+@FragmentScope
+class TopicOverviewFragmentPresenter @Inject constructor(
+ private val fragment: Fragment
+) {
+ fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
+ val binding = TopicOverviewFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
+ binding.let {
+ it.lifecycleOwner = fragment
+ }
+ return binding.root
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/topic/play/TopicPlayFragment.kt b/app/src/main/java/org/oppia/app/topic/play/TopicPlayFragment.kt
new file mode 100644
index 00000000000..cae8ef953cd
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/play/TopicPlayFragment.kt
@@ -0,0 +1,24 @@
+package org.oppia.app.topic.play
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import org.oppia.app.fragment.InjectableFragment
+import javax.inject.Inject
+
+/** Fragment that contains subtopic list for play mode. */
+class TopicPlayFragment : InjectableFragment() {
+ @Inject
+ lateinit var topicPlayFragmentPresenter: TopicPlayFragmentPresenter
+
+ override fun onAttach(context: Context?) {
+ super.onAttach(context)
+ fragmentComponent.inject(this)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return topicPlayFragmentPresenter.handleCreateView(inflater, container)
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/topic/play/TopicPlayFragmentPresenter.kt b/app/src/main/java/org/oppia/app/topic/play/TopicPlayFragmentPresenter.kt
new file mode 100644
index 00000000000..fd863ef33a0
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/play/TopicPlayFragmentPresenter.kt
@@ -0,0 +1,23 @@
+package org.oppia.app.topic.play
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import org.oppia.app.databinding.TopicPlayFragmentBinding
+import org.oppia.app.fragment.FragmentScope
+import javax.inject.Inject
+
+/** The presenter for [TopicPlayFragment]. */
+@FragmentScope
+class TopicPlayFragmentPresenter @Inject constructor(
+ private val fragment: Fragment
+) {
+ fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
+ val binding = TopicPlayFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
+ binding.let {
+ it.lifecycleOwner = fragment
+ }
+ return binding.root
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/topic/review/TopicReviewFragment.kt b/app/src/main/java/org/oppia/app/topic/review/TopicReviewFragment.kt
new file mode 100644
index 00000000000..e2f03bf1c44
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/review/TopicReviewFragment.kt
@@ -0,0 +1,24 @@
+package org.oppia.app.topic.review
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import org.oppia.app.fragment.InjectableFragment
+import javax.inject.Inject
+
+/** Fragment that card for topic review. */
+class TopicReviewFragment : InjectableFragment() {
+ @Inject
+ lateinit var topicReviewFragmentPresenter: TopicReviewFragmentPresenter
+
+ override fun onAttach(context: Context?) {
+ super.onAttach(context)
+ fragmentComponent.inject(this)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return topicReviewFragmentPresenter.handleCreateView(inflater, container)
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/topic/review/TopicReviewFragmentPresenter.kt b/app/src/main/java/org/oppia/app/topic/review/TopicReviewFragmentPresenter.kt
new file mode 100644
index 00000000000..0800fd3e48c
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/review/TopicReviewFragmentPresenter.kt
@@ -0,0 +1,23 @@
+package org.oppia.app.topic.review
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import org.oppia.app.databinding.TopicReviewFragmentBinding
+import org.oppia.app.fragment.FragmentScope
+import javax.inject.Inject
+
+/** The presenter for [TopicReviewFragment]. */
+@FragmentScope
+class TopicReviewFragmentPresenter @Inject constructor(
+ private val fragment: Fragment
+) {
+ fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
+ val binding = TopicReviewFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
+ binding.let {
+ it.lifecycleOwner = fragment
+ }
+ return binding.root
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/topic/train/TopicTrainFragment.kt b/app/src/main/java/org/oppia/app/topic/train/TopicTrainFragment.kt
new file mode 100644
index 00000000000..4d1a5062f9e
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/train/TopicTrainFragment.kt
@@ -0,0 +1,24 @@
+package org.oppia.app.topic.train
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import org.oppia.app.fragment.InjectableFragment
+import javax.inject.Inject
+
+/** Fragment that contains skills for topic train mode. */
+class TopicTrainFragment : InjectableFragment() {
+ @Inject
+ lateinit var topicTrainFragmentPresenter: TopicTrainFragmentPresenter
+
+ override fun onAttach(context: Context?) {
+ super.onAttach(context)
+ fragmentComponent.inject(this)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return topicTrainFragmentPresenter.handleCreateView(inflater, container)
+ }
+}
diff --git a/app/src/main/java/org/oppia/app/topic/train/TopicTrainFragmentPresenter.kt b/app/src/main/java/org/oppia/app/topic/train/TopicTrainFragmentPresenter.kt
new file mode 100644
index 00000000000..af436a7e294
--- /dev/null
+++ b/app/src/main/java/org/oppia/app/topic/train/TopicTrainFragmentPresenter.kt
@@ -0,0 +1,23 @@
+package org.oppia.app.topic.train
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import org.oppia.app.databinding.TopicTrainFragmentBinding
+import org.oppia.app.fragment.FragmentScope
+import javax.inject.Inject
+
+/** The presenter for [TopicTrainFragment]. */
+@FragmentScope
+class TopicTrainFragmentPresenter @Inject constructor(
+ private val fragment: Fragment
+) {
+ fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
+ val binding = TopicTrainFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
+ binding.let {
+ it.lifecycleOwner = fragment
+ }
+ return binding.root
+ }
+}
diff --git a/app/src/main/res/layout/topic_activity.xml b/app/src/main/res/layout/topic_activity.xml
new file mode 100644
index 00000000000..350aafeef5c
--- /dev/null
+++ b/app/src/main/res/layout/topic_activity.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/app/src/main/res/layout/topic_fragment.xml b/app/src/main/res/layout/topic_fragment.xml
new file mode 100644
index 00000000000..e9b0824c1b7
--- /dev/null
+++ b/app/src/main/res/layout/topic_fragment.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/topic_overview_fragment.xml b/app/src/main/res/layout/topic_overview_fragment.xml
new file mode 100644
index 00000000000..e9b0824c1b7
--- /dev/null
+++ b/app/src/main/res/layout/topic_overview_fragment.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/topic_play_fragment.xml b/app/src/main/res/layout/topic_play_fragment.xml
new file mode 100644
index 00000000000..e9b0824c1b7
--- /dev/null
+++ b/app/src/main/res/layout/topic_play_fragment.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/topic_review_fragment.xml b/app/src/main/res/layout/topic_review_fragment.xml
new file mode 100644
index 00000000000..e9b0824c1b7
--- /dev/null
+++ b/app/src/main/res/layout/topic_review_fragment.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/topic_train_fragment.xml b/app/src/main/res/layout/topic_train_fragment.xml
new file mode 100644
index 00000000000..e9b0824c1b7
--- /dev/null
+++ b/app/src/main/res/layout/topic_train_fragment.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/sharedTest/java/org/oppia/app/player/exploration/ExplorationActivityTest.kt b/app/src/sharedTest/java/org/oppia/app/player/exploration/ExplorationActivityTest.kt
index 7bd82e85405..45746f98f5b 100644
--- a/app/src/sharedTest/java/org/oppia/app/player/exploration/ExplorationActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/app/player/exploration/ExplorationActivityTest.kt
@@ -3,9 +3,10 @@ package org.oppia.app.player.exploration
import android.app.Application
import android.content.Context
import androidx.test.core.app.ActivityScenario
-import androidx.test.espresso.Espresso
-import androidx.test.espresso.assertion.ViewAssertions
-import androidx.test.espresso.matcher.ViewMatchers
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.BindsInstance
import dagger.Component
@@ -26,8 +27,7 @@ class ExplorationActivityTest {
@Test
fun testExplorationActivity_loadExplorationFragment_hasDummyString() {
ActivityScenario.launch(ExplorationActivity::class.java).use {
- Espresso.onView(ViewMatchers.withId(R.id.dummy_text_view))
- .check(ViewAssertions.matches(ViewMatchers.withText("This is dummy TextView for testing")))
+ onView(withId(R.id.dummy_text_view)).check(matches(withText("This is dummy TextView for testing")))
}
}
diff --git a/app/src/sharedTest/java/org/oppia/app/topic/TopicActivityTest.kt b/app/src/sharedTest/java/org/oppia/app/topic/TopicActivityTest.kt
new file mode 100644
index 00000000000..341b0afbe66
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/app/topic/TopicActivityTest.kt
@@ -0,0 +1,66 @@
+package org.oppia.app.topic
+
+import android.app.Application
+import android.content.Context
+import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.BindsInstance
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineDispatcher
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.app.R
+import org.oppia.app.player.exploration.ExplorationActivity
+import org.oppia.util.threading.BackgroundDispatcher
+import org.oppia.util.threading.BlockingDispatcher
+import javax.inject.Singleton
+
+/** Tests for [TopicActivity]. */
+@RunWith(AndroidJUnit4::class)
+class TopicActivityTest {
+
+ @Test
+ fun testTopicActivity_loadTopicFragment_hasDummyString() {
+ ActivityScenario.launch(ExplorationActivity::class.java).use {
+ onView(withId(R.id.dummy_text_view)).check(matches(withText("This is dummy TextView for testing")))
+ }
+ }
+
+ @Module
+ class TestModule {
+ @Provides
+ @Singleton
+ fun provideContext(application: Application): Context {
+ return application
+ }
+
+ // TODO(#89): Introduce a proper IdlingResource for background dispatchers to ensure they all complete before
+ // proceeding in an Espresso test. This solution should also be interoperative with Robolectric contexts by using a
+ // test coroutine dispatcher.
+
+ @Singleton
+ @Provides
+ @BackgroundDispatcher
+ fun provideBackgroundDispatcher(@BlockingDispatcher blockingDispatcher: CoroutineDispatcher): CoroutineDispatcher {
+ return blockingDispatcher
+ }
+ }
+
+ @Singleton
+ @Component(modules = [TestModule::class])
+ interface TestApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+ }
+}
diff --git a/app/src/sharedTest/java/org/oppia/app/topic/conceptcard/ConceptCardFragmentTest.kt b/app/src/sharedTest/java/org/oppia/app/topic/conceptcard/ConceptCardFragmentTest.kt
index 5d739a7756f..7a02c220535 100644
--- a/app/src/sharedTest/java/org/oppia/app/topic/conceptcard/ConceptCardFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/app/topic/conceptcard/ConceptCardFragmentTest.kt
@@ -1,12 +1,10 @@
-package org.oppia.app.player.audio
+package org.oppia.app.topic.conceptcard
import android.app.Application
import android.content.Context
import androidx.test.core.app.ActivityScenario
-import androidx.test.espresso.Espresso
-import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
-import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -31,7 +29,7 @@ class ConceptCardFragmentTest {
fun testConceptCardFragment_loadFragment_textIsDisplayed() {
// I'm not sure how to launch just the fragment because it doesn't have its own activity
ActivityScenario.launch(HomeActivity::class.java).use {
- Espresso.onView(withId(R.id.rich_text_card)).check(matches(withText("Hello World")))
+ onView(withId(R.id.rich_text_card)).check(matches(withText("Hello World")))
}
}
diff --git a/app/src/sharedTest/java/org/oppia/app/topic/overview/TopicOverviewFragmentTest.kt b/app/src/sharedTest/java/org/oppia/app/topic/overview/TopicOverviewFragmentTest.kt
new file mode 100644
index 00000000000..95a895c912e
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/app/topic/overview/TopicOverviewFragmentTest.kt
@@ -0,0 +1,66 @@
+package org.oppia.app.topic.overview
+
+import android.app.Application
+import android.content.Context
+import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.BindsInstance
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineDispatcher
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.app.R
+import org.oppia.app.topic.TopicActivity
+import org.oppia.util.threading.BackgroundDispatcher
+import org.oppia.util.threading.BlockingDispatcher
+import javax.inject.Singleton
+
+/** Tests for [TopicOverviewFragment]. */
+@RunWith(AndroidJUnit4::class)
+class TopicOverviewFragmentTest {
+
+ @Test
+ fun testTopicOverviewFragment_loadFragment_textIsDisplayed() {
+ ActivityScenario.launch(TopicActivity::class.java).use {
+ onView(withId(R.id.dummy_text_view)).check(matches(withText("This is dummy TextView for testing")))
+ }
+ }
+
+ @Module
+ class TestModule {
+ @Provides
+ @Singleton
+ fun provideContext(application: Application): Context {
+ return application
+ }
+
+ // TODO(#89): Introduce a proper IdlingResource for background dispatchers to ensure they all complete before
+ // proceeding in an Espresso test. This solution should also be interoperative with Robolectric contexts by using a
+ // test coroutine dispatcher.
+
+ @Singleton
+ @Provides
+ @BackgroundDispatcher
+ fun provideBackgroundDispatcher(@BlockingDispatcher blockingDispatcher: CoroutineDispatcher): CoroutineDispatcher {
+ return blockingDispatcher
+ }
+ }
+
+ @Singleton
+ @Component(modules = [TestModule::class])
+ interface TestApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+ }
+}
diff --git a/app/src/sharedTest/java/org/oppia/app/topic/play/TopicPlayFragmentTest.kt b/app/src/sharedTest/java/org/oppia/app/topic/play/TopicPlayFragmentTest.kt
new file mode 100644
index 00000000000..7c6f9a4b465
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/app/topic/play/TopicPlayFragmentTest.kt
@@ -0,0 +1,66 @@
+package org.oppia.app.topic.play
+
+import android.app.Application
+import android.content.Context
+import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.BindsInstance
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineDispatcher
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.app.R
+import org.oppia.app.topic.TopicActivity
+import org.oppia.util.threading.BackgroundDispatcher
+import org.oppia.util.threading.BlockingDispatcher
+import javax.inject.Singleton
+
+/** Tests for [TopicPlayFragment]. */
+@RunWith(AndroidJUnit4::class)
+class TopicPlayFragmentTest {
+
+ @Test
+ fun testTopicPlayFragment_loadFragment_textIsDisplayed() {
+ ActivityScenario.launch(TopicActivity::class.java).use {
+ onView(withId(R.id.dummy_text_view)).check(matches(withText("This is dummy TextView for testing")))
+ }
+ }
+
+ @Module
+ class TestModule {
+ @Provides
+ @Singleton
+ fun provideContext(application: Application): Context {
+ return application
+ }
+
+ // TODO(#89): Introduce a proper IdlingResource for background dispatchers to ensure they all complete before
+ // proceeding in an Espresso test. This solution should also be interoperative with Robolectric contexts by using a
+ // test coroutine dispatcher.
+
+ @Singleton
+ @Provides
+ @BackgroundDispatcher
+ fun provideBackgroundDispatcher(@BlockingDispatcher blockingDispatcher: CoroutineDispatcher): CoroutineDispatcher {
+ return blockingDispatcher
+ }
+ }
+
+ @Singleton
+ @Component(modules = [TestModule::class])
+ interface TestApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+ }
+}
diff --git a/app/src/sharedTest/java/org/oppia/app/topic/review/TopicReviewFragmentTest.kt b/app/src/sharedTest/java/org/oppia/app/topic/review/TopicReviewFragmentTest.kt
new file mode 100644
index 00000000000..1eca380b699
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/app/topic/review/TopicReviewFragmentTest.kt
@@ -0,0 +1,66 @@
+package org.oppia.app.topic.review
+
+import android.app.Application
+import android.content.Context
+import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.BindsInstance
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineDispatcher
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.app.R
+import org.oppia.app.topic.TopicActivity
+import org.oppia.util.threading.BackgroundDispatcher
+import org.oppia.util.threading.BlockingDispatcher
+import javax.inject.Singleton
+
+/** Tests for [TopicReviewFragment]. */
+@RunWith(AndroidJUnit4::class)
+class TopicReviewFragmentTest {
+
+ @Test
+ fun testTopicReviewFragment_loadFragment_textIsDisplayed() {
+ ActivityScenario.launch(TopicActivity::class.java).use {
+ onView(withId(R.id.dummy_text_view)).check(matches(withText("This is dummy TextView for testing")))
+ }
+ }
+
+ @Module
+ class TestModule {
+ @Provides
+ @Singleton
+ fun provideContext(application: Application): Context {
+ return application
+ }
+
+ // TODO(#89): Introduce a proper IdlingResource for background dispatchers to ensure they all complete before
+ // proceeding in an Espresso test. This solution should also be interoperative with Robolectric contexts by using a
+ // test coroutine dispatcher.
+
+ @Singleton
+ @Provides
+ @BackgroundDispatcher
+ fun provideBackgroundDispatcher(@BlockingDispatcher blockingDispatcher: CoroutineDispatcher): CoroutineDispatcher {
+ return blockingDispatcher
+ }
+ }
+
+ @Singleton
+ @Component(modules = [TestModule::class])
+ interface TestApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+ }
+}
diff --git a/app/src/sharedTest/java/org/oppia/app/topic/train/TopicTrainFragmentTest.kt b/app/src/sharedTest/java/org/oppia/app/topic/train/TopicTrainFragmentTest.kt
new file mode 100644
index 00000000000..b55c8fa420f
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/app/topic/train/TopicTrainFragmentTest.kt
@@ -0,0 +1,66 @@
+package org.oppia.app.topic.train
+
+import android.app.Application
+import android.content.Context
+import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.BindsInstance
+import dagger.Component
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineDispatcher
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.app.R
+import org.oppia.app.topic.TopicActivity
+import org.oppia.util.threading.BackgroundDispatcher
+import org.oppia.util.threading.BlockingDispatcher
+import javax.inject.Singleton
+
+/** Tests for [TopicTrainFragment]. */
+@RunWith(AndroidJUnit4::class)
+class TopicTrainFragmentTest {
+
+ @Test
+ fun testTopicTrainFragment_loadFragment_textIsDisplayed() {
+ ActivityScenario.launch(TopicActivity::class.java).use {
+ onView(withId(R.id.dummy_text_view)).check(matches(withText("This is dummy TextView for testing")))
+ }
+ }
+
+ @Module
+ class TestModule {
+ @Provides
+ @Singleton
+ fun provideContext(application: Application): Context {
+ return application
+ }
+
+ // TODO(#89): Introduce a proper IdlingResource for background dispatchers to ensure they all complete before
+ // proceeding in an Espresso test. This solution should also be interoperative with Robolectric contexts by using a
+ // test coroutine dispatcher.
+
+ @Singleton
+ @Provides
+ @BackgroundDispatcher
+ fun provideBackgroundDispatcher(@BlockingDispatcher blockingDispatcher: CoroutineDispatcher): CoroutineDispatcher {
+ return blockingDispatcher
+ }
+ }
+
+ @Singleton
+ @Component(modules = [TestModule::class])
+ interface TestApplicationComponent {
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ fun setApplication(application: Application): Builder
+
+ fun build(): TestApplicationComponent
+ }
+ }
+}