1 package com.expo.modules.devclient.scenarios
2 
3 import android.app.Activity
4 import android.app.Application
5 import androidx.test.core.app.ActivityScenario
6 import androidx.test.core.app.launchActivity
7 import androidx.test.platform.app.InstrumentationRegistry
8 import com.expo.modules.devclient.interceptors.DevLauncherTestInterceptor
9 import com.expo.modules.devclient.interceptors.DevMenuTestInterceptor
10 import com.expo.modules.devclient.selectors.Views
11 import com.facebook.react.ReactNativeHost
12 import com.facebook.react.ReactPackage
13 import com.facebook.react.shell.MainReactPackage
14 import expo.modules.devlauncher.DevLauncherController
15 import expo.modules.devlauncher.koin.DevLauncherKoinContext
16 import expo.modules.devlauncher.launcher.DevLauncherActivity
17 import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
18 import expo.modules.devmenu.DevMenuDefaultPreferences
19 import expo.modules.devmenu.DevMenuManager
20 import java.lang.ref.WeakReference
21 
22 internal typealias ReactNativeHostCreator = (application: Application) -> ReactNativeHost
23 internal typealias DevMenuTestInterceptorCreator = () -> expo.modules.devmenu.tests.DevMenuTestInterceptor
24 internal typealias DevLauncherTestInterceptorCreator = () -> expo.modules.devlauncher.tests.DevLauncherTestInterceptor
25 internal typealias KoinDeclaration = () -> Unit
26 
27 internal class DevLauncherBasicScenario(
28   private val reactNativeHostCreator: ReactNativeHostCreator? = null,
29   private val devMenuTestInterceptorCreator: DevMenuTestInterceptorCreator? = null,
30   private val devLauncherTestInterceptorCreator: DevLauncherTestInterceptorCreator? = null,
31   private val koinDeclaration: KoinDeclaration? = null,
32   private val launcherClass: Class<*>? = null
33 ) : AutoCloseable {
34   private var basicScenario: ActivityScenario<DevLauncherActivity>? = null
35   private var appHost: WeakReference<ReactNativeHost?> = WeakReference(null)
36 
setUpnull37   fun setUp() {
38     DevLauncherKoinContext.reinitialize()
39 
40     val appContext = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as Application
41     val rnHost = reactNativeHostCreator?.invoke(appContext) ?: defaultRNHost(appContext)
42     val devMenuTestInterceptor = devMenuTestInterceptorCreator?.invoke()
43       ?: defaultDevMenuTestInterceptor()
44     val devLauncherTestInterceptor = devLauncherTestInterceptorCreator?.invoke()
45       ?: defaultDevLauncherTestInterceptor()
46 
47     DevMenuManager.testInterceptor = devMenuTestInterceptor
48     DevLauncherKoinContext.app.koin.declare(devLauncherTestInterceptor)
49     koinDeclaration?.invoke()
50 
51     DevLauncherController.initialize(appContext, rnHost, launcherClass = launcherClass)
52 
53     appHost = WeakReference(rnHost)
54   }
55 
teardownnull56   fun teardown() {
57     basicScenario?.close()
58   }
59 
launchnull60   fun launch() {
61     basicScenario = launchActivity()
62 
63     Views.DevLauncher.main.isDisplayed()
64   }
65 
66   @Suppress("UNCHECKED_CAST")
onAppActivitynull67   fun <T : Activity> onAppActivity(activityAction: ActivityScenario.ActivityAction<T>) {
68     activityAction.perform(
69       appHost.get()!!.reactInstanceManager.currentReactContext!!.currentActivity as T
70     )
71   }
72 
onLauncherActivitynull73   fun onLauncherActivity(activityAction: ActivityScenario.ActivityAction<DevLauncherActivity>) {
74     basicScenario!!.onActivity(activityAction)
75   }
76 
launcherControllernull77   fun launcherController(): DevLauncherControllerInterface {
78     return DevLauncherKoinContext.app.koin.get()
79   }
80 
koinnull81   fun koin() = DevLauncherKoinContext.app.koin
82 
83   private fun defaultRNHost(application: Application): ReactNativeHost {
84     return object : ReactNativeHost(application) {
85       override fun getUseDeveloperSupport(): Boolean {
86         return false
87       }
88 
89       override fun getPackages(): List<ReactPackage> {
90         return listOf(MainReactPackage(null))
91       }
92     }
93   }
94 
defaultDevMenuTestInterceptornull95   private fun defaultDevMenuTestInterceptor(): expo.modules.devmenu.tests.DevMenuTestInterceptor {
96     return DevMenuTestInterceptor(object : DevMenuDefaultPreferences() {
97       override var showsAtLaunch: Boolean
98         get() = false
99         set(_) {}
100 
101       override var isOnboardingFinished: Boolean
102         get() = true
103         set(_) {}
104     })
105   }
106 
defaultDevLauncherTestInterceptornull107   private fun defaultDevLauncherTestInterceptor(): expo.modules.devlauncher.tests.DevLauncherTestInterceptor {
108     return DevLauncherTestInterceptor()
109   }
110 
closenull111   override fun close() {
112     teardown()
113   }
114 
setUpAndLaunchnull115   inline fun <R> setUpAndLaunch(block: (DevLauncherBasicScenario) -> R) {
116     setUp()
117     launch()
118     (this as AutoCloseable).use {
119       block(this)
120     }
121   }
122 }
123