Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove plugin implementation from view and retrieve node instance from LocalComposition #197

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Pending changes

- [#197](https://github.com/bumble-tech/appyx/pull/197) – **Breaking change**: `ParentNodeView` does not implement plugin anymore. Node instance is retrieved via LocalComposition.
KovalevAndrey marked this conversation as resolved.
Show resolved Hide resolved
- [#185](https://github.com/bumble-tech/appyx/issues/185) – **Breaking change**: `Activity` must implement `IntegrationPointProvider` and create `IntegrationPoint` manually. Weak references usage has been removed.
- [#173](https://github.com/bumble-tech/appyx/pull/173) – **Breaking change**: `ActivityStarter` and `PermissionRequester` now exposes coroutine based API instead of `minimal.reactive`.
- [#43](https://github.com/bumble-tech/appyx/pull/43) – **Updated**: Jetpack Compose to 1.2.1 and kotlin to 1.7.10.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import androidx.test.core.app.ActivityScenario
import com.bumble.appyx.core.integration.NodeFactory
import com.bumble.appyx.core.integration.NodeHost
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.testing.ui.rules.AppyxViewActivity
import com.bumble.appyx.testing.ui.rules.AppyxTestActivity
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

/**
* [com.bumble.appyx.testing.ui.rules.AppyxTestRule] based on [ActivityScenario] to support lifecycle tests.
* [com.bumble.appyx.testing.ui.rules.AppyxActivityTestRule] based on [ActivityScenario] to support lifecycle tests.
*
* TODO: Consider merging with AppyxTestRule.
*/
Expand All @@ -27,7 +27,7 @@ class AppyxTestScenario<T : Node>(
@get:WorkerThread
val activityScenario: ActivityScenario<BackPressHandlerTestActivity> by lazy {
val awaitNode = CountDownLatch(1)
AppyxViewActivity.composableView = { activity ->
AppyxTestActivity.composableView = { activity ->
decorator {
NodeHost(integrationPoint = activity.appyxIntegrationPoint, factory = { buildContext ->
node = nodeFactory.create(buildContext)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package com.bumble.appyx.core.plugin
import android.os.Bundle
import androidx.activity.OnBackPressedCallback
import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.testing.ui.rules.AppyxViewActivity
import com.bumble.appyx.testing.ui.rules.AppyxTestActivity
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch

class BackPressHandlerTestActivity : AppyxViewActivity() {
class BackPressHandlerTestActivity : AppyxTestActivity() {

private val callback = object : OnBackPressedCallback(handleBackPress.value) {
override fun handleOnBackPressed() {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ object EmptyNodeView : NodeView {
override fun View(modifier: Modifier) = Unit
}

class EmptyParentNodeView<NavTarget : Any> : AbstractParentNodeView<NavTarget>() {
class EmptyParentNodeView<NavTarget : Any> : ParentNodeView<NavTarget> {

@Composable
override fun ParentNode<NavTarget>.NodeView(modifier: Modifier) = Unit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import com.bumble.appyx.core.children.nodeOrNull
import com.bumble.appyx.core.composable.ChildRenderer
import com.bumble.appyx.core.lifecycle.ChildNodeLifecycleManager
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.navigation.NavKey
import com.bumble.appyx.core.navigation.NavModel
import com.bumble.appyx.core.navigation.Resolver
import com.bumble.appyx.core.navigation.NavKey
import com.bumble.appyx.core.navigation.isTransitioning
import com.bumble.appyx.core.navigation.model.combined.plus
import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
Expand Down Expand Up @@ -57,7 +57,7 @@ abstract class ParentNode<NavTarget : Any>(
) : Node(
view = view,
buildContext = buildContext,
plugins = plugins + navModel + childAware + view
plugins = plugins + navModel + childAware
), Resolver<NavTarget> {

private val permanentNavModel = PermanentNavModel<NavTarget>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ package com.bumble.appyx.core.node

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.plugin.NodeAware

interface ParentNodeView<NavTarget : Any> : NodeView, NodeAware<ParentNode<NavTarget>> {
interface ParentNodeView<NavTarget : Any> : NodeView {

@Composable
fun ParentNode<NavTarget>.NodeView(modifier: Modifier)

@Suppress("UNCHECKED_CAST")
@Composable
override fun View(modifier: Modifier) {
KovalevAndrey marked this conversation as resolved.
Show resolved Hide resolved
val node = LocalNode.current as? ParentNode<NavTarget>
?: error("${this::class.qualifiedName} is not provided to the appropriate ParentNode")
node.NodeView(modifier = modifier)
}
}
2 changes: 1 addition & 1 deletion libraries/testing-ui-activity/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<application>
<activity
android:name="com.bumble.appyx.testing.ui.rules.AppyxViewActivity"
android:name="com.bumble.appyx.testing.ui.rules.AppyxTestActivity"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
</application>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import com.bumble.appyx.core.integrationpoint.NodeActivity

open class AppyxViewActivity : NodeActivity() {
open class AppyxTestActivity : NodeActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val composableView = composableView
setContent {
requireNotNull(composableView) { "AppyxViewActivity View has not been setup" }
requireNotNull(composableView) { "AppyxTestActivity View has not been setup" }
composableView(this)
}
}

companion object {
var composableView: (@Composable (activity: AppyxViewActivity) -> Unit)? = null
var composableView: (@Composable (activity: AppyxTestActivity) -> Unit)? = null
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement

open class AppyxTestRule<T : Node>(
open class AppyxActivityTestRule<T : Node>(
private val launchActivity: Boolean = true,
private val composeTestRule: ComposeTestRule = createEmptyComposeRule(),
/** Add decorations like custom theme or CompositionLocalProvider. Do not forget to invoke `content()`. */
private val decorator: (@Composable (content: @Composable () -> Unit) -> Unit) = { content -> content() },
private val nodeFactory: NodeFactory<T>,
) : ActivityTestRule<AppyxViewActivity>(
/* activityClass = */ AppyxViewActivity::class.java,
) : ActivityTestRule<AppyxTestActivity>(
/* activityClass = */ AppyxTestActivity::class.java,
/* initialTouchMode = */ true,
/* launchActivity = */ launchActivity
), ComposeTestRule by composeTestRule {
Expand All @@ -42,7 +42,7 @@ open class AppyxTestRule<T : Node>(
}

override fun beforeActivityLaunched() {
AppyxViewActivity.composableView = { activity ->
AppyxTestActivity.composableView = { activity ->
decorator {
NodeHost(integrationPoint = activity.appyxIntegrationPoint, factory = { buildContext ->
node = nodeFactory.create(buildContext)
Expand All @@ -53,7 +53,7 @@ open class AppyxTestRule<T : Node>(
}

override fun afterActivityLaunched() {
AppyxViewActivity.composableView = null
AppyxTestActivity.composableView = null
}

@CallSuper
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.bumble.appyx.testing.ui.rules

import androidx.annotation.CallSuper
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.junit4.createEmptyComposeRule
import androidx.test.rule.ActivityTestRule
import com.bumble.appyx.core.node.LocalNode
import com.bumble.appyx.core.node.NodeView
import com.bumble.appyx.core.node.ViewFactory
import com.bumble.appyx.testing.ui.utils.DummyParentNode
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
Expand All @@ -15,8 +18,8 @@ open class AppyxViewTestRule<View : NodeView>(
viewFactory: ViewFactory<View>,
private val launchActivity: Boolean = true,
private val composeTestRule: ComposeTestRule = createEmptyComposeRule()
) : ActivityTestRule<AppyxViewActivity>(
/* activityClass = */ AppyxViewActivity::class.java,
) : ActivityTestRule<AppyxTestActivity>(
/* activityClass = */ AppyxTestActivity::class.java,
/* initialTouchMode = */ true,
/* launchActivity = */ launchActivity
), ComposeTestRule by composeTestRule {
Expand Down Expand Up @@ -46,13 +49,17 @@ open class AppyxViewTestRule<View : NodeView>(
}

override fun beforeActivityLaunched() {
AppyxViewActivity.composableView = {
view.View(modifier = Modifier)
AppyxTestActivity.composableView = {
CompositionLocalProvider(
LocalNode provides DummyParentNode<Any>(),
) {
view.View(modifier = Modifier)
}
}
}

override fun afterActivityLaunched() {
AppyxViewActivity.composableView = null
AppyxTestActivity.composableView = null
}

@CallSuper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import com.bumble.appyx.core.navigation.onscreen.OnScreenStateResolver
class DummyNavModel<NavTarget : Any, State> : BaseNavModel<NavTarget, State>(
savedStateMap = null,
finalState = null,
screenResolver = object : OnScreenStateResolver<State> {
override fun isOnScreen(state: State) = true
}
screenResolver = OnScreenStateResolver { true }
) {
override val initialElements: NavElements<NavTarget, State>
get() = emptyList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.performClick
import com.bumble.appyx.testing.ui.utils.DummyNavModel
import com.bumble.appyx.sandbox.client.mvicoreexample.MviCoreExampleViewImpl.Event
import com.bumble.appyx.sandbox.client.mvicoreexample.feature.ViewModel
import com.bumble.appyx.sandbox.client.test.appyxParentViewRule
import com.bumble.appyx.sandbox.client.test.appyxViewRule
import com.bumble.appyx.sandbox.client.test.assertLastValueEqual
import com.bumble.appyx.testing.ui.utils.DummyNavModel
import org.junit.Rule
import org.junit.Test

Expand All @@ -18,7 +18,7 @@ internal class MviCoreExampleViewTest {
private var title = "Title"

@get:Rule
val rule = appyxParentViewRule(launchActivity = false) {
val rule = appyxViewRule(launchActivity = false) {
createView()
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,25 +1,14 @@
package com.bumble.appyx.sandbox.client.test

import com.bumble.appyx.core.node.ParentNodeView
import com.bumble.appyx.core.node.NodeView
import com.bumble.appyx.core.node.ViewFactory
import io.reactivex.ObservableSource
import io.reactivex.functions.Consumer

fun <NavTarget : Any, ViewModel : Any, Event : Any, View> appyxParentViewRule(
fun <ViewModel : Any, Event : Any, View> appyxViewRule(
launchActivity: Boolean = true,
viewFactory: ViewFactory<View>,
) where View : ParentNodeView<NavTarget>, View : Consumer<in ViewModel>, View : ObservableSource<out Event> =
AppyxMviParentViewTestRule(
launchActivity = launchActivity,
modelConsumer = { it },
eventObservable = { it },
viewFactory = viewFactory
)

fun <NavTarget : Any, ViewModel : Any, Event : Any, View> appyxViewRule(
launchActivity: Boolean = true,
viewFactory: ViewFactory<View>,
) where View : ParentNodeView<NavTarget>, View : Consumer<in ViewModel>, View : ObservableSource<out Event> =
) where View : NodeView, View : Consumer<in ViewModel>, View : ObservableSource<out Event> =
AppyxMviViewTestRule(
launchActivity = launchActivity,
modelConsumer = { it },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.bumble.appyx.core.composable.Children
import com.bumble.appyx.core.node.AbstractParentNodeView
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.navigation.NavModel
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.node.ParentNodeView
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.transitionhandler.rememberBackstackSlider
import com.bumble.appyx.sandbox.client.mvicoreexample.MviCoreExampleNode.NavTarget
Expand All @@ -47,7 +47,7 @@ class MviCoreExampleViewImpl(
private val title: String = "Title",
private val backStack: NavModel<NavTarget, BackStack.State>,
private val events: PublishRelay<Event> = PublishRelay.create()
) : AbstractParentNodeView<NavTarget>(),
) : ParentNodeView<NavTarget>,
MviCoreExampleView,
ObservableSource<Event> by events {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.bumble.appyx.sandbox.stub

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.bumble.appyx.core.node.AbstractParentNodeView
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.node.ParentNodeView
import com.jakewharton.rxrelay2.PublishRelay
import io.reactivex.ObservableSource
import io.reactivex.disposables.Disposable
Expand All @@ -14,7 +14,7 @@ open class NodeViewStub<Event : Any, ViewModel : Any, NavTarget : Any>(
val eventsRelay: PublishRelay<Event> = PublishRelay.create(),
val viewModelRelay: PublishRelay<ViewModel> = PublishRelay.create(),
private val disposable: Disposable = Disposables.empty()
) : AbstractParentNodeView<NavTarget>(),
) : ParentNodeView<NavTarget>,
ObservableSource<Event> by eventsRelay,
Consumer<ViewModel> by viewModelRelay,
Disposable by disposable {
Expand Down