From fb451282fe1e0dfa0b4a80e66a56af51b5ceb031 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 22 Feb 2024 08:57:12 +0100 Subject: [PATCH 1/8] Use method references, and avoid using `navigateUp()`: let the parent Node decide how to navigate. --- .../io/element/android/appnav/LoggedInFlowNode.kt | 4 ++++ .../verifysession/api/VerifySessionEntryPoint.kt | 1 + .../verifysession/impl/VerifySelfSessionNode.kt | 10 ++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index db75acff64..e4290d5bdf 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -338,6 +338,10 @@ class LoggedInFlowNode @AssistedInject constructor( ) ) } + + override fun onDone() { + backstack.pop() + } } verifySessionEntryPoint .nodeBuilder(this, buildContext) diff --git a/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt index 5eb1bc8daa..8d19ca5698 100644 --- a/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt +++ b/features/verifysession/api/src/main/kotlin/io/element/android/features/verifysession/api/VerifySessionEntryPoint.kt @@ -31,5 +31,6 @@ interface VerifySessionEntryPoint : FeatureEntryPoint { interface Callback : Plugin { fun onEnterRecoveryKey() + fun onDone() } } diff --git a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt index 3aa75f0c26..cc97faa6e3 100644 --- a/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt +++ b/features/verifysession/impl/src/main/kotlin/io/element/android/features/verifysession/impl/VerifySelfSessionNode.kt @@ -40,14 +40,20 @@ class VerifySelfSessionNode @AssistedInject constructor( } } + private fun onDone() { + plugins().forEach { + it.onDone() + } + } + @Composable override fun View(modifier: Modifier) { val state = presenter.present() VerifySelfSessionView( state = state, modifier = modifier, - onEnterRecoveryKey = { onEnterRecoveryKey() }, - goBack = { navigateUp() } + onEnterRecoveryKey = ::onEnterRecoveryKey, + goBack = ::onDone, ) } } From 229ee205d80300ee38dad357d89433abb13cbb06 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 22 Feb 2024 09:11:08 +0100 Subject: [PATCH 2/8] Use `mxCallbackFlow` --- .../libraries/matrix/impl/RustMatrixClient.kt | 2 +- .../impl/encryption/RustEncryptionService.kt | 39 ++++++++----------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index e642813e3b..521d26f062 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -138,7 +138,7 @@ class RustMatrixClient( syncService = rustSyncService, sessionCoroutineScope = sessionCoroutineScope, dispatchers = dispatchers, - ).apply { start() } + ) private val sessionDirectoryNameProvider = SessionDirectoryNameProvider() private val isLoggingOut = AtomicBoolean(false) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt index 84e9fb2f14..fda22122fd 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt @@ -25,7 +25,7 @@ import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.impl.sync.RustSyncService -import io.element.android.libraries.matrix.impl.util.cancelAndDestroy +import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.currentCoroutineContext @@ -46,7 +46,6 @@ import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.EnableRecoveryProgressListener import org.matrix.rustcomponents.sdk.Encryption import org.matrix.rustcomponents.sdk.RecoveryStateListener -import org.matrix.rustcomponents.sdk.TaskHandle import org.matrix.rustcomponents.sdk.BackupState as RustBackupState import org.matrix.rustcomponents.sdk.BackupUploadState as RustBackupUploadState import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecoveryProgress @@ -66,10 +65,15 @@ internal class RustEncryptionService( private val enableRecoveryProgressMapper = EnableRecoveryProgressMapper() private val backupUploadStateMapper = BackupUploadStateMapper() private val steadyStateExceptionMapper = SteadyStateExceptionMapper() - private var backupStateListenerTaskHandle: TaskHandle? = null - private var recoveryStateListenerTaskHandle: TaskHandle? = null - private val backupStateFlow = MutableStateFlow(service.backupState().let(backupStateMapper::map)) + private val backupStateFlow = mxCallbackFlow { + val listener = object : BackupStateListener { + override fun onUpdate(status: RustBackupState) { + trySend(backupStateMapper.map(status)) + } + } + service.backupStateListener(listener) + }.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, service.backupState().let(backupStateMapper::map)) override val backupStateStateFlow = combine( backupStateFlow, @@ -82,7 +86,14 @@ internal class RustEncryptionService( } }.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, BackupState.WAITING_FOR_SYNC) - private val recoveryStateFlow: MutableStateFlow = MutableStateFlow(service.recoveryState().let(recoveryStateMapper::map)) + private val recoveryStateFlow = mxCallbackFlow { + val listener = object : RecoveryStateListener { + override fun onUpdate(status: RustRecoveryState) { + trySend(recoveryStateMapper.map(status)) + } + } + service.recoveryStateListener(listener) + }.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, service.recoveryState().let(recoveryStateMapper::map)) override val recoveryStateStateFlow = combine( recoveryStateFlow, @@ -111,23 +122,7 @@ internal class RustEncryptionService( } .stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false) - fun start() { - backupStateListenerTaskHandle = service.backupStateListener(object : BackupStateListener { - override fun onUpdate(status: RustBackupState) { - backupStateFlow.value = backupStateMapper.map(status) - } - }) - - recoveryStateListenerTaskHandle = service.recoveryStateListener(object : RecoveryStateListener { - override fun onUpdate(status: RustRecoveryState) { - recoveryStateFlow.value = recoveryStateMapper.map(status) - } - }) - } - fun destroy() { - backupStateListenerTaskHandle?.cancelAndDestroy() - recoveryStateListenerTaskHandle?.cancelAndDestroy() service.destroy() } From 25ba857da6cd9c48f78132b006f746c77b567db8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 22 Feb 2024 09:24:45 +0100 Subject: [PATCH 3/8] Add RECOVERY_KEY to Maestro env variable. --- .github/workflows/maestro.yml | 1 + .maestro/README.md | 1 + .maestro/scripts/checkEnv.js | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index 54889c58b7..d6f56cf79a 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -57,6 +57,7 @@ jobs: env: | USERNAME=maestroelement PASSWORD=${{ secrets.MATRIX_MAESTRO_ACCOUNT_PASSWORD }} + RECOVERY_KEY=${{ secrets.MATRIX_MAESTRO_ACCOUNT_RECOVERY_KEY }} ROOM_NAME=MyRoom INVITEE1_MXID=@maestroelement2:matrix.org INVITEE2_MXID=@maestroelement3:matrix.org diff --git a/.maestro/README.md b/.maestro/README.md index 9d9090744c..aa667c81c8 100644 --- a/.maestro/README.md +++ b/.maestro/README.md @@ -25,6 +25,7 @@ maestro test \ -e APP_ID=io.element.android.x.debug \ -e USERNAME=user1 \ -e PASSWORD=123 \ + -e RECOVERY_KEY=ABC \ -e ROOM_NAME="MyRoom" \ -e INVITEE1_MXID=user2 \ -e INVITEE2_MXID=user3 \ diff --git a/.maestro/scripts/checkEnv.js b/.maestro/scripts/checkEnv.js index 74b4b56956..638f4c5232 100644 --- a/.maestro/scripts/checkEnv.js +++ b/.maestro/scripts/checkEnv.js @@ -4,6 +4,7 @@ if (APP_ID == null) throw "Fatal: missing env variable APP_ID" if (USERNAME == null) throw "Fatal: missing env variable USERNAME" if (PASSWORD == null) throw "Fatal: missing env variable PASSWORD" +if (RECOVERY_KEY == null) throw "Fatal: missing env variable RECOVERY_KEY" if (ROOM_NAME == null) throw "Fatal: missing env variable ROOM_NAME" if (INVITEE1_MXID == null) throw "Fatal: missing env variable INVITEE1_MXID" if (INVITEE2_MXID == null) throw "Fatal: missing env variable INVITEE2_MXID" From 03c8a140934de5e37e53f1868ce73eaa54ee391e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 22 Feb 2024 09:29:05 +0100 Subject: [PATCH 4/8] Add prefix `MAESTRO_` to Maestro env variable. --- .github/workflows/maestro.yml | 14 +++++++------- .maestro/README.md | 14 +++++++------- .maestro/allTests.yaml | 2 +- .maestro/scripts/checkEnv.js | 14 +++++++------- .maestro/tests/account/changeServer.yaml | 2 +- .maestro/tests/account/login.yaml | 6 +++--- .maestro/tests/account/logout.yaml | 2 +- .../tests/assertions/assertAnalyticsDisplayed.yaml | 2 +- .maestro/tests/assertions/assertHomeDisplayed.yaml | 2 +- .maestro/tests/assertions/assertInitDisplayed.yaml | 2 +- .../tests/assertions/assertLoginDisplayed.yaml | 2 +- .../tests/assertions/assertRoomListSynced.yaml | 4 ++-- .../assertions/assertWelcomeScreenDisplayed.yaml | 2 +- .maestro/tests/init.yaml | 2 +- .maestro/tests/roomList/createAndDeleteDM.yaml | 6 +++--- .maestro/tests/roomList/createAndDeleteRoom.yaml | 10 +++++----- .maestro/tests/roomList/roomContextMenu.yaml | 6 +++--- .maestro/tests/roomList/roomList.yaml | 2 +- .maestro/tests/roomList/searchRoomList.yaml | 6 +++--- .../tests/roomList/timeline/messages/location.yaml | 2 +- .../tests/roomList/timeline/messages/poll.yaml | 2 +- .../tests/roomList/timeline/messages/text.yaml | 2 +- .maestro/tests/roomList/timeline/timeline.yaml | 4 ++-- .maestro/tests/settings/settings.yaml | 2 +- 24 files changed, 56 insertions(+), 56 deletions(-) diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index d6f56cf79a..23b5fc12a0 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -55,10 +55,10 @@ jobs: # app-file should point to an x86 compatible APK file, so upload the x86_64 one (much smaller than the universal APK). app-file: app/build/outputs/apk/gplay/debug/app-gplay-x86_64-debug.apk env: | - USERNAME=maestroelement - PASSWORD=${{ secrets.MATRIX_MAESTRO_ACCOUNT_PASSWORD }} - RECOVERY_KEY=${{ secrets.MATRIX_MAESTRO_ACCOUNT_RECOVERY_KEY }} - ROOM_NAME=MyRoom - INVITEE1_MXID=@maestroelement2:matrix.org - INVITEE2_MXID=@maestroelement3:matrix.org - APP_ID=io.element.android.x.debug + MAESTRO_USERNAME=maestroelement + MAESTRO_PASSWORD=${{ secrets.MATRIX_MAESTRO_ACCOUNT_PASSWORD }} + MAESTRO_RECOVERY_KEY=${{ secrets.MATRIX_MAESTRO_ACCOUNT_RECOVERY_KEY }} + MAESTRO_ROOM_NAME=MyRoom + MAESTRO_INVITEE1_MXID=@maestroelement2:matrix.org + MAESTRO_INVITEE2_MXID=@maestroelement3:matrix.org + MAESTRO_APP_ID=io.element.android.x.debug diff --git a/.maestro/README.md b/.maestro/README.md index aa667c81c8..c0ee525ac7 100644 --- a/.maestro/README.md +++ b/.maestro/README.md @@ -22,13 +22,13 @@ From root dir of the project ```shell maestro test \ - -e APP_ID=io.element.android.x.debug \ - -e USERNAME=user1 \ - -e PASSWORD=123 \ - -e RECOVERY_KEY=ABC \ - -e ROOM_NAME="MyRoom" \ - -e INVITEE1_MXID=user2 \ - -e INVITEE2_MXID=user3 \ + -e MAESTRO_APP_ID=io.element.android.x.debug \ + -e MAESTRO_USERNAME=user1 \ + -e MAESTRO_PASSWORD=123 \ + -e MAESTRO_RECOVERY_KEY=ABC \ + -e MAESTRO_ROOM_NAME="MyRoom" \ + -e MAESTRO_INVITEE1_MXID=user2 \ + -e MAESTRO_INVITEE2_MXID=user3 \ .maestro/allTests.yaml ``` diff --git a/.maestro/allTests.yaml b/.maestro/allTests.yaml index cd3ca342e2..927a1cb0e5 100644 --- a/.maestro/allTests.yaml +++ b/.maestro/allTests.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- ## Check that all env variables required in the whole test suite are declared (to fail faster) - runScript: ./scripts/checkEnv.js diff --git a/.maestro/scripts/checkEnv.js b/.maestro/scripts/checkEnv.js index 638f4c5232..fa61d3c763 100644 --- a/.maestro/scripts/checkEnv.js +++ b/.maestro/scripts/checkEnv.js @@ -1,10 +1,10 @@ // This array contains all the required environment variable. When adding a variable, add it here also. // If a variable is missing, an error will occur. -if (APP_ID == null) throw "Fatal: missing env variable APP_ID" -if (USERNAME == null) throw "Fatal: missing env variable USERNAME" -if (PASSWORD == null) throw "Fatal: missing env variable PASSWORD" -if (RECOVERY_KEY == null) throw "Fatal: missing env variable RECOVERY_KEY" -if (ROOM_NAME == null) throw "Fatal: missing env variable ROOM_NAME" -if (INVITEE1_MXID == null) throw "Fatal: missing env variable INVITEE1_MXID" -if (INVITEE2_MXID == null) throw "Fatal: missing env variable INVITEE2_MXID" +if (MAESTRO_APP_ID == null) throw "Fatal: missing env variable MAESTRO_APP_ID" +if (MAESTRO_USERNAME == null) throw "Fatal: missing env variable MAESTRO_USERNAME" +if (MAESTRO_PASSWORD == null) throw "Fatal: missing env variable MAESTRO_PASSWORD" +if (MAESTRO_RECOVERY_KEY == null) throw "Fatal: missing env variable MAESTRO_RECOVERY_KEY" +if (MAESTRO_ROOM_NAME == null) throw "Fatal: missing env variable MAESTRO_ROOM_NAME" +if (MAESTRO_INVITEE1_MXID == null) throw "Fatal: missing env variable MAESTRO_INVITEE1_MXID" +if (MAESTRO_INVITEE2_MXID == null) throw "Fatal: missing env variable MAESTRO_INVITEE2_MXID" diff --git a/.maestro/tests/account/changeServer.yaml b/.maestro/tests/account/changeServer.yaml index 1971e5a71b..7ae4c3450f 100644 --- a/.maestro/tests/account/changeServer.yaml +++ b/.maestro/tests/account/changeServer.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - tapOn: id: "login-change_server" diff --git a/.maestro/tests/account/login.yaml b/.maestro/tests/account/login.yaml index 6126e34459..b2abb5da13 100644 --- a/.maestro/tests/account/login.yaml +++ b/.maestro/tests/account/login.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - tapOn: "Continue" - runFlow: ../assertions/assertLoginDisplayed.yaml @@ -9,7 +9,7 @@ appId: ${APP_ID} id: "login-continue" - tapOn: id: "login-email_username" -- inputText: ${USERNAME} +- inputText: ${MAESTRO_USERNAME} - pressKey: Enter - tapOn: id: "login-password" @@ -20,7 +20,7 @@ appId: ${APP_ID} - tapOn: id: "login-password" - eraseText: 20 -- inputText: ${PASSWORD} +- inputText: ${MAESTRO_PASSWORD} - pressKey: Enter - tapOn: "Continue" - runFlow: ../assertions/assertWelcomeScreenDisplayed.yaml diff --git a/.maestro/tests/account/logout.yaml b/.maestro/tests/account/logout.yaml index 3019f1d2c3..f27f5dada3 100644 --- a/.maestro/tests/account/logout.yaml +++ b/.maestro/tests/account/logout.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - tapOn: id: "home_screen-settings" diff --git a/.maestro/tests/assertions/assertAnalyticsDisplayed.yaml b/.maestro/tests/assertions/assertAnalyticsDisplayed.yaml index 9c63c99ffc..516dcc86ea 100644 --- a/.maestro/tests/assertions/assertAnalyticsDisplayed.yaml +++ b/.maestro/tests/assertions/assertAnalyticsDisplayed.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - extendedWaitUntil: visible: "Help improve Element X dbg" diff --git a/.maestro/tests/assertions/assertHomeDisplayed.yaml b/.maestro/tests/assertions/assertHomeDisplayed.yaml index ca409705e1..40b6dc3e67 100644 --- a/.maestro/tests/assertions/assertHomeDisplayed.yaml +++ b/.maestro/tests/assertions/assertHomeDisplayed.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - extendedWaitUntil: visible: "All Chats" diff --git a/.maestro/tests/assertions/assertInitDisplayed.yaml b/.maestro/tests/assertions/assertInitDisplayed.yaml index 9424f382c1..6e895d9bbf 100644 --- a/.maestro/tests/assertions/assertInitDisplayed.yaml +++ b/.maestro/tests/assertions/assertInitDisplayed.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - extendedWaitUntil: visible: "Be in your element" diff --git a/.maestro/tests/assertions/assertLoginDisplayed.yaml b/.maestro/tests/assertions/assertLoginDisplayed.yaml index b18078f916..6d8558c38e 100644 --- a/.maestro/tests/assertions/assertLoginDisplayed.yaml +++ b/.maestro/tests/assertions/assertLoginDisplayed.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - extendedWaitUntil: visible: "Change account provider" diff --git a/.maestro/tests/assertions/assertRoomListSynced.yaml b/.maestro/tests/assertions/assertRoomListSynced.yaml index 5fcd6e093e..0eb1c52ac2 100644 --- a/.maestro/tests/assertions/assertRoomListSynced.yaml +++ b/.maestro/tests/assertions/assertRoomListSynced.yaml @@ -1,5 +1,5 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - extendedWaitUntil: - visible: ${ROOM_NAME} + visible: ${MAESTRO_ROOM_NAME} timeout: 10000 diff --git a/.maestro/tests/assertions/assertWelcomeScreenDisplayed.yaml b/.maestro/tests/assertions/assertWelcomeScreenDisplayed.yaml index 3fbd9d2513..340d21ff2e 100644 --- a/.maestro/tests/assertions/assertWelcomeScreenDisplayed.yaml +++ b/.maestro/tests/assertions/assertWelcomeScreenDisplayed.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - extendedWaitUntil: visible: diff --git a/.maestro/tests/init.yaml b/.maestro/tests/init.yaml index acd5f86dfd..6cb056d96d 100644 --- a/.maestro/tests/init.yaml +++ b/.maestro/tests/init.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - clearState - launchApp: diff --git a/.maestro/tests/roomList/createAndDeleteDM.yaml b/.maestro/tests/roomList/createAndDeleteDM.yaml index 6c2ebed11e..604263fbda 100644 --- a/.maestro/tests/roomList/createAndDeleteDM.yaml +++ b/.maestro/tests/roomList/createAndDeleteDM.yaml @@ -1,11 +1,11 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- # Purpose: Test the creation and deletion of a DM room. - tapOn: "Create a new conversation or room" - tapOn: "Search for someone" -- inputText: ${INVITEE1_MXID} +- inputText: ${MAESTRO_INVITEE1_MXID} - tapOn: - text: ${INVITEE1_MXID} + text: ${MAESTRO_INVITEE1_MXID} index: 1 - takeScreenshot: build/maestro/330-createAndDeleteDM - tapOn: "maestroelement2" diff --git a/.maestro/tests/roomList/createAndDeleteRoom.yaml b/.maestro/tests/roomList/createAndDeleteRoom.yaml index 9fed7707dd..6061915493 100644 --- a/.maestro/tests/roomList/createAndDeleteRoom.yaml +++ b/.maestro/tests/roomList/createAndDeleteRoom.yaml @@ -1,12 +1,12 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- # Purpose: Test the creation and deletion of a room - tapOn: "Create a new conversation or room" - tapOn: "New room" - tapOn: "Search for someone" -- inputText: ${INVITEE1_MXID} +- inputText: ${MAESTRO_INVITEE1_MXID} - tapOn: - text: ${INVITEE1_MXID} + text: ${MAESTRO_INVITEE1_MXID} index: 1 - tapOn: "Next" - tapOn: "e.g. your project name" @@ -19,9 +19,9 @@ appId: ${APP_ID} - tapOn: "Invite people" # assert there's 1 member and 1 invitee - tapOn: "Search for someone" -- inputText: ${INVITEE2_MXID} +- inputText: ${MAESTRO_INVITEE2_MXID} - tapOn: - text: ${INVITEE2_MXID} + text: ${MAESTRO_INVITEE2_MXID} index: 1 - tapOn: "Invite" - tapOn: "Back" diff --git a/.maestro/tests/roomList/roomContextMenu.yaml b/.maestro/tests/roomList/roomContextMenu.yaml index c2a8764558..160f8a31f7 100644 --- a/.maestro/tests/roomList/roomContextMenu.yaml +++ b/.maestro/tests/roomList/roomContextMenu.yaml @@ -1,13 +1,13 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- # Purpose: Test the context menu of a room in the room list -- longPressOn: ${ROOM_NAME} +- longPressOn: ${MAESTRO_ROOM_NAME} - takeScreenshot: build/maestro/310-RoomList-ContextMenu - tapOn: text: "Settings" index: 0 - tapOn: "Back" -- longPressOn: ${ROOM_NAME} +- longPressOn: ${MAESTRO_ROOM_NAME} - tapOn: text: "Leave room" index: 0 diff --git a/.maestro/tests/roomList/roomList.yaml b/.maestro/tests/roomList/roomList.yaml index 6365759e72..5cc9e269c5 100644 --- a/.maestro/tests/roomList/roomList.yaml +++ b/.maestro/tests/roomList/roomList.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - runFlow: searchRoomList.yaml - takeScreenshot: build/maestro/300-RoomList diff --git a/.maestro/tests/roomList/searchRoomList.yaml b/.maestro/tests/roomList/searchRoomList.yaml index 5939c0bc5e..8b41c4d259 100644 --- a/.maestro/tests/roomList/searchRoomList.yaml +++ b/.maestro/tests/roomList/searchRoomList.yaml @@ -1,10 +1,10 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - runFlow: ../assertions/assertRoomListSynced.yaml - tapOn: "search" -- inputText: ${ROOM_NAME.substring(0, 3)} +- inputText: ${MAESTRO_ROOM_NAME.substring(0, 3)} - takeScreenshot: build/maestro/400-SearchRoom -- tapOn: ${ROOM_NAME} +- tapOn: ${MAESTRO_ROOM_NAME} # Back from timeline - back - assertVisible: "MyR" diff --git a/.maestro/tests/roomList/timeline/messages/location.yaml b/.maestro/tests/roomList/timeline/messages/location.yaml index 73dca6eeb4..c9382bd30c 100644 --- a/.maestro/tests/roomList/timeline/messages/location.yaml +++ b/.maestro/tests/roomList/timeline/messages/location.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - takeScreenshot: build/maestro/520-Timeline - tapOn: "Add attachment" diff --git a/.maestro/tests/roomList/timeline/messages/poll.yaml b/.maestro/tests/roomList/timeline/messages/poll.yaml index 65495dda60..c6fffebd7d 100644 --- a/.maestro/tests/roomList/timeline/messages/poll.yaml +++ b/.maestro/tests/roomList/timeline/messages/poll.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - takeScreenshot: build/maestro/530-Timeline - tapOn: "Add attachment" diff --git a/.maestro/tests/roomList/timeline/messages/text.yaml b/.maestro/tests/roomList/timeline/messages/text.yaml index 963b2cf9e9..6767886d8d 100644 --- a/.maestro/tests/roomList/timeline/messages/text.yaml +++ b/.maestro/tests/roomList/timeline/messages/text.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - takeScreenshot: build/maestro/510-Timeline - tapOn: diff --git a/.maestro/tests/roomList/timeline/timeline.yaml b/.maestro/tests/roomList/timeline/timeline.yaml index 1acb10a9aa..5f85366e9e 100644 --- a/.maestro/tests/roomList/timeline/timeline.yaml +++ b/.maestro/tests/roomList/timeline/timeline.yaml @@ -1,7 +1,7 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- # This is the name of one room -- tapOn: ${ROOM_NAME} +- tapOn: ${MAESTRO_ROOM_NAME} - takeScreenshot: build/maestro/500-Timeline - runFlow: messages/text.yaml - runFlow: messages/location.yaml diff --git a/.maestro/tests/settings/settings.yaml b/.maestro/tests/settings/settings.yaml index d5f1e110e5..c77d118a3b 100644 --- a/.maestro/tests/settings/settings.yaml +++ b/.maestro/tests/settings/settings.yaml @@ -1,4 +1,4 @@ -appId: ${APP_ID} +appId: ${MAESTRO_APP_ID} --- - tapOn: id: "home_screen-settings" From c85b716098dd0bbac2dc511b62f02d3832c7f4f0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 22 Feb 2024 09:53:12 +0100 Subject: [PATCH 5/8] Add Maestro test to verify the session using recovery key. --- .maestro/tests/account/login.yaml | 1 + .maestro/tests/account/verifySession.yaml | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 .maestro/tests/account/verifySession.yaml diff --git a/.maestro/tests/account/login.yaml b/.maestro/tests/account/login.yaml index b2abb5da13..397cc61f5c 100644 --- a/.maestro/tests/account/login.yaml +++ b/.maestro/tests/account/login.yaml @@ -28,3 +28,4 @@ appId: ${MAESTRO_APP_ID} - runFlow: ../assertions/assertAnalyticsDisplayed.yaml - tapOn: "Not now" - runFlow: ../assertions/assertHomeDisplayed.yaml +- runFlow: ./verifySession.yaml diff --git a/.maestro/tests/account/verifySession.yaml b/.maestro/tests/account/verifySession.yaml new file mode 100644 index 0000000000..479fd17462 --- /dev/null +++ b/.maestro/tests/account/verifySession.yaml @@ -0,0 +1,10 @@ +appId: ${MAESTRO_APP_ID} +--- +- tapOn: "Continue" +- takeScreenshot: build/maestro/150-Verify +- tapOn: "Enter recovery key" +- tapOn: "Enter…" +- inputText: ${MAESTRO_RECOVERY_KEY} +- hideKeyboard +- tapOn: "Confirm" +- runFlow: ../assertions/assertHomeDisplayed.yaml From 64db9fa173d079b1b75f7818006f6b180fa5e806 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 22 Feb 2024 10:25:47 +0100 Subject: [PATCH 6/8] Add TestTag to the Recovery key field. --- .maestro/tests/account/verifySession.yaml | 3 ++- features/securebackup/impl/build.gradle.kts | 1 + .../securebackup/impl/setup/views/RecoveryKeyView.kt | 3 +++ .../kotlin/io/element/android/libraries/testtags/TestTags.kt | 5 +++++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.maestro/tests/account/verifySession.yaml b/.maestro/tests/account/verifySession.yaml index 479fd17462..eeb0489e3e 100644 --- a/.maestro/tests/account/verifySession.yaml +++ b/.maestro/tests/account/verifySession.yaml @@ -3,7 +3,8 @@ appId: ${MAESTRO_APP_ID} - tapOn: "Continue" - takeScreenshot: build/maestro/150-Verify - tapOn: "Enter recovery key" -- tapOn: "Enter…" +- tapOn: + id: "verification-recovery_key" - inputText: ${MAESTRO_RECOVERY_KEY} - hideKeyboard - tapOn: "Confirm" diff --git a/features/securebackup/impl/build.gradle.kts b/features/securebackup/impl/build.gradle.kts index a3509b65dd..1d7a97b344 100644 --- a/features/securebackup/impl/build.gradle.kts +++ b/features/securebackup/impl/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) + implementation(projects.libraries.testtags) api(libs.statemachine) api(projects.features.securebackup.api) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt index 208248a8d5..7a4d92fcd3 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt @@ -53,6 +53,8 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.OutlinedTextField import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.autofill +import io.element.android.libraries.testtags.TestTags +import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -169,6 +171,7 @@ private fun RecoveryKeyFormContent( OutlinedTextField( modifier = Modifier .fillMaxWidth() + .testTag(TestTags.recoveryKey) .autofill( autofillTypes = listOf(AutofillType.Password), onFill = { onChange(it) }, diff --git a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt index 7992ddee17..5eb7780e1f 100644 --- a/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt +++ b/libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt @@ -33,6 +33,11 @@ object TestTags { val loginPassword = TestTag("login-password") val loginContinue = TestTag("login-continue") + /** + * Verification screen. + */ + val recoveryKey = TestTag("verification-recovery_key") + /** * Sign out screen. */ From e623a4ec3fb818a5fb4b769226aec07add1d636e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 22 Feb 2024 10:31:19 +0100 Subject: [PATCH 7/8] Maestro: scroll to be able to leave the DM. --- .maestro/tests/roomList/createAndDeleteDM.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.maestro/tests/roomList/createAndDeleteDM.yaml b/.maestro/tests/roomList/createAndDeleteDM.yaml index 604263fbda..6e0d55ab26 100644 --- a/.maestro/tests/roomList/createAndDeleteDM.yaml +++ b/.maestro/tests/roomList/createAndDeleteDM.yaml @@ -9,5 +9,6 @@ appId: ${MAESTRO_APP_ID} index: 1 - takeScreenshot: build/maestro/330-createAndDeleteDM - tapOn: "maestroelement2" +- scroll - tapOn: "Leave conversation" - tapOn: "Leave" From d51430dfe45881836b9199c1026cf649dcb3de97 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 22 Feb 2024 12:35:12 +0100 Subject: [PATCH 8/8] Create extensions `backupStateFlow()` and `recoveryStateFlow`. --- .../impl/encryption/EncryptionExtension.kt | 49 +++++++++++++++++++ .../impl/encryption/RustEncryptionService.kt | 29 +---------- 2 files changed, 51 insertions(+), 27 deletions(-) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/EncryptionExtension.kt diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/EncryptionExtension.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/EncryptionExtension.kt new file mode 100644 index 0000000000..976ed4c2de --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/EncryptionExtension.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.encryption + +import io.element.android.libraries.matrix.api.encryption.BackupState +import io.element.android.libraries.matrix.api.encryption.RecoveryState +import io.element.android.libraries.matrix.impl.util.mxCallbackFlow +import kotlinx.coroutines.flow.Flow +import org.matrix.rustcomponents.sdk.BackupStateListener +import org.matrix.rustcomponents.sdk.EncryptionInterface +import org.matrix.rustcomponents.sdk.RecoveryStateListener +import org.matrix.rustcomponents.sdk.BackupState as RustBackupState +import org.matrix.rustcomponents.sdk.RecoveryState as RustRecoveryState + +internal fun EncryptionInterface.backupStateFlow(): Flow = mxCallbackFlow { + val backupStateMapper = BackupStateMapper() + trySend(backupStateMapper.map(backupState())) + val listener = object : BackupStateListener { + override fun onUpdate(status: RustBackupState) { + trySend(backupStateMapper.map(status)) + } + } + backupStateListener(listener) +} + +internal fun EncryptionInterface.recoveryStateFlow(): Flow = mxCallbackFlow { + val recoveryStateMapper = RecoveryStateMapper() + trySend(recoveryStateMapper.map(recoveryState())) + val listener = object : RecoveryStateListener { + override fun onUpdate(status: RustRecoveryState) { + trySend(recoveryStateMapper.map(status)) + } + } + recoveryStateListener(listener) +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt index fda22122fd..f5a6390989 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt @@ -25,7 +25,6 @@ import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.encryption.RecoveryState import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.impl.sync.RustSyncService -import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.currentCoroutineContext @@ -40,16 +39,12 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext -import org.matrix.rustcomponents.sdk.BackupStateListener import org.matrix.rustcomponents.sdk.BackupSteadyStateListener import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.EnableRecoveryProgressListener import org.matrix.rustcomponents.sdk.Encryption -import org.matrix.rustcomponents.sdk.RecoveryStateListener -import org.matrix.rustcomponents.sdk.BackupState as RustBackupState import org.matrix.rustcomponents.sdk.BackupUploadState as RustBackupUploadState import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecoveryProgress -import org.matrix.rustcomponents.sdk.RecoveryState as RustRecoveryState import org.matrix.rustcomponents.sdk.SteadyStateException as RustSteadyStateException internal class RustEncryptionService( @@ -60,23 +55,12 @@ internal class RustEncryptionService( ) : EncryptionService { private val service: Encryption = client.encryption() - private val backupStateMapper = BackupStateMapper() - private val recoveryStateMapper = RecoveryStateMapper() private val enableRecoveryProgressMapper = EnableRecoveryProgressMapper() private val backupUploadStateMapper = BackupUploadStateMapper() private val steadyStateExceptionMapper = SteadyStateExceptionMapper() - private val backupStateFlow = mxCallbackFlow { - val listener = object : BackupStateListener { - override fun onUpdate(status: RustBackupState) { - trySend(backupStateMapper.map(status)) - } - } - service.backupStateListener(listener) - }.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, service.backupState().let(backupStateMapper::map)) - override val backupStateStateFlow = combine( - backupStateFlow, + service.backupStateFlow(), syncService.syncState, ) { backupState, syncState -> if (syncState == SyncState.Running) { @@ -86,17 +70,8 @@ internal class RustEncryptionService( } }.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, BackupState.WAITING_FOR_SYNC) - private val recoveryStateFlow = mxCallbackFlow { - val listener = object : RecoveryStateListener { - override fun onUpdate(status: RustRecoveryState) { - trySend(recoveryStateMapper.map(status)) - } - } - service.recoveryStateListener(listener) - }.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, service.recoveryState().let(recoveryStateMapper::map)) - override val recoveryStateStateFlow = combine( - recoveryStateFlow, + service.recoveryStateFlow(), syncService.syncState, ) { recoveryState, syncState -> if (syncState == SyncState.Running) {