Skip to content
This repository has been archived by the owner on Jun 1, 2024. It is now read-only.

feat: add support to bind in field value in action add children #1544

Merged
merged 3 commits into from
May 7, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -65,21 +71,49 @@ enum class Mode {
@BeagleJson(name = "addChildren")
data class AddChildren(
var componentId: String,
var value: List<ServerDrivenComponent>,
var value: Bind<List<ServerDrivenComponent>>,
var mode: Mode? = Mode.APPEND,
override var analytics: ActionAnalyticsConfig? = null,
) : AnalyticsAction {

constructor(
componentId: String,
value: List<ServerDrivenComponent>,
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<ViewGroup>(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<ServerDrivenComponent> {
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<List<ServerDrivenComponent>>(type).fromJsonValue(result) ?: emptyList()
}

private fun convertServerDrivenListOnViewList(list: List<ServerDrivenComponent>, rootView: RootView): List<View> {
val result: ArrayList<View> = ArrayList()
list.forEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ServerDrivenComponent>(relaxed = true)
private val value = listOf(serverDrivenComponent)
private val rootView = mockk<RootView>(relaxed = true)
private val origin = mockk<View>(relaxed = true)
private val viewGroup = mockk<ViewGroup>(relaxed = true)
private val context = mockk<AppCompatActivity>(relaxed = true)
private val view = mockk<BeagleFlexView>(relaxed = true)
private val beagleSdk = mockk<BeagleSdk>(relaxed = true)
private val id = "id"
private val viewFactoryMock = mockk<ViewFactory>(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<ViewGroup>(any()) } returns viewGroup
every { context.findViewById<ViewGroup>(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<ServerDrivenComponent>

//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) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -55,7 +57,19 @@ enum class Mode {
*/
data class AddChildren(
var componentId: String,
var value: List<ServerDrivenComponent>,
var value: Bind<List<ServerDrivenComponent>>,
var mode: Mode? = Mode.APPEND,
override var analytics: ActionAnalyticsConfig? = null
) : AnalyticsAction
) : AnalyticsAction {
constructor(
componentId: String,
value: List<ServerDrivenComponent>,
mode: Mode? = Mode.APPEND,
analytics: ActionAnalyticsConfig? = null
) : this(
componentId = componentId,
value = valueOf(value),
mode = mode,
analytics = analytics
)
}