From 19b350086508aeef9099ac03338a4f29d696204f Mon Sep 17 00:00:00 2001 From: Uzias Ferreira <63263091+uziasferreirazup@users.noreply.github.com> Date: Tue, 13 Oct 2020 10:40:51 -0300 Subject: [PATCH] fix: navigation crashing when open a invalid route and poptoview not working to first route. (#1027) * refactor: remove deprecated from constructor * fix: adjust crash when has invalid uri and adjust path to navigation * adjust in navigation * create method * adjust to internal * adjust internal * change method --- .../android/networking/HttpClientDefault.kt | 9 +++- .../beagle/android/utils/StringExtensions.kt | 3 ++ .../android/view/custom/BeagleNavigator.kt | 4 +- .../android/view/viewmodel/BeagleViewModel.kt | 5 ++- .../networking/HttpClientDefaultTest.kt | 20 +++++++++ .../android/view/BeagleNavigatorTest.kt | 15 +++++++ .../view/viewmodel/BeagleViewModelTest.kt | 43 +++++++++++++------ 7 files changed, 81 insertions(+), 18 deletions(-) diff --git a/android/beagle/src/main/java/br/com/zup/beagle/android/networking/HttpClientDefault.kt b/android/beagle/src/main/java/br/com/zup/beagle/android/networking/HttpClientDefault.kt index e3b76b96f4..19636ca224 100644 --- a/android/beagle/src/main/java/br/com/zup/beagle/android/networking/HttpClientDefault.kt +++ b/android/beagle/src/main/java/br/com/zup/beagle/android/networking/HttpClientDefault.kt @@ -67,7 +67,13 @@ internal class HttpClientDefault : HttpClient, CoroutineScope { private fun doHttpRequest( request: RequestData ): ResponseData { - val urlConnection = request.uri.toURL().openConnection() as HttpURLConnection + val urlConnection: HttpURLConnection + + try { + urlConnection = request.uri.toURL().openConnection() as HttpURLConnection + } catch (e: Exception) { + throw BeagleApiException(ResponseData(-1, data = byteArrayOf()), request) + } request.headers.forEach { urlConnection.setRequestProperty(it.key, it.value) @@ -98,7 +104,6 @@ internal class HttpClientDefault : HttpClient, CoroutineScope { return BeagleApiException(responseData, request) } - private fun addRequestMethod(urlConnection: HttpURLConnection, method: HttpMethod) { val methodValue = method.toString() diff --git a/android/beagle/src/main/java/br/com/zup/beagle/android/utils/StringExtensions.kt b/android/beagle/src/main/java/br/com/zup/beagle/android/utils/StringExtensions.kt index 94bcadf30d..866a26d608 100644 --- a/android/beagle/src/main/java/br/com/zup/beagle/android/utils/StringExtensions.kt +++ b/android/beagle/src/main/java/br/com/zup/beagle/android/utils/StringExtensions.kt @@ -17,6 +17,7 @@ package br.com.zup.beagle.android.utils import br.com.zup.beagle.android.logger.BeagleMessageLogs +import br.com.zup.beagle.android.setup.BeagleEnvironment internal fun String.toAndroidColor(): Int? = try { ColorUtils.hexColor(this) @@ -36,3 +37,5 @@ fun String.getExpressions(): List { } return expressions } + +internal fun String.removeBaseUrl(): String = this.removePrefix(BeagleEnvironment.beagleSdk.config.baseUrl) \ No newline at end of file diff --git a/android/beagle/src/main/java/br/com/zup/beagle/android/view/custom/BeagleNavigator.kt b/android/beagle/src/main/java/br/com/zup/beagle/android/view/custom/BeagleNavigator.kt index a62a6e48a6..229ad5a38a 100644 --- a/android/beagle/src/main/java/br/com/zup/beagle/android/view/custom/BeagleNavigator.kt +++ b/android/beagle/src/main/java/br/com/zup/beagle/android/view/custom/BeagleNavigator.kt @@ -26,6 +26,7 @@ import androidx.fragment.app.FragmentActivity import br.com.zup.beagle.android.action.Route import br.com.zup.beagle.android.logger.BeagleLoggerProxy import br.com.zup.beagle.android.setup.BeagleEnvironment +import br.com.zup.beagle.android.utils.removeBaseUrl import br.com.zup.beagle.android.view.BeagleActivity import br.com.zup.beagle.android.view.ScreenRequest import br.com.zup.beagle.android.widget.RootView @@ -86,7 +87,8 @@ internal object BeagleNavigator { fun popToView(context: Context, route: String) { if (context is AppCompatActivity) { - context.supportFragmentManager.popBackStack(route, 0) + val relativePath = route.removeBaseUrl() + context.supportFragmentManager.popBackStack(relativePath, 0) } } diff --git a/android/beagle/src/main/java/br/com/zup/beagle/android/view/viewmodel/BeagleViewModel.kt b/android/beagle/src/main/java/br/com/zup/beagle/android/view/viewmodel/BeagleViewModel.kt index 24e198de8e..110dc595b1 100644 --- a/android/beagle/src/main/java/br/com/zup/beagle/android/view/viewmodel/BeagleViewModel.kt +++ b/android/beagle/src/main/java/br/com/zup/beagle/android/view/viewmodel/BeagleViewModel.kt @@ -23,8 +23,10 @@ import br.com.zup.beagle.android.components.layout.ScreenComponent import br.com.zup.beagle.android.data.ComponentRequester import br.com.zup.beagle.android.exception.BeagleException import br.com.zup.beagle.android.logger.BeagleLoggerProxy +import br.com.zup.beagle.android.setup.BeagleEnvironment import br.com.zup.beagle.android.utils.BeagleRetry import br.com.zup.beagle.android.utils.CoroutineDispatchers +import br.com.zup.beagle.android.utils.removeBaseUrl import br.com.zup.beagle.android.view.ScreenRequest import br.com.zup.beagle.core.IdentifierComponent import br.com.zup.beagle.core.ServerDrivenComponent @@ -80,7 +82,8 @@ internal class BeagleViewModel( try { setLoading(true) val component = componentRequester.fetchComponent(screenRequest) - postLiveDataResponse(ViewState.DoRender(screenRequest.url, component)) + val relativePath = screenRequest.url.removeBaseUrl() + postLiveDataResponse(ViewState.DoRender(relativePath, component)) } catch (exception: BeagleException) { if (screen != null) { postLiveDataResponse(ViewState.DoRender(identifier, screen)) diff --git a/android/beagle/src/test/java/br/com/zup/beagle/android/networking/HttpClientDefaultTest.kt b/android/beagle/src/test/java/br/com/zup/beagle/android/networking/HttpClientDefaultTest.kt index 76418d688c..ff68218fdf 100644 --- a/android/beagle/src/test/java/br/com/zup/beagle/android/networking/HttpClientDefaultTest.kt +++ b/android/beagle/src/test/java/br/com/zup/beagle/android/networking/HttpClientDefaultTest.kt @@ -233,6 +233,26 @@ class HttpClientDefaultTest { }) } + @Test + fun `GIVEN request data with invalid uri when execute request THEN it should throw exception`() = runBlockingTest { + // Given + every { uri.toURL() } throws IllegalArgumentException("URI is not absolute") + val method = HttpMethod.GET + val requestData = RequestData( + uri = uri, + body = RandomData.string(), + method = method + ) + + // When + urlRequestDispatchingDefault.execute(requestData, onSuccess = {}, onError = { + // Then + assertEquals(-1, it.statusCode) + assertEquals(0, it.data.size) + assertEquals(0, it.headers.size) + }) + } + @Test fun execute_should_throw_IllegalStateException_when_data_is_set_for_HttpMethod_DELETE() = runBlockingTest { // Given diff --git a/android/beagle/src/test/java/br/com/zup/beagle/android/view/BeagleNavigatorTest.kt b/android/beagle/src/test/java/br/com/zup/beagle/android/view/BeagleNavigatorTest.kt index c4b1af2a29..feec38abca 100644 --- a/android/beagle/src/test/java/br/com/zup/beagle/android/view/BeagleNavigatorTest.kt +++ b/android/beagle/src/test/java/br/com/zup/beagle/android/view/BeagleNavigatorTest.kt @@ -242,6 +242,21 @@ class BeagleNavigatorTest { verify(exactly = once()) { context.supportFragmentManager.popBackStack(url, 0) } } + @Test + fun `GIVEN pop to view with full url WHEN call pop to view THEN format url to relative()`() { + // Given + every { context.supportFragmentManager.popBackStack("/test", 0) } just Runs + every { BeagleEnvironment.beagleSdk.config.baseUrl } returns url + val url = "$url/test" + + // When + BeagleNavigator.popToView(context, url) + + // Then + verify(exactly = once()) { context.supportFragmentManager.popBackStack("/test", 0) } + } + + @Test fun pushStack_should_start_BeagleActivity() { // Given diff --git a/android/beagle/src/test/java/br/com/zup/beagle/android/view/viewmodel/BeagleViewModelTest.kt b/android/beagle/src/test/java/br/com/zup/beagle/android/view/viewmodel/BeagleViewModelTest.kt index e7b16699b0..064ad84b17 100644 --- a/android/beagle/src/test/java/br/com/zup/beagle/android/view/viewmodel/BeagleViewModelTest.kt +++ b/android/beagle/src/test/java/br/com/zup/beagle/android/view/viewmodel/BeagleViewModelTest.kt @@ -18,6 +18,7 @@ package br.com.zup.beagle.android.view.viewmodel import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer +import br.com.zup.beagle.android.BaseTest import br.com.zup.beagle.android.action.Action import br.com.zup.beagle.android.components.layout.ScreenComponent import br.com.zup.beagle.android.data.ActionRequester @@ -45,7 +46,7 @@ import org.junit.Rule import org.junit.Test @ExperimentalCoroutinesApi -class BeagleViewModelTest { +class BeagleViewModelTest : BaseTest() { @get:Rule val rule = InstantTaskExecutorRule() @@ -70,11 +71,10 @@ class BeagleViewModelTest { private lateinit var beagleUIViewModel: BeagleViewModel - private val slotViewState = mutableListOf() + private val slotViewState = mutableListOf() - @Before - fun setUp() { - MockKAnnotations.init(this) + override fun setUp() { + super.setUp(); beagleUIViewModel = BeagleViewModel(componentRequester = componentRequester) @@ -144,7 +144,7 @@ class BeagleViewModelTest { } @Test - fun `GIVEN a ServerDrivenComponent WHEN fetchComponents called SHOULD post ViewState doRender `(){ + fun `GIVEN a ServerDrivenComponent WHEN fetchComponents called SHOULD post ViewState doRender `() { //GIVEN val screenRequest = ScreenRequest("") @@ -156,12 +156,12 @@ class BeagleViewModelTest { } @Test - fun `GIVEN a IdentifierComponent WHEN fetchComponents called SHOULD post ViewState doRender `(){ + fun `GIVEN a IdentifierComponent WHEN fetchComponents called SHOULD post ViewState doRender `() { //GIVEN val screenRequest = ScreenRequest("") - val component : IdentifierComponent = mockk() + val component: IdentifierComponent = mockk() val id = "id" - every {component.id} returns id + every { component.id } returns id //WHEN beagleUIViewModel.fetchComponent(screenRequest, component).observeForever(observer) @@ -171,7 +171,7 @@ class BeagleViewModelTest { } @Test - fun `GIVEN a NULL ScreenComponent WHEN fetchComponents called SHOULD post ViewState doRender `(){ + fun `GIVEN a NULL ScreenComponent WHEN fetchComponents called SHOULD post ViewState doRender `() { //GIVEN val screenRequest = ScreenRequest("url") @@ -184,15 +184,29 @@ class BeagleViewModelTest { } @Test - fun `GIIVEN a ScreenComponent WHEN fetchComponent called SHOULD use identifier as screenId on ViewState doRender`(){ + fun `GIVEN screen with full path WHEN fetchComponents called SHOULD post ViewState doRender with correct screen id`() { + //GIVEN + every { beagleSdk.config.baseUrl } returns "http://localhost:2020/" + + val screenRequest = ScreenRequest("http://localhost:2020/test") + + //WHEN + beagleUIViewModel.fetchComponent(screenRequest, null).observeForever(observer) + + //THEN + verify(exactly = once()) { observer.onChanged(ViewState.DoRender("test", component)) } + } + + @Test + fun `GIIVEN a ScreenComponent WHEN fetchComponent called SHOULD use identifier as screenId on ViewState doRender`() { //Given val screenRequest = ScreenRequest("") - val component : ScreenComponent = mockk() + val component: ScreenComponent = mockk() val id = "id" val identifier = "identifier" - every {component.id} returns id - every {component.identifier} returns identifier + every { component.id } returns id + every { component.identifier } returns identifier //WHEN beagleUIViewModel.fetchComponent(screenRequest, component).observeForever(observer) @@ -201,4 +215,5 @@ class BeagleViewModelTest { verify(exactly = once()) { observer.onChanged(ViewState.DoRender(identifier, component)) } } + }