diff --git a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireExtension.kt b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireExtension.kt index 838ee0194d3..40ec4261bbe 100644 --- a/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireExtension.kt +++ b/android/engine/src/main/java/org/smartregister/fhircore/engine/util/extension/QuestionnaireExtension.kt @@ -249,6 +249,10 @@ suspend fun Questionnaire.prepopulateWithComputedConfigValues( ?: questionnaireConfig.linkIds?.firstOrNull { it.type == LinkIdType.BARCODE }?.linkId) ?.let { barcodeLinkId -> find(barcodeLinkId)?.apply { + if (hasExtension(EXTENSION_INITIAL_EXPRESSION_URL)) { + removeExtension(EXTENSION_INITIAL_EXPRESSION_URL) + } + initial = mutableListOf( Questionnaire.QuestionnaireItemInitialComponent() @@ -285,6 +289,10 @@ suspend fun Questionnaire.prepopulateUniqueIdAssignment( questionnaireComputedValues, ) if (extractedId.isNotEmpty()) { + if (hasExtension(EXTENSION_INITIAL_EXPRESSION_URL)) { + removeExtension(EXTENSION_INITIAL_EXPRESSION_URL) + } + initial = mutableListOf( Questionnaire.QuestionnaireItemInitialComponent().setValue(StringType(extractedId)), diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index dd89294f9c8..d15bbaaaf8a 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -28,10 +28,10 @@ dokkaBase = "1.9.20" easyRulesCore = "4.1.1-SNAPSHOT" espresso-core = "3.6.1" fhir-sdk-common = "0.1.0-alpha05-preview3-SNAPSHOT" -fhir-sdk-contrib-barcode = "0.1.0-beta3-preview7-SNAPSHOT" -fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview2-SNAPSHOT" -fhir-sdk-data-capture = "1.3.0-preview-SNAPSHOT" -fhir-sdk-engine = "1.2.0-preview-SNAPSHOT" +fhir-sdk-contrib-barcode = "0.1.0-beta3-preview8-SNAPSHOT" +fhir-sdk-contrib-locationwidget = "0.1.0-alpha01-preview3-SNAPSHOT" +fhir-sdk-data-capture = "1.3.0-preview9-SNAPSHOT" +fhir-sdk-engine = "1.2.0-preview1-SNAPSHOT" fhir-sdk-knowledge = "0.1.0-beta01-preview1-SNAPSHOT" fhir-sdk-workflow = "0.1.0-beta01-preview2-SNAPSHOT" generativeai = "0.9.0" diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivity.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivity.kt index 78dcaa3d4bf..46ad2c99c06 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivity.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireActivity.kt @@ -452,7 +452,9 @@ class QuestionnaireActivity : BaseMultiLanguageActivity() { ) } ?.let { setQuestionnaireResponse(questionnaireResponse.json()) } - ?: showToast(getString(R.string.error_populating_questionnaire)) + ?: withContext(dispatcherProvider.main()) { + showToast(getString(R.string.error_populating_questionnaire)) + } } launchContextResources diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt index af5b0b4ac67..f9ab40f411e 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModel.kt @@ -1164,12 +1164,6 @@ constructor( launchContextResources(resourceType, resourceIdentifier, actionParameters) val launchContexts = launchContextResources.associateBy { it.resourceType.name.lowercase() } - // Populate questionnaire with initial default values - ResourceMapper.populate( - questionnaire, - launchContexts = launchContexts, - ) - questionnaire.prepopulateWithComputedConfigValues( questionnaireConfig, actionParameters, @@ -1194,82 +1188,118 @@ constructor( }, ) - // Populate questionnaire with latest QuestionnaireResponse + // Populate questionnaire with latest QuestionnaireResponse and initial default values val questionnaireResponse = - if ( - resourceType != null && - !resourceIdentifier.isNullOrEmpty() && - (questionnaireConfig.isEditable() || - questionnaireConfig.isReadOnly() || - questionnaireConfig.isSummary() || - questionnaireConfig.saveDraft) - ) { - defaultRepository - .searchQuestionnaireResponse( - resourceId = resourceIdentifier, - resourceType = resourceType, - questionnaireId = questionnaire.logicalId, - encounterId = questionnaireConfig.encounterId, - questionnaireResponseStatus = questionnaireConfig.questionnaireResponseStatus(), - ) - ?.let { - QuestionnaireResponse().apply { - /** - * Only set the ID value when [saveDraft] is set to true] this ensues that a new - * [QuestionnaireResponse] is generated when the questionnaire is submitted - */ - if (questionnaireConfig.saveDraft) { - id = it.id - } - status = it.status - item = it.item.removeUnAnsweredItems() - // Clearing the text prompts the SDK to re-process the content, which includes - // HTML - clearText() - } - } - } else { - null - } + fetchRepositoryQuestionnaireResponse( + questionnaireConfig, + resourceIdentifier, + resourceType, + questionnaire, + defaultQuestionnaire = + withContext(dispatcherProvider.default()) { + ResourceMapper.populate( + questionnaire, + launchContexts = launchContexts, + ) + }, + ) - // Exclude the configured fields from QR - if (questionnaireResponse != null) { - val exclusionLinkIdsMap: Map = - questionnaireConfig.linkIds - ?.asSequence() - ?.filter { it.type == LinkIdType.PREPOPULATION_EXCLUSION } - ?.associateBy { it.linkId } - ?.mapValues { it.value.type == LinkIdType.PREPOPULATION_EXCLUSION } ?: emptyMap() - - questionnaireResponse.item = - excludePrepopulationFields( - questionnaireResponse.item.toMutableList(), - exclusionLinkIdsMap, + questionnaireResponse.apply { item = item.removeUnAnsweredItems() } + + Pair(questionnaireResponse, launchContextResources) + } + + private suspend fun fetchRepositoryQuestionnaireResponse( + questionnaireConfig: QuestionnaireConfig, + resourceIdentifier: String?, + resourceType: ResourceType?, + questionnaire: Questionnaire, + defaultQuestionnaire: QuestionnaireResponse, + ): QuestionnaireResponse { + if (resourceType == null || resourceIdentifier.isNullOrEmpty()) return defaultQuestionnaire + + val searchQR = + questionnaireConfig + .takeIf { it.isEditable() || it.isReadOnly() || it.isSummary() || it.saveDraft } + ?.let { + defaultRepository.searchQuestionnaireResponse( + resourceId = resourceIdentifier, + resourceType = resourceType, + questionnaireId = questionnaire.logicalId, + encounterId = questionnaireConfig.encounterId, + questionnaireResponseStatus = questionnaireConfig.questionnaireResponseStatus(), ) + } + + if (searchQR == null) return defaultQuestionnaire + + val exclusionLinkIdsMap: Map = + questionnaireConfig.linkIds + ?.asSequence() + ?.filter { it.type == LinkIdType.PREPOPULATION_EXCLUSION } + ?.associateBy { it.linkId } + ?.mapValues { it.value.type == LinkIdType.PREPOPULATION_EXCLUSION } ?: emptyMap() + + return QuestionnaireResponse().apply { + /** + * Only set the ID value when [saveDraft] is set to true] this ensues that a new + * [QuestionnaireResponse] is generated when the questionnaire is submitted + */ + if (questionnaireConfig.saveDraft) { + id = searchQR.id } + status = searchQR.status - Pair(questionnaireResponse, launchContextResources) + // Exclude the configured fields from QR + item = + excludePreviouslyAnsweredFields( + searchQR.item, + defaultQuestionnaire.item, + exclusionLinkIdsMap, + ) + + // Clearing the text prompts the SDK to re-process the content, which includes + // HTML + clearText() } + } - fun excludePrepopulationFields( - items: MutableList, + fun excludePreviouslyAnsweredFields( + previouslyAnsweredItems: MutableList, + newlyPopulatedItems: List, exclusionMap: Map, ): MutableList { - val stack = LinkedList>() - stack.push(items) - while (stack.isNotEmpty()) { - val currentItems = stack.pop() - val iterator = currentItems.iterator() - while (iterator.hasNext()) { - val item = iterator.next() - if (exclusionMap.containsKey(item.linkId)) { - iterator.remove() - } else if (item.item.isNotEmpty()) { - stack.push(item.item) + val prevStack = + LinkedList>().apply { + push(previouslyAnsweredItems) + } + val newlyStack = + LinkedList>().apply { push(newlyPopulatedItems) } + + while (prevStack.isNotEmpty()) { + val prevAnswerItemsIterator = prevStack.pop().listIterator() + val newItemsIterator: Iterator = + (if (newlyStack.isNotEmpty()) newlyStack.pop() else emptyList()).iterator() + + while (prevAnswerItemsIterator.hasNext()) { + val item = prevAnswerItemsIterator.next() + val newPopulatedItem = if (newItemsIterator.hasNext()) newItemsIterator.next() else null + + when { + exclusionMap.containsKey(item.linkId) && newPopulatedItem?.linkId == item.linkId -> { + prevAnswerItemsIterator.set(newPopulatedItem) + } + exclusionMap.containsKey(item.linkId) -> { + prevAnswerItemsIterator.remove() + } + else -> { + prevStack.push(item.item) + newPopulatedItem?.let { newlyStack.push(it.item) } + } } } } - return items + return previouslyAnsweredItems } private fun List.removeUnAnsweredItems(): diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/password/PasswordViewHolderFactory.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/password/PasswordViewHolderFactory.kt index e355405f9a4..363953ada62 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/password/PasswordViewHolderFactory.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/password/PasswordViewHolderFactory.kt @@ -31,8 +31,8 @@ import com.google.android.fhir.datacapture.extensions.itemControlCode import com.google.android.fhir.datacapture.extensions.tryUnwrapContext import com.google.android.fhir.datacapture.views.HeaderView import com.google.android.fhir.datacapture.views.QuestionnaireViewItem -import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolderDelegate -import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemAndroidViewHolderDelegate +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemAndroidViewHolderFactory import com.google.android.material.textfield.TextInputEditText import kotlinx.coroutines.launch import org.hl7.fhir.r4.model.Questionnaire @@ -40,11 +40,11 @@ import org.hl7.fhir.r4.model.QuestionnaireResponse import org.hl7.fhir.r4.model.StringType object PasswordViewHolderFactory : - QuestionnaireItemViewHolderFactory( + QuestionnaireItemAndroidViewHolderFactory( org.smartregister.fhircore.quest.R.layout.password_view, ) { override fun getQuestionnaireItemViewHolderDelegate() = - object : QuestionnaireItemViewHolderDelegate { + object : QuestionnaireItemAndroidViewHolderDelegate { override lateinit var questionnaireViewItem: QuestionnaireViewItem private lateinit var header: HeaderView @@ -58,7 +58,7 @@ object PasswordViewHolderFactory : passwordEditText = itemView - .findViewById( + .findViewById( org.smartregister.fhircore.quest.R.id.password_edit_text, ) .apply { diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeItemViewHolderFactory.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeItemViewHolderFactory.kt index 468f58c079f..de052d9c14d 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeItemViewHolderFactory.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeItemViewHolderFactory.kt @@ -20,17 +20,26 @@ import android.annotation.SuppressLint import android.content.Context import android.text.Editable import android.text.InputType +import android.text.TextWatcher import android.view.MotionEvent import android.view.View +import android.view.View.FOCUS_DOWN +import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputMethodManager +import androidx.appcompat.app.AppCompatActivity +import androidx.core.widget.doAfterTextChanged +import androidx.lifecycle.lifecycleScope +import com.google.android.fhir.datacapture.extensions.getRequiredOrOptionalText import com.google.android.fhir.datacapture.extensions.getValidationErrorMessage +import com.google.android.fhir.datacapture.extensions.localizedFlyoverSpanned import com.google.android.fhir.datacapture.extensions.tryUnwrapContext +import com.google.android.fhir.datacapture.views.HeaderView import com.google.android.fhir.datacapture.views.QuestionnaireViewItem -import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemEditTextViewHolderDelegate -import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolderDelegate -import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemAndroidViewHolderDelegate +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemAndroidViewHolderFactory import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputLayout +import kotlinx.coroutines.launch import org.hl7.fhir.r4.model.QuestionnaireResponse import org.hl7.fhir.r4.model.StringType import org.smartregister.fhircore.quest.R @@ -38,15 +47,56 @@ import org.smartregister.fhircore.quest.util.QrCodeScanUtils internal class EditTextQrCodeItemViewHolderFactory( private val qrCodeAnswerChangeListener: QrCodeChangeListener, -) : QuestionnaireItemViewHolderFactory(R.layout.edit_text_single_line_qr_code_item_view) { - override fun getQuestionnaireItemViewHolderDelegate(): QuestionnaireItemViewHolderDelegate = - object : - QuestionnaireItemEditTextViewHolderDelegate( - InputType.TYPE_NULL, - ) { +) : QuestionnaireItemAndroidViewHolderFactory(R.layout.edit_text_single_line_qr_code_item_view) { + override fun getQuestionnaireItemViewHolderDelegate() = + object : QuestionnaireItemAndroidViewHolderDelegate { + + override lateinit var questionnaireViewItem: QuestionnaireViewItem + + private lateinit var context: AppCompatActivity + private lateinit var header: HeaderView + private lateinit var textInputLayout: TextInputLayout + private lateinit var textInputEditText: TextInputEditText + private var textWatcher: TextWatcher? = null + + private fun superInit(itemView: View) { + context = itemView.context.tryUnwrapContext()!! + header = itemView.findViewById(R.id.header) + textInputLayout = itemView.findViewById(R.id.text_input_layout) + textInputEditText = itemView.findViewById(R.id.text_input_edit_text) + + textInputEditText.setRawInputType(InputType.TYPE_NULL) + // Override `setOnEditorActionListener` to avoid crash with `IllegalStateException` if it's + // not + // possible to move focus forward. + // See + // https://stackoverflow.com/questions/13614101/fatal-crash-focus-search-returned-a-view-that-wasnt-able-to-take-focus/47991577 + textInputEditText.setOnEditorActionListener { view, actionId, _ -> + if (actionId != EditorInfo.IME_ACTION_NEXT) { + return@setOnEditorActionListener false + } + view.focusSearch(FOCUS_DOWN)?.requestFocus(FOCUS_DOWN) ?: false + } + textInputEditText.setOnFocusChangeListener { view, focused -> + if (!focused) { + (view.context.applicationContext.getSystemService(Context.INPUT_METHOD_SERVICE) + as InputMethodManager) + .hideSoftInputFromWindow(view.windowToken, 0) + + context.lifecycleScope.launch { + // Update answer even if the text box loses focus without any change. This will mark + // the + // questionnaire response item as being modified in the view model and trigger + // validation. + handleInput(textInputEditText.editableText, questionnaireViewItem) + } + } + } + } + @SuppressLint("ClickableViewAccessibility") override fun init(itemView: View) { - super.init(itemView) + superInit(itemView) val onQrCodeIconClickListener: (Context) -> Unit = { it.tryUnwrapContext()?.let { appCompatActivity -> @@ -77,14 +127,58 @@ internal class EditTextQrCodeItemViewHolderFactory( } } + override fun bind(questionnaireViewItem: QuestionnaireViewItem) { + header.bind(questionnaireViewItem) + with(textInputLayout) { + hint = questionnaireViewItem.enabledDisplayItems.localizedFlyoverSpanned + helperText = getRequiredOrOptionalText(questionnaireViewItem, context) + } + + /** + * Ensures that any validation errors or warnings are immediately reflected in the UI + * whenever the view is bound to a new or updated item. + */ + updateValidationTextUI(questionnaireViewItem, textInputLayout) + + /** + * Updates the EditText *only* if the EditText is not currently focused. + * + * This is done to avoid disrupting the user's typing experience and prevent conflicts if + * they are actively editing the field. Updating the text programmatically is safe in the + * following scenarios: + * 1. **ViewHolder Reuse:** When the same ViewHolder is being used to display a different + * QuestionnaireViewItem, the EditText needs to be updated with the new item's content. + * 2. **Read-Only Items:** When the item is read-only, its value may change dynamically due + * to expressions, and the EditText needs to reflect this updated value. + * + * The following actions are performed if the EditText is not focused: + * - Removes any existing text change listener. + * - Updates the input text UI based on the QuestionnaireViewItem. + * - Updates the unit text view (if applicable). + * - Attaches a new text change listener to handle user input. + */ + if (!textInputEditText.isFocused) { + textInputEditText.removeTextChangedListener(textWatcher) + updateInputTextUI(questionnaireViewItem, textInputEditText) + + // TextWatcher is set only once for each question item in scenario 1 + textWatcher = + textInputEditText.doAfterTextChanged { editable: Editable? -> + context.lifecycleScope.launch { handleInput(editable!!, questionnaireViewItem) } + } + } + } + override fun setReadOnly(isReadOnly: Boolean) { val questionnaireItemHasAnswer = questionnaireViewItem.answers.any { !it.value.isEmpty } val readOnly = questionnaireItemHasAnswer && (isReadOnly || questionnaireViewItem.isSetOnceReadOnly) - super.setReadOnly(readOnly) + + textInputLayout.isEnabled = !readOnly + textInputEditText.isEnabled = !readOnly } - override suspend fun handleInput( + suspend fun handleInput( editable: Editable, questionnaireViewItem: QuestionnaireViewItem, ) { @@ -104,7 +198,7 @@ internal class EditTextQrCodeItemViewHolderFactory( ) } - override fun updateInputTextUI( + fun updateInputTextUI( questionnaireViewItem: QuestionnaireViewItem, textInputEditText: TextInputEditText, ) { @@ -115,7 +209,7 @@ internal class EditTextQrCodeItemViewHolderFactory( } } - override fun updateValidationTextUI( + fun updateValidationTextUI( questionnaireViewItem: QuestionnaireViewItem, textInputLayout: TextInputLayout, ) { diff --git a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeViewHolderFactory.kt b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeViewHolderFactory.kt index 3bac623fb09..e5d8575cfad 100644 --- a/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeViewHolderFactory.kt +++ b/android/quest/src/main/java/org/smartregister/fhircore/quest/ui/sdc/qrCode/EditTextQrCodeViewHolderFactory.kt @@ -25,9 +25,9 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.google.android.fhir.datacapture.views.QuestionnaireViewItem +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemAndroidViewHolderDelegate +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemAndroidViewHolderFactory import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolder -import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolderDelegate -import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolderFactory import org.hl7.fhir.r4.model.BooleanType import org.hl7.fhir.r4.model.Questionnaire import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent @@ -35,9 +35,9 @@ import org.hl7.fhir.r4.model.StringType import org.smartregister.fhircore.quest.R object EditTextQrCodeViewHolderFactory : - QuestionnaireItemViewHolderFactory(R.layout.edit_text_qr_code_view) { - override fun getQuestionnaireItemViewHolderDelegate(): QuestionnaireItemViewHolderDelegate = - object : QuestionnaireItemViewHolderDelegate { + QuestionnaireItemAndroidViewHolderFactory(R.layout.edit_text_qr_code_view) { + override fun getQuestionnaireItemViewHolderDelegate() = + object : QuestionnaireItemAndroidViewHolderDelegate { override lateinit var questionnaireViewItem: QuestionnaireViewItem private val canHaveMultipleAnswers diff --git a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt index 996a232fa90..ab231abbbfa 100644 --- a/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt +++ b/android/quest/src/test/java/org/smartregister/fhircore/quest/ui/questionnaire/QuestionnaireViewModelTest.kt @@ -1935,7 +1935,7 @@ class QuestionnaireViewModelTest : RobolectricTest() { } @Test - fun testThatPopulateQuestionnaireSetInitialDefaultValueForQuestionnaireInitialExpression() = + fun testThatPopulateQuestionnaireSetsQuestionnaireResponseWithInitialDefaultValueForQuestionnaireInitialExpression() = runTest { val questionnaireViewModelInstance = QuestionnaireViewModel( @@ -1972,22 +1972,22 @@ class QuestionnaireViewModelTest : RobolectricTest() { coEvery { fhirEngine.get(ResourceType.Questionnaire, questionnaireConfig.id) } returns questionnaireWithDefaultDate - questionnaireViewModelInstance.populateQuestionnaire( - questionnaireWithDefaultDate, - questionnaireConfig, - emptyList(), + val (questionnaireResponse, _) = + questionnaireViewModelInstance.populateQuestionnaire( + questionnaireWithDefaultDate, + questionnaireConfig, + emptyList(), + ) + + Assert.assertTrue( + (questionnaireResponse!!.item.single { it.linkId == "defaultedDate" }.answerFirstRep.value + as DateType) + .isToday, ) - val initialValueDate = - questionnaireWithDefaultDate.item - .first { it.linkId == "defaultedDate" } - .initial - .first() - .value as DateType - Assert.assertTrue(initialValueDate.isToday) } @Test - fun testThatPopulateQuestionnaireSetInitialDefaultValueButExcludesFieldFromResponse() = + fun testThatPopulateQuestionnaireSetsInitialDefaultValueButExcludesValueFromPreviousResponse() = runTest(timeout = 90.seconds) { val thisQuestionnaireConfig = questionnaireConfig.copy( @@ -2069,7 +2069,8 @@ class QuestionnaireViewModelTest : RobolectricTest() { ) Assert.assertNotNull(result?.item) - Assert.assertTrue(result!!.item.isEmpty()) + Assert.assertTrue(result!!.item.isNotEmpty()) + Assert.assertTrue((result.item.single().answerFirstRep.value as DateType).isToday) } @Test @@ -2513,7 +2514,8 @@ class QuestionnaireViewModelTest : RobolectricTest() { val items = mutableListOf(item1, item2, item3) val exclusionMap = mapOf("2" to true, "3.1" to true, "3.2.2" to true) - val filteredItems = questionnaireViewModel.excludePrepopulationFields(items, exclusionMap) + val filteredItems = + questionnaireViewModel.excludePreviouslyAnsweredFields(items, emptyList(), exclusionMap) Assert.assertEquals(2, filteredItems.size) Assert.assertEquals("1", filteredItems.first().linkId) val itemThree = filteredItems.last()