1 // Copyright 2015-present 650 Industries. All rights reserved.
2 package host.exp.exponent
3 
4 import android.Manifest
5 import android.content.Intent
6 import android.net.Uri
7 import androidx.test.InstrumentationRegistry
8 import androidx.test.espresso.Espresso
9 import androidx.test.espresso.IdlingResource
10 import androidx.test.espresso.assertion.ViewAssertions
11 import androidx.test.espresso.matcher.ViewMatchers
12 import androidx.test.rule.GrantPermissionRule
13 import androidx.test.uiautomator.By
14 import androidx.test.uiautomator.Until
15 import host.exp.exponent.annotations.ExpoAlwaysPassThroughFilter
16 import host.exp.exponent.annotations.ExpoSdkVersionTest
17 import host.exp.exponent.annotations.ExpoTestSuiteTest
18 import host.exp.exponent.generated.ExponentBuildConstants
19 import host.exp.exponent.kernel.KernelConfig
20 import host.exp.exponent.utils.*
21 import org.json.JSONException
22 import org.json.JSONObject
23 import org.junit.*
24 import org.junit.rules.RuleChain
25 import org.junit.runner.RunWith
26 import org.junit.runners.MethodSorters
27 
28 @RunWith(ExpoTestRunner::class)
29 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
30 class TestSuiteTests : BaseTestClass() {
31   private lateinit var loadingScreenIdlingResource: IdlingResource
32   private lateinit var elapsedTimeIdlingResource: ElapsedTimeIdlingResource
33   private lateinit var jsTestRunnerIdlingResource: JSTestRunnerIdlingResource
34 
35   @Before
36   fun before() {
37     loadingScreenIdlingResource = LoadingScreenIdlingResource()
38     elapsedTimeIdlingResource = ElapsedTimeIdlingResource()
39     jsTestRunnerIdlingResource = JSTestRunnerIdlingResource()
40     Espresso.registerIdlingResources(
41       loadingScreenIdlingResource,
42       elapsedTimeIdlingResource,
43       jsTestRunnerIdlingResource
44     )
45     KernelConfig.FORCE_UNVERSIONED_PUBLISHED_EXPERIENCES = false
46   }
47 
48   @After
49   fun after() {
50     Espresso.unregisterIdlingResources(
51       loadingScreenIdlingResource,
52       elapsedTimeIdlingResource,
53       jsTestRunnerIdlingResource
54     )
55     KernelConfig.FORCE_UNVERSIONED_PUBLISHED_EXPERIENCES = false
56   }
57 
58   private var haveContactsBeenAdded = false
59   private fun ensureContactsAdded() {
60     if (haveContactsBeenAdded) {
61       return
62     }
63     haveContactsBeenAdded = true
64     TestContacts.add(InstrumentationRegistry.getTargetContext())
65   }
66 
67   private fun runTestSuiteTest(testSuiteUriString: String, shouldAddDeepLink: Boolean) {
68     var testSuiteUri = Uri.parse(testSuiteUriString)
69     ensureContactsAdded()
70     if (shouldAddDeepLink) {
71       val deepLink = TestConfig.get().toString()
72       testSuiteUri = Uri.withAppendedPath(testSuiteUri, "/--/$deepLink")
73     }
74 
75     // Launch the app
76     val context = InstrumentationRegistry.getContext()
77     val intent = Intent(Intent.ACTION_VIEW, testSuiteUri)
78     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
79     context.startActivity(intent)
80 
81     // Wait for the app to appear
82     uiDevice.wait(Until.hasObject(By.pkg("host.exp.exponent").depth(0)), LAUNCH_TIMEOUT.toLong())
83 
84     // Need this to wait on idling resources
85     Espresso.onView(ExponentMatchers.withTestId("test_suite_container"))
86       .check(ViewAssertions.matches(ViewMatchers.isEnabled()))
87     val result = jsTestRunnerIdlingResource.testResult
88     try {
89       val jsonObject = JSONObject(result)
90       val numFailed = jsonObject.getInt("failed")
91       if (jsonObject.has("results")) {
92         testReporterRule.logTestInfo(jsonObject.getString("results"))
93       }
94       if (jsonObject.has("failures")) {
95         testReporterRule.logTestInfo(jsonObject.getString("failures"))
96       }
97       if (numFailed > 0) {
98         throw AssertionError("$numFailed JS test(s) failed")
99       }
100     } catch (e: JSONException) {
101       throw AssertionError("JSON error $e")
102     }
103   }
104 
105   private val testReporterRule = TestReporterRule()
106 
107   @Rule
108   @JvmField
109   val chain: RuleChain = RuleChain.outerRule(testReporterRule).around(RetryTestRule(2))
110 
111   @Rule
112   @JvmField
113   val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(
114     Manifest.permission.SYSTEM_ALERT_WINDOW, Manifest.permission.READ_CONTACTS,
115     Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION,
116     Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
117     Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR
118   )
119 
120   @Test
121   @ExpoTestSuiteTest
122   @ExpoSdkVersionTest("UNVERSIONED")
123   fun sdkUnversionedTestSuite() {
124     if (!isCurrentTestSuiteAvailable) {
125       return
126     }
127     KernelConfig.FORCE_UNVERSIONED_PUBLISHED_EXPERIENCES = true
128     runTestSuiteTest(ExponentBuildConstants.TEST_APP_URI, true)
129   }
130 
131   @Test
132   @ExpoAlwaysPassThroughFilter
133   fun junitIsSillyAndWillFailIfThereIsntOneTestRunPerFile() {
134   }
135 
136   companion object {
137     @BeforeClass
138     @JvmStatic
139     fun beforeClass() {
140       BaseTestClass.beforeClass()
141 
142       // Press home
143       uiDevice.pressHome()
144       val launcherPackage = uiDevice.launcherPackageName
145       uiDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT.toLong())
146     }
147 
148     val isCurrentTestSuiteAvailable: Boolean
149       get() = ExponentBuildConstants.TEST_APP_URI != ""
150   }
151 }
152