diff --git a/android/beagle/src/main/java/br/com/zup/beagle/android/action/AddChildren.kt b/android/beagle/src/main/java/br/com/zup/beagle/android/action/AddChildren.kt index de0a6ad51b..0757d62877 100644 --- a/android/beagle/src/main/java/br/com/zup/beagle/android/action/AddChildren.kt +++ b/android/beagle/src/main/java/br/com/zup/beagle/android/action/AddChildren.kt @@ -19,13 +19,19 @@ package br.com.zup.beagle.android.action import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity -import br.com.zup.beagle.newanalytics.ActionAnalyticsConfig +import br.com.zup.beagle.android.context.Bind +import br.com.zup.beagle.android.context.valueOf +import br.com.zup.beagle.android.data.serializer.BeagleMoshi import br.com.zup.beagle.android.logger.BeagleMessageLogs +import br.com.zup.beagle.android.utils.evaluateExpression import br.com.zup.beagle.android.utils.toAndroidId import br.com.zup.beagle.android.utils.toView import br.com.zup.beagle.android.widget.RootView import br.com.zup.beagle.core.BeagleJson import br.com.zup.beagle.core.ServerDrivenComponent +import br.com.zup.beagle.newanalytics.ActionAnalyticsConfig +import com.squareup.moshi.Types +import java.lang.reflect.Type /** * Defines the placement of where the children will be inserted in the list or if the contents @@ -65,21 +71,49 @@ enum class Mode { @BeagleJson(name = "addChildren") data class AddChildren( var componentId: String, - var value: List, + var value: Bind>, var mode: Mode? = Mode.APPEND, override var analytics: ActionAnalyticsConfig? = null, ) : AnalyticsAction { + constructor( + componentId: String, + value: List, + mode: Mode? = Mode.APPEND, + analytics: ActionAnalyticsConfig? = null, + ) : this( + componentId = componentId, + value = valueOf(value), + mode = mode, + analytics = analytics + ) + override fun execute(rootView: RootView, origin: View) { try { val view = (rootView.getContext() as AppCompatActivity).findViewById(componentId.toAndroidId()) - val viewList = convertServerDrivenListOnViewList(value, rootView) + val list = getList(rootView, origin) + val viewList = convertServerDrivenListOnViewList(list, rootView) addValueToView(view, viewList) } catch (exception: Exception) { BeagleMessageLogs.errorWhileTryingToAddViewWithAddChildrenAction(componentId) } } + // Temporary solution to evaluate expression of server driven component + private fun getList(rootView: RootView, origin: View): List { + val result = evaluateExpression(rootView, origin, value) + + if (value is Bind.Value<*>) { + return result ?: emptyList() + } + + val moshi = BeagleMoshi.moshi + + val type: Type = Types.newParameterizedType(List::class.java, ServerDrivenComponent::class.java) + + return moshi.adapter>(type).fromJsonValue(result) ?: emptyList() + } + private fun convertServerDrivenListOnViewList(list: List, rootView: RootView): List { val result: ArrayList = ArrayList() list.forEach { diff --git a/android/beagle/src/test/java/br/com/zup/beagle/android/action/AddChildrenTest.kt b/android/beagle/src/test/java/br/com/zup/beagle/android/action/AddChildrenTest.kt index 96975c279b..6c2506582e 100644 --- a/android/beagle/src/test/java/br/com/zup/beagle/android/action/AddChildrenTest.kt +++ b/android/beagle/src/test/java/br/com/zup/beagle/android/action/AddChildrenTest.kt @@ -19,94 +19,141 @@ package br.com.zup.beagle.android.action import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity -import br.com.zup.beagle.android.setup.BeagleEnvironment -import br.com.zup.beagle.android.setup.BeagleSdk +import br.com.zup.beagle.android.BaseTest +import br.com.zup.beagle.android.context.expressionOf +import br.com.zup.beagle.android.logger.BeagleMessageLogs +import br.com.zup.beagle.android.utils.evaluateExpression +import br.com.zup.beagle.android.utils.toAndroidId import br.com.zup.beagle.android.utils.viewFactory import br.com.zup.beagle.android.view.ViewFactory import br.com.zup.beagle.android.view.custom.BeagleFlexView -import br.com.zup.beagle.android.widget.RootView import br.com.zup.beagle.core.ServerDrivenComponent import io.mockk.* -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test +import org.junit.jupiter.api.* -class AddChildrenTest { +@DisplayName("Given an Add Children") +class AddChildrenTest : BaseTest() { private val serverDrivenComponent = mockk(relaxed = true) private val value = listOf(serverDrivenComponent) - private val rootView = mockk(relaxed = true) private val origin = mockk(relaxed = true) private val viewGroup = mockk(relaxed = true) private val context = mockk(relaxed = true) private val view = mockk(relaxed = true) - private val beagleSdk = mockk(relaxed = true) private val id = "id" private val viewFactoryMock = mockk(relaxed = true) @BeforeEach - fun setUp() { - mockkObject(BeagleEnvironment) + override fun setUp() { + super.setUp() viewFactory = viewFactoryMock - every { BeagleEnvironment.beagleSdk } returns beagleSdk + + mockkObject(BeagleMessageLogs) + + mockkStatic("br.com.zup.beagle.android.utils.ActionExtensionsKt") + every { beagleSdk.logger } returns mockk(relaxed = true) every { rootView.getContext() } returns context - every { context.findViewById(any()) } returns viewGroup + every { context.findViewById(id.toAndroidId()) } returns viewGroup every { viewFactory.makeBeagleFlexView(rootView) } returns view every { viewGroup.addView(any()) } just Runs } - @AfterEach - fun tearDown() { - unmockkObject(BeagleEnvironment) - } - @Test - fun addChildren_with_no_mode_should_append() { - //GIVEN - val action = AddChildren(id, value) + @DisplayName("When call execute with mode APPEND") + @Nested + inner class ModeAppendTest { + + @DisplayName("Then should execute action correctly") + @Test + fun testModeAppend() { + // Given + val action = AddChildren(id, value, Mode.APPEND) + + // When + action.execute(rootView, origin) - //WHEN - action.execute(rootView, origin) + // Then + verify(exactly = 1) { viewGroup.addView(view) } + } + } - //THEN - verify(exactly = 1) { viewGroup.addView(view) } + @DisplayName("When call execute with mode REPLACE") + @Nested + inner class ModeReplaceTest { + + @DisplayName("Then should execute action correctly") + @Test + fun testModeReplace() { + // Given + val action = AddChildren(id, value, Mode.REPLACE) + + // When + action.execute(rootView, origin) + + // Then + verify(exactly = 1) { + viewGroup.removeAllViews() + viewGroup.addView(view) + } + } } - @Test - fun addChildren_with_append_mode_should_call_view_group_add_view() { - //GIVEN - val action = AddChildren(id, value, Mode.APPEND) + @DisplayName("When call execute with mode PREPEND") + @Nested + inner class ModePrependTest { + + @DisplayName("Then should execute action correctly") + @Test + fun testModePrepend() { + // Given + val action = AddChildren(id, value, Mode.PREPEND) - //WHEN - action.execute(rootView, origin) + // When + action.execute(rootView, origin) - //THEN - verify(exactly = 1) { viewGroup.addView(view) } + // Then + verify(exactly = 1) { viewGroup.addView(view, 0) } + } } - @Test - fun addChildren_with_replace_mode_should_call_view_group_remove_all_views_than_add_view() { - //GIVEN - val action = AddChildren(id, value, Mode.REPLACE) + @DisplayName("When call execute with invalid component id") + @Nested + inner class WrongViewTest { + + @DisplayName("Then should emit log exception") + @Test + fun testLogException() { + // Given + val action = AddChildren("test", value, Mode.PREPEND) - //WHEN - action.execute(rootView, origin) + // When + action.execute(rootView, origin) - //THEN - verify(exactly = 1) { viewGroup.removeAllViews() } - verify(exactly = 1) { viewGroup.addView(view) } + // Then + verify(exactly = 1) { BeagleMessageLogs.errorWhileTryingToAddViewWithAddChildrenAction("test") } + } } - @Test - fun addChildren_with_prepend_mode_should_call_view_group_add_view_index_zero() { - //GIVEN - val action = AddChildren(id, value, Mode.PREPEND) + @DisplayName("When call execute with expression value") + @Nested + inner class ExpressionValueTest { + + @DisplayName("Then should execute action correctly") + @Test + fun testExpressionValue() { + // Given + val action = AddChildren(id, expressionOf("@{test}"), Mode.APPEND) + + val list = arrayListOf(linkedMapOf(Pair("_beagleComponent_", " beagle:container"))) + + every { action.evaluateExpression(rootView, origin, action.value) } returns list as List - //WHEN - action.execute(rootView, origin) + // When + action.execute(rootView, origin) - //THEN - verify(exactly = 1) { viewGroup.addView(view, 0) } + // Then + verify(exactly = 1) { viewGroup.addView(view) } + } } } diff --git a/android/beagle/src/test/java/br/com/zup/beagle/android/data/serializer/BeagleMoshiTest.kt b/android/beagle/src/test/java/br/com/zup/beagle/android/data/serializer/BeagleMoshiTest.kt index bd2fd8c0bc..ca1e0c7247 100644 --- a/android/beagle/src/test/java/br/com/zup/beagle/android/data/serializer/BeagleMoshiTest.kt +++ b/android/beagle/src/test/java/br/com/zup/beagle/android/data/serializer/BeagleMoshiTest.kt @@ -524,7 +524,7 @@ class BeagleMoshiTest : BaseTest() { @Test fun `GIVEN AddChildren action WHEN moshi to serialize the action THEN it should return action serialized `() { // Given - val component = AddChildren(componentId = "", value = listOf()) + val component = AddChildren(componentId = "", value = valueOf(listOf())) // When val actual = moshi.adapter(Action::class.java).toJson(component) diff --git a/backend/widgets/src/main/kotlin/br/com/zup/beagle/widget/action/AddChildren.kt b/backend/widgets/src/main/kotlin/br/com/zup/beagle/widget/action/AddChildren.kt index 96bedc7990..38f15726c0 100644 --- a/backend/widgets/src/main/kotlin/br/com/zup/beagle/widget/action/AddChildren.kt +++ b/backend/widgets/src/main/kotlin/br/com/zup/beagle/widget/action/AddChildren.kt @@ -18,6 +18,8 @@ package br.com.zup.beagle.widget.action import br.com.zup.beagle.newanalytics.ActionAnalyticsConfig import br.com.zup.beagle.core.ServerDrivenComponent +import br.com.zup.beagle.widget.context.Bind +import br.com.zup.beagle.widget.context.valueOf /** * Defines the placement of where the children will be inserted in the list or if the contents @@ -55,7 +57,19 @@ enum class Mode { */ data class AddChildren( var componentId: String, - var value: List, + var value: Bind>, var mode: Mode? = Mode.APPEND, override var analytics: ActionAnalyticsConfig? = null -) : AnalyticsAction +) : AnalyticsAction { + constructor( + componentId: String, + value: List, + mode: Mode? = Mode.APPEND, + analytics: ActionAnalyticsConfig? = null + ) : this( + componentId = componentId, + value = valueOf(value), + mode = mode, + analytics = analytics + ) +} \ No newline at end of file