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 + } + } +}