diff --git a/mvi-presenter/src/main/java/pl/valueadd/mvi/exception/ViewNotAttachedException.kt b/mvi-presenter/src/main/java/pl/valueadd/mvi/exception/ViewNotAttachedException.kt index 282cee6..824f694 100644 --- a/mvi-presenter/src/main/java/pl/valueadd/mvi/exception/ViewNotAttachedException.kt +++ b/mvi-presenter/src/main/java/pl/valueadd/mvi/exception/ViewNotAttachedException.kt @@ -1,4 +1,4 @@ package pl.valueadd.mvi.exception -internal class ViewNotAttachedException : +class ViewNotAttachedException internal constructor() : RuntimeException("View was called before that has been attached to presenter") \ No newline at end of file diff --git a/mvi-presenter/src/main/java/pl/valueadd/mvi/exception/ViewWasNotDetachedException.kt b/mvi-presenter/src/main/java/pl/valueadd/mvi/exception/ViewWasNotDetachedException.kt index 7668abd..fd366cb 100644 --- a/mvi-presenter/src/main/java/pl/valueadd/mvi/exception/ViewWasNotDetachedException.kt +++ b/mvi-presenter/src/main/java/pl/valueadd/mvi/exception/ViewWasNotDetachedException.kt @@ -1,5 +1,4 @@ package pl.valueadd.mvi.exception -import java.lang.RuntimeException - -internal class ViewWasNotDetachedException : RuntimeException("Detach previous view first.") \ No newline at end of file +class ViewWasNotDetachedException internal constructor() : + RuntimeException("Detach previous view first.") \ No newline at end of file diff --git a/mvi-presenter/src/main/java/pl/valueadd/mvi/presenter/BaseMviPresenter.kt b/mvi-presenter/src/main/java/pl/valueadd/mvi/presenter/BaseMviPresenter.kt index d5d7bf2..9eb13be 100644 --- a/mvi-presenter/src/main/java/pl/valueadd/mvi/presenter/BaseMviPresenter.kt +++ b/mvi-presenter/src/main/java/pl/valueadd/mvi/presenter/BaseMviPresenter.kt @@ -29,7 +29,9 @@ abstract class BaseMviPresenter by lazy { BehaviorSubject.createDefault(currentState) @@ -89,6 +91,16 @@ abstract class BaseMviPresenter> { + fun initializeState(view: V) + fun attachView(view: V) fun detachView() diff --git a/mvi-presenter/src/test/java/pl/valueadd/mvi/presenter/BaseMviPresenterTest.kt b/mvi-presenter/src/test/java/pl/valueadd/mvi/presenter/BaseMviPresenterTest.kt index 67eca80..2d986f5 100644 --- a/mvi-presenter/src/test/java/pl/valueadd/mvi/presenter/BaseMviPresenterTest.kt +++ b/mvi-presenter/src/test/java/pl/valueadd/mvi/presenter/BaseMviPresenterTest.kt @@ -8,7 +8,8 @@ import io.mockk.verify import io.reactivex.Observable import io.reactivex.schedulers.Schedulers import io.reactivex.subjects.PublishSubject -import org.junit.jupiter.api.Assertions +/* ktlint-disable no-wildcard-imports */ +import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -47,11 +48,12 @@ class BaseMviPresenterTest { val firstView = createMockView(Observable.never()) val secondView: IBaseView = mockk() + presenter.initializeState(firstView) presenter.attachView(firstView) // When // Then - Assertions.assertThrows(ViewWasNotDetachedException::class.java) { + assertThrows(ViewWasNotDetachedException::class.java) { presenter.attachView(secondView) } } @@ -61,6 +63,7 @@ class BaseMviPresenterTest { // Given val viewIntentsSubject = PublishSubject.create() val mockView = createMockView(viewIntentsSubject) + presenter.initializeState(mockView) // When presenter.attachView(mockView) @@ -75,6 +78,7 @@ class BaseMviPresenterTest { // Given val viewIntentsSubject = PublishSubject.create() val mockView = createMockView(viewIntentsSubject) + presenter.initializeState(mockView) presenter.attachView(mockView) // When @@ -82,7 +86,7 @@ class BaseMviPresenterTest { // Then verify(exactly = 1) { mockView.provideViewIntents() } - assert(viewIntentsSubject.hasObservers() == false) + assertFalse(viewIntentsSubject.hasObservers()) } @Test @@ -94,6 +98,7 @@ class BaseMviPresenterTest { val mockView = createMockView(Observable.never()) every { mockReducer.reduce(any(), testPresenterPartialState) } returns expectedTestViewState + presenter.initializeState(mockView) presenter.attachView(mockView) // When @@ -117,6 +122,7 @@ class BaseMviPresenterTest { val reducedViewState = TestViewState(1) every { mockMapper.mapViewIntentToPartialState(testViewIntent) } returns testPartialStatePublishSubject every { mockReducer.reduce(any(), testPartialState) } returns reducedViewState + presenter.initializeState(mockView) presenter.attachView(mockView) viewIntentsSubject.onNext(testViewIntent) // For example user press login button @@ -139,6 +145,7 @@ class BaseMviPresenterTest { every { mockMapper.mapViewIntentToPartialState(testViewIntent) } returns testPartialStatePublishSubject every { mockReducer.reduce(any(), testPartialState) } returns reducedViewState + presenter.initializeState(mockView) presenter.attachView(mockView) viewIntentsSubject.onNext(testViewIntent) // For example user press login button presenter.detachView() // View of fragment is destroyed by system @@ -161,6 +168,7 @@ class BaseMviPresenterTest { val mockView = createMockView(viewIntentsSubject) every { mockMapper.mapViewIntentToPartialState(testViewIntent) } returns testPartialStatePublishSubject every { mockReducer.reduce(any(), testPartialState) } returns reducedViewState + presenter.initializeState(mockView) presenter.attachView(mockView) viewIntentsSubject.onNext(testViewIntent) // For example user press login button @@ -186,6 +194,7 @@ class BaseMviPresenterTest { every { mockMapper.mapViewIntentToPartialState(testViewIntent) } returns testPartialStatePublishSubject every { mockReducer.reduce(any(), testPartialState) } returns reducedViewState + presenter.initializeState(mockView) presenter.attachView(mockView) viewIntentsSubject.onNext(testViewIntent) // For example user press login button @@ -208,6 +217,7 @@ class BaseMviPresenterTest { every { mockMapper.mapViewIntentToPartialState(testViewIntent) } returns testPartialStatePublishSubject every { mockReducer.reduce(any(), testPartialState) } returns reducedViewState + presenter.initializeState(mockView) presenter.attachView(mockView) viewIntentsSubject.onNext(testViewIntent) // For example user press login button @@ -215,8 +225,8 @@ class BaseMviPresenterTest { presenter.destroy() // Then - assert(testPartialStatePublishSubject.hasObservers() == false) - assert(presenterPublishSubject.hasObservers() == false) + assertFalse(testPartialStatePublishSubject.hasObservers()) + assertFalse(presenterPublishSubject.hasObservers()) } @Test @@ -228,6 +238,7 @@ class BaseMviPresenterTest { every { mockThrowable.stackTrace } returns emptyArray() every { mockThrowable.cause } returns null every { mockTestLogger.logError(any()) } returns Unit + presenter.initializeState(mockView) presenter.attachView(mockView) // When @@ -246,6 +257,7 @@ class BaseMviPresenterTest { every { mockThrowable.stackTrace } returns emptyArray() every { mockThrowable.cause } returns null every { mockTestLogger.logError(any()) } returns Unit + presenter.initializeState(mockView) presenter.attachView(mockView) // When diff --git a/mvi/build.gradle b/mvi/build.gradle index ba66a81..5ae0ef8 100644 --- a/mvi/build.gradle +++ b/mvi/build.gradle @@ -27,6 +27,7 @@ android { unitTests.all { useJUnitPlatform() } + unitTests.returnDefaultValues = true } buildTypes { diff --git a/mvi/src/main/java/pl/valueadd/mvi/fragment/delegate/fragment/MviFragmentDelegateImpl.kt b/mvi/src/main/java/pl/valueadd/mvi/fragment/delegate/fragment/MviFragmentDelegateImpl.kt index 119acb7..ce52020 100644 --- a/mvi/src/main/java/pl/valueadd/mvi/fragment/delegate/fragment/MviFragmentDelegateImpl.kt +++ b/mvi/src/main/java/pl/valueadd/mvi/fragment/delegate/fragment/MviFragmentDelegateImpl.kt @@ -1,6 +1,7 @@ package pl.valueadd.mvi.fragment.delegate.fragment import android.os.Bundle +import androidx.annotation.CallSuper import pl.valueadd.mvi.presenter.BaseMviPresenter import pl.valueadd.mvi.presenter.IBaseView @@ -14,22 +15,27 @@ open class MviFragmentDelegateImpl>( protected val presenter: BaseMviPresenter<*, *, *, V> ) : MviFragmentDelegate { + @CallSuper override fun onCreate(savedInstanceState: Bundle?) { - // no-op + presenter.initializeState(fragment) } + @CallSuper override fun onSaveInstanceState(outState: Bundle) { // no-op } + @CallSuper override fun onStart() { presenter.attachView(fragment) } + @CallSuper override fun onStop() { presenter.detachView() } + @CallSuper override fun onDestroy() { presenter.destroy() } diff --git a/mvi/src/main/java/pl/valueadd/mvi/fragment/delegate/fragment/MviFragmentSaveInstanceStateDelegateImpl.kt b/mvi/src/main/java/pl/valueadd/mvi/fragment/delegate/fragment/MviFragmentSaveInstanceStateDelegateImpl.kt index f37d51a..b3d09b3 100644 --- a/mvi/src/main/java/pl/valueadd/mvi/fragment/delegate/fragment/MviFragmentSaveInstanceStateDelegateImpl.kt +++ b/mvi/src/main/java/pl/valueadd/mvi/fragment/delegate/fragment/MviFragmentSaveInstanceStateDelegateImpl.kt @@ -26,11 +26,13 @@ class MviFragmentSaveInstanceStateDelegateImpl, VS : IBaseV private set override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) this.restoredViewState = savedInstanceState?.getParcelable(VIEW_STATE_BUNDLE_KEY) } override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) outState.putParcelable( VIEW_STATE_BUNDLE_KEY, presenter.currentState as Parcelable diff --git a/mvi/src/test/java/pl/valueadd/mvi/fragment/base/BaseMviFragmentTest.kt b/mvi/src/test/java/pl/valueadd/mvi/fragment/base/BaseMviFragmentTest.kt index db88be0..83be3fb 100644 --- a/mvi/src/test/java/pl/valueadd/mvi/fragment/base/BaseMviFragmentTest.kt +++ b/mvi/src/test/java/pl/valueadd/mvi/fragment/base/BaseMviFragmentTest.kt @@ -1,11 +1,11 @@ package pl.valueadd.mvi.fragment.base -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify +/* ktlint-disable no-wildcard-imports */ +import io.mockk.* import io.reactivex.Observable import io.reactivex.schedulers.Schedulers import kotlinx.android.parcel.Parcelize +import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import pl.valueadd.mvi.IBaseViewState @@ -27,6 +27,26 @@ class BaseMviFragmentTest { fragment.presenter = mockPresenter } + @AfterEach + fun tearDown() { + clearAllMocks() + } + + @Test + fun `Should call initialize state on presenter on create`() { + // Given + val mockActivity = mockk(relaxed = true) { + every { supportDelegate } returns mockk(relaxed = true) + } + fragment.onAttach(mockActivity) + + // When + fragment.onCreate(null) + + // Then + verify(exactly = 1) { mockPresenter.initializeState(fragment) } + } + @Test fun `Should attach view to presenter on start`() { // Given