Added Self verification UI test
This commit is contained in:
parent
8eda089edc
commit
419673675c
|
@ -194,7 +194,7 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
val megolmBackupCreationInfo = bobKeysBackupService.prepareKeysBackupVersion(keyBackupPassword, null)
|
||||
val version = bobKeysBackupService.createKeysBackupVersion(megolmBackupCreationInfo)
|
||||
|
||||
Log.v("#E2E TEST", "... Key backup started and enabled for bob")
|
||||
Log.v("#E2E TEST", "... Key backup started and enabled for bob: version:$version")
|
||||
// Bob session should now have
|
||||
|
||||
val aliceRoomPOV = aliceSession.getRoom(e2eRoomID)!!
|
||||
|
@ -230,6 +230,7 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
fail("All keys should be backedup")
|
||||
}
|
||||
) {
|
||||
Log.v("#E2E TEST", "backedUp=${ bobKeysBackupService.getTotalNumbersOfBackedUpKeys()}, known=${bobKeysBackupService.getTotalNumbersOfKeys()}")
|
||||
bobKeysBackupService.getTotalNumbersOfBackedUpKeys() == bobKeysBackupService.getTotalNumbersOfKeys()
|
||||
}
|
||||
Log.v("#E2E TEST", "... Key backup done for Bob")
|
||||
|
@ -268,7 +269,7 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
) // MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID)
|
||||
|
||||
// Let's now import keys from backup
|
||||
|
||||
Log.v("#E2E TEST", "Restore backup for the new session")
|
||||
newBobSession.cryptoService().keysBackupService().let { kbs ->
|
||||
val keyVersionResult = kbs.getVersion(version.version)
|
||||
|
||||
|
@ -284,9 +285,12 @@ class E2eeSanityTests : InstrumentedTest {
|
|||
}
|
||||
|
||||
// ensure bob can now decrypt
|
||||
|
||||
Log.v("#E2E TEST", "Check that bob can decrypt now")
|
||||
cryptoTestHelper.ensureCanDecrypt(sentEventIds, newBobSession, e2eRoomID, messagesText)
|
||||
|
||||
// Check key trust
|
||||
Log.v("#E2E TEST", "Check key safety")
|
||||
sentEventIds.forEach { sentEventId ->
|
||||
val timelineEvent = newBobSession.getRoom(e2eRoomID)?.getTimelineEvent(sentEventId)!!
|
||||
val result = newBobSession.cryptoService().decryptEvent(timelineEvent.root, "")
|
||||
|
|
|
@ -296,6 +296,8 @@ internal abstract class SessionModule {
|
|||
var uri = sessionParams.homeServerConnectionConfig.homeServerUriBase.toString()
|
||||
if (uri == "http://localhost:8080") {
|
||||
uri = "http://10.0.2.2:8080"
|
||||
} else if (uri == "http://localhost:8081") {
|
||||
uri = "http://10.0.2.2:8081"
|
||||
}
|
||||
return retrofitFactory
|
||||
.create(okHttpClient, uri)
|
||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.app
|
|||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.Observer
|
||||
import im.vector.app.ui.robot.ElementRobot
|
||||
import im.vector.app.ui.robot.OnboardingRobot
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -41,6 +42,7 @@ abstract class VerificationTestBase {
|
|||
val homeServerUrl: String = "http://10.0.2.2:8080"
|
||||
|
||||
protected val uiTestBase = OnboardingRobot()
|
||||
protected val elementRobot = ElementRobot()
|
||||
|
||||
fun createAccountAndSync(
|
||||
matrix: Matrix,
|
||||
|
@ -114,9 +116,10 @@ abstract class VerificationTestBase {
|
|||
private fun syncSession(session: Session) {
|
||||
val lock = CountDownLatch(1)
|
||||
|
||||
GlobalScope.launch(Dispatchers.Main) { session.open() }
|
||||
|
||||
session.syncService().startSync(true)
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
session.open()
|
||||
session.syncService().startSync(true)
|
||||
}
|
||||
|
||||
val syncLiveData = runBlocking(Dispatchers.Main) {
|
||||
session.syncService().getSyncStateLive()
|
||||
|
|
|
@ -19,10 +19,8 @@ package im.vector.app
|
|||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.IdlingRegistry
|
||||
import androidx.test.espresso.IdlingResource
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem
|
||||
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
|
||||
|
@ -33,19 +31,25 @@ import androidx.test.espresso.matcher.ViewMatchers.withText
|
|||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.LargeTest
|
||||
import com.adevinta.android.barista.internal.viewaction.SleepViewAction
|
||||
import im.vector.app.core.utils.getMatrixInstance
|
||||
import im.vector.app.espresso.tools.waitUntilActivityVisible
|
||||
import im.vector.app.espresso.tools.waitUntilViewVisible
|
||||
import im.vector.app.features.MainActivity
|
||||
import im.vector.app.features.home.HomeActivity
|
||||
import im.vector.app.ui.robot.AnalyticsRobot
|
||||
import im.vector.app.ui.robot.ElementRobot
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import org.amshove.kluent.internal.assertEquals
|
||||
import org.hamcrest.CoreMatchers.not
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -54,9 +58,11 @@ import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
|||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.SasTransactionState
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.getRequest
|
||||
import org.matrix.android.sdk.api.session.crypto.verification.getTransaction
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
@ -64,7 +70,6 @@ import kotlin.random.Random
|
|||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
@Ignore
|
||||
class VerifySessionInteractiveTest : VerificationTestBase() {
|
||||
|
||||
var existingSession: Session? = null
|
||||
|
@ -72,6 +77,8 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
|
|||
@get:Rule
|
||||
val activityRule = ActivityScenarioRule(MainActivity::class.java)
|
||||
|
||||
private val testScope = CoroutineScope(SupervisorJob())
|
||||
|
||||
@Before
|
||||
fun createSessionWithCrossSigning() {
|
||||
val matrix = getMatrixInstance()
|
||||
|
@ -101,28 +108,23 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
|
|||
|
||||
uiTestBase.login(userId = userId, password = password, homeServerUrl = homeServerUrl)
|
||||
|
||||
// Thread.sleep(6000)
|
||||
withIdlingResource(activityIdlingResource(HomeActivity::class.java)) {
|
||||
onView(withId(R.id.roomListContainer))
|
||||
.check(matches(isDisplayed()))
|
||||
.perform(closeSoftKeyboard())
|
||||
}
|
||||
val analyticsRobot = AnalyticsRobot()
|
||||
analyticsRobot.optOut()
|
||||
|
||||
waitUntilActivityVisible<HomeActivity> {
|
||||
waitUntilViewVisible(withId(R.id.roomListContainer))
|
||||
}
|
||||
val activity = EspressoHelper.getCurrentActivity()!!
|
||||
val uiSession = (activity as HomeActivity).activeSessionHolder.getActiveSession()
|
||||
|
||||
withIdlingResource(initialSyncIdlingResource(uiSession)) {
|
||||
onView(withId(R.id.roomListContainer))
|
||||
.check(matches(isDisplayed()))
|
||||
waitUntilViewVisible(withId(R.id.roomListContainer))
|
||||
}
|
||||
|
||||
// THIS IS THE ONLY WAY I FOUND TO CLICK ON ALERTERS... :(
|
||||
// Cannot wait for view because of alerter animation? ...
|
||||
onView(isRoot())
|
||||
.perform(waitForView(withId(com.tapadoo.alerter.R.id.llAlertBackground)))
|
||||
// Thread.sleep(1000)
|
||||
// onView(withId(com.tapadoo.alerter.R.id.llAlertBackground))
|
||||
// .perform(click())
|
||||
|
||||
Thread.sleep(1000)
|
||||
val popup = activity.findViewById<View>(com.tapadoo.alerter.R.id.llAlertBackground)
|
||||
activity.runOnUiThread {
|
||||
|
@ -131,74 +133,105 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
|
|||
|
||||
onView(isRoot())
|
||||
.perform(waitForView(withId(R.id.bottomSheetFragmentContainer)))
|
||||
// .check()
|
||||
// onView(withId(R.id.bottomSheetFragmentContainer))
|
||||
// .check(matches(isDisplayed()))
|
||||
|
||||
// onView(isRoot()).perform(SleepViewAction.sleep(2000))
|
||||
|
||||
onView(withText(R.string.use_latest_app))
|
||||
onView(withText(R.string.verification_verify_identity))
|
||||
.check(matches(isDisplayed()))
|
||||
|
||||
// 4S is not setup so passphrase option should be hidden
|
||||
onView(withId(R.id.bottomSheetFragmentContainer))
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.check(matches(not(hasDescendant(withText(R.string.verification_cannot_access_other_session)))))
|
||||
|
||||
val request = runBlockingTest {
|
||||
existingSession!!.cryptoService().verificationService().requestSelfKeyVerification(
|
||||
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW),
|
||||
)
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.check(matches(hasDescendant(withText(R.string.verification_verify_with_another_device))))
|
||||
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.check(matches(hasDescendant(withText(R.string.bad_passphrase_key_reset_all_action))))
|
||||
|
||||
val otherRequest = CompletableDeferred<PendingVerificationRequest>()
|
||||
|
||||
testScope.launch {
|
||||
existingSession!!.cryptoService().verificationService().requestEventFlow().collect {
|
||||
if (it.getRequest() != null) {
|
||||
otherRequest.complete(it.getRequest()!!)
|
||||
return@collect cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val transactionId = request.transactionId
|
||||
val sasReadyIdle = verificationStateIdleResource(transactionId, SasTransactionState.SasShortCodeReady, uiSession)
|
||||
val otherSessionSasReadyIdle = verificationStateIdleResource(transactionId, SasTransactionState.SasShortCodeReady, existingSession!!)
|
||||
|
||||
onView(isRoot()).perform(SleepViewAction.sleep(1000))
|
||||
|
||||
// Assert QR code option is there and available
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.check(matches(hasDescendant(withText(R.string.verification_scan_their_code))))
|
||||
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.check(matches(hasDescendant(withId(R.id.itemVerificationQrCodeImage))))
|
||||
|
||||
// Send out a self verification request
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.perform(
|
||||
actionOnItem<RecyclerView.ViewHolder>(
|
||||
hasDescendant(withText(R.string.verification_scan_emoji_title)),
|
||||
hasDescendant(withText(R.string.verification_verify_with_another_device)),
|
||||
click()
|
||||
)
|
||||
)
|
||||
|
||||
val firstSessionTr = runBlockingTest {
|
||||
existingSession!!.cryptoService().verificationService().getExistingTransaction(
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.check(matches(hasDescendant(withText(R.string.verification_request_was_sent))))
|
||||
|
||||
val txId = runBlockingTest {
|
||||
otherRequest.await().transactionId
|
||||
}
|
||||
|
||||
// accept from other session
|
||||
runBlockingTest {
|
||||
existingSession!!.cryptoService().verificationService().readyPendingVerification(
|
||||
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW),
|
||||
existingSession!!.myUserId,
|
||||
transactionId
|
||||
) as SasVerificationTransaction
|
||||
txId
|
||||
)
|
||||
}
|
||||
|
||||
IdlingRegistry.getInstance().register(sasReadyIdle)
|
||||
IdlingRegistry.getInstance().register(otherSessionSasReadyIdle)
|
||||
onView(isRoot()).perform(SleepViewAction.sleep(300))
|
||||
// will only execute when Idle is ready
|
||||
val expectedEmojis = firstSessionTr.getEmojiCodeRepresentation()
|
||||
val targets = listOf(R.id.emoji0, R.id.emoji1, R.id.emoji2, R.id.emoji3, R.id.emoji4, R.id.emoji5, R.id.emoji6)
|
||||
targets.forEachIndexed { index, res ->
|
||||
onView(withId(res))
|
||||
.check(
|
||||
matches(hasDescendant(withText(expectedEmojis[index].nameResId)))
|
||||
)
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.perform(waitForView(hasDescendant(withText(R.string.verification_scan_self_notice))))
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.perform(waitForView(hasDescendant(withText(R.string.verification_scan_self_emoji_subtitle))))
|
||||
|
||||
// there should be the QR code also
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.check(matches(hasDescendant(withId(R.id.itemVerificationQrCodeImage))))
|
||||
|
||||
// proceed with emoji
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.perform(
|
||||
actionOnItem<RecyclerView.ViewHolder>(
|
||||
hasDescendant(withText(R.string.verification_scan_self_emoji_subtitle)),
|
||||
click()
|
||||
)
|
||||
)
|
||||
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.perform(waitForView(hasDescendant(withText(R.string.verification_sas_do_not_match))))
|
||||
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.perform(waitForView(hasDescendant(withText(R.string.verification_sas_match))))
|
||||
|
||||
// check that the code matches
|
||||
val uiCode = runBlockingTest {
|
||||
(uiSession.cryptoService()
|
||||
.verificationService()
|
||||
.getExistingTransaction(uiSession.myUserId, txId) as SasVerificationTransaction)
|
||||
.getDecimalCodeRepresentation()!!
|
||||
}
|
||||
|
||||
IdlingRegistry.getInstance().unregister(sasReadyIdle)
|
||||
IdlingRegistry.getInstance().unregister(otherSessionSasReadyIdle)
|
||||
val backgroundCode = runBlockingTest {
|
||||
(existingSession!!.cryptoService()
|
||||
.verificationService()
|
||||
.getExistingTransaction(uiSession.myUserId, txId) as SasVerificationTransaction)
|
||||
.getDecimalCodeRepresentation()!!
|
||||
}
|
||||
|
||||
val verificationSuccessIdle =
|
||||
verificationStateIdleResource(transactionId, SasTransactionState.Done(true), uiSession)
|
||||
assertEquals("SAS code should be equals", backgroundCode, uiCode)
|
||||
|
||||
// CLICK ON THEY MATCH
|
||||
runBlockingTest {
|
||||
(existingSession!!.cryptoService()
|
||||
.verificationService()
|
||||
.getExistingTransaction(uiSession.myUserId, txId) as SasVerificationTransaction)
|
||||
.userHasVerifiedShortCode()
|
||||
}
|
||||
|
||||
// Do the same on ui
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.perform(
|
||||
actionOnItem<RecyclerView.ViewHolder>(
|
||||
|
@ -207,23 +240,10 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
|
|||
)
|
||||
)
|
||||
|
||||
runBlockingTest {
|
||||
firstSessionTr.userHasVerifiedShortCode()
|
||||
}
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.perform(waitForView(hasDescendant(withText(R.string.verification_conclusion_ok_notice))))
|
||||
|
||||
onView(isRoot()).perform(SleepViewAction.sleep(1000))
|
||||
|
||||
withIdlingResource(verificationSuccessIdle) {
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.check(
|
||||
matches(hasDescendant(withText(R.string.verification_conclusion_ok_self_notice)))
|
||||
)
|
||||
}
|
||||
|
||||
// Wait a bit before done (to delay a bit sending of secrets to let other have time
|
||||
// to mark as verified :/
|
||||
Thread.sleep(5_000)
|
||||
// Click on done
|
||||
// click on done
|
||||
onView(withId(R.id.bottomSheetVerificationRecyclerView))
|
||||
.perform(
|
||||
actionOnItem<RecyclerView.ViewHolder>(
|
||||
|
@ -232,11 +252,14 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
|
|||
)
|
||||
)
|
||||
|
||||
// Wait until local secrets are known (gossip)
|
||||
withIdlingResource(allSecretsKnownIdling(uiSession)) {
|
||||
onView(withId(R.id.roomListContainer))
|
||||
.check(matches(isDisplayed()))
|
||||
}
|
||||
Thread.sleep(1_000)
|
||||
|
||||
// check that current session is actually trusted
|
||||
assertTrue("I should be verified",
|
||||
runBlockingTest { uiSession.cryptoService().crossSigningService().isCrossSigningVerified() }
|
||||
)
|
||||
|
||||
ElementRobot().signout(false)
|
||||
}
|
||||
|
||||
fun signout() {
|
||||
|
|
Loading…
Reference in New Issue