From 20afd0c3450bdafa9808e15842c44ebb62e3386f Mon Sep 17 00:00:00 2001 From: CherryPerry Date: Mon, 21 Nov 2022 11:20:39 +0000 Subject: [PATCH] Cover interop back press with test --- gradle/libs.versions.toml | 1 + libraries/interop-ribs/build.gradle.kts | 8 ++ .../src/androidTest/AndroidManifest.xml | 12 ++ .../interop/ribs/AppyxRibsInteropActivity.kt | 20 ++++ .../appyx/interop/ribs/InteropNodeImplTest.kt | 36 ++++++ .../com/bumble/appyx/interop/ribs/RibsNode.kt | 112 ++++++++++++++++++ 6 files changed, 189 insertions(+) create mode 100644 libraries/interop-ribs/src/androidTest/AndroidManifest.xml create mode 100644 libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/AppyxRibsInteropActivity.kt create mode 100644 libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/InteropNodeImplTest.kt create mode 100644 libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/RibsNode.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e46fe27d3..e346e0a80 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -54,6 +54,7 @@ mvicore-android = { module = "com.github.badoo.mvicore:mvicore-android", version mvicore-binder = { module = "com.github.badoo.mvicore:binder", version.ref = "mvicore" } ribs-base = { module = "com.github.badoo.RIBs:rib-base", version.ref = "ribs" } ribs-base-test = { module = "com.github.badoo.RIBs:rib-base-test", version.ref = "ribs" } +ribs-base-test-activity = { module = "com.github.badoo.RIBs:rib-base-test-activity", version.ref = "ribs" } ribs-base-test-rx2 = { module = "com.github.badoo.RIBs:rib-base-test-rx2", version.ref = "ribs" } ribs-compose = { module = "com.github.badoo.RIBs:rib-compose", version.ref = "ribs" } ribs-rx = { module = "com.github.badoo.RIBs:rib-rx2", version.ref = "ribs" } diff --git a/libraries/interop-ribs/build.gradle.kts b/libraries/interop-ribs/build.gradle.kts index fbad672c3..26b8c3404 100644 --- a/libraries/interop-ribs/build.gradle.kts +++ b/libraries/interop-ribs/build.gradle.kts @@ -1,6 +1,7 @@ plugins { id("com.android.library") id("kotlin-android") + id("kotlin-parcelize") id("appyx-publish-android") id("appyx-lint") id("appyx-detekt") @@ -33,4 +34,11 @@ dependencies { implementation(libs.androidx.activity.compose) implementation(libs.compose.ui.ui) implementation(libs.ribs.compose) + + androidTestImplementation(libs.androidx.activity.compose) + androidTestImplementation(libs.androidx.test.espresso.core) + androidTestImplementation(libs.androidx.test.junit) + androidTestImplementation(libs.compose.foundation.layout) + androidTestImplementation(libs.compose.ui.test.junit4) + androidTestImplementation(libs.ribs.base.test.activity) } diff --git a/libraries/interop-ribs/src/androidTest/AndroidManifest.xml b/libraries/interop-ribs/src/androidTest/AndroidManifest.xml new file mode 100644 index 000000000..875944729 --- /dev/null +++ b/libraries/interop-ribs/src/androidTest/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/AppyxRibsInteropActivity.kt b/libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/AppyxRibsInteropActivity.kt new file mode 100644 index 000000000..1795c9607 --- /dev/null +++ b/libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/AppyxRibsInteropActivity.kt @@ -0,0 +1,20 @@ +package com.bumble.appyx.interop.ribs + +import android.os.Bundle +import android.view.ViewGroup +import com.badoo.ribs.core.Rib +import com.badoo.ribs.core.modality.BuildContext + +class AppyxRibsInteropActivity : InteropActivity() { + + lateinit var ribsNode: RibsNode + + override val rootViewGroup: ViewGroup + get() = findViewById(android.R.id.content) + + override fun createRib(savedInstanceState: Bundle?): Rib = + RibsNodeBuilder() + .build(BuildContext.root(savedInstanceState), appyxIntegrationPoint) + .also { ribsNode = it } + +} \ No newline at end of file diff --git a/libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/InteropNodeImplTest.kt b/libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/InteropNodeImplTest.kt new file mode 100644 index 000000000..10dbd7e9a --- /dev/null +++ b/libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/InteropNodeImplTest.kt @@ -0,0 +1,36 @@ +package com.bumble.appyx.interop.ribs + +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.test.espresso.Espresso +import org.junit.Rule +import org.junit.Test + +class InteropNodeImplTest { + + @get:Rule + val rule = createAndroidComposeRule(AppyxRibsInteropActivity::class.java) + + @Test + fun appyx_back_press_is_handled_before_rib_parent() { + // push a new child into the backstack + var newChild = "" + var oldChild = "" + rule.activityRule.scenario.onActivity { + oldChild = it.ribsNode.current() + newChild = it.ribsNode.push() + } + + Espresso.pressBack() + + // the back press is swollen by the child node, newChild is still displayed + rule.onNodeWithTag(newChild).assertExists() + + Espresso.pressBack() + + // the back press is handled by RIBs now and the old child is displayed + rule.onNodeWithTag(oldChild).assertExists() + } + + +} \ No newline at end of file diff --git a/libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/RibsNode.kt b/libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/RibsNode.kt new file mode 100644 index 000000000..b16a61fd1 --- /dev/null +++ b/libraries/interop-ribs/src/androidTest/kotlin/com/bumble/appyx/interop/ribs/RibsNode.kt @@ -0,0 +1,112 @@ +package com.bumble.appyx.interop.ribs + +import android.os.Parcelable +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.lifecycle.Lifecycle +import com.badoo.ribs.builder.Builder +import com.badoo.ribs.core.Node +import com.badoo.ribs.core.modality.BuildParams +import com.badoo.ribs.core.plugin.Plugin +import com.badoo.ribs.core.view.AndroidRibView2 +import com.badoo.ribs.core.view.ViewFactory +import com.badoo.ribs.routing.Routing +import com.badoo.ribs.routing.resolution.ChildResolution +import com.badoo.ribs.routing.resolution.Resolution +import com.badoo.ribs.routing.router.Router +import com.badoo.ribs.routing.source.backstack.BackStack +import com.badoo.ribs.routing.source.backstack.operation.push +import com.bumble.appyx.core.integrationpoint.IntegrationPoint +import com.bumble.appyx.core.modality.BuildContext +import kotlinx.parcelize.Parcelize +import java.util.UUID + +class RibsNode( + buildParams: BuildParams<*>, + private val backStack: BackStack, + plugins: List, +) : Node( + buildParams = buildParams, + viewFactory = View.Factory(), + plugins = plugins, +) { + + fun current(): String = + backStack.activeConfiguration.id + + fun push(): String { + val id = UUID.randomUUID().toString() + backStack.push(RibsNodeRouter.Configuration(id)) + return id + } + + class View( + androidView: ViewGroup, + lifecycle: Lifecycle, + ) : AndroidRibView2( + androidView, + lifecycle + ) { + class Factory : ViewFactory { + override fun invoke(context: ViewFactory.Context): View = + View(FrameLayout(context.parent.context), context.lifecycle) + } + } +} + +class RibsNodeBuilder : Builder() { + override fun build(buildParams: BuildParams): RibsNode { + val backStack = BackStack( + RibsNodeRouter.Configuration(UUID.randomUUID().toString()), + buildParams + ) + val router = RibsNodeRouter(buildParams, buildParams.payload, backStack) + return RibsNode(buildParams, backStack, listOf(router)) + } +} + +class RibsNodeRouter( + buildParams: BuildParams<*>, + private val integrationPoint: IntegrationPoint, + backStack: BackStack, +) : Router( + buildParams = buildParams, + routingSource = backStack +) { + @Parcelize + data class Configuration(val id: String) : Parcelable + + override fun resolve(routing: Routing): Resolution = + ChildResolution.child { + InteropBuilder( + nodeFactory = { buildContext -> AppyxNode(buildContext, routing.configuration.id) }, + integrationPoint = integrationPoint, + ).build(it) + } +} + +class AppyxNode( + buildContext: BuildContext, + private val s: String, +) : com.bumble.appyx.core.node.Node( + buildContext, +) { + var shouldInterceptBackPress by mutableStateOf(true) + + @Composable + override fun View(modifier: Modifier) { + Box(modifier = modifier.testTag(s)) { + BackHandler(shouldInterceptBackPress) { + shouldInterceptBackPress = false + } + } + } +}