mirror of
https://github.com/vector-im/riotX-android
synced 2025-10-06 00:02:48 +02:00
Compare commits
7 Commits
v1.6.32
...
feature/mi
Author | SHA1 | Date | |
---|---|---|---|
|
6c4c3d5404 | ||
|
7ba26e624f | ||
|
9d0e57a44c | ||
|
5bb006ad09 | ||
|
362a58d702 | ||
|
0c4451ffc6 | ||
|
0330a624b6 |
1
changelog.d/6729.misc
Normal file
1
changelog.d/6729.misc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Demo trafficlight client for element android https://github.com/matrix-org/trafficlight
|
@@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.ui
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import androidx.test.espresso.IdlingPolicies
|
||||||
|
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.rule.GrantPermissionRule
|
||||||
|
import im.vector.app.espresso.tools.ScreenshotFailureRule
|
||||||
|
import im.vector.app.features.MainActivity
|
||||||
|
import im.vector.app.ui.robot.ElementRobot
|
||||||
|
import okhttp3.MediaType
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.rules.RuleChain
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import java.lang.Thread.sleep
|
||||||
|
import java.util.UUID
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TrafficLight client is a UI test that receives requests from a TrafficLight server and fufils them on the UI
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class TrafficLightClientTest {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val testRule: RuleChain = RuleChain
|
||||||
|
.outerRule(ActivityScenarioRule(MainActivity::class.java))
|
||||||
|
.around(GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE))
|
||||||
|
.around(ScreenshotFailureRule())
|
||||||
|
|
||||||
|
private val elementRobot = ElementRobot()
|
||||||
|
|
||||||
|
private val client = OkHttpClient()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun trafficLightCycle() {
|
||||||
|
IdlingPolicies.setMasterPolicyTimeout(120, TimeUnit.SECONDS)
|
||||||
|
|
||||||
|
// keep the same UUID during each run; otherwise it should be entirely random.
|
||||||
|
val uuid = UUID.randomUUID().toString()
|
||||||
|
val trafficLightServer = "http://10.0.2.2:5000"
|
||||||
|
val postURL = "$trafficLightServer/client/$uuid/respond"
|
||||||
|
val pollURL = "$trafficLightServer/client/$uuid/poll"
|
||||||
|
val registerURL = "$trafficLightServer/client/$uuid/register"
|
||||||
|
|
||||||
|
register(registerURL)
|
||||||
|
while (true) {
|
||||||
|
var pollResponse = poll(pollURL)
|
||||||
|
|
||||||
|
var action = pollResponse.get("action")
|
||||||
|
if (action == "login") {
|
||||||
|
val data = pollResponse.getJSONObject("data")
|
||||||
|
val user = data.getString("username")
|
||||||
|
val pass = data.getString("password")
|
||||||
|
val homeserverURL = data.getJSONObject("homeserver_url").getString("local_docker")
|
||||||
|
elementRobot.login(homeserverURL, user, pass)
|
||||||
|
|
||||||
|
// TODO: Determine how to avoid these 60s delays before prompt arrives.
|
||||||
|
// TODO (alt): Determine how to wait for the prompt to be visible before continuing instead of explicit wait.
|
||||||
|
sleep(20000) // try to wait for cross signing to have kicked in...
|
||||||
|
println("Waited 20s")
|
||||||
|
sleep(20000)
|
||||||
|
println("Waited 40s")
|
||||||
|
sleep(20000)
|
||||||
|
println("Waited 60s")
|
||||||
|
post(postURL, "{\"response\": \"loggedin\"}")
|
||||||
|
}
|
||||||
|
if (action == "register") {
|
||||||
|
val data = pollResponse.getJSONObject("data")
|
||||||
|
val user = data.getString("username")
|
||||||
|
val pass = data.getString("password")
|
||||||
|
val homeserverURL = data.getJSONObject("homeserver_url").getString("local_docker")
|
||||||
|
elementRobot.register(homeserverURL, user, pass)
|
||||||
|
|
||||||
|
sleep(20000) // try to wait for cross signing to have kicked in...
|
||||||
|
println("Waited 20s")
|
||||||
|
sleep(20000)
|
||||||
|
println("Waited 40s")
|
||||||
|
sleep(20000)
|
||||||
|
println("Waited 60s")
|
||||||
|
|
||||||
|
post(postURL, "{\"response\": \"registered\"}")
|
||||||
|
}
|
||||||
|
if (action == "idle") {
|
||||||
|
val data = pollResponse.getJSONObject("data")
|
||||||
|
val delay = data.getLong("delay")
|
||||||
|
sleep(delay)
|
||||||
|
}
|
||||||
|
// client will be told to start OR accept cross signing request
|
||||||
|
if (action == "start_crosssign") {
|
||||||
|
elementRobot.startVerification()
|
||||||
|
post(postURL, "{\"response\": \"started_crosssign\"}")
|
||||||
|
}
|
||||||
|
if (action == "accept_crosssign") {
|
||||||
|
elementRobot.acceptVerification()
|
||||||
|
post(postURL, "{\"response\": \"accepted_crosssign\"}")
|
||||||
|
}
|
||||||
|
// Both clients will be told to verify the cross sign
|
||||||
|
if (action == "verify_crosssign_emoji") {
|
||||||
|
elementRobot.completeVerification()
|
||||||
|
post(postURL, "{\"response\": \"verified_crosssign\"}")
|
||||||
|
}
|
||||||
|
// exit test
|
||||||
|
if (action == "exit") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sleep(500) // provide a minimum delay between polls, prevent tightly spinning loops
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val JSON: MediaType = "application/json".toMediaType()
|
||||||
|
|
||||||
|
private fun post(url: String, json: String): String {
|
||||||
|
val body: RequestBody = json.toRequestBody(JSON)
|
||||||
|
val request: Request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(body)
|
||||||
|
.build()
|
||||||
|
client.newCall(request).execute().use { response -> return response.body!!.string() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun poll(url: String): JSONObject {
|
||||||
|
val request: Request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
client.newCall(request).execute().use { response -> return JSONObject(response.body!!.string()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun register(url: String): JSONObject {
|
||||||
|
val json = "{ \"type\": \"element-android\", \"version\":\"0.whatever.1\"}"
|
||||||
|
|
||||||
|
val body: RequestBody = json.toRequestBody("application/json".toMediaType())
|
||||||
|
val request: Request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(body)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
client.newCall(request).execute().use { response -> return JSONObject(response.body!!.string()) }
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.ui.robot
|
||||||
|
|
||||||
|
import androidx.test.espresso.PerformException
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withChild
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
|
import com.adevinta.android.barista.interaction.BaristaClickInteractions
|
||||||
|
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
|
||||||
|
import com.adevinta.android.barista.interaction.BaristaDrawerInteractions
|
||||||
|
import com.adevinta.android.barista.interaction.BaristaListInteractions.clickListItem
|
||||||
|
import com.adevinta.android.barista.internal.performAction
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.espresso.tools.clickOnPreference
|
||||||
|
import im.vector.app.espresso.tools.waitUntilViewVisible
|
||||||
|
import im.vector.app.waitForView
|
||||||
|
import im.vector.app.withRetry
|
||||||
|
import org.hamcrest.core.AllOf
|
||||||
|
|
||||||
|
class CryptoRobot {
|
||||||
|
|
||||||
|
// Do this if we don't get the popup in time; but actually; just wait for the pop-up
|
||||||
|
fun manualVerification() {
|
||||||
|
// Settings
|
||||||
|
BaristaDrawerInteractions.openDrawer()
|
||||||
|
clickOn(R.id.homeDrawerHeaderSettingsView)
|
||||||
|
BaristaClickInteractions.clickOn(R.string.settings_security_and_privacy)
|
||||||
|
clickOnPreference(R.string.settings_active_sessions_show_all)
|
||||||
|
clickListItem(R.id.genericRecyclerView, 3) // the first remote client.
|
||||||
|
AllOf.allOf(withId(R.id.itemVerificationClickableZone), withChild(withText(R.string.verification_verify_device))).performAction(click())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startVerification() {
|
||||||
|
// These are kinda async popups, be somewhat lenient with them.
|
||||||
|
withRetry {
|
||||||
|
waitUntilViewVisible(withText(R.string.crosssigning_verify_this_session))
|
||||||
|
clickOn(R.string.crosssigning_verify_this_session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun acceptVerification() {
|
||||||
|
// This is somewhat async; be lenient
|
||||||
|
withRetry {
|
||||||
|
waitUntilViewVisible(withText(R.string.verification_request))
|
||||||
|
clickOn(R.string.verification_request)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
withRetry {
|
||||||
|
waitUntilViewVisible(withText(R.string.verification_scan_emoji_title))
|
||||||
|
clickOn(R.string.verification_scan_emoji_title)
|
||||||
|
}
|
||||||
|
} catch (e: PerformException) {
|
||||||
|
// Ignore...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun completeVerification() {
|
||||||
|
waitForView(withText(R.string.verification_emoji_notice))
|
||||||
|
clickOn(R.string.verification_sas_match)
|
||||||
|
waitForView(withText(R.string.verification_conclusion_ok_self_notice))
|
||||||
|
waitForView(withText(R.string.done))
|
||||||
|
clickOn(R.string.done)
|
||||||
|
}
|
||||||
|
}
|
@@ -66,6 +66,38 @@ class ElementRobot {
|
|||||||
waitForHome()
|
waitForHome()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used for michaelk testing
|
||||||
|
fun login(homeserverURL: String, userId: String, password: String) {
|
||||||
|
val onboardingRobot = OnboardingRobot()
|
||||||
|
onboardingRobot.login(userId = userId, password = password, homeServerUrl = homeserverURL)
|
||||||
|
val analyticsRobot = AnalyticsRobot() // The application stores the analytics state on first open
|
||||||
|
analyticsRobot.optOut()
|
||||||
|
waitForHome()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for michaelk testing
|
||||||
|
fun register(homeserverURL: String, userId: String, password: String) {
|
||||||
|
onboarding { createAccount(userId, password, homeServerUrl = homeserverURL) }
|
||||||
|
val analyticsRobot = AnalyticsRobot()
|
||||||
|
analyticsRobot.optOut()
|
||||||
|
waitForHome()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startVerification() {
|
||||||
|
val cryptoRobot = CryptoRobot()
|
||||||
|
cryptoRobot.startVerification()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun acceptVerification() {
|
||||||
|
val cryptoRobot = CryptoRobot()
|
||||||
|
cryptoRobot.acceptVerification()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun completeVerification() {
|
||||||
|
val cryptoRobot = CryptoRobot()
|
||||||
|
cryptoRobot.completeVerification()
|
||||||
|
}
|
||||||
|
|
||||||
private fun waitForHome() {
|
private fun waitForHome() {
|
||||||
waitUntilActivityVisible<HomeActivity> {
|
waitUntilActivityVisible<HomeActivity> {
|
||||||
waitUntilViewVisible(withId(R.id.roomListContainer))
|
waitUntilViewVisible(withId(R.id.roomListContainer))
|
||||||
|
Reference in New Issue
Block a user