This repository has been archived by the owner on Jun 1, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Android pull to refresh component (#1636)
* feat: add Android PullToRefresh Signed-off-by: Paulo Cesar Meurer <[email protected]> * feat: add Android PullToRefresh component Signed-off-by: Paulo Cesar Meurer <[email protected]> * chore: add PullToRefresh serialization tests Signed-off-by: Paulo Cesar Meurer <[email protected]> * chore: organized imports Signed-off-by: Paulo Cesar Meurer <[email protected]> * chore: updated BFF sample Signed-off-by: Paulo Cesar Meurer <[email protected]> * Synced Spring BFF sample to Micronaut BFF sample. Signed-off-by: Beagle <[email protected]> * refactor: refactored code style Signed-off-by: Paulo Cesar Meurer <[email protected]> * Synced Spring BFF sample to Micronaut BFF sample. Signed-off-by: Beagle <[email protected]> * chore: updated appium android app dependencies Signed-off-by: Paulo Cesar Meurer <[email protected]> * chore: improved PullToRefresh tests Signed-off-by: Paulo Cesar Meurer <[email protected]> * refactor: changed PullToRefresh contract Signed-off-by: Paulo Cesar Meurer <[email protected]> * refactor: changed PullToRefresh contract Signed-off-by: Paulo Cesar Meurer <[email protected]> * refactor: refactored some tests Signed-off-by: Paulo Cesar Meurer <[email protected]> * refactor: add backend serializer test Signed-off-by: Paulo Cesar Meurer <[email protected]> Co-authored-by: Beagle <[email protected]>
- Loading branch information
1 parent
52ee644
commit e89fc28
Showing
32 changed files
with
1,138 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
android/beagle/src/main/java/br/com/zup/beagle/android/components/refresh/PullToRefresh.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/* | ||
* Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package br.com.zup.beagle.android.components.refresh | ||
|
||
import android.graphics.Color | ||
import android.view.View | ||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout | ||
import br.com.zup.beagle.android.action.Action | ||
import br.com.zup.beagle.android.context.Bind | ||
import br.com.zup.beagle.android.context.ContextComponent | ||
import br.com.zup.beagle.android.context.ContextData | ||
import br.com.zup.beagle.android.context.valueOfNullable | ||
import br.com.zup.beagle.android.utils.handleEvent | ||
import br.com.zup.beagle.android.utils.observeBindChanges | ||
import br.com.zup.beagle.android.view.ViewFactory | ||
import br.com.zup.beagle.android.widget.RootView | ||
import br.com.zup.beagle.android.widget.WidgetView | ||
import br.com.zup.beagle.annotation.RegisterWidget | ||
import br.com.zup.beagle.core.ServerDrivenComponent | ||
import br.com.zup.beagle.core.SingleChildComponent | ||
|
||
@RegisterWidget("pullToRefresh") | ||
data class PullToRefresh constructor( | ||
override val context: ContextData? = null, | ||
val onPull: List<Action>, | ||
val isRefreshing: Bind<Boolean>? = null, | ||
val color: Bind<String>? = null, | ||
override val child: ServerDrivenComponent, | ||
) : WidgetView(), ContextComponent, SingleChildComponent { | ||
|
||
constructor( | ||
context: ContextData? = null, | ||
onPull: List<Action>, | ||
isRefreshing: Bind<Boolean>? = null, | ||
color: String? = null, | ||
child: ServerDrivenComponent, | ||
) : this( | ||
context = context, | ||
onPull = onPull, | ||
isRefreshing = isRefreshing, | ||
color = valueOfNullable(color), | ||
child = child, | ||
) | ||
|
||
@Transient | ||
private val viewFactory = ViewFactory() | ||
|
||
override fun buildView(rootView: RootView): View { | ||
return viewFactory.makeSwipeRefreshLayout(rootView.getContext()).apply { | ||
addView(buildChildView(rootView)) | ||
setOnRefreshListener { | ||
handleEvent(rootView, this, onPull) | ||
} | ||
observeRefreshState(rootView, this) | ||
observeColor(rootView, this) | ||
} | ||
} | ||
|
||
private fun buildChildView(rootView: RootView) = viewFactory.makeBeagleFlexView(rootView).apply { | ||
addView(child, false) | ||
} | ||
|
||
private fun observeRefreshState(rootView: RootView, view: SwipeRefreshLayout) { | ||
isRefreshing?.let { bind -> | ||
observeBindChanges(rootView, view, bind) { | ||
view.isRefreshing = it ?: false | ||
} | ||
} | ||
} | ||
|
||
private fun observeColor(rootView: RootView, view: SwipeRefreshLayout) { | ||
color?.let { bind -> | ||
observeBindChanges(rootView, view, bind) { | ||
view.setColorSchemeColors(Color.parseColor(it)) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
254 changes: 254 additions & 0 deletions
254
...id/beagle/src/test/java/br/com/zup/beagle/android/components/refresh/PullToRefreshTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,254 @@ | ||
/* | ||
* Copyright 2020 ZUP IT SERVICOS EM TECNOLOGIA E INOVACAO SA | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package br.com.zup.beagle.android.components.refresh | ||
|
||
import android.graphics.Color | ||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout | ||
import br.com.zup.beagle.android.action.Action | ||
import br.com.zup.beagle.android.components.BaseComponentTest | ||
import br.com.zup.beagle.android.context.Bind | ||
import br.com.zup.beagle.android.context.ContextData | ||
import br.com.zup.beagle.android.utils.Observer | ||
import br.com.zup.beagle.android.utils.observeBindChanges | ||
import br.com.zup.beagle.android.view.ViewFactory | ||
import br.com.zup.beagle.core.ServerDrivenComponent | ||
import io.mockk.Runs | ||
import io.mockk.every | ||
import io.mockk.just | ||
import io.mockk.mockk | ||
import io.mockk.mockkStatic | ||
import io.mockk.slot | ||
import io.mockk.verify | ||
import org.junit.jupiter.api.Assertions | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.DisplayName | ||
import org.junit.jupiter.api.Nested | ||
import org.junit.jupiter.api.Test | ||
|
||
@DisplayName("Given a PullToRefresh") | ||
class PullToRefreshTest : BaseComponentTest() { | ||
|
||
private val swipeRefreshLayout: SwipeRefreshLayout = mockk(relaxed = true) | ||
private val context: ContextData = mockk(relaxed = true) | ||
private val action: Action = mockk(relaxed = true) | ||
private val onPullActions = listOf(action) | ||
private val isRefreshing: Bind<Boolean> = mockk(relaxed = true) | ||
private val color: Bind<String> = mockk(relaxed = true) | ||
private val child = mockk<ServerDrivenComponent>() | ||
private val listenerSlot = slot<SwipeRefreshLayout.OnRefreshListener>() | ||
private val booleanObserverSlot = slot<Observer<Boolean?>>() | ||
private val stringObserverSlot = slot<Observer<String?>>() | ||
private lateinit var pullToRefreshComponent: PullToRefresh | ||
|
||
@BeforeEach | ||
override fun setUp() { | ||
super.setUp() | ||
|
||
mockkStatic("br.com.zup.beagle.android.utils.WidgetExtensionsKt") | ||
mockkStatic(Color::class) | ||
|
||
every { anyConstructed<ViewFactory>().makeSwipeRefreshLayout(any()) } returns swipeRefreshLayout | ||
every { swipeRefreshLayout.setOnRefreshListener(capture(listenerSlot)) } just Runs | ||
|
||
pullToRefreshComponent = PullToRefresh( | ||
context, | ||
onPullActions, | ||
isRefreshing, | ||
color, | ||
child | ||
) | ||
} | ||
|
||
@DisplayName("When build view") | ||
@Nested | ||
inner class PullToRefreshBuildTest { | ||
|
||
@Test | ||
@DisplayName("Then should create correct pullToRefresh") | ||
fun testBuildCorrectPullToRefresh() { | ||
// When | ||
val view = pullToRefreshComponent.buildView(rootView) | ||
|
||
// Then | ||
Assertions.assertTrue(view is SwipeRefreshLayout) | ||
verify(exactly = 1) { anyConstructed<ViewFactory>().makeSwipeRefreshLayout(rootView.getContext()) } | ||
} | ||
|
||
@Test | ||
@DisplayName("Then should set RefreshListener on pullToRefresh") | ||
fun testBuildSetRefreshListener() { | ||
// When | ||
pullToRefreshComponent.buildView(rootView) | ||
|
||
// Then | ||
verify(exactly = 1) { swipeRefreshLayout.setOnRefreshListener(any()) } | ||
} | ||
|
||
@Test | ||
@DisplayName("Then should observe isRefreshing changes") | ||
fun testBuildObserveIsRefreshing() { | ||
// When | ||
pullToRefreshComponent.buildView(rootView) | ||
|
||
// Then | ||
verify(exactly = 1) { | ||
pullToRefreshComponent.observeBindChanges(rootView, swipeRefreshLayout, isRefreshing, captureLambda()) | ||
} | ||
} | ||
|
||
@Test | ||
@DisplayName("Then should observe color changes") | ||
fun testBuildObserveColor() { | ||
// When | ||
pullToRefreshComponent.buildView(rootView) | ||
|
||
// Then | ||
verify(exactly = 1) { | ||
pullToRefreshComponent.observeBindChanges(rootView, swipeRefreshLayout, color, captureLambda()) | ||
} | ||
} | ||
|
||
@Test | ||
@DisplayName("Then should add child") | ||
fun testBuildAddChild() { | ||
// When | ||
pullToRefreshComponent.buildView(rootView) | ||
|
||
// Then | ||
verify(exactly = 1) { | ||
beagleFlexView.addView(child, false) | ||
} | ||
} | ||
} | ||
|
||
@DisplayName("When build view with null params") | ||
@Nested | ||
inner class PullToRefreshBuildWithNullParamsTest { | ||
|
||
@Test | ||
@DisplayName("Then should not observe isRefreshing changes") | ||
fun testBuildNotObserveIsRefreshing() { | ||
// Given | ||
pullToRefreshComponent = PullToRefresh( | ||
context, | ||
onPullActions, | ||
null, | ||
"#FF0000", | ||
child | ||
) | ||
|
||
// When | ||
pullToRefreshComponent.buildView(rootView) | ||
|
||
// Then | ||
verify(exactly = 0) { | ||
pullToRefreshComponent.observeBindChanges(rootView, swipeRefreshLayout, isRefreshing, captureLambda()) | ||
} | ||
} | ||
|
||
@Test | ||
@DisplayName("Then should not observe color changes") | ||
fun testBuildNotObserveColor() { | ||
// Given | ||
pullToRefreshComponent = PullToRefresh( | ||
context, | ||
onPullActions, | ||
isRefreshing, | ||
null as String?, | ||
child | ||
) | ||
|
||
// When | ||
pullToRefreshComponent.buildView(rootView) | ||
|
||
// Then | ||
verify(exactly = 0) { | ||
pullToRefreshComponent.observeBindChanges(rootView, swipeRefreshLayout, color, captureLambda()) | ||
} | ||
} | ||
} | ||
|
||
@DisplayName("When onRefresh triggered") | ||
@Nested | ||
inner class PullToRefreshOnRefreshTest { | ||
|
||
@Test | ||
@DisplayName("Then should call onPull actions") | ||
fun testTriggerOnRefresh() { | ||
|
||
// When | ||
pullToRefreshComponent.buildView(rootView) | ||
listenerSlot.captured.onRefresh() | ||
|
||
verify(exactly = 1) { action.execute(rootView, swipeRefreshLayout) } | ||
} | ||
} | ||
|
||
@DisplayName("When refresh state change") | ||
@Nested | ||
inner class PullToRefreshRefreshStateChangeTest { | ||
|
||
@Test | ||
@DisplayName("Then should update refresh state to true") | ||
fun testChangeIsRefreshingToTrue() { | ||
testIsRefreshingStateChange(true) | ||
} | ||
|
||
@Test | ||
@DisplayName("Then should update refresh state to false") | ||
fun testChangeIsRefreshingToFalse() { | ||
testIsRefreshingStateChange(false) | ||
} | ||
|
||
@Test | ||
@DisplayName("Then should update refresh state to false when isRefreshing evaluates to null") | ||
fun testChangeIsRefreshingToFalseWhenEvaluatesNull() { | ||
testIsRefreshingStateChange(null) | ||
} | ||
|
||
private fun testIsRefreshingStateChange(refreshing: Boolean?) { | ||
every { pullToRefreshComponent.observeBindChanges(rootView, swipeRefreshLayout, isRefreshing, capture(booleanObserverSlot)) } just Runs | ||
|
||
// When | ||
pullToRefreshComponent.buildView(rootView) | ||
booleanObserverSlot.captured.invoke(refreshing) | ||
|
||
verify(exactly = 1) { swipeRefreshLayout.isRefreshing = refreshing ?: false } | ||
} | ||
} | ||
|
||
@DisplayName("When color state change") | ||
@Nested | ||
inner class PullToRefreshColorStateChangeTest { | ||
|
||
@Test | ||
@DisplayName("Then should update the correct color") | ||
fun testChangeColorState() { | ||
// Given | ||
val colorString = "#FF0000" | ||
every { pullToRefreshComponent.observeBindChanges(rootView, swipeRefreshLayout, color, capture(stringObserverSlot)) } just Runs | ||
every { Color.parseColor(colorString) } returns -65536 | ||
val expectedColor = Color.parseColor(colorString) | ||
|
||
// When | ||
pullToRefreshComponent.buildView(rootView) | ||
stringObserverSlot.captured.invoke(colorString) | ||
|
||
verify(exactly = 1) { swipeRefreshLayout.setColorSchemeColors(expectedColor) } | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.