1 package host.exp.exponent.utils
2 
3 import android.content.Intent
4 import android.net.Uri
5 import android.os.Build
6 import androidx.test.InstrumentationRegistry
7 import androidx.test.espresso.Espresso
8 import androidx.test.espresso.assertion.ViewAssertions
9 import androidx.test.espresso.matcher.ViewMatchers
10 import androidx.test.uiautomator.By
11 import androidx.test.uiautomator.UiDevice
12 import androidx.test.uiautomator.Until
13 import host.exp.exponent.generated.ExponentBuildConstants
14 import host.exp.exponent.kernel.ExponentUrls
15 import okhttp3.MediaType
16 import okhttp3.OkHttpClient
17 import okhttp3.Request
18 import okhttp3.RequestBody
19 import org.json.JSONException
20 import org.json.JSONObject
21 import java.io.IOException
22 import java.util.*
23 
24 object TestServerUtils {
25   private const val LAUNCH_TIMEOUT = 5000
26 
27   private val JSON = MediaType.parse("application/json; charset=utf-8")
28   private val isTestServerAvailable: Boolean
29     get() = ExponentBuildConstants.TEST_SERVER_URL != "TODO"
30 
31   @Throws(Exception::class)
32   fun runFixtureTest(device: UiDevice, fixtureName: String) {
33     if (!isTestServerAvailable) {
34       return
35     }
36 
37     // Get a fixture server
38     val fixtureServer = getFixtureServerInstance(fixtureName)
39 
40     // Launch the app
41     val context = InstrumentationRegistry.getContext()
42     val intent = Intent(Intent.ACTION_VIEW, Uri.parse(fixtureServer!!.manifestServerUrl)).apply {
43       addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
44     }
45     context.startActivity(intent)
46 
47     // Wait for the app to appear
48     device.wait(Until.hasObject(By.pkg("host.exp.exponent").depth(0)), LAUNCH_TIMEOUT.toLong())
49 
50     // Need this to wait on idling resources
51     Espresso.onView(ExponentMatchers.withTestId("test_container"))
52       .check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
53     for (event in fixtureServer.testEvents) {
54       event.waitForCompleted(device, fixtureServer.manifestServerUrl)
55     }
56   }
57 
58   @Throws(IOException::class)
59   private fun httpRequest(request: Request): String {
60     val client = OkHttpClient()
61     val response = client.newCall(request).execute()
62     if (!response.isSuccessful) {
63       throw IOException("Unexpected code $response")
64     }
65     return response.body()!!.string()
66   }
67 
68   private fun getFixtureServerInstance(fixtureName: String): FixtureServerInstance? {
69     try {
70       val request = Request.Builder()
71         .url(ExponentBuildConstants.TEST_SERVER_URL + "/start-fixture-server?fixtureName=" + fixtureName)
72         .build()
73       val responseJson = JSONObject(httpRequest(request))
74       val manifestServerUrl = responseJson.getString("manifestServerUrl")
75       val jsonTestEvents = responseJson.getJSONArray("testEvents")
76       val testEvents: MutableList<TestEvent> = ArrayList()
77       for (i in 0 until jsonTestEvents.length()) {
78         val jsonTestEvent = jsonTestEvents.getJSONObject(i)
79         testEvents.add(
80           TestEvent(
81             jsonTestEvent.getString("type"),
82             jsonTestEvent.getString("data"),
83             jsonTestEvent.getInt("testEventId")
84           )
85         )
86       }
87       return FixtureServerInstance(manifestServerUrl, testEvents)
88     } catch (e: IOException) {
89       e.printStackTrace()
90     } catch (e: JSONException) {
91       e.printStackTrace()
92     }
93     return null
94   }
95 
96   @Throws(Exception::class)
97   fun reportTestResult(success: Boolean, testName: String?, logs: String?) {
98     if (!isTestServerAvailable) {
99       return
100     }
101     val jsonBody = JSONObject().apply {
102       put("testRunId", ExponentBuildConstants.TEST_RUN_ID)
103       put("testName", testName)
104       put("success", success)
105       put("logs", logs)
106       put("deviceName", Build.MODEL)
107       put("systemVersion", Build.VERSION.RELEASE)
108     }
109     val request = Request.Builder()
110       .url(ExponentBuildConstants.TEST_SERVER_URL + "/report-test-result")
111       .post(RequestBody.create(JSON, jsonBody.toString()))
112       .build()
113     httpRequest(request)
114   }
115 
116   class TestEvent(private val type: String, private val data: String, private val testEventId: Int) {
117     @Throws(Exception::class)
118     fun waitForCompleted(device: UiDevice, manifestUrl: String) {
119       if (type == "findTextOnScreen") {
120         ExpoConditionWatcher.waitForText(device, data)
121       }
122       try {
123         val request = Request.Builder()
124           .url(ExponentUrls.toHttp(manifestUrl) + "/finished-test-event")
125           .addHeader("test-event-id", testEventId.toString())
126           .build()
127         httpRequest(request)
128       } catch (e: RuntimeException) {
129       } catch (e: IOException) {
130       }
131     }
132   }
133 
134   class FixtureServerInstance(val manifestServerUrl: String, val testEvents: List<TestEvent>)
135 }
136