diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt index 49dd3b78309..a2023dd1dac 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt @@ -5830,6 +5830,54 @@ class StateFragmentTest { } } + @Test + fun testFlashback_featureFlagOff_thenFeatureFlagOn() { + // Set up the previous app instance with the flashback feature flag OFF. + TestPlatformParameterModule.forceEnableFlashbackSupport(false) + executeInPreviousAppInstance { _ -> } + + // In the current app instance, enable the feature flag and verify flashback button is shown. + setUpTestWithFlashbackFeatureOn() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + + navigateToPrototypeRatioInputState() + + // Submit wrong answer. + typeRatioExpression("4:8") + clickSubmitAnswerButton() + + // Verify flashback button is visible. + scrollToViewType(FLASHBACK_BUTTON) + onView(withId(R.id.flashback_button)).check( + matches(withText(R.string.state_flashback_button)) + ) + } + } + + @Test + fun testFlashback_featureFlagOn_persistsAcrossAppInstances() { + // Set up the previous app instance with the flashback feature flag ON. + TestPlatformParameterModule.forceEnableFlashbackSupport(true) + executeInPreviousAppInstance { _ -> } + + // In the current app instance, flag should still be on. + setUpTestWithFlashbackFeatureOn() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + + navigateToPrototypeRatioInputState() + + // Submit wrong answer. + typeRatioExpression("4:8") + clickSubmitAnswerButton() + + // Verify flashback button IS shown (flag persisted across app instances). + scrollToViewType(FLASHBACK_BUTTON) + onView(withId(R.id.flashback_button)).check(matches(isDisplayed())) + } + } + @Test fun testStateFragment_submitMultipleChoiceAnswer_verifyMultipleChoiceSubmittedAnswer() { setUpTestWithFlashbackFeatureOn() @@ -5837,7 +5885,6 @@ class StateFragmentTest { startPlayingExploration() playThroughPrototypeState1() playThroughPrototypeState2() - // Submit Multiple choice answer. selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "Eagle") clickSubmitAnswerButton() @@ -7075,6 +7122,31 @@ class StateFragmentTest { fun isOnRobolectric(): Boolean } + /** + * Creates a separate test application component and executes the specified block. This should be + * called before setUpTestApplicationComponent to avoid undefined behavior in production code. + * This can be used to simulate arranging state in a "prior" run of the app. + * + * Note that only dependencies fetched from the specified [TestApplicationComponent] should be + * used, not any class-level injected dependencies. + */ + private fun executeInPreviousAppInstance(block: (TestApplicationComponent) -> Unit) { + val testApplication = TestApplication() + // The true application is hooked as a base context. This is to make sure the new application + // can behave like a real Android application class (per Robolectric) without having a shared + // Dagger dependency graph with the application under test. + testApplication.attachBaseContext(ApplicationProvider.getApplicationContext()) + block( + DaggerStateFragmentTest_TestApplicationComponent.builder() + .setApplication(testApplication) + .build() + ) + + // Resetting after the previous instance is important so that the next (main) app instance + // can set its own flags. + TestPlatformParameterModule.reset() + } + class TestApplication : Application(), ActivityComponentFactory, ApplicationInjectorProvider { private val component: TestApplicationComponent by lazy { DaggerStateFragmentTest_TestApplicationComponent.builder() @@ -7090,6 +7162,10 @@ class StateFragmentTest { return component.getActivityComponentBuilderProvider().get().setActivity(activity).build() } + public override fun attachBaseContext(base: Context?) { + super.attachBaseContext(base) + } + override fun getApplicationInjector(): ApplicationInjector = component }