From 556e36a2175d9791138a6a0d1e26598ece10447d Mon Sep 17 00:00:00 2001 From: Sasikanth Date: Wed, 19 Feb 2025 07:12:29 +0530 Subject: [PATCH] Add `CholesterolEntrySheet` (#5275) **Story:** https://app.shortcut.com/simpledotorg/story/14623/add-cholesterol-entry-sheet --- CHANGELOG.md | 3 +- .../clinic/main/TheActivityComponent.kt | 4 +- .../clinic/medicalhistory/MedicalHistory.kt | 4 + .../addcholesterol/CholesterolEntryEffect.kt | 17 ++ .../CholesterolEntryEffectHandler.kt | 54 ++++++ .../addcholesterol/CholesterolEntryEvent.kt | 15 ++ .../addcholesterol/CholesterolEntryModel.kt | 37 ++++ .../CholesterolEntrySaveState.kt | 6 + .../addcholesterol/CholesterolEntrySheet.kt | 161 ++++++++++++++++++ .../addcholesterol/CholesterolEntryUi.kt | 6 + .../CholesterolEntryUiActions.kt | 8 + .../CholesterolEntryUiRenderer.kt | 15 ++ .../addcholesterol/CholesterolEntryUpdate.kt | 57 +++++++ .../CholesterolEntryViewEffectHandler.kt | 17 ++ .../res/layout/sheet_cholesterol_entry.xml | 90 ++++++++++ app/src/main/res/values/strings.xml | 5 + .../CholesterolEntryEffectHandlerTest.kt | 100 +++++++++++ .../CholesterolEntryUiRendererTest.kt | 49 ++++++ .../CholesterolEntryUpdateTest.kt | 99 +++++++++++ 19 files changed, 745 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffect.kt create mode 100644 app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffectHandler.kt create mode 100644 app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEvent.kt create mode 100644 app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryModel.kt create mode 100644 app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntrySaveState.kt create mode 100644 app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntrySheet.kt create mode 100644 app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUi.kt create mode 100644 app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiActions.kt create mode 100644 app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiRenderer.kt create mode 100644 app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUpdate.kt create mode 100644 app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryViewEffectHandler.kt create mode 100644 app/src/main/res/layout/sheet_cholesterol_entry.xml create mode 100644 app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffectHandlerTest.kt create mode 100644 app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiRendererTest.kt create mode 100644 app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUpdateTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 99ab4d12a33..c7460cbda6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,7 +64,8 @@ - Add `cholesterol_value` to `MedicalHistory` table - Bump AGP to v8.8.1 - Fix JSON variable name in non-lab based statin calculation sheet -- Add `LabBasedCVDRiskCalculator` and effect to calculate lab based cvd risk. +- Add `LabBasedCVDRiskCalculator` and effect to calculate lab based cvd risk +- Add `CholesterolEntrySheet` ### Fixes diff --git a/app/src/main/java/org/simple/clinic/main/TheActivityComponent.kt b/app/src/main/java/org/simple/clinic/main/TheActivityComponent.kt index 88fdf270288..1e41fbb67e2 100644 --- a/app/src/main/java/org/simple/clinic/main/TheActivityComponent.kt +++ b/app/src/main/java/org/simple/clinic/main/TheActivityComponent.kt @@ -71,6 +71,7 @@ import org.simple.clinic.security.pin.PinEntryCardView import org.simple.clinic.settings.SettingsScreen import org.simple.clinic.settings.changelanguage.ChangeLanguageScreen import org.simple.clinic.summary.PatientSummaryScreen +import org.simple.clinic.summary.addcholesterol.CholesterolEntrySheet import org.simple.clinic.summary.addphone.AddPhoneNumberDialog import org.simple.clinic.summary.assignedfacility.AssignedFacilityView import org.simple.clinic.summary.bloodpressures.view.BloodPressureSummaryViewInjector @@ -169,7 +170,8 @@ interface TheActivityComponent : MonthlyReportsScreen.Injector, MonthlyReportCompleteScreen.Injector, ReassignPatientSheet.Injector, - BMIEntrySheet.Injector { + BMIEntrySheet.Injector, + CholesterolEntrySheet.Injector { fun inject(target: TheActivity) @Subcomponent.Factory diff --git a/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistory.kt b/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistory.kt index 4ea4e167e4f..0842f165093 100644 --- a/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistory.kt +++ b/app/src/main/java/org/simple/clinic/medicalhistory/MedicalHistory.kt @@ -85,6 +85,10 @@ data class MedicalHistory( } } + fun cholesterolChanged(cholesterol: Float): MedicalHistory { + return copy(cholesterol = cholesterol) + } + @Dao interface RoomDao { diff --git a/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffect.kt b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffect.kt new file mode 100644 index 00000000000..d87d6130fe5 --- /dev/null +++ b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffect.kt @@ -0,0 +1,17 @@ +package org.simple.clinic.summary.addcholesterol + +import java.util.UUID + +sealed interface CholesterolEntryEffect + +data class SaveCholesterol(val patientUuid: UUID, val cholesterolValue: Float) : CholesterolEntryEffect + +sealed interface CholesterolEntryViewEffect : CholesterolEntryEffect + +data object HideCholesterolErrorMessage : CholesterolEntryViewEffect + +data object ShowReqMinCholesterolValidationError : CholesterolEntryViewEffect + +data object ShowReqMaxCholesterolValidationError : CholesterolEntryViewEffect + +data object DismissSheet : CholesterolEntryViewEffect diff --git a/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffectHandler.kt b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffectHandler.kt new file mode 100644 index 00000000000..c76aee82837 --- /dev/null +++ b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffectHandler.kt @@ -0,0 +1,54 @@ +package org.simple.clinic.summary.addcholesterol + +import com.spotify.mobius.functions.Consumer +import com.spotify.mobius.rx2.RxMobius +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import io.reactivex.ObservableTransformer +import org.simple.clinic.medicalhistory.MedicalHistoryRepository +import org.simple.clinic.util.UtcClock +import org.simple.clinic.util.scheduler.SchedulersProvider +import org.simple.clinic.uuid.UuidGenerator +import java.time.Instant + +class CholesterolEntryEffectHandler @AssistedInject constructor( + private val clock: UtcClock, + private val uuidGenerator: UuidGenerator, + private val medicalHistoryRepository: MedicalHistoryRepository, + private val schedulersProvider: SchedulersProvider, + @Assisted private val viewEffectConsumer: Consumer +) { + + @AssistedFactory + interface Factory { + fun create(viewEffectConsumer: Consumer): CholesterolEntryEffectHandler + } + + fun build(): ObservableTransformer { + return RxMobius + .subtypeEffectHandler() + .addTransformer(SaveCholesterol::class.java, saveCholesterol()) + .addConsumer(CholesterolEntryViewEffect::class.java, viewEffectConsumer::accept) + .build() + } + + private fun saveCholesterol(): ObservableTransformer { + return ObservableTransformer { effects -> + effects + .observeOn(schedulersProvider.io()) + .map { effect -> + val medicalHistory = medicalHistoryRepository.historyForPatientOrDefaultImmediate( + defaultHistoryUuid = uuidGenerator.v4(), + patientUuid = effect.patientUuid + ) + + medicalHistoryRepository.save( + medicalHistory.cholesterolChanged(effect.cholesterolValue), + Instant.now(clock), + ) + } + .map { CholesterolSaved } + } + } +} diff --git a/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEvent.kt b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEvent.kt new file mode 100644 index 00000000000..080550f4cf0 --- /dev/null +++ b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEvent.kt @@ -0,0 +1,15 @@ +package org.simple.clinic.summary.addcholesterol + +import org.simple.clinic.widgets.UiEvent + +sealed interface CholesterolEntryEvent : UiEvent + +data class CholesterolChanged(val cholesterolValue: Float) : CholesterolEntryEvent + +data object SaveClicked : CholesterolEntryEvent { + override val analyticsName: String = "Cholesterol Entry:Save Clicked" +} + +data object CholesterolSaved : CholesterolEntryEvent + +data object KeyboardClosed : CholesterolEntryEvent diff --git a/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryModel.kt b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryModel.kt new file mode 100644 index 00000000000..0199a1cf315 --- /dev/null +++ b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryModel.kt @@ -0,0 +1,37 @@ +package org.simple.clinic.summary.addcholesterol + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import org.simple.clinic.summary.addcholesterol.CholesterolEntrySaveState.NOT_SAVING_CHOLESTEROL +import org.simple.clinic.summary.addcholesterol.CholesterolEntrySaveState.SAVING_CHOLESTEROL +import java.util.UUID + +@Parcelize +data class CholesterolEntryModel( + val patientUUID: UUID, + val cholesterolValue: Float, + val cholesterolSaveState: CholesterolEntrySaveState, +) : Parcelable { + + companion object { + fun create(patientUUID: UUID): CholesterolEntryModel { + return CholesterolEntryModel( + patientUUID = patientUUID, + cholesterolValue = 0f, + cholesterolSaveState = NOT_SAVING_CHOLESTEROL, + ) + } + } + + fun cholesterolChanged(cholesterolValue: Float): CholesterolEntryModel { + return copy(cholesterolValue = cholesterolValue) + } + + fun savingCholesterol(): CholesterolEntryModel { + return copy(cholesterolSaveState = SAVING_CHOLESTEROL) + } + + fun cholesterolSaved(): CholesterolEntryModel { + return copy(cholesterolSaveState = NOT_SAVING_CHOLESTEROL) + } +} diff --git a/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntrySaveState.kt b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntrySaveState.kt new file mode 100644 index 00000000000..080596f0220 --- /dev/null +++ b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntrySaveState.kt @@ -0,0 +1,6 @@ +package org.simple.clinic.summary.addcholesterol + +enum class CholesterolEntrySaveState { + SAVING_CHOLESTEROL, + NOT_SAVING_CHOLESTEROL +} diff --git a/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntrySheet.kt b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntrySheet.kt new file mode 100644 index 00000000000..d8dc941fee9 --- /dev/null +++ b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntrySheet.kt @@ -0,0 +1,161 @@ +package org.simple.clinic.summary.addcholesterol + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import androidx.fragment.app.Fragment +import com.jakewharton.rxbinding3.widget.editorActions +import com.spotify.mobius.Update +import com.spotify.mobius.functions.Consumer +import io.reactivex.Observable +import io.reactivex.ObservableTransformer +import io.reactivex.rxkotlin.cast +import kotlinx.parcelize.Parcelize +import org.simple.clinic.R +import org.simple.clinic.ReportAnalyticsEvents +import org.simple.clinic.databinding.SheetCholesterolEntryBinding +import org.simple.clinic.di.injector +import org.simple.clinic.mobius.ViewEffectsHandler +import org.simple.clinic.mobius.ViewRenderer +import org.simple.clinic.navigation.v2.Router +import org.simple.clinic.navigation.v2.ScreenKey +import org.simple.clinic.navigation.v2.fragments.BaseBottomSheet +import org.simple.clinic.widgets.UiEvent +import org.simple.clinic.widgets.textChanges +import org.simple.clinic.widgets.visibleOrGone +import java.util.UUID +import javax.inject.Inject + +class CholesterolEntrySheet : BaseBottomSheet< + CholesterolEntrySheet.Key, + SheetCholesterolEntryBinding, + CholesterolEntryModel, + CholesterolEntryEvent, + CholesterolEntryEffect, + CholesterolEntryViewEffect>(), CholesterolEntryUi, CholesterolEntryUiActions { + + @Inject + lateinit var cholesterolEntryEffectHandlerFactory: CholesterolEntryEffectHandler.Factory + + @Inject + lateinit var router: Router + + private val rootLayout + get() = binding.rootLayout + + private val cholesterolTextField + get() = binding.cholesterolTextField + + private val cholesterolErrorTextView + get() = binding.cholesterolErrorTextView + + private val progressIndicator + get() = binding.progressLoader + + override fun onAttach(context: Context) { + super.onAttach(context) + context.injector().inject(this) + } + + override fun defaultModel() = CholesterolEntryModel.create( + patientUUID = screenKey.patientUuid, + ) + + override fun bindView(inflater: LayoutInflater, container: ViewGroup?) = SheetCholesterolEntryBinding + .inflate(inflater, container, false) + + override fun createUpdate(): Update { + return CholesterolEntryUpdate() + } + + override fun createEffectHandler(viewEffectsConsumer: Consumer): ObservableTransformer { + return cholesterolEntryEffectHandlerFactory.create(viewEffectsConsumer).build() + } + + override fun viewEffectsHandler(): ViewEffectsHandler { + return CholesterolEntryViewEffectHandler(this) + } + + override fun uiRenderer(): ViewRenderer { + return CholesterolEntryUiRenderer(this) + } + + override fun events() = Observable + .mergeArray( + hardwareBackPresses(), + cholesterolTextChanges(), + imeDoneClicks(), + ) + .compose(ReportAnalyticsEvents()) + .cast() + + + override fun hideErrorMessage() { + cholesterolErrorTextView.visibleOrGone(isVisible = false) + } + + override fun dismissSheet() { + router.pop() + } + + override fun showReqMaxCholesterolError() { + showCholesterolErrorMessage(getString(R.string.cholesterol_error_upper_limit)) + } + + override fun showReqMinCholesterolError() { + showCholesterolErrorMessage(getString(R.string.cholesterol_error_lower_limit)) + } + + override fun showProgress() { + progressIndicator.visibleOrGone(isVisible = true) + cholesterolTextField.visibleOrGone(isVisible = false) + } + + override fun hideProgress() { + progressIndicator.visibleOrGone(isVisible = false) + cholesterolTextField.visibleOrGone(isVisible = true) + } + + private fun showCholesterolErrorMessage(message: String) { + with(cholesterolErrorTextView) { + text = message + visibility = android.view.View.VISIBLE + } + } + + private fun hardwareBackPresses(): Observable { + return Observable.create { emitter -> + val interceptor = { + emitter.onNext(KeyboardClosed) + } + emitter.setCancellable { rootLayout.backKeyPressInterceptor = null } + rootLayout.backKeyPressInterceptor = interceptor + } + } + + private fun cholesterolTextChanges() = cholesterolTextField + .textChanges { CholesterolChanged(it.toFloatOrNull() ?: 0f) } + + private fun imeDoneClicks(): Observable { + return cholesterolTextField + .editorActions { actionId -> actionId == EditorInfo.IME_ACTION_DONE } + .map { SaveClicked } + } + + @Parcelize + data class Key( + val patientUuid: UUID, + override val analyticsName: String = "Cholesterol Entry Sheet", + override val type: ScreenType = ScreenType.Modal, + ) : ScreenKey() { + + override fun instantiateFragment(): Fragment { + return CholesterolEntrySheet() + } + } + + interface Injector { + fun inject(target: CholesterolEntrySheet) + } +} diff --git a/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUi.kt b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUi.kt new file mode 100644 index 00000000000..286358c044b --- /dev/null +++ b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUi.kt @@ -0,0 +1,6 @@ +package org.simple.clinic.summary.addcholesterol + +interface CholesterolEntryUi { + fun showProgress() + fun hideProgress() +} diff --git a/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiActions.kt b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiActions.kt new file mode 100644 index 00000000000..0a7fa3046da --- /dev/null +++ b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiActions.kt @@ -0,0 +1,8 @@ +package org.simple.clinic.summary.addcholesterol + +interface CholesterolEntryUiActions { + fun hideErrorMessage() + fun dismissSheet() + fun showReqMaxCholesterolError() + fun showReqMinCholesterolError() +} diff --git a/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiRenderer.kt b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiRenderer.kt new file mode 100644 index 00000000000..be79b937094 --- /dev/null +++ b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiRenderer.kt @@ -0,0 +1,15 @@ +package org.simple.clinic.summary.addcholesterol + +import org.simple.clinic.mobius.ViewRenderer + +class CholesterolEntryUiRenderer( + private val ui: CholesterolEntryUi +) : ViewRenderer { + + override fun render(model: CholesterolEntryModel) { + when (model.cholesterolSaveState) { + CholesterolEntrySaveState.SAVING_CHOLESTEROL -> ui.showProgress() + CholesterolEntrySaveState.NOT_SAVING_CHOLESTEROL -> ui.hideProgress() + } + } +} diff --git a/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUpdate.kt b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUpdate.kt new file mode 100644 index 00000000000..f53899e131e --- /dev/null +++ b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUpdate.kt @@ -0,0 +1,57 @@ +package org.simple.clinic.summary.addcholesterol + +import com.spotify.mobius.Next +import com.spotify.mobius.Update +import org.simple.clinic.mobius.dispatch +import org.simple.clinic.mobius.next + +class CholesterolEntryUpdate( + private val minReqCholesterol: Float = 25f, + private val maxReqCholesterol: Float = 1000f, +) : Update { + + override fun update(model: CholesterolEntryModel, event: CholesterolEntryEvent): Next { + return when (event) { + is CholesterolChanged -> cholesterolChanged(model, event) + SaveClicked -> onSaveClicked(model) + CholesterolSaved -> onCholesterolSaved(model) + KeyboardClosed -> onKeyboardClosed() + } + } + + private fun onKeyboardClosed(): Next = + dispatch(DismissSheet) + + private fun onCholesterolSaved(model: CholesterolEntryModel): Next { + return next(model.cholesterolSaved(), DismissSheet) + } + + private fun onSaveClicked(model: CholesterolEntryModel): Next { + return when { + model.cholesterolValue < minReqCholesterol -> { + dispatch(ShowReqMinCholesterolValidationError) + } + + model.cholesterolValue > maxReqCholesterol -> { + dispatch(ShowReqMaxCholesterolValidationError) + } + + else -> { + next( + model.savingCholesterol(), + SaveCholesterol( + patientUuid = model.patientUUID, + cholesterolValue = model.cholesterolValue + ) + ) + } + } + } + + private fun cholesterolChanged(model: CholesterolEntryModel, event: CholesterolChanged): Next { + return next( + model.cholesterolChanged(event.cholesterolValue), + HideCholesterolErrorMessage + ) + } +} diff --git a/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryViewEffectHandler.kt b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryViewEffectHandler.kt new file mode 100644 index 00000000000..9733bd82e57 --- /dev/null +++ b/app/src/main/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryViewEffectHandler.kt @@ -0,0 +1,17 @@ +package org.simple.clinic.summary.addcholesterol + +import org.simple.clinic.mobius.ViewEffectsHandler + +class CholesterolEntryViewEffectHandler( + private val uiActions: CholesterolEntryUiActions +) : ViewEffectsHandler { + + override fun handle(viewEffect: CholesterolEntryViewEffect) { + return when (viewEffect) { + HideCholesterolErrorMessage -> uiActions.hideErrorMessage() + DismissSheet -> uiActions.dismissSheet() + ShowReqMaxCholesterolValidationError -> uiActions.showReqMaxCholesterolError() + ShowReqMinCholesterolValidationError -> uiActions.showReqMinCholesterolError() + } + } +} diff --git a/app/src/main/res/layout/sheet_cholesterol_entry.xml b/app/src/main/res/layout/sheet_cholesterol_entry.xml new file mode 100644 index 00000000000..e848e8f2e5d --- /dev/null +++ b/app/src/main/res/layout/sheet_cholesterol_entry.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e768855e608..d161818d5ae 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1113,4 +1113,9 @@ The Simple app contains private health information of patients (“Data”).\n\n Not a smoker Done Cancel + + + Cholesterol (mg/dL) + Cholesterol cannot be more than 1000 + Cholesterol cannot be less than 25 diff --git a/app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffectHandlerTest.kt b/app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffectHandlerTest.kt new file mode 100644 index 00000000000..46b1ac0212d --- /dev/null +++ b/app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryEffectHandlerTest.kt @@ -0,0 +1,100 @@ +package org.simple.clinic.summary.addcholesterol + +import org.junit.After +import org.junit.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever +import org.simple.clinic.TestData +import org.simple.clinic.medicalhistory.MedicalHistoryRepository +import org.simple.clinic.mobius.EffectHandlerTestCase +import org.simple.clinic.util.TestUtcClock +import org.simple.clinic.util.scheduler.TestSchedulersProvider +import org.simple.clinic.uuid.FakeUuidGenerator +import java.util.UUID + +class CholesterolEntryEffectHandlerTest { + + private val uiActions = mock() + private val medicalHistoryRepository = mock() + private val viewEffectHandler = CholesterolEntryViewEffectHandler(uiActions) + private val effectHandler = CholesterolEntryEffectHandler( + clock = TestUtcClock(), + uuidGenerator = FakeUuidGenerator( + uuid = UUID.fromString("94c8371d-0d3a-4343-9787-da6bca1a5843"), + ), + medicalHistoryRepository = medicalHistoryRepository, + schedulersProvider = TestSchedulersProvider.trampoline(), + viewEffectConsumer = viewEffectHandler::handle + ).build() + private val testCase = EffectHandlerTestCase(effectHandler) + + @After + fun teardown() { + testCase.dispose() + } + + @Test + fun `when save cholesterol effect is received, then save cholesterol value`() { + // given + val patientUuid = UUID.fromString("f6d17dbb-845e-4288-8dce-003e6e4743f8") + + whenever(medicalHistoryRepository.historyForPatientOrDefaultImmediate( + defaultHistoryUuid = UUID.fromString("94c8371d-0d3a-4343-9787-da6bca1a5843"), + patientUuid = patientUuid + )) doReturn TestData.medicalHistory() + + val effect = SaveCholesterol( + patientUuid = patientUuid, + cholesterolValue = 400f + ) + + // when + testCase.dispatch(effect) + + // then + testCase.assertOutgoingEvents(CholesterolSaved) + } + + @Test + fun `when hide cholesterol error message effect is received, then hide the errors`() { + // when + testCase.dispatch(HideCholesterolErrorMessage) + + // then + verify(uiActions).hideErrorMessage() + verifyNoMoreInteractions(uiActions) + } + + @Test + fun `when dismiss sheet effect is received, then dismiss sheet`() { + // when + testCase.dispatch(DismissSheet) + + // then + verify(uiActions).dismissSheet() + verifyNoMoreInteractions(uiActions) + } + + @Test + fun `when show req max cholesterol validation error effect is received, then show error`() { + // when + testCase.dispatch(ShowReqMaxCholesterolValidationError) + + // then + verify(uiActions).showReqMaxCholesterolError() + verifyNoMoreInteractions(uiActions) + } + + @Test + fun `when show req min cholesterol validation error effect is received, then show error`() { + // when + testCase.dispatch(ShowReqMinCholesterolValidationError) + + // then + verify(uiActions).showReqMinCholesterolError() + verifyNoMoreInteractions(uiActions) + } +} diff --git a/app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiRendererTest.kt b/app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiRendererTest.kt new file mode 100644 index 00000000000..ba3eb97752b --- /dev/null +++ b/app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUiRendererTest.kt @@ -0,0 +1,49 @@ +package org.simple.clinic.summary.addcholesterol + +import org.junit.Test +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import java.util.UUID + +class CholesterolEntryUiRendererTest { + + private val ui = mock() + private val uiRenderer = CholesterolEntryUiRenderer(ui) + + @Test + fun `when cholesterol save state is saving, then show progress`() { + // given + val model = CholesterolEntryModel + .create( + patientUUID = UUID.fromString("e406d663-b71f-4367-b2b7-43f52e1eee1c") + ) + .cholesterolChanged(cholesterolValue = 400f) + .savingCholesterol() + + // when + uiRenderer.render(model) + + // then + verify(ui).showProgress() + verifyNoMoreInteractions(ui) + } + + @Test + fun `when cholesterol save state is not saving, then hide progress`() { + // given + val model = CholesterolEntryModel + .create( + patientUUID = UUID.fromString("e406d663-b71f-4367-b2b7-43f52e1eee1c") + ) + .cholesterolChanged(cholesterolValue = 400f) + .cholesterolSaved() + + // when + uiRenderer.render(model) + + // then + verify(ui).hideProgress() + verifyNoMoreInteractions(ui) + } +} diff --git a/app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUpdateTest.kt b/app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUpdateTest.kt new file mode 100644 index 00000000000..98c22fd3401 --- /dev/null +++ b/app/src/test/java/org/simple/clinic/summary/addcholesterol/CholesterolEntryUpdateTest.kt @@ -0,0 +1,99 @@ +package org.simple.clinic.summary.addcholesterol + +import com.spotify.mobius.test.NextMatchers.hasEffects +import com.spotify.mobius.test.NextMatchers.hasModel +import com.spotify.mobius.test.NextMatchers.hasNoModel +import com.spotify.mobius.test.UpdateSpec +import com.spotify.mobius.test.UpdateSpec.assertThatNext +import org.junit.Test +import java.util.UUID + +class CholesterolEntryUpdateTest { + + private val patientUuid = UUID.fromString("74cf8bf8-2a82-4cd0-a685-d1c3d10c42d9") + private val defaultModel = CholesterolEntryModel.create( + patientUUID = patientUuid + ) + private val updateSpec = UpdateSpec( + CholesterolEntryUpdate() + ) + + @Test + fun `when cholesterol value changes, hide any error message`() { + val cholesterolValue = 20f + updateSpec + .given(defaultModel) + .whenEvent(CholesterolChanged(cholesterolValue)) + .then(assertThatNext( + hasModel(defaultModel.cholesterolChanged(cholesterolValue)), + hasEffects(HideCholesterolErrorMessage) + )) + } + + @Test + fun `when cholesterol value is under min range and save is clicked then show validation error`() { + val cholesterolValue = 20f + updateSpec + .given(defaultModel.cholesterolChanged(cholesterolValue)) + .whenEvent(SaveClicked) + .then(assertThatNext( + hasNoModel(), + hasEffects(ShowReqMinCholesterolValidationError) + )) + } + + @Test + fun `when cholesterol value is over max range and save is clicked then show validation error`() { + val cholesterolValue = 1001f + updateSpec + .given(defaultModel.cholesterolChanged(cholesterolValue)) + .whenEvent(SaveClicked) + .then(assertThatNext( + hasNoModel(), + hasEffects(ShowReqMaxCholesterolValidationError) + )) + } + + @Test + fun `when cholesterol value is within range and save is clicked, then save the cholesterol value`() { + val cholesterolValue = 400f + updateSpec + .given(defaultModel.cholesterolChanged(cholesterolValue)) + .whenEvent(SaveClicked) + .then(assertThatNext( + hasModel( + defaultModel + .cholesterolChanged(cholesterolValue) + .savingCholesterol() + ), + hasEffects(SaveCholesterol(patientUuid, cholesterolValue)) + )) + } + + @Test + fun `when cholesterol value is saved, then reset save state and dismiss sheet`() { + val cholesterolValue = 400f + updateSpec + .given(defaultModel.cholesterolChanged(cholesterolValue)) + .whenEvent(CholesterolSaved) + .then(assertThatNext( + hasModel( + defaultModel + .cholesterolChanged(cholesterolValue) + .cholesterolSaved() + ), + hasEffects(DismissSheet) + )) + } + + @Test + fun `when keyboard is closed, then dismiss sheet`() { + updateSpec + .given(defaultModel) + .whenEvent(KeyboardClosed) + .then(assertThatNext( + hasNoModel(), + hasEffects(DismissSheet) + )) + } +}