Merge pull request #6681 from hughns/hughns/link-new-device-done

Improve detection of completion for Link new device flow
This commit is contained in:
Benoit Marty 2026-04-29 22:32:22 +02:00 committed by GitHub
commit 8b8eedb419
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 83 additions and 8 deletions

View file

@ -151,10 +151,7 @@ class LinkNewDeviceFlowNode(
LinkMobileStep.Starting -> {
// This step is not received at the moment, so do nothing
}
LinkMobileStep.SyncingSecrets -> {
// LinkMobileStep.Done is not received at the moment, so consider that the flow is done here
callback.onDone()
}
LinkMobileStep.SyncingSecrets -> Unit
is LinkMobileStep.WaitingForAuth -> {
navigateToBrowser(linkMobileStep.verificationUri)
}

View file

@ -54,6 +54,8 @@ class RustLinkDesktopHandler(
}
}
)
// We emit Done in case the progress listener was deallocated before scan() sent the Done
_linkDesktopStep.emit(LinkDesktopStep.Done)
} catch (e: QrCodeDecodeException) {
Timber.tag(tag.value).w(e, "Invalid QR code scanned")
_linkDesktopStep.emit(

View file

@ -49,6 +49,8 @@ class RustLinkMobileHandler(
}
}
)
// We emit Done in case the progress listener was deallocated before generate() sent the Done
_linkMobileStep.emit(LinkMobileStep.Done)
} catch (e: HumanQrGrantLoginException) {
Timber.tag(tag.value).w(e, "Error during QR login grant")
_linkMobileStep.emit(LinkMobileStep.Error(e.map()))

View file

@ -16,8 +16,8 @@ import org.matrix.rustcomponents.sdk.NoHandle
import org.matrix.rustcomponents.sdk.QrCodeData
class FakeFfiGrantLoginWithQrCodeHandler(
private val generateResult: () -> Unit = {},
private val scanResult: (QrCodeData) -> Unit = {},
private val generateResult: suspend () -> Unit = {},
private val scanResult: suspend (QrCodeData) -> Unit = {},
) : GrantLoginWithQrCodeHandler(NoHandle) {
private var generateProgressListener: GrantGeneratedQrLoginProgressListener? = null
private var scanProgressListener: GrantQrLoginProgressListener? = null

View file

@ -15,6 +15,7 @@ import io.element.android.libraries.matrix.api.linknewdevice.ErrorType
import io.element.android.libraries.matrix.api.linknewdevice.LinkDesktopStep
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiGrantLoginWithQrCodeHandler
import io.element.android.libraries.matrix.test.QR_CODE_DATA
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
@ -29,7 +30,13 @@ import org.matrix.rustcomponents.sdk.QrCodeDecodeException
class RustLinkDesktopHandlerTest {
@Test
fun `handleScannedQrCode function works as expected`() = runTest {
val handler = FakeFfiGrantLoginWithQrCodeHandler()
val completable = CompletableDeferred<Unit>()
val handler = FakeFfiGrantLoginWithQrCodeHandler(
scanResult = {
// Ensure that the coroutine is hold
completable.await()
}
)
val sut = createRustLinkDesktopHandler(
handler,
)
@ -53,6 +60,36 @@ class RustLinkDesktopHandlerTest {
handler.emitScanProgress(progress)
assertThat(awaitItem()).isEqualTo(expectedStep)
}
// scan returns, no new event is emitted
completable.complete(Unit)
expectNoEvents()
}
}
@Test
fun `when scan does not emits the Done state, the code emits it`() = runTest {
val completable = CompletableDeferred<Unit>()
val handler = FakeFfiGrantLoginWithQrCodeHandler(
scanResult = {
// Ensure that the coroutine is hold
completable.await()
}
)
val sut = createRustLinkDesktopHandler(
handler,
)
sut.linkDesktopStep.test {
val initialItem = awaitItem()
assertThat(initialItem).isEqualTo(LinkDesktopStep.Uninitialized)
backgroundScope.launch {
sut.handleScannedQrCode(QR_CODE_DATA)
}
runCurrent()
handler.emitScanProgress(GrantQrLoginProgress.Starting)
assertThat(awaitItem()).isEqualTo(LinkDesktopStep.Starting)
// scan returns, Done event is emitted
completable.complete(Unit)
assertThat(awaitItem()).isEqualTo(LinkDesktopStep.Done)
}
}

View file

@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiCheckCodeS
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiGrantLoginWithQrCodeHandler
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiQrCodeData
import io.element.android.libraries.matrix.test.QR_CODE_DATA_RECIPROCATE
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
@ -30,7 +31,13 @@ import org.matrix.rustcomponents.sdk.HumanQrGrantLoginException
class RustLinkMobileHandlerTest {
@Test
fun `start function works as expected`() = runTest {
val handler = FakeFfiGrantLoginWithQrCodeHandler()
val completable = CompletableDeferred<Unit>()
val handler = FakeFfiGrantLoginWithQrCodeHandler(
generateResult = {
// Ensure that the coroutine is hold
completable.await()
}
)
val sut = createRustLinkMobileHandler(
handler,
)
@ -56,6 +63,36 @@ class RustLinkMobileHandlerTest {
handler.emitGenerateProgress(progress)
assertThat(awaitItem()).isInstanceOf(expectedStepClass)
}
// generate returns, no new event is emitted
completable.complete(Unit)
expectNoEvents()
}
}
@Test
fun `when generates does not emits the Done state, the code emits it`() = runTest {
val completable = CompletableDeferred<Unit>()
val handler = FakeFfiGrantLoginWithQrCodeHandler(
generateResult = {
// Ensure that the coroutine is hold
completable.await()
}
)
val sut = createRustLinkMobileHandler(
handler,
)
sut.linkMobileStep.test {
val initialItem = awaitItem()
assertThat(initialItem).isEqualTo(LinkMobileStep.Uninitialized)
backgroundScope.launch {
sut.start()
}
runCurrent()
handler.emitGenerateProgress(GrantGeneratedQrLoginProgress.Starting)
assertThat(awaitItem()).isEqualTo(LinkMobileStep.Starting)
// generate returns, Done event is emitted
completable.complete(Unit)
assertThat(awaitItem()).isEqualTo(LinkMobileStep.Done)
}
}