diff --git a/buildSrc/src/main/kotlin/Releases.kt b/buildSrc/src/main/kotlin/Releases.kt index a7e711aaf7..f570d3ebb2 100644 --- a/buildSrc/src/main/kotlin/Releases.kt +++ b/buildSrc/src/main/kotlin/Releases.kt @@ -54,7 +54,7 @@ object Releases { object DataCapture : LibraryArtifact { override val artifactId = "data-capture" - override val version = "0.1.0-beta06-preview8-SNAPSHOT" + override val version = "1.0.0" override val name = "Android FHIR Structured Data Capture Library" } @@ -72,7 +72,7 @@ object Releases { } } - object ImplmentationGuide : LibraryArtifact { + object ImplementationGuide : LibraryArtifact { override val artifactId = "implementationguide" override val version = "0.1.0-alpha001" override val name = "Android FHIR Implementation Guide Library" diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/ComponentsRecyclerViewadapter.kt b/catalog/src/main/java/com/google/android/fhir/catalog/ComponentsRecyclerViewadapter.kt index fe5881e647..b503e7d8c3 100644 --- a/catalog/src/main/java/com/google/android/fhir/catalog/ComponentsRecyclerViewadapter.kt +++ b/catalog/src/main/java/com/google/android/fhir/catalog/ComponentsRecyclerViewadapter.kt @@ -81,8 +81,8 @@ class ComponentListViewHolder( class ComponentHeaderViewHolder(private val binding: ComponentHeaderLayoutBinding) : RecyclerView.ViewHolder(binding.root), ComponentViewHolder { - override fun bind(viewItem: ComponentListViewModel.ViewItem) { - val headerItem = viewItem as ComponentListViewModel.ViewItem.HeaderItem + override fun bind(component: ComponentListViewModel.ViewItem) { + val headerItem = component as ComponentListViewModel.ViewItem.HeaderItem binding.tvComponentHeader.text = binding.tvComponentHeader.context.getString(headerItem.header.textId) } diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/CustomNumberPickerFactory.kt b/catalog/src/main/java/com/google/android/fhir/catalog/CustomNumberPickerFactory.kt index 83e6d58889..1aa2c21000 100644 --- a/catalog/src/main/java/com/google/android/fhir/catalog/CustomNumberPickerFactory.kt +++ b/catalog/src/main/java/com/google/android/fhir/catalog/CustomNumberPickerFactory.kt @@ -18,16 +18,16 @@ package com.google.android.fhir.catalog import android.view.View import android.widget.NumberPicker -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewHolderDelegate -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewItem +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 object CustomNumberPickerFactory : QuestionnaireItemViewHolderFactory(R.layout.custom_number_picker_layout) { override fun getQuestionnaireItemViewHolderDelegate(): QuestionnaireItemViewHolderDelegate = object : QuestionnaireItemViewHolderDelegate { private lateinit var numberPicker: NumberPicker - override lateinit var questionnaireItemViewItem: QuestionnaireItemViewItem + override lateinit var questionnaireViewItem: QuestionnaireViewItem override fun init(itemView: View) { /** @@ -37,7 +37,7 @@ object CustomNumberPickerFactory : numberPicker = itemView.findViewById(R.id.number_picker) } - override fun bind(questionnaireItemViewItem: QuestionnaireItemViewItem) { + override fun bind(questionnaireViewItem: QuestionnaireViewItem) { numberPicker.minValue = 1 numberPicker.maxValue = 100 } diff --git a/catalog/src/main/java/com/google/android/fhir/catalog/CustomQuestionnaireFragment.kt b/catalog/src/main/java/com/google/android/fhir/catalog/CustomQuestionnaireFragment.kt index 92aa9181f3..9d78a1be48 100644 --- a/catalog/src/main/java/com/google/android/fhir/catalog/CustomQuestionnaireFragment.kt +++ b/catalog/src/main/java/com/google/android/fhir/catalog/CustomQuestionnaireFragment.kt @@ -17,7 +17,7 @@ package com.google.android.fhir.catalog import com.google.android.fhir.datacapture.QuestionnaireFragment.QuestionnaireItemViewHolderFactoryMatcher -import com.google.android.fhir.datacapture.contrib.views.barcode.QuestionnaireItemBarCodeReaderViewHolderFactory +import com.google.android.fhir.datacapture.contrib.views.barcode.BarCodeReaderViewHolderFactory // TODO Remove this file and move this code to maybe a custom view in catalog app? class CustomQuestionnaireFragment /*: QuestionnaireFragment()*/ { @@ -29,14 +29,12 @@ class CustomQuestionnaireFragment /*: QuestionnaireFragment()*/ { if (it == null) false else it.value.toString() == CustomNumberPickerFactory.WIDGET_TYPE } }, - QuestionnaireItemViewHolderFactoryMatcher(QuestionnaireItemBarCodeReaderViewHolderFactory) { - questionnaireItem -> - questionnaireItem - .getExtensionByUrl(QuestionnaireItemBarCodeReaderViewHolderFactory.WIDGET_EXTENSION) - .let { - if (it == null) false - else it.value.toString() == QuestionnaireItemBarCodeReaderViewHolderFactory.WIDGET_TYPE - } + QuestionnaireItemViewHolderFactoryMatcher(BarCodeReaderViewHolderFactory) { questionnaireItem + -> + questionnaireItem.getExtensionByUrl(BarCodeReaderViewHolderFactory.WIDGET_EXTENSION).let { + if (it == null) false + else it.value.toString() == BarCodeReaderViewHolderFactory.WIDGET_TYPE + } } ) } diff --git a/contrib/barcode/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/barcode/QuestionnaireItemBarCodeReaderViewHolderFactoryInstrumentedTest.kt b/contrib/barcode/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/barcode/BarCodeReaderViewHolderFactoryInstrumentedTest.kt similarity index 90% rename from contrib/barcode/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/barcode/QuestionnaireItemBarCodeReaderViewHolderFactoryInstrumentedTest.kt rename to contrib/barcode/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/barcode/BarCodeReaderViewHolderFactoryInstrumentedTest.kt index 552a88495c..8b701bdcac 100644 --- a/contrib/barcode/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/barcode/QuestionnaireItemBarCodeReaderViewHolderFactoryInstrumentedTest.kt +++ b/contrib/barcode/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/barcode/BarCodeReaderViewHolderFactoryInstrumentedTest.kt @@ -24,8 +24,8 @@ import androidx.test.annotation.UiThreadTest import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.google.android.fhir.datacapture.validation.NotValidated -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewHolder -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewItem +import com.google.android.fhir.datacapture.views.QuestionnaireViewItem +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolder import com.google.common.truth.Truth.assertThat import org.hl7.fhir.r4.model.Questionnaire import org.hl7.fhir.r4.model.QuestionnaireResponse @@ -35,7 +35,7 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class QuestionnaireItemBarCodeReaderViewHolderFactoryInstrumentedTest { +class BarCodeReaderViewHolderFactoryInstrumentedTest { private lateinit var context: ContextThemeWrapper private lateinit var parent: FrameLayout @@ -49,13 +49,13 @@ class QuestionnaireItemBarCodeReaderViewHolderFactoryInstrumentedTest { R.style.Theme_MaterialComponents ) parent = FrameLayout(context) - viewHolder = QuestionnaireItemBarCodeReaderViewHolderFactory.create(parent) + viewHolder = BarCodeReaderViewHolderFactory.create(parent) } @Test fun shouldShowPrefixText() { viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { prefix = "Prefix?" }, QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, @@ -72,7 +72,7 @@ class QuestionnaireItemBarCodeReaderViewHolderFactoryInstrumentedTest { @Test fun shouldHidePrefixText() { viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { prefix = "" }, QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, @@ -87,7 +87,7 @@ class QuestionnaireItemBarCodeReaderViewHolderFactoryInstrumentedTest { @Test fun shouldSetTextViewText() { viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { text = "Question?" }, QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, @@ -104,7 +104,7 @@ class QuestionnaireItemBarCodeReaderViewHolderFactoryInstrumentedTest { @UiThreadTest fun shouldSetInputText() { viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { text = "Question?" }, QuestionnaireResponse.QuestionnaireResponseItemComponent() .addAnswer( diff --git a/contrib/barcode/src/main/java/com/google/android/fhir/datacapture/contrib/views/barcode/QuestionnaireItemBarCodeReaderViewHolderFactory.kt b/contrib/barcode/src/main/java/com/google/android/fhir/datacapture/contrib/views/barcode/BarCodeReaderViewHolderFactory.kt similarity index 75% rename from contrib/barcode/src/main/java/com/google/android/fhir/datacapture/contrib/views/barcode/QuestionnaireItemBarCodeReaderViewHolderFactory.kt rename to contrib/barcode/src/main/java/com/google/android/fhir/datacapture/contrib/views/barcode/BarCodeReaderViewHolderFactory.kt index 4f0c6b7367..154a5336be 100644 --- a/contrib/barcode/src/main/java/com/google/android/fhir/datacapture/contrib/views/barcode/QuestionnaireItemBarCodeReaderViewHolderFactory.kt +++ b/contrib/barcode/src/main/java/com/google/android/fhir/datacapture/contrib/views/barcode/BarCodeReaderViewHolderFactory.kt @@ -22,14 +22,14 @@ import android.widget.TextView import com.google.android.fhir.datacapture.contrib.views.barcode.mlkit.md.LiveBarcodeScanningFragment import com.google.android.fhir.datacapture.localizedPrefixSpanned import com.google.android.fhir.datacapture.localizedTextSpanned -import com.google.android.fhir.datacapture.utilities.tryUnwrapContext -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewHolderDelegate -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewItem +import com.google.android.fhir.datacapture.tryUnwrapContext +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 org.hl7.fhir.r4.model.QuestionnaireResponse import org.hl7.fhir.r4.model.StringType -object QuestionnaireItemBarCodeReaderViewHolderFactory : +object BarCodeReaderViewHolderFactory : QuestionnaireItemViewHolderFactory(R.layout.questionnaire_item_bar_code_reader_view) { override fun getQuestionnaireItemViewHolderDelegate() = object : QuestionnaireItemViewHolderDelegate { @@ -37,7 +37,7 @@ object QuestionnaireItemBarCodeReaderViewHolderFactory : private lateinit var textQuestion: TextView private lateinit var barcodeTextView: TextView private lateinit var reScanView: TextView - override lateinit var questionnaireItemViewItem: QuestionnaireItemViewItem + override lateinit var questionnaireViewItem: QuestionnaireViewItem override fun init(itemView: View) { prefixTextView = itemView.findViewById(R.id.prefix) @@ -69,31 +69,31 @@ object QuestionnaireItemBarCodeReaderViewHolderFactory : } if (answer == null) { - questionnaireItemViewItem.clearAnswer() + questionnaireViewItem.clearAnswer() } else { - questionnaireItemViewItem.setAnswer(answer) + questionnaireViewItem.setAnswer(answer) } - setInitial(questionnaireItemViewItem.answers.singleOrNull(), reScanView) + setInitial(questionnaireViewItem.answers.singleOrNull(), reScanView) } LiveBarcodeScanningFragment() .show( context.supportFragmentManager, - QuestionnaireItemBarCodeReaderViewHolderFactory.javaClass.simpleName + BarCodeReaderViewHolderFactory.javaClass.simpleName ) } } - override fun bind(questionnaireItemViewItem: QuestionnaireItemViewItem) { - this.questionnaireItemViewItem = questionnaireItemViewItem - if (!questionnaireItemViewItem.questionnaireItem.prefix.isNullOrEmpty()) { + override fun bind(questionnaireViewItem: QuestionnaireViewItem) { + this.questionnaireViewItem = questionnaireViewItem + if (!questionnaireViewItem.questionnaireItem.prefix.isNullOrEmpty()) { prefixTextView.visibility = View.VISIBLE - prefixTextView.text = questionnaireItemViewItem.questionnaireItem.localizedPrefixSpanned + prefixTextView.text = questionnaireViewItem.questionnaireItem.localizedPrefixSpanned } else { prefixTextView.visibility = View.GONE } - textQuestion.text = questionnaireItemViewItem.questionnaireItem.localizedTextSpanned - setInitial(questionnaireItemViewItem.answers.singleOrNull(), reScanView) + textQuestion.text = questionnaireViewItem.questionnaireItem.localizedTextSpanned + setInitial(questionnaireViewItem.answers.singleOrNull(), reScanView) } private fun setInitial( diff --git a/datacapture/sampledata/component_date_picker.json b/datacapture/sampledata/component_date_picker.json new file mode 100644 index 0000000000..473bfc2fe3 --- /dev/null +++ b/datacapture/sampledata/component_date_picker.json @@ -0,0 +1,36 @@ +{ + "resourceType": "Questionnaire", + "item": [ + { + "linkId": "1", + "text": "Enter a date", + "type": "date", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/entryFormat", + "valueString": "yyyy-mm-dd" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "linkId": "1-most-recent", + "text": "Use keyboard entry or date picker", + "type": "display" + } + ] + } + ] +} diff --git a/datacapture/sampledata/component_date_time_picker.json b/datacapture/sampledata/component_date_time_picker.json new file mode 100644 index 0000000000..1084fe3225 --- /dev/null +++ b/datacapture/sampledata/component_date_time_picker.json @@ -0,0 +1,30 @@ +{ + "resourceType": "Questionnaire", + "item": [ + { + "linkId": "1", + "text": "Schedule an appointment", + "type": "dateTime", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "linkId": "1-most-recent", + "text": "Select a date 4 weeks from now", + "type": "display" + } + ] + } + ] +} diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/QuestionnaireUiEspressoTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/QuestionnaireUiEspressoTest.kt index 7da5b72339..ae47c039b9 100644 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/QuestionnaireUiEspressoTest.kt +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/QuestionnaireUiEspressoTest.kt @@ -17,21 +17,40 @@ package com.google.android.fhir.datacapture import android.widget.FrameLayout -import androidx.core.os.bundleOf -import androidx.fragment.app.add import androidx.fragment.app.commitNow import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions.typeText import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.RootMatchers import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.google.android.fhir.datacapture.TestQuestionnaireFragment.Companion.QUESTIONNAIRE_FILE_PATH_KEY +import androidx.test.platform.app.InstrumentationRegistry +import ca.uhn.fhir.context.FhirContext +import ca.uhn.fhir.context.FhirVersionEnum +import ca.uhn.fhir.parser.IParser import com.google.android.fhir.datacapture.test.R +import com.google.android.fhir.datacapture.utilities.clickIcon import com.google.android.fhir.datacapture.utilities.clickOnText +import com.google.android.fhir.datacapture.validation.Invalid +import com.google.android.fhir.datacapture.validation.QuestionnaireResponseValidator +import com.google.android.fhir.datacapture.validation.Valid +import com.google.android.fhir.datacapture.views.factories.localDate +import com.google.android.fhir.datacapture.views.factories.localDateTime import com.google.android.material.textfield.TextInputLayout import com.google.common.truth.Truth.assertThat +import java.time.LocalDate +import java.time.LocalDateTime +import java.util.Calendar +import java.util.Date +import org.hamcrest.CoreMatchers +import org.hl7.fhir.r4.model.DateTimeType +import org.hl7.fhir.r4.model.DateType +import org.hl7.fhir.r4.model.Questionnaire +import org.hl7.fhir.r4.model.QuestionnaireResponse +import org.junit.Assert import org.junit.Before import org.junit.Rule import org.junit.Test @@ -46,6 +65,8 @@ class QuestionnaireUiEspressoTest { ActivityScenarioRule(TestActivity::class.java) private lateinit var parent: FrameLayout + private val parser: IParser = FhirContext.forCached(FhirVersionEnum.R4).newJsonParser() + private val context = InstrumentationRegistry.getInstrumentation().context @Before fun setup() { @@ -54,16 +75,9 @@ class QuestionnaireUiEspressoTest { @Test fun shouldDisplayReviewButtonWhenNoMorePagesToDisplay() { - val bundle = - bundleOf(QUESTIONNAIRE_FILE_PATH_KEY to "/paginated_questionnaire_with_dependent_answer.json") - activityScenarioRule.scenario.onActivity { activity -> - activity.supportFragmentManager.commitNow { - setReorderingAllowed(true) - add(R.id.container_holder, args = bundle) - } - } + buildFragmentFromQuestionnaire("/paginated_questionnaire_with_dependent_answer.json", true) - onView(ViewMatchers.withId(R.id.review_mode_button)) + onView(withId(R.id.review_mode_button)) .check( ViewAssertions.matches( ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE) @@ -71,13 +85,13 @@ class QuestionnaireUiEspressoTest { ) clickOnText("Yes") - onView(ViewMatchers.withId(R.id.review_mode_button)) + onView(withId(R.id.review_mode_button)) .check( ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.GONE)) ) clickOnText("No") - onView(ViewMatchers.withId(R.id.review_mode_button)) + onView(withId(R.id.review_mode_button)) .check( ViewAssertions.matches( ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE) @@ -87,17 +101,307 @@ class QuestionnaireUiEspressoTest { @Test fun integerTextEdit_inputOutOfRange_shouldShowError() { - val bundle = bundleOf(QUESTIONNAIRE_FILE_PATH_KEY to "/text_questionnaire_integer.json") + buildFragmentFromQuestionnaire("/text_questionnaire_integer.json") + + onView(withId(R.id.text_input_edit_text)).perform(typeText("12345678901")) + onView(withId(R.id.text_input_layout)).check { view, _ -> + val actualError = (view as TextInputLayout).error + assertThat(actualError).isEqualTo("Number must be between -2,147,483,648 and 2,147,483,647") + } + } + + @Test + fun dateTimePicker_shouldShowErrorForWrongDate() { + buildFragmentFromQuestionnaire("/component_date_time_picker.json") + + // Add month and day. No need to add slashes as they are added automatically + onView(withId(R.id.date_input_edit_text)) + .perform(ViewActions.click()) + .perform(ViewActions.typeTextIntoFocusedView("0105")) + + onView(withId(R.id.date_input_layout)).check { view, _ -> + val actualError = (view as TextInputLayout).error + assertThat(actualError).isEqualTo("Date format needs to be MM/dd/yyyy (e.g. 01/31/2023)") + } + onView(withId(R.id.time_input_layout)).check { view, _ -> assertThat(view.isEnabled).isFalse() } + } + + @Test + fun dateTimePicker_shouldEnableTimePickerWithCorrectDate_butNotSaveInQuestionnaireResponse() { + buildFragmentFromQuestionnaire("/component_date_time_picker.json") + + onView(withId(R.id.date_input_edit_text)) + .perform(ViewActions.click()) + .perform(ViewActions.typeTextIntoFocusedView("01052005")) + + onView(withId(R.id.date_input_layout)).check { view, _ -> + val actualError = (view as TextInputLayout).error + assertThat(actualError).isEqualTo(null) + } + + onView(withId(R.id.time_input_layout)).check { view, _ -> assertThat(view.isEnabled).isTrue() } + + assertThat(getQuestionnaireResponse().item.size).isEqualTo(1) + assertThat(getQuestionnaireResponse().item.first().answer.size).isEqualTo(0) + } + + @Test + fun dateTimePicker_shouldSetAnswerWhenDateAndTimeAreFilled() { + buildFragmentFromQuestionnaire("/component_date_time_picker.json") + + onView(withId(R.id.date_input_edit_text)) + .perform(ViewActions.click()) + .perform(ViewActions.typeTextIntoFocusedView("01052005")) + + onView(withId(R.id.time_input_layout)).perform(clickIcon(true)) + clickOnText("AM") + clickOnText("6") + clickOnText("10") + clickOnText("OK") + + val answer = getQuestionnaireResponse().item.first().answer.first().valueDateTimeType + + assertThat(answer.localDateTime).isEqualTo(LocalDateTime.of(2005, 1, 5, 6, 10)) + } + + @Test + fun datePicker_shouldShowErrorForWrongDate() { + buildFragmentFromQuestionnaire("/component_date_picker.json") + + // Add month and day. No need to add slashes as they are added automatically + onView(withId(R.id.text_input_edit_text)) + .perform(ViewActions.click()) + .perform(ViewActions.typeTextIntoFocusedView("0105")) + + onView(withId(R.id.text_input_layout)).check { view, _ -> + val actualError = (view as TextInputLayout).error + assertThat(actualError).isEqualTo("Date format needs to be MM/dd/yyyy (e.g. 01/31/2023)") + } + } + + @Test + fun datePicker_shouldSaveInQuestionnaireResponseWhenCorrectDateEntered() { + buildFragmentFromQuestionnaire("/component_date_picker.json") + + onView(withId(R.id.text_input_edit_text)) + .perform(ViewActions.click()) + .perform(ViewActions.typeTextIntoFocusedView("01052005")) + + onView(withId(R.id.text_input_layout)).check { view, _ -> + val actualError = (view as TextInputLayout).error + assertThat(actualError).isEqualTo(null) + } + + val answer = getQuestionnaireResponse().item.first().answer.first().valueDateType + assertThat(answer.localDate).isEqualTo(LocalDate.of(2005, 1, 5)) + } + + @Test + fun datePicker_shouldSetDateInput_withinRange() { + val questionnaire = + Questionnaire().apply { + id = "a-questionnaire" + addItem( + Questionnaire.QuestionnaireItemComponent().apply { + type = Questionnaire.QuestionnaireItemType.DATE + linkId = "link-1" + addExtension().apply { + url = "http://hl7.org/fhir/StructureDefinition/minValue" + val minDate = DateType(Date()).apply { add(Calendar.YEAR, -1) } + setValue(minDate) + } + addExtension().apply { + url = "http://hl7.org/fhir/StructureDefinition/maxValue" + val maxDate = DateType(Date()).apply { add(Calendar.YEAR, 4) } + setValue(maxDate) + } + } + ) + } + + buildFragmentFromQuestionnaire(questionnaire) + onView(withId(R.id.text_input_layout)).perform(clickIcon(true)) + onView(CoreMatchers.allOf(ViewMatchers.withText("OK"))) + .inRoot(RootMatchers.isDialog()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .perform(ViewActions.click()) + + val today = DateTimeType.today().valueAsString + val answer = getQuestionnaireResponse().item.first().answer.first().valueDateType.valueAsString + + assertThat(answer).isEqualTo(today) + val validationResult = + QuestionnaireResponseValidator.validateQuestionnaireResponse( + questionnaire, + getQuestionnaireResponse(), + context + ) + + assertThat(validationResult["link-1"]?.first()).isEqualTo(Valid) + } + + @Test + fun datePicker_shouldNotSetDateInput_outsideMaxRange() { + val maxDate = DateType(Date()).apply { add(Calendar.YEAR, -2) } + val questionnaire = + Questionnaire().apply { + id = "a-questionnaire" + addItem( + Questionnaire.QuestionnaireItemComponent().apply { + type = Questionnaire.QuestionnaireItemType.DATE + linkId = "link-1" + addExtension().apply { + url = "http://hl7.org/fhir/StructureDefinition/minValue" + val minDate = DateType(Date()).apply { add(Calendar.YEAR, -4) } + setValue(minDate) + } + addExtension().apply { + url = "http://hl7.org/fhir/StructureDefinition/maxValue" + setValue(maxDate) + } + } + ) + } + + buildFragmentFromQuestionnaire(questionnaire) + onView(withId(R.id.text_input_layout)).perform(clickIcon(true)) + onView(CoreMatchers.allOf(ViewMatchers.withText("OK"))) + .inRoot(RootMatchers.isDialog()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .perform(ViewActions.click()) + + val maxDateAllowed = maxDate.valueAsString + val validationResult = + QuestionnaireResponseValidator.validateQuestionnaireResponse( + questionnaire, + getQuestionnaireResponse(), + context + ) + + assertThat((validationResult["link-1"]?.first() as Invalid).getSingleStringValidationMessage()) + .isEqualTo("Maximum value allowed is:$maxDateAllowed") + } + + @Test + fun datePicker_shouldNotSetDateInput_outsideMinRange() { + val minDate = DateType(Date()).apply { add(Calendar.YEAR, 1) } + val questionnaire = + Questionnaire().apply { + id = "a-questionnaire" + addItem( + Questionnaire.QuestionnaireItemComponent().apply { + type = Questionnaire.QuestionnaireItemType.DATE + linkId = "link-1" + addExtension().apply { + url = "http://hl7.org/fhir/StructureDefinition/minValue" + setValue(minDate) + } + addExtension().apply { + url = "http://hl7.org/fhir/StructureDefinition/maxValue" + val maxDate = DateType(Date()).apply { add(Calendar.YEAR, 2) } + setValue(maxDate) + } + } + ) + } + + buildFragmentFromQuestionnaire(questionnaire) + onView(withId(R.id.text_input_layout)).perform(clickIcon(true)) + onView(CoreMatchers.allOf(ViewMatchers.withText("OK"))) + .inRoot(RootMatchers.isDialog()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .perform(ViewActions.click()) + + val minDateAllowed = minDate.valueAsString + val validationResult = + QuestionnaireResponseValidator.validateQuestionnaireResponse( + questionnaire, + getQuestionnaireResponse(), + context + ) + + assertThat((validationResult["link-1"]?.first() as Invalid).getSingleStringValidationMessage()) + .isEqualTo("Minimum value allowed is:$minDateAllowed") + } + + @Test + fun datePicker_shouldThrowException_whenMinValueRangeIsGreaterThanMaxValueRange() { + val questionnaire = + Questionnaire().apply { + id = "a-questionnaire" + addItem( + Questionnaire.QuestionnaireItemComponent().apply { + type = Questionnaire.QuestionnaireItemType.DATE + linkId = "link-1" + addExtension().apply { + url = "http://hl7.org/fhir/StructureDefinition/minValue" + val minDate = DateType(Date()).apply { add(Calendar.YEAR, 1) } + + setValue(minDate) + } + addExtension().apply { + url = "http://hl7.org/fhir/StructureDefinition/maxValue" + val maxDate = DateType(Date()).apply { add(Calendar.YEAR, -1) } + setValue(maxDate) + } + } + ) + } + + buildFragmentFromQuestionnaire(questionnaire) + val exception = + Assert.assertThrows(IllegalArgumentException::class.java) { + onView(withId(com.google.android.fhir.datacapture.R.id.text_input_layout)) + .perform(clickIcon(true)) + onView(CoreMatchers.allOf(ViewMatchers.withText("OK"))) + .inRoot(RootMatchers.isDialog()) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + .perform(ViewActions.click()) + } + assertThat(exception.message).isEqualTo("minValue cannot be greater than maxValue") + } + + private fun buildFragmentFromQuestionnaire(fileName: String, isReviewMode: Boolean = false) { + val questionnaireJsonString = readFileFromAssets(fileName) + val questionnaireFragment = + QuestionnaireFragment.builder() + .setQuestionnaire(questionnaireJsonString) + .showReviewPageBeforeSubmit(isReviewMode) + .build() activityScenarioRule.scenario.onActivity { activity -> activity.supportFragmentManager.commitNow { setReorderingAllowed(true) - add(R.id.container_holder, args = bundle) + add(R.id.container_holder, questionnaireFragment) } } - onView(withId(R.id.text_input_edit_text)).perform(typeText("12345678901")) - onView(withId(R.id.text_input_layout)).check { view, _ -> - val actualError = (view as TextInputLayout).error - assertThat(actualError).isEqualTo("Number must be between -2,147,483,648 and 2,147,483,647") + } + + private fun buildFragmentFromQuestionnaire( + questionnaire: Questionnaire, + isReviewMode: Boolean = false + ) { + val questionnaireFragment = + QuestionnaireFragment.builder() + .setQuestionnaire(parser.encodeResourceToString(questionnaire)) + .showReviewPageBeforeSubmit(isReviewMode) + .build() + activityScenarioRule.scenario.onActivity { activity -> + activity.supportFragmentManager.commitNow { + setReorderingAllowed(true) + add(R.id.container_holder, questionnaireFragment) + } + } + } + + private fun readFileFromAssets(filename: String) = + javaClass.getResourceAsStream(filename)!!.bufferedReader().use { it.readText() } + private fun getQuestionnaireResponse(): QuestionnaireResponse { + var testQuestionnaireFragment: QuestionnaireFragment? = null + activityScenarioRule.scenario.onActivity { activity -> + testQuestionnaireFragment = + activity.supportFragmentManager.findFragmentById(R.id.container_holder) + as QuestionnaireFragment } + return testQuestionnaireFragment!!.getQuestionnaireResponse() } } diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/TestQuestionnaireFragment.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/TestQuestionnaireFragment.kt deleted file mode 100644 index b1ea038362..0000000000 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/TestQuestionnaireFragment.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2022 Google LLC - * - * 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 com.google.android.fhir.datacapture - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.os.bundleOf -import androidx.fragment.app.Fragment -import androidx.fragment.app.add -import androidx.fragment.app.commitNow -import androidx.fragment.app.viewModels -import androidx.lifecycle.lifecycleScope -import com.google.android.fhir.datacapture.test.R -import kotlinx.coroutines.launch - -class TestQuestionnaireFragment : Fragment() { - - private val viewModel: TestQuestionnaireViewModel by viewModels() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - return inflater.inflate(R.layout.test_fragment_layout, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - if (savedInstanceState == null) { - addQuestionnaireFragment() - } - } - - private fun addQuestionnaireFragment() { - viewLifecycleOwner.lifecycleScope.launch { - if (childFragmentManager.findFragmentByTag(QUESTIONNAIRE_FRAGMENT_TAG) == null) { - childFragmentManager.commitNow { - setReorderingAllowed(true) - add( - R.id.container, - tag = QUESTIONNAIRE_FRAGMENT_TAG, - args = - bundleOf( - QuestionnaireFragment.EXTRA_QUESTIONNAIRE_JSON_STRING to - viewModel.getQuestionnaireJson(), - QuestionnaireFragment.EXTRA_ENABLE_REVIEW_PAGE to true - ) - ) - } - } - } - } - - companion object { - const val QUESTIONNAIRE_FRAGMENT_TAG = "questionnaire-fragment-tag" - const val QUESTIONNAIRE_FILE_PATH_KEY = "questionnaire-file-path-key" - const val QUESTIONNAIRE_FILE_WITH_VALIDATION_PATH_KEY = - "questionnaire-file-with-validation-path-key" - } -} diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/TestQuestionnaireViewModel.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/TestQuestionnaireViewModel.kt deleted file mode 100644 index fb970db001..0000000000 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/TestQuestionnaireViewModel.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2022 Google LLC - * - * 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 com.google.android.fhir.datacapture - -import android.app.Application -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.SavedStateHandle -import androidx.lifecycle.viewModelScope -import com.google.android.fhir.datacapture.TestQuestionnaireFragment.Companion.QUESTIONNAIRE_FILE_PATH_KEY -import com.google.android.fhir.datacapture.TestQuestionnaireFragment.Companion.QUESTIONNAIRE_FILE_WITH_VALIDATION_PATH_KEY -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -class TestQuestionnaireViewModel(application: Application, private val state: SavedStateHandle) : - AndroidViewModel(application) { - private val backgroundContext = viewModelScope.coroutineContext - private var questionnaireJson: String? = null - private var questionnaireWithValidationJson: String? = null - - init { - viewModelScope.launch { - getQuestionnaireJson() - // TODO remove check once all files are added - if (!state.get(QUESTIONNAIRE_FILE_WITH_VALIDATION_PATH_KEY).isNullOrEmpty()) { - getQuestionnaireWithValidationJson() - } - } - } - - suspend fun getQuestionnaireJson(): String { - return withContext(backgroundContext) { - if (questionnaireJson == null) { - questionnaireJson = readFileFromAssets(state[QUESTIONNAIRE_FILE_PATH_KEY]!!) - } - questionnaireJson!! - } - } - - private suspend fun getQuestionnaireWithValidationJson(): String { - return withContext(backgroundContext) { - if (questionnaireWithValidationJson == null) { - questionnaireWithValidationJson = - readFileFromAssets(state[QUESTIONNAIRE_FILE_WITH_VALIDATION_PATH_KEY]!!) - } - questionnaireWithValidationJson!! - } - } - - private suspend fun readFileFromAssets(filename: String) = - withContext(backgroundContext) { - javaClass.getResourceAsStream(filename)!!.bufferedReader().use { it.readText() } - } -} diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/PhoneNumberViewHolderFactoryInstrumentedTest.kt similarity index 83% rename from datacapture/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest.kt rename to datacapture/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/PhoneNumberViewHolderFactoryInstrumentedTest.kt index 5c04b3a627..518a9154b0 100644 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest.kt +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/contrib/views/PhoneNumberViewHolderFactoryInstrumentedTest.kt @@ -22,13 +22,13 @@ import androidx.appcompat.view.ContextThemeWrapper import androidx.test.annotation.UiThreadTest import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry -import com.google.android.fhir.datacapture.QuestionnaireItemEditAdapter -import com.google.android.fhir.datacapture.QuestionnaireItemViewHolderType +import com.google.android.fhir.datacapture.QuestionnaireEditAdapter +import com.google.android.fhir.datacapture.QuestionnaireViewHolderType import com.google.android.fhir.datacapture.R import com.google.android.fhir.datacapture.validation.Invalid import com.google.android.fhir.datacapture.validation.NotValidated -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewHolder -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewItem +import com.google.android.fhir.datacapture.views.QuestionnaireViewItem +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolder import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputLayout import com.google.common.truth.Truth.assertThat @@ -42,11 +42,11 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest { +class PhoneNumberViewHolderFactoryInstrumentedTest { private lateinit var context: ContextThemeWrapper private lateinit var parent: FrameLayout private lateinit var viewHolder: QuestionnaireItemViewHolder - private lateinit var questionnaireItemEditAdapter: QuestionnaireItemEditAdapter + private lateinit var questionnaireEditAdapter: QuestionnaireEditAdapter @Before fun setUp() { @@ -56,18 +56,18 @@ class QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest { R.style.Theme_Material3_DayNight ) parent = FrameLayout(context) - viewHolder = QuestionnaireItemPhoneNumberViewHolderFactory.create(parent) - questionnaireItemEditAdapter = QuestionnaireItemEditAdapter() + viewHolder = PhoneNumberViewHolderFactory.create(parent) + questionnaireEditAdapter = QuestionnaireEditAdapter() } @Test fun createViewHolder_shouldReturn_phoneNumberViewHolder() { val viewHolderFromAdapter = - questionnaireItemEditAdapter.createViewHolder( + questionnaireEditAdapter.createViewHolder( parent, - QuestionnaireItemEditAdapter.ViewType.from( - type = QuestionnaireItemEditAdapter.ViewType.Type.QUESTION, - subtype = QuestionnaireItemViewHolderType.PHONE_NUMBER.value, + QuestionnaireEditAdapter.ViewType.from( + type = QuestionnaireEditAdapter.ViewType.Type.QUESTION, + subtype = QuestionnaireViewHolderType.PHONE_NUMBER.value, ) .viewType, ) @@ -77,7 +77,7 @@ class QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest { @Test fun shouldSetTextViewText() { viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { text = "Question?" }, QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, @@ -92,7 +92,7 @@ class QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest { @UiThreadTest fun shouldSetInputText() { viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { text = "Question?" }, QuestionnaireResponse.QuestionnaireResponseItemComponent() .addAnswer( @@ -117,7 +117,7 @@ class QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest { @UiThreadTest fun shouldSetInputTextToEmpty() { viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent(), QuestionnaireResponse.QuestionnaireResponseItemComponent() .addAnswer( @@ -130,7 +130,7 @@ class QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest { ) ) viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent(), QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, @@ -150,42 +150,42 @@ class QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest { @UiThreadTest @Ignore("https://github.com/google/android-fhir/issues/1494") fun shouldSetQuestionnaireResponseItemAnswer() { - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent(), QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - viewHolder.bind(questionnaireItemViewItem) + viewHolder.bind(questionnaireViewItem) viewHolder.itemView .findViewById(R.id.text_input_edit_text) .setText("+12345678910") - assertThat(questionnaireItemViewItem.answers.single().valueStringType.value) + assertThat(questionnaireViewItem.answers.single().valueStringType.value) .isEqualTo("+12345678910") } @Test @UiThreadTest fun shouldSetQuestionnaireResponseItemAnswerToEmpty() { - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent(), QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - viewHolder.bind(questionnaireItemViewItem) + viewHolder.bind(questionnaireViewItem) viewHolder.itemView.findViewById(R.id.text_input_edit_text).setText("") - assertThat(questionnaireItemViewItem.answers).isEmpty() + assertThat(questionnaireViewItem.answers).isEmpty() } @Test @UiThreadTest fun displayValidationResult_noError_shouldShowNoErrorMessage() { viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/minLength" @@ -212,7 +212,7 @@ class QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest { @UiThreadTest fun displayValidationResult_error_shouldShowErrorMessage() { viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { maxLength = 10 }, QuestionnaireResponse.QuestionnaireResponseItemComponent().apply { addAnswer( @@ -236,7 +236,7 @@ class QuestionnaireItemPhoneNumberViewHolderFactoryInstrumentedTest { @UiThreadTest fun bind_readOnly_shouldDisableView() { viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { readOnly = true }, QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemAttachmentViewHolderFactoryEspressoTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/AttachmentViewHolderFactoryEspressoTest.kt similarity index 96% rename from datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemAttachmentViewHolderFactoryEspressoTest.kt rename to datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/AttachmentViewHolderFactoryEspressoTest.kt index 7fe80d8cf8..47e5f03b1b 100644 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemAttachmentViewHolderFactoryEspressoTest.kt +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/AttachmentViewHolderFactoryEspressoTest.kt @@ -28,6 +28,8 @@ import androidx.test.platform.app.InstrumentationRegistry import com.google.android.fhir.datacapture.R import com.google.android.fhir.datacapture.TestActivity import com.google.android.fhir.datacapture.validation.NotValidated +import com.google.android.fhir.datacapture.views.factories.AttachmentViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolder import com.google.common.truth.Truth.assertThat import org.hl7.fhir.r4.model.Attachment import org.hl7.fhir.r4.model.CodeType @@ -39,7 +41,7 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { +class AttachmentViewHolderFactoryEspressoTest { @Rule @JvmField @@ -52,14 +54,14 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { @Before fun setup() { activityScenarioRule.getScenario().onActivity { activity -> parent = FrameLayout(activity) } - viewHolder = QuestionnaireItemAttachmentViewHolderFactory.create(parent) + viewHolder = AttachmentViewHolderFactory.create(parent) setTestLayout(viewHolder.itemView) } @Test fun shouldDisplayTakePhotoAndUploadPhotoButton() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" @@ -86,7 +88,7 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { @Test fun shouldDisplayUploadAudioButton() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" @@ -110,7 +112,7 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { @Test fun shouldDisplayUploadVideoButton() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" @@ -134,7 +136,7 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { @Test fun shouldDisplayUploadDocumentButton() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" @@ -158,7 +160,7 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { @Test fun shouldDisplayTakePhotoAndUploadFileButton() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" @@ -189,7 +191,7 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { @Test fun shouldDisplayImagePreviewFromAnswer() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" @@ -227,7 +229,7 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { @Test fun shouldDisplayAudioFilePreviewFromAnswer() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" @@ -266,7 +268,7 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { @Test fun shouldDisplayVideoFilePreviewFromAnswer() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" @@ -305,7 +307,7 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { @Test fun shouldDisplayDocumentFilePreviewFromAnswer() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" @@ -344,7 +346,7 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { @Test fun doNotShowPreviewIfAnswerDoesNotHaveAttachment() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" @@ -372,7 +374,7 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { @Test fun doNotShowPreviewOfPreviousAnswerAttachmentForCurrentAnswerItem() { val questionnaireItem = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" @@ -409,7 +411,7 @@ class QuestionnaireItemAttachmentViewHolderFactoryEspressoTest { .isEqualTo(View.VISIBLE) val questionnaireItemWithNullAnswer = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { addExtension().apply { url = "http://hl7.org/fhir/StructureDefinition/mimeType" diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDateTimePickerViewHolderFactoryEspressoTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/DateTimePickerViewHolderFactoryEspressoTest.kt similarity index 54% rename from datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDateTimePickerViewHolderFactoryEspressoTest.kt rename to datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/DateTimePickerViewHolderFactoryEspressoTest.kt index 7c0b5c879c..570308552b 100644 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDateTimePickerViewHolderFactoryEspressoTest.kt +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/DateTimePickerViewHolderFactoryEspressoTest.kt @@ -18,7 +18,6 @@ package com.google.android.fhir.datacapture.views import android.view.View import android.widget.FrameLayout -import android.widget.TextView import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions import androidx.test.espresso.assertion.ViewAssertions.matches @@ -33,7 +32,8 @@ import com.google.android.fhir.datacapture.R import com.google.android.fhir.datacapture.TestActivity import com.google.android.fhir.datacapture.utilities.clickIcon import com.google.android.fhir.datacapture.validation.NotValidated -import com.google.common.truth.Truth.assertThat +import com.google.android.fhir.datacapture.views.factories.DateTimePickerViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolder import org.hamcrest.CoreMatchers.allOf import org.hl7.fhir.r4.model.Questionnaire import org.hl7.fhir.r4.model.QuestionnaireResponse @@ -43,7 +43,7 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class QuestionnaireItemDateTimePickerViewHolderFactoryEspressoTest { +class DateTimePickerViewHolderFactoryEspressoTest { @Rule @JvmField @@ -54,99 +54,15 @@ class QuestionnaireItemDateTimePickerViewHolderFactoryEspressoTest { private lateinit var viewHolder: QuestionnaireItemViewHolder @Before fun setup() { - activityScenarioRule.getScenario().onActivity { activity -> parent = FrameLayout(activity) } - viewHolder = QuestionnaireItemDateTimePickerViewHolderFactory.create(parent) + activityScenarioRule.scenario.onActivity { activity -> parent = FrameLayout(activity) } + viewHolder = DateTimePickerViewHolderFactory.create(parent) setTestLayout(viewHolder.itemView) } - @Test - fun shouldSetFirstDateInputThenTimeInput() { - val questionnaireItemView = - QuestionnaireItemViewItem( - Questionnaire.QuestionnaireItemComponent().apply { text = "Question?" }, - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, _, _ -> }, - ) - - runOnUI { viewHolder.bind(questionnaireItemView) } - - assertThat( - viewHolder.itemView.findViewById(R.id.date_input_edit_text).text.toString() - ) - .isEmpty() - assertThat( - viewHolder.itemView.findViewById(R.id.time_input_edit_text).text.toString() - ) - .isEmpty() - - onView(withId(R.id.date_input_layout)).perform(clickIcon(true)) - onView(allOf(withText("OK"))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(ViewActions.click()) - onView(withId(R.id.time_input_layout)).perform(clickIcon(true)) - onView(allOf(withText("OK"))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(ViewActions.click()) - - assertThat( - viewHolder.itemView.findViewById(R.id.date_input_edit_text).text.toString() - ) - .isNotEmpty() - assertThat( - viewHolder.itemView.findViewById(R.id.time_input_edit_text).text.toString() - ) - .isNotEmpty() - } - - @Test - fun shouldSetFirstTimeInputThenDateInput() { - val questionnaireItemView = - QuestionnaireItemViewItem( - Questionnaire.QuestionnaireItemComponent().apply { text = "Question?" }, - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, _, _ -> }, - ) - - runOnUI { viewHolder.bind(questionnaireItemView) } - - assertThat( - viewHolder.itemView.findViewById(R.id.date_input_edit_text).text.toString() - ) - .isEmpty() - assertThat( - viewHolder.itemView.findViewById(R.id.time_input_edit_text).text.toString() - ) - .isEmpty() - - onView(withId(R.id.time_input_layout)).perform(clickIcon(true)) - onView(allOf(withText("OK"))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(ViewActions.click()) - onView(withId(R.id.date_input_layout)).perform(clickIcon(true)) - onView(allOf(withText("OK"))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(ViewActions.click()) - - assertThat( - viewHolder.itemView.findViewById(R.id.date_input_edit_text).text.toString() - ) - .isNotEmpty() - assertThat( - viewHolder.itemView.findViewById(R.id.time_input_edit_text).text.toString() - ) - .isNotEmpty() - } - @Test fun showsTimePickerInInputMode() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent(), QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, @@ -169,7 +85,7 @@ class QuestionnaireItemDateTimePickerViewHolderFactoryEspressoTest { @Test fun showsTimePickerInClockMode() { val questionnaireItemView = - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent(), QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, @@ -189,12 +105,12 @@ class QuestionnaireItemDateTimePickerViewHolderFactoryEspressoTest { /** Method to run code snippet on UI/main thread */ private fun runOnUI(action: () -> Unit) { - activityScenarioRule.getScenario().onActivity { activity -> action() } + activityScenarioRule.scenario.onActivity { activity -> action() } } /** Method to set content view for test activity */ private fun setTestLayout(view: View) { - activityScenarioRule.getScenario().onActivity { activity -> activity.setContentView(view) } + activityScenarioRule.scenario.onActivity { activity -> activity.setContentView(view) } InstrumentationRegistry.getInstrumentation().waitForIdleSync() } } diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDropDownViewHolderFactoryEspressoTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/DropDownViewHolderFactoryEspressoTest.kt similarity index 96% rename from datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDropDownViewHolderFactoryEspressoTest.kt rename to datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/DropDownViewHolderFactoryEspressoTest.kt index 6c250f26a9..5b5e4bc4be 100644 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDropDownViewHolderFactoryEspressoTest.kt +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/DropDownViewHolderFactoryEspressoTest.kt @@ -34,6 +34,8 @@ import com.google.android.fhir.datacapture.R import com.google.android.fhir.datacapture.TestActivity import com.google.android.fhir.datacapture.utilities.showDropDown import com.google.android.fhir.datacapture.validation.NotValidated +import com.google.android.fhir.datacapture.views.factories.DropDownViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolder import com.google.android.material.textfield.MaterialAutoCompleteTextView import com.google.common.truth.Truth.assertThat import org.hl7.fhir.r4.model.Attachment @@ -46,7 +48,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test -class QuestionnaireItemDropDownViewHolderFactoryEspressoTest { +class DropDownViewHolderFactoryEspressoTest { @Rule @JvmField var activityScenarioRule: ActivityScenarioRule = @@ -69,39 +71,39 @@ class QuestionnaireItemDropDownViewHolderFactoryEspressoTest { @Before fun setup() { activityScenarioRule.scenario.onActivity { activity -> parent = FrameLayout(activity) } - viewHolder = QuestionnaireItemDropDownViewHolderFactory.create(parent) + viewHolder = DropDownViewHolderFactory.create(parent) setTestLayout(viewHolder.itemView) } @Test fun shouldClearAutoCompleteTextView() { - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( answerOptions("Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5"), responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } onView(withId(R.id.auto_complete)).perform(showDropDown()) onView(withText("-")).inRoot(isPlatformPopup()).check(matches(isDisplayed())).perform(click()) assertThat(viewHolder.itemView.findViewById(R.id.auto_complete).text.toString()) .isEqualTo("-") - assertThat(questionnaireItemViewItem.answers).isEmpty() + assertThat(questionnaireViewItem.answers).isEmpty() } @Test fun shouldSetDropDownValueToAutoCompleteTextView() { var answerHolder: List? = null - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( answerOptions("Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5"), responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } onView(withId(R.id.auto_complete)).perform(showDropDown()) onView(withText("Coding 3")) @@ -116,14 +118,14 @@ class QuestionnaireItemDropDownViewHolderFactoryEspressoTest { @Test fun shouldClearAutoCompleteTextViewOnRebindingView() { var answerHolder: List? = null - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( answerOptions("Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5"), responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } onView(withId(R.id.auto_complete)).perform(showDropDown()) onView(withText("Coding 3")) @@ -133,7 +135,7 @@ class QuestionnaireItemDropDownViewHolderFactoryEspressoTest { assertThat(viewHolder.itemView.findViewById(R.id.auto_complete).text.toString()) .isEqualTo("Coding 3") - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } assertThat(viewHolder.itemView.findViewById(R.id.auto_complete).text.toString()) .isEqualTo("") val autoCompleteTextView = @@ -147,8 +149,8 @@ class QuestionnaireItemDropDownViewHolderFactoryEspressoTest { @Test fun shouldSetImageToAutoCompleteTextViewOnItemSelection() { var answerHolder: List? = null - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( createAnswerOptions( "Coding 1", "Coding 2", @@ -161,7 +163,7 @@ class QuestionnaireItemDropDownViewHolderFactoryEspressoTest { validationResult = NotValidated, answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } onView(withId(R.id.auto_complete)).perform(showDropDown()) onView(withText("Coding 3")) @@ -181,14 +183,14 @@ class QuestionnaireItemDropDownViewHolderFactoryEspressoTest { @Test fun shouldSetDropDownValueStringToAutoCompleteTextView() { var answerHolder: List? = null - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( createAnswerOptions("Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5"), responseValueStringOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } onView(withId(R.id.auto_complete)).perform(showDropDown()) onView(withText("Coding 1")) @@ -202,14 +204,14 @@ class QuestionnaireItemDropDownViewHolderFactoryEspressoTest { @Test fun shouldReturnNonFilteredDropDownMenuItems() { - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( createAnswerOptions("Coding 1", "Coding 2", "Coding 3", "Add", "Subtract"), responseValueStringOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } onView(withId(R.id.auto_complete)).perform(showDropDown()) assertThat( @@ -221,14 +223,14 @@ class QuestionnaireItemDropDownViewHolderFactoryEspressoTest { } @Test fun shouldReturnFilteredDropDownMenuItems() { - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( createAnswerOptions("Coding 1", "Coding 2", "Coding 3", "Add", "Subtract"), responseValueStringOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } onView(withId(R.id.auto_complete)).perform(showDropDown()) onView(withId(R.id.auto_complete)).perform(typeText("Coding")) @@ -242,14 +244,14 @@ class QuestionnaireItemDropDownViewHolderFactoryEspressoTest { @Test fun shouldReturnFilteredWithNoResultsDropDownMenuItems() { - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( createAnswerOptions("Coding 1", "Coding 2", "Coding 3", "Add", "Subtract"), responseValueStringOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } onView(withId(R.id.auto_complete)).perform(showDropDown()) onView(withId(R.id.auto_complete)).perform(typeText("Division")) diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemMediaViewInstrumentedTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/MediaViewInstrumentedTest.kt similarity index 97% rename from datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemMediaViewInstrumentedTest.kt rename to datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/MediaViewInstrumentedTest.kt index 1d121f1fdd..a4e3eb96fa 100644 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemMediaViewInstrumentedTest.kt +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/MediaViewInstrumentedTest.kt @@ -37,7 +37,7 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class QuestionnaireItemMediaViewInstrumentedTest { +class MediaViewInstrumentedTest { @Rule @JvmField @@ -45,12 +45,12 @@ class QuestionnaireItemMediaViewInstrumentedTest { ActivityScenarioRule(TestActivity::class.java) private lateinit var parent: FrameLayout - private lateinit var view: QuestionnaireItemMediaView + private lateinit var view: MediaView @Before fun setUp() { activityScenarioRule.scenario.onActivity { activity -> parent = FrameLayout(activity) } - view = QuestionnaireItemMediaView(parent.context, null) + view = MediaView(parent.context, null) setTestLayout(view) } diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDatePickerViewHolderFactoryEspressoTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDatePickerViewHolderFactoryEspressoTest.kt deleted file mode 100644 index 114d4dcc66..0000000000 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDatePickerViewHolderFactoryEspressoTest.kt +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright 2022 Google LLC - * - * 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 com.google.android.fhir.datacapture.views - -import android.content.Context -import android.view.View -import android.widget.FrameLayout -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.RootMatchers.isDialog -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.ext.junit.rules.ActivityScenarioRule -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.google.android.fhir.datacapture.R -import com.google.android.fhir.datacapture.TestActivity -import com.google.android.fhir.datacapture.utilities.clickIcon -import com.google.android.fhir.datacapture.validation.Invalid -import com.google.android.fhir.datacapture.validation.NotValidated -import com.google.android.fhir.datacapture.validation.QuestionnaireResponseItemValidator -import com.google.android.fhir.datacapture.validation.Valid -import com.google.android.material.textfield.TextInputLayout -import com.google.common.truth.Truth.assertThat -import java.util.Calendar -import java.util.Date -import java.util.Locale -import org.hamcrest.CoreMatchers.allOf -import org.hl7.fhir.r4.model.DateTimeType -import org.hl7.fhir.r4.model.DateType -import org.hl7.fhir.r4.model.Questionnaire -import org.hl7.fhir.r4.model.QuestionnaireResponse -import org.junit.Assert.assertThrows -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class QuestionnaireItemDatePickerViewHolderFactoryEspressoTest { - - @Rule - @JvmField - var activityScenarioRule: ActivityScenarioRule = - ActivityScenarioRule(TestActivity::class.java) - - private lateinit var parent: FrameLayout - private lateinit var viewHolder: QuestionnaireItemViewHolder - @Before - fun setup() { - Locale.setDefault(Locale.US) - activityScenarioRule.getScenario().onActivity { activity -> parent = FrameLayout(activity) } - viewHolder = QuestionnaireItemDatePickerViewHolderFactory.create(parent) - setTestLayout(viewHolder.itemView) - } - - @Test - fun shouldSetDateInput() { - var answerHolder: List? = null - val questionnaireItemView = - QuestionnaireItemViewItem( - Questionnaire.QuestionnaireItemComponent(), - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, - ) - - runOnUI { viewHolder.bind(questionnaireItemView) } - - onView(withId(R.id.text_input_layout)).perform(clickIcon(true)) - onView(allOf(withText("OK"))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(ViewActions.click()) - - val today = DateTimeType.today().valueAsString - - assertThat(answerHolder!!.single().valueDateType?.valueAsString).isEqualTo(today) - } - - @Test - fun shouldSetDateInput_withinRange() { - var answerHolder: List? = null - val questionnaireItemView = - QuestionnaireItemViewItem( - Questionnaire.QuestionnaireItemComponent().apply { - addExtension().apply { - url = "http://hl7.org/fhir/StructureDefinition/minValue" - val minDate = DateType(Date()).apply { add(Calendar.YEAR, -1) } - setValue(minDate) - } - addExtension().apply { - url = "http://hl7.org/fhir/StructureDefinition/maxValue" - val maxDate = DateType(Date()).apply { add(Calendar.YEAR, 4) } - setValue(maxDate) - } - }, - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, - ) - - runOnUI { viewHolder.bind(questionnaireItemView) } - - onView(withId(R.id.text_input_layout)).perform(clickIcon(true)) - onView(allOf(withText("OK"))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(ViewActions.click()) - - val today = DateTimeType.today().valueAsString - val validationResult = - QuestionnaireResponseItemValidator.validate( - questionnaireItemView.questionnaireItem, - questionnaireItemView.answers, - viewHolder.itemView.context - ) - - assertThat(answerHolder!!.single().valueDateType?.valueAsString).isEqualTo(today) - assertThat(validationResult).isEqualTo(Valid) - } - - @Test - fun shouldSetDateInput_invalid_date_entry_invalid_month_day_year() { - val questionnaireItemView = - QuestionnaireItemViewItem( - Questionnaire.QuestionnaireItemComponent(), - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, _, _ -> }, - ) - - runOnUI { viewHolder.bind(questionnaireItemView) } - - onView(withId(R.id.text_input_layout)).perform(ViewActions.click()) - onView(withId(R.id.text_input_edit_text)).perform(ViewActions.typeText("40/0/-9992")) - - assertThat(viewHolder.itemView.findViewById(R.id.text_input_layout).error) - .isEqualTo("Date format needs to be MM/dd/yyyy (e.g. 01/31/2023)") - } - - @Test - fun shouldSetDateInput_invalid_date_entry_invalid_day() { - val questionnaireItemView = - QuestionnaireItemViewItem( - Questionnaire.QuestionnaireItemComponent(), - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, _, _ -> }, - ) - - runOnUI { viewHolder.bind(questionnaireItemView) } - - onView(withId(R.id.text_input_layout)).perform(ViewActions.click()) - onView(withId(R.id.text_input_edit_text)).perform(ViewActions.typeText("1/100/2")) - - assertThat(viewHolder.itemView.findViewById(R.id.text_input_layout).error) - .isEqualTo("Date format needs to be MM/dd/yyyy (e.g. 01/31/2023)") - } - - @Test - fun shouldSetDateInput_invalid_date_entry_invalid_month() { - val questionnaireItemView = - QuestionnaireItemViewItem( - Questionnaire.QuestionnaireItemComponent(), - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, _, _ -> }, - ) - - runOnUI { viewHolder.bind(questionnaireItemView) } - - onView(withId(R.id.text_input_layout)).perform(ViewActions.click()) - onView(withId(R.id.text_input_edit_text)).perform(ViewActions.typeText("40/1/2")) - - assertThat(viewHolder.itemView.findViewById(R.id.text_input_layout).error) - .isEqualTo("Date format needs to be MM/dd/yyyy (e.g. 01/31/2023)") - } - - @Test - fun shouldSetDateInput_invalid_date_entry_invalid_year() { - val questionnaireItemView = - QuestionnaireItemViewItem( - Questionnaire.QuestionnaireItemComponent(), - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, _, _ -> }, - ) - - runOnUI { viewHolder.bind(questionnaireItemView) } - - onView(withId(R.id.text_input_layout)).perform(ViewActions.click()) - onView(withId(R.id.text_input_edit_text)).perform(ViewActions.typeText("1/1/22222")) - - assertThat(viewHolder.itemView.findViewById(R.id.text_input_layout).error) - .isEqualTo("Date format needs to be MM/dd/yyyy (e.g. 01/31/2023)") - } - @Test - fun shouldNotSetDateInput_outsideMaxRange() { - var answerHolder: List? = null - val maxDate = DateType(Date()).apply { add(Calendar.YEAR, -2) } - val questionnaireItemView = - QuestionnaireItemViewItem( - Questionnaire.QuestionnaireItemComponent().apply { - addExtension().apply { - url = "http://hl7.org/fhir/StructureDefinition/minValue" - val minDate = DateType(Date()).apply { add(Calendar.YEAR, -4) } - setValue(minDate) - } - addExtension().apply { - url = "http://hl7.org/fhir/StructureDefinition/maxValue" - setValue(maxDate) - } - }, - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, - ) - - runOnUI { viewHolder.bind(questionnaireItemView) } - - onView(withId(R.id.text_input_layout)).perform(clickIcon(true)) - onView(allOf(withText("OK"))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(ViewActions.click()) - - val maxDateAllowed = maxDate.valueAsString - val validationResult = - QuestionnaireResponseItemValidator.validate( - questionnaireItemView.questionnaireItem, - answerHolder!!, - viewHolder.itemView.context - ) - - assertThat((validationResult as Invalid).getSingleStringValidationMessage()) - .isEqualTo("Maximum value allowed is:$maxDateAllowed") - } - - @Test - fun shouldNotSetDateInput_outsideMinRange() { - var answerHolder: List? = null - val minDate = DateType(Date()).apply { add(Calendar.YEAR, 1) } - val questionnaireItemView = - QuestionnaireItemViewItem( - Questionnaire.QuestionnaireItemComponent().apply { - addExtension().apply { - url = "http://hl7.org/fhir/StructureDefinition/minValue" - setValue(minDate) - } - addExtension().apply { - url = "http://hl7.org/fhir/StructureDefinition/maxValue" - val maxDate = DateType(Date()).apply { add(Calendar.YEAR, 2) } - setValue(maxDate) - } - }, - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, - ) - - runOnUI { viewHolder.bind(questionnaireItemView) } - - onView(withId(R.id.text_input_layout)).perform(clickIcon(true)) - onView(allOf(withText("OK"))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(ViewActions.click()) - - val minDateAllowed = minDate.valueAsString - val validationResult = - QuestionnaireResponseItemValidator.validate( - questionnaireItemView.questionnaireItem, - answerHolder!!, - viewHolder.itemView.context - ) - - assertThat((validationResult as Invalid).getSingleStringValidationMessage()) - .isEqualTo("Minimum value allowed is:$minDateAllowed") - } - - @Test - fun shouldThrowException_whenMinValueRangeIsGreaterThanMaxValueRange() { - val questionnaireItemView = - QuestionnaireItemViewItem( - Questionnaire.QuestionnaireItemComponent().apply { - addExtension().apply { - url = "http://hl7.org/fhir/StructureDefinition/minValue" - val minDate = DateType(Date()).apply { add(Calendar.YEAR, 1) } - setValue(minDate) - } - addExtension().apply { - url = "http://hl7.org/fhir/StructureDefinition/maxValue" - val maxDate = DateType(Date()).apply { add(Calendar.YEAR, -1) } - setValue(maxDate) - } - }, - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, _, _ -> }, - ) - - runOnUI { viewHolder.bind(questionnaireItemView) } - - val exception = - assertThrows(IllegalArgumentException::class.java) { - onView(withId(R.id.text_input_layout)).perform(clickIcon(true)) - onView(allOf(withText("OK"))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(ViewActions.click()) - } - assertThat(exception.message).isEqualTo("minValue cannot be greater than maxValue") - } - - /** Runs code snippet on UI/main thread */ - private fun runOnUI(action: () -> Unit) { - activityScenarioRule.scenario.onActivity { activity -> action() } - } - - /** Sets content view for test activity */ - private fun setTestLayout(view: View) { - activityScenarioRule.scenario.onActivity { activity -> activity.setContentView(view) } - InstrumentationRegistry.getInstrumentation().waitForIdleSync() - } - - private fun setLocale(locale: Locale, context: Context) { - Locale.setDefault(locale) - context.resources.configuration.setLocale(locale) - } -} diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest.kt index 41e321b220..23f1726d91 100644 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest.kt +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest.kt @@ -42,6 +42,8 @@ import com.google.android.fhir.datacapture.utilities.clickOnText import com.google.android.fhir.datacapture.utilities.clickOnTextInDialog import com.google.android.fhir.datacapture.utilities.endIconClickInTextInputLayout import com.google.android.fhir.datacapture.validation.NotValidated +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemDialogSelectViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolder import com.google.android.material.textfield.TextInputLayout import com.google.common.truth.StringSubject import com.google.common.truth.Truth.assertThat @@ -74,15 +76,15 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { @Test fun multipleChoice_selectMultiple_clickSave_shouldSaveMultipleOptions() { var answerHolder: List? = null - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( answerOptions(true, "Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5"), responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } endIconClickInTextInputLayout(R.id.multi_select_summary_holder) clickOnTextInDialog("Coding 1") @@ -96,34 +98,34 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { @Test fun multipleChoice_SelectNothing_clickSave_shouldSaveNothing() { - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( answerOptions(true, "Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5"), responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } endIconClickInTextInputLayout(R.id.multi_select_summary_holder) clickOnTextInDialog("Save") assertDisplayedText().isEmpty() - assertThat(questionnaireItemViewItem.answers).isEmpty() + assertThat(questionnaireViewItem.answers).isEmpty() } @Test fun multipleChoice_selectMultiple_clickCancel_shouldSaveNothing() { - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( answerOptions(true, "Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5"), responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } endIconClickInTextInputLayout(R.id.multi_select_summary_holder) clickOnTextInDialog("Coding 3") @@ -131,21 +133,21 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { clickOnText("Cancel") assertDisplayedText().isEmpty() - assertThat(questionnaireItemViewItem.answers).isEmpty() + assertThat(questionnaireViewItem.answers).isEmpty() } @Test fun shouldSelectSingleOptionOnChangeInOptionFromDropDown() { var answerHolder: List? = null - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( answerOptions(false, "Coding 1", "Coding 2", "Coding 3"), responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } endIconClickInTextInputLayout(R.id.multi_select_summary_holder) clickOnTextInDialog("Coding 2") @@ -159,15 +161,15 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { @Test fun singleOption_select_clickSave_shouldSaveSingleOption() { var answerHolder: List? = null - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( answerOptions(false, "Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5"), responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } endIconClickInTextInputLayout(R.id.multi_select_summary_holder) clickOnTextInDialog("Coding 2") @@ -179,27 +181,27 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { @Test fun singleOption_selectNothing_clickSave_shouldSaveNothing() { - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( answerOptions(false, "Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5"), responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } endIconClickInTextInputLayout(R.id.multi_select_summary_holder) clickOnTextInDialog("Save") assertDisplayedText().isEmpty() - assertThat(questionnaireItemViewItem.answers).isEmpty() + assertThat(questionnaireViewItem.answers).isEmpty() } @Test fun bindView_setHintText() { - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( answerOptions(false, "Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5") .addItem( Questionnaire.QuestionnaireItemComponent().apply { @@ -224,7 +226,7 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } assertThat( viewHolder.itemView @@ -236,21 +238,21 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { @Test fun singleOption_select_clickCancel_shouldSaveNothing() { - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( answerOptions(false, "Coding 1", "Coding 2", "Coding 3", "Coding 4", "Coding 5"), responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } endIconClickInTextInputLayout(R.id.multi_select_summary_holder) clickOnTextInDialog("Coding 2") clickOnText("Cancel") assertDisplayedText().isEmpty() - assertThat(questionnaireItemViewItem.answers).isEmpty() + assertThat(questionnaireViewItem.answers).isEmpty() } @Test @@ -268,15 +270,15 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { "Coding 8" ) questionnaireItem.addExtension(openChoiceType) - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( questionnaireItem, responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } endIconClickInTextInputLayout(R.id.multi_select_summary_holder) onView(withId(R.id.recycler_view)) @@ -300,15 +302,15 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { "Coding 8" ) questionnaireItem.addExtension(openChoiceType) - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( questionnaireItem, responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } endIconClickInTextInputLayout(R.id.multi_select_summary_holder) onView(withId(R.id.recycler_view)) @@ -333,15 +335,15 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { "Coding 8" ) questionnaireItem.addExtension(openChoiceType) - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( questionnaireItem, responseOptions(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } endIconClickInTextInputLayout(R.id.multi_select_summary_holder) onView(withId(R.id.recycler_view)) @@ -355,15 +357,15 @@ class QuestionnaireItemDialogMultiSelectViewHolderFactoryEspressoTest { fun `shouldHideErrorTextviewInHeader`() { val questionnaireItem = answerOptions(true, "Coding 1") questionnaireItem.addExtension(openChoiceType) - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( questionnaireItem, QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, answersChangedCallback = { _, _, _, _ -> }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } onView(withId(R.id.error_text_at_header)).check(matches(not(isDisplayed()))) } diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemEditTextQuantityViewHolderFactoryEspressoTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemEditTextQuantityViewHolderFactoryEspressoTest.kt index 0e7590a007..da7a5ecd4f 100644 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemEditTextQuantityViewHolderFactoryEspressoTest.kt +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemEditTextQuantityViewHolderFactoryEspressoTest.kt @@ -28,6 +28,8 @@ import androidx.test.platform.app.InstrumentationRegistry import com.google.android.fhir.datacapture.R import com.google.android.fhir.datacapture.TestActivity import com.google.android.fhir.datacapture.validation.NotValidated +import com.google.android.fhir.datacapture.views.factories.EditTextQuantityViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolder import com.google.common.truth.Truth.assertThat import java.math.BigDecimal import org.hl7.fhir.r4.model.Quantity @@ -49,15 +51,15 @@ class QuestionnaireItemEditTextQuantityViewHolderFactoryEspressoTest { @Before fun setup() { activityScenarioRule.scenario.onActivity { activity -> parent = FrameLayout(activity) } - viewHolder = QuestionnaireItemEditTextQuantityViewHolderFactory.create(parent) + viewHolder = EditTextQuantityViewHolderFactory.create(parent) setTestLayout(viewHolder.itemView) } @Test fun getValue_WithInitial_shouldReturn_Quantity_With_UnitAndSystem() { var answerHolder: List? = null - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { required = true addInitial( @@ -73,7 +75,7 @@ class QuestionnaireItemEditTextQuantityViewHolderFactoryEspressoTest { validationResult = NotValidated, answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } onView(withId(R.id.text_input_edit_text)).perform(click()) onView(withId(R.id.text_input_edit_text)).perform(typeText("22")) @@ -91,14 +93,14 @@ class QuestionnaireItemEditTextQuantityViewHolderFactoryEspressoTest { @Test fun getValue_WithoutInitial_shouldReturn_Quantity_Without_UnitAndSystem() { var answerHolder: List? = null - val questionnaireItemViewItem = - QuestionnaireItemViewItem( + val questionnaireViewItem = + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { required = true }, QuestionnaireResponse.QuestionnaireResponseItemComponent(), validationResult = NotValidated, answersChangedCallback = { _, _, answers, _ -> answerHolder = answers }, ) - runOnUI { viewHolder.bind(questionnaireItemViewItem) } + runOnUI { viewHolder.bind(questionnaireViewItem) } onView(withId(R.id.text_input_edit_text)).perform(click()) onView(withId(R.id.text_input_edit_text)).perform(typeText("22")) diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemMultiSelectHolderFactoryInstrumentedTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemMultiSelectHolderFactoryInstrumentedTest.kt index 3ec074637f..8a3a2490c7 100644 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemMultiSelectHolderFactoryInstrumentedTest.kt +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemMultiSelectHolderFactoryInstrumentedTest.kt @@ -25,6 +25,8 @@ import com.google.android.fhir.datacapture.R import com.google.android.fhir.datacapture.TestActivity import com.google.android.fhir.datacapture.validation.Invalid import com.google.android.fhir.datacapture.validation.NotValidated +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemDialogSelectViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolder import com.google.android.material.textfield.TextInputLayout import com.google.common.truth.Truth.assertThat import org.hl7.fhir.r4.model.Coding @@ -41,7 +43,7 @@ class QuestionnaireItemMultiSelectHolderFactoryInstrumentedTest { @Test fun emptyResponseOptions_showNoneSelected() = withViewHolder { holder -> holder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( answerOptions("Coding 1", "Coding 2"), responseOptions(), validationResult = NotValidated, @@ -55,7 +57,7 @@ class QuestionnaireItemMultiSelectHolderFactoryInstrumentedTest { @Test fun selectedResponseOptions_showSelectedOptions() = withViewHolder { holder -> holder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( answerOptions("Coding 1", "Coding 2", "Coding 3"), responseOptions("Coding 1", "Coding 3"), validationResult = NotValidated, @@ -70,7 +72,7 @@ class QuestionnaireItemMultiSelectHolderFactoryInstrumentedTest { @UiThreadTest fun displayValidationResult_error_shouldShowErrorMessage() = withViewHolder { viewHolder -> viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { linkId = "1" required = true @@ -91,7 +93,7 @@ class QuestionnaireItemMultiSelectHolderFactoryInstrumentedTest { @UiThreadTest fun displayValidationResult_noError_shouldShowNoErrorMessage() = withViewHolder { viewHolder -> viewHolder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { linkId = "1" required = true @@ -122,7 +124,7 @@ class QuestionnaireItemMultiSelectHolderFactoryInstrumentedTest { @Test fun bind_readOnly_shouldDisableView() = withViewHolder { holder -> holder.bind( - QuestionnaireItemViewItem( + QuestionnaireViewItem( Questionnaire.QuestionnaireItemComponent().apply { linkId = "1" readOnly = true diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireAdapterItem.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireAdapterItem.kt index 6309e18dea..66d0a77da7 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireAdapterItem.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireAdapterItem.kt @@ -16,10 +16,10 @@ package com.google.android.fhir.datacapture -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewItem +import com.google.android.fhir.datacapture.views.QuestionnaireViewItem /** Various types of rows that can be used in a Questionnaire RecyclerView. */ internal sealed interface QuestionnaireAdapterItem { - /** A row for a quesion in a Questionnaire RecyclerView. */ - data class Question(val item: QuestionnaireItemViewItem) : QuestionnaireAdapterItem + /** A row for a question in a Questionnaire RecyclerView. */ + data class Question(val item: QuestionnaireViewItem) : QuestionnaireAdapterItem } diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireItemEditAdapter.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireEditAdapter.kt similarity index 51% rename from datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireItemEditAdapter.kt rename to datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireEditAdapter.kt index 9732bdef06..077ddb6f43 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireItemEditAdapter.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireEditAdapter.kt @@ -19,36 +19,36 @@ package com.google.android.fhir.datacapture import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter -import com.google.android.fhir.datacapture.contrib.views.QuestionnaireItemPhoneNumberViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemAttachmentViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemAutoCompleteViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemBooleanTypePickerViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemCheckBoxGroupViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemDatePickerViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemDateTimePickerViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemDialogSelectViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemDisplayViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemDropDownViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemEditTextDecimalViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemEditTextIntegerViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemEditTextMultiLineViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemEditTextQuantityViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemEditTextSingleLineViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemGroupViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemRadioGroupViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemSliderViewHolderFactory -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewHolder -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewItem +import com.google.android.fhir.datacapture.contrib.views.PhoneNumberViewHolderFactory +import com.google.android.fhir.datacapture.views.QuestionnaireViewItem +import com.google.android.fhir.datacapture.views.factories.AttachmentViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.AutoCompleteViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.BooleanChoiceViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.CheckBoxGroupViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.DatePickerViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.DateTimePickerViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.DisplayViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.DropDownViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.EditTextDecimalViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.EditTextIntegerViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.EditTextMultiLineViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.EditTextQuantityViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.EditTextSingleLineViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.GroupViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemDialogSelectViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolder +import com.google.android.fhir.datacapture.views.factories.RadioGroupViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.SliderViewHolderFactory import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType -internal class QuestionnaireItemEditAdapter( +internal class QuestionnaireEditAdapter( private val questionnaireItemViewHolderMatchers: List = emptyList(), ) : ListAdapter(DiffCallbacks.ITEMS) { /** - * @param viewType the integer value of the [QuestionnaireItemViewHolderType] used to render the - * [QuestionnaireItemViewItem]. + * @param viewType the integer value of the [QuestionnaireViewHolderType] used to render the + * [QuestionnaireViewItem]. */ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuestionnaireItemViewHolder { val typedViewType = ViewType.parse(viewType) @@ -62,7 +62,7 @@ internal class QuestionnaireItemEditAdapter( parent: ViewGroup, subtype: Int, ): QuestionnaireItemViewHolder { - val numOfCanonicalWidgets = QuestionnaireItemViewHolderType.values().size + val numOfCanonicalWidgets = QuestionnaireViewHolderType.values().size check(subtype < numOfCanonicalWidgets + questionnaireItemViewHolderMatchers.size) { "Invalid widget type specified. Widget Int type cannot exceed the total number of supported custom and canonical widgets" } @@ -73,36 +73,25 @@ internal class QuestionnaireItemEditAdapter( .factory.create(parent) val viewHolderFactory = - when (QuestionnaireItemViewHolderType.fromInt(subtype)) { - QuestionnaireItemViewHolderType.GROUP -> QuestionnaireItemGroupViewHolderFactory - QuestionnaireItemViewHolderType.BOOLEAN_TYPE_PICKER -> - QuestionnaireItemBooleanTypePickerViewHolderFactory - QuestionnaireItemViewHolderType.DATE_PICKER -> QuestionnaireItemDatePickerViewHolderFactory - QuestionnaireItemViewHolderType.DATE_TIME_PICKER -> - QuestionnaireItemDateTimePickerViewHolderFactory - QuestionnaireItemViewHolderType.EDIT_TEXT_SINGLE_LINE -> - QuestionnaireItemEditTextSingleLineViewHolderFactory - QuestionnaireItemViewHolderType.EDIT_TEXT_MULTI_LINE -> - QuestionnaireItemEditTextMultiLineViewHolderFactory - QuestionnaireItemViewHolderType.EDIT_TEXT_INTEGER -> - QuestionnaireItemEditTextIntegerViewHolderFactory - QuestionnaireItemViewHolderType.EDIT_TEXT_DECIMAL -> - QuestionnaireItemEditTextDecimalViewHolderFactory - QuestionnaireItemViewHolderType.RADIO_GROUP -> QuestionnaireItemRadioGroupViewHolderFactory - QuestionnaireItemViewHolderType.DROP_DOWN -> QuestionnaireItemDropDownViewHolderFactory - QuestionnaireItemViewHolderType.DISPLAY -> QuestionnaireItemDisplayViewHolderFactory - QuestionnaireItemViewHolderType.QUANTITY -> - QuestionnaireItemEditTextQuantityViewHolderFactory - QuestionnaireItemViewHolderType.CHECK_BOX_GROUP -> - QuestionnaireItemCheckBoxGroupViewHolderFactory - QuestionnaireItemViewHolderType.AUTO_COMPLETE -> - QuestionnaireItemAutoCompleteViewHolderFactory - QuestionnaireItemViewHolderType.DIALOG_SELECT -> - QuestionnaireItemDialogSelectViewHolderFactory - QuestionnaireItemViewHolderType.SLIDER -> QuestionnaireItemSliderViewHolderFactory - QuestionnaireItemViewHolderType.PHONE_NUMBER -> - QuestionnaireItemPhoneNumberViewHolderFactory - QuestionnaireItemViewHolderType.ATTACHMENT -> QuestionnaireItemAttachmentViewHolderFactory + when (QuestionnaireViewHolderType.fromInt(subtype)) { + QuestionnaireViewHolderType.GROUP -> GroupViewHolderFactory + QuestionnaireViewHolderType.BOOLEAN_TYPE_PICKER -> BooleanChoiceViewHolderFactory + QuestionnaireViewHolderType.DATE_PICKER -> DatePickerViewHolderFactory + QuestionnaireViewHolderType.DATE_TIME_PICKER -> DateTimePickerViewHolderFactory + QuestionnaireViewHolderType.EDIT_TEXT_SINGLE_LINE -> EditTextSingleLineViewHolderFactory + QuestionnaireViewHolderType.EDIT_TEXT_MULTI_LINE -> EditTextMultiLineViewHolderFactory + QuestionnaireViewHolderType.EDIT_TEXT_INTEGER -> EditTextIntegerViewHolderFactory + QuestionnaireViewHolderType.EDIT_TEXT_DECIMAL -> EditTextDecimalViewHolderFactory + QuestionnaireViewHolderType.RADIO_GROUP -> RadioGroupViewHolderFactory + QuestionnaireViewHolderType.DROP_DOWN -> DropDownViewHolderFactory + QuestionnaireViewHolderType.DISPLAY -> DisplayViewHolderFactory + QuestionnaireViewHolderType.QUANTITY -> EditTextQuantityViewHolderFactory + QuestionnaireViewHolderType.CHECK_BOX_GROUP -> CheckBoxGroupViewHolderFactory + QuestionnaireViewHolderType.AUTO_COMPLETE -> AutoCompleteViewHolderFactory + QuestionnaireViewHolderType.DIALOG_SELECT -> QuestionnaireItemDialogSelectViewHolderFactory + QuestionnaireViewHolderType.SLIDER -> SliderViewHolderFactory + QuestionnaireViewHolderType.PHONE_NUMBER -> PhoneNumberViewHolderFactory + QuestionnaireViewHolderType.ATTACHMENT -> AttachmentViewHolderFactory } return viewHolderFactory.create(parent) } @@ -161,90 +150,90 @@ internal class QuestionnaireItemEditAdapter( } /** - * Returns the integer value of the [QuestionnaireItemViewHolderType] that will be used to render - * the [QuestionnaireItemViewItem]. This is determined by a combination of the data type of the - * question and any additional Questionnaire Item UI Control Codes + * Returns the integer value of the [QuestionnaireViewHolderType] that will be used to render the + * [QuestionnaireViewItem]. This is determined by a combination of the data type of the question + * and any additional Questionnaire Item UI Control Codes * (http://hl7.org/fhir/R4/valueset-questionnaire-item-control.html) used in the itemControl * extension (http://hl7.org/fhir/R4/extension-questionnaire-itemcontrol.html). */ internal fun getItemViewTypeForQuestion( - questionnaireItemViewItem: QuestionnaireItemViewItem, + questionnaireViewItem: QuestionnaireViewItem, ): Int { - val questionnaireItem = questionnaireItemViewItem.questionnaireItem + val questionnaireItem = questionnaireViewItem.questionnaireItem // For custom widgets, generate an int value that's greater than any int assigned to the // canonical FHIR widgets questionnaireItemViewHolderMatchers.forEachIndexed { index, matcher -> if (matcher.matches(questionnaireItem)) { - return index + QuestionnaireItemViewHolderType.values().size + return index + QuestionnaireViewHolderType.values().size } } - if (questionnaireItemViewItem.answerOption.isNotEmpty()) { - return getChoiceViewHolderType(questionnaireItemViewItem).value + if (questionnaireViewItem.answerOption.isNotEmpty()) { + return getChoiceViewHolderType(questionnaireViewItem).value } return when (val type = questionnaireItem.type) { - QuestionnaireItemType.GROUP -> QuestionnaireItemViewHolderType.GROUP - QuestionnaireItemType.BOOLEAN -> QuestionnaireItemViewHolderType.BOOLEAN_TYPE_PICKER - QuestionnaireItemType.DATE -> QuestionnaireItemViewHolderType.DATE_PICKER - QuestionnaireItemType.DATETIME -> QuestionnaireItemViewHolderType.DATE_TIME_PICKER - QuestionnaireItemType.STRING -> getStringViewHolderType(questionnaireItemViewItem) - QuestionnaireItemType.TEXT -> QuestionnaireItemViewHolderType.EDIT_TEXT_MULTI_LINE - QuestionnaireItemType.INTEGER -> getIntegerViewHolderType(questionnaireItemViewItem) - QuestionnaireItemType.DECIMAL -> QuestionnaireItemViewHolderType.EDIT_TEXT_DECIMAL - QuestionnaireItemType.CHOICE -> getChoiceViewHolderType(questionnaireItemViewItem) - QuestionnaireItemType.DISPLAY -> QuestionnaireItemViewHolderType.DISPLAY - QuestionnaireItemType.QUANTITY -> QuestionnaireItemViewHolderType.QUANTITY - QuestionnaireItemType.REFERENCE -> getChoiceViewHolderType(questionnaireItemViewItem) - QuestionnaireItemType.ATTACHMENT -> QuestionnaireItemViewHolderType.ATTACHMENT + QuestionnaireItemType.GROUP -> QuestionnaireViewHolderType.GROUP + QuestionnaireItemType.BOOLEAN -> QuestionnaireViewHolderType.BOOLEAN_TYPE_PICKER + QuestionnaireItemType.DATE -> QuestionnaireViewHolderType.DATE_PICKER + QuestionnaireItemType.DATETIME -> QuestionnaireViewHolderType.DATE_TIME_PICKER + QuestionnaireItemType.STRING -> getStringViewHolderType(questionnaireViewItem) + QuestionnaireItemType.TEXT -> QuestionnaireViewHolderType.EDIT_TEXT_MULTI_LINE + QuestionnaireItemType.INTEGER -> getIntegerViewHolderType(questionnaireViewItem) + QuestionnaireItemType.DECIMAL -> QuestionnaireViewHolderType.EDIT_TEXT_DECIMAL + QuestionnaireItemType.CHOICE -> getChoiceViewHolderType(questionnaireViewItem) + QuestionnaireItemType.DISPLAY -> QuestionnaireViewHolderType.DISPLAY + QuestionnaireItemType.QUANTITY -> QuestionnaireViewHolderType.QUANTITY + QuestionnaireItemType.REFERENCE -> getChoiceViewHolderType(questionnaireViewItem) + QuestionnaireItemType.ATTACHMENT -> QuestionnaireViewHolderType.ATTACHMENT else -> throw NotImplementedError("Question type $type not supported.") }.value } private fun getChoiceViewHolderType( - questionnaireItemViewItem: QuestionnaireItemViewItem, - ): QuestionnaireItemViewHolderType { - val questionnaireItem = questionnaireItemViewItem.questionnaireItem + questionnaireViewItem: QuestionnaireViewItem, + ): QuestionnaireViewHolderType { + val questionnaireItem = questionnaireViewItem.questionnaireItem // Use the view type that the client wants if they specified an itemControl return questionnaireItem.itemControl?.viewHolderType // Otherwise, choose a sensible UI element automatically ?: run { - val numOptions = questionnaireItemViewItem.answerOption.size + val numOptions = questionnaireViewItem.answerOption.size when { // Always use a dialog for questions with a large number of options numOptions >= MINIMUM_NUMBER_OF_ANSWER_OPTIONS_FOR_DIALOG -> - QuestionnaireItemViewHolderType.DIALOG_SELECT + QuestionnaireViewHolderType.DIALOG_SELECT // Use a check box group if repeated answers are permitted - questionnaireItem.repeats -> QuestionnaireItemViewHolderType.CHECK_BOX_GROUP + questionnaireItem.repeats -> QuestionnaireViewHolderType.CHECK_BOX_GROUP // Use a dropdown if there are a medium number of options numOptions >= MINIMUM_NUMBER_OF_ANSWER_OPTIONS_FOR_DROP_DOWN -> - QuestionnaireItemViewHolderType.DROP_DOWN + QuestionnaireViewHolderType.DROP_DOWN // Use a radio group only if there are a small number of options - else -> QuestionnaireItemViewHolderType.RADIO_GROUP + else -> QuestionnaireViewHolderType.RADIO_GROUP } } } private fun getIntegerViewHolderType( - questionnaireItemViewItem: QuestionnaireItemViewItem, - ): QuestionnaireItemViewHolderType { - val questionnaireItem = questionnaireItemViewItem.questionnaireItem + questionnaireViewItem: QuestionnaireViewItem, + ): QuestionnaireViewHolderType { + val questionnaireItem = questionnaireViewItem.questionnaireItem // Use the view type that the client wants if they specified an itemControl return questionnaireItem.itemControl?.viewHolderType - ?: QuestionnaireItemViewHolderType.EDIT_TEXT_INTEGER + ?: QuestionnaireViewHolderType.EDIT_TEXT_INTEGER } private fun getStringViewHolderType( - questionnaireItemViewItem: QuestionnaireItemViewItem, - ): QuestionnaireItemViewHolderType { - val questionnaireItem = questionnaireItemViewItem.questionnaireItem + questionnaireViewItem: QuestionnaireViewItem, + ): QuestionnaireViewHolderType { + val questionnaireItem = questionnaireViewItem.questionnaireItem // Use the view type that the client wants if they specified an itemControl return questionnaireItem.itemControl?.viewHolderType - ?: QuestionnaireItemViewHolderType.EDIT_TEXT_SINGLE_LINE + ?: QuestionnaireViewHolderType.EDIT_TEXT_SINGLE_LINE } internal companion object { @@ -285,12 +274,12 @@ internal object DiffCallbacks { val QUESTIONS = object : DiffUtil.ItemCallback() { /** - * [QuestionnaireItemViewItem] is a transient object for the UI only. Whenever the user makes - * any change via the UI, a new list of [QuestionnaireItemViewItem]s will be created, each - * holding references to the underlying [QuestionnaireItem] and [QuestionnaireResponseItem], - * both of which should be read-only, and the current answers. To help recycler view handle - * update and/or animations, we consider two [QuestionnaireItemViewItem]s to be the same if - * they have the same underlying [QuestionnaireItem] and [QuestionnaireResponseItem]. + * [QuestionnaireViewItem] is a transient object for the UI only. Whenever the user makes any + * change via the UI, a new list of [QuestionnaireViewItem]s will be created, each holding + * references to the underlying [QuestionnaireItem] and [QuestionnaireResponseItem], both of + * which should be read-only, and the current answers. To help recycler view handle update + * and/or animations, we consider two [QuestionnaireViewItem]s to be the same if they have the + * same underlying [QuestionnaireItem] and [QuestionnaireResponseItem]. */ override fun areItemsTheSame( oldItem: QuestionnaireAdapterItem.Question, diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireFragment.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireFragment.kt index cff7b49b33..1695c44de4 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireFragment.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/QuestionnaireFragment.kt @@ -34,7 +34,7 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.fhir.datacapture.validation.Invalid -import com.google.android.fhir.datacapture.views.QuestionnaireItemViewHolderFactory +import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolderFactory import com.google.android.material.progressindicator.LinearProgressIndicator import org.hl7.fhir.r4.model.Questionnaire import timber.log.Timber @@ -113,9 +113,9 @@ class QuestionnaireFragment : Fragment() { } val questionnaireProgressIndicator: LinearProgressIndicator = view.findViewById(R.id.questionnaire_progress_indicator) - val questionnaireItemEditAdapter = - QuestionnaireItemEditAdapter(questionnaireItemViewHolderFactoryMatchersProvider.get()) - val questionnaireItemReviewAdapter = QuestionnaireItemReviewAdapter() + val questionnaireEditAdapter = + QuestionnaireEditAdapter(questionnaireItemViewHolderFactoryMatchersProvider.get()) + val questionnaireReviewAdapter = QuestionnaireReviewAdapter() val submitButton = requireView().findViewById