From 13bc132ff53d8f27429a6c5ed2c4ead252696d6e Mon Sep 17 00:00:00 2001 From: AdityaKBhadragond14 Date: Wed, 17 Apr 2024 18:31:53 +0530 Subject: [PATCH 1/4] Date Picker Constraint Issue Fix. --- ...DatePickerViewHolderFactoryEspressoTest.kt | 106 ++++++++++++++++++ .../factories/DatePickerViewHolderFactory.kt | 29 +++-- 2 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactoryEspressoTest.kt diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactoryEspressoTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactoryEspressoTest.kt new file mode 100644 index 0000000000..737815c772 --- /dev/null +++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactoryEspressoTest.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2024 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.factories + +import android.view.View +import android.widget.FrameLayout +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +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.test.TestActivity +import com.google.android.fhir.datacapture.test.utilities.clickIcon +import com.google.android.fhir.datacapture.validation.MAX_VALUE_EXTENSION_URL +import com.google.android.fhir.datacapture.validation.MIN_VALUE_EXTENSION_URL +import com.google.android.fhir.datacapture.validation.NotValidated +import com.google.android.fhir.datacapture.views.QuestionnaireViewItem +import org.hamcrest.CoreMatchers +import org.hl7.fhir.r4.model.DateType +import org.hl7.fhir.r4.model.Extension +import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent +import org.hl7.fhir.r4.model.QuestionnaireResponse +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class DatePickerViewHolderFactoryEspressoTest { + @Rule + @JvmField + var activityScenarioRule: ActivityScenarioRule = + ActivityScenarioRule(TestActivity::class.java) + + private lateinit var parent: FrameLayout + private lateinit var viewHolder: QuestionnaireItemViewHolder + + @Before + fun setUp() { + activityScenarioRule.scenario.onActivity { activity -> parent = FrameLayout(activity) } + viewHolder = DatePickerViewHolderFactory.create(parent) + setTestLayout(viewHolder.itemView) + } + + @Test + fun shouldSelectMinDateFromConstraint() { + val questionnaireItem = QuestionnaireItemComponent() + questionnaireItem.addExtension( + Extension().apply { + this.url = MIN_VALUE_EXTENSION_URL + setValue(DateType("2023-04-15")) + }, + ) + questionnaireItem.addExtension( + Extension().apply { + this.url = MAX_VALUE_EXTENSION_URL + setValue(DateType("2023-04-18")) + }, + ) + val questionnaireItemView = + QuestionnaireViewItem( + questionnaireItem, + QuestionnaireResponse.QuestionnaireResponseItemComponent(), + validationResult = NotValidated, + answersChangedCallback = { _, _, _, _ -> }, + ) + runOnUI { viewHolder.bind(questionnaireItemView) } + onView(withId(R.id.text_input_layout)).perform(clickIcon(true)) + onView(CoreMatchers.allOf(withText("OK"))) + .inRoot(isDialog()) + .check(matches(isDisplayed())) + .perform(click()) + onView(withText("04/15/2023")).check(matches(isDisplayed())) + } + + /** Method to run code snippet on UI/main thread */ + private fun runOnUI(action: () -> Unit) { + activityScenarioRule.scenario.onActivity { activity -> action() } + } + + /** Method to set content view for test activity */ + private fun setTestLayout(view: View) { + activityScenarioRule.scenario.onActivity { activity -> activity.setContentView(view) } + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + } +} diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt index 8d65eddea2..06e04ef5bf 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt @@ -48,6 +48,7 @@ import com.google.android.material.textfield.TextInputLayout import java.text.ParseException import java.time.Instant import java.time.LocalDate +import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeParseException import java.util.Date @@ -154,21 +155,31 @@ internal object DatePickerViewHolderFactory : } private fun buildMaterialDatePicker(localDate: LocalDate?): MaterialDatePicker { - val selectedDateMillis = - localDate?.atStartOfDay(ZONE_ID_UTC)?.toInstant()?.toEpochMilli() - ?: MaterialDatePicker.todayInUtcMilliseconds() + val min = + (questionnaireViewItem.minAnswerValue as? DateType)?.value?.time + ?: localDate?.atStartOfDay(ZONE_ID_UTC)?.toInstant()?.toEpochMilli() + ?: MaterialDatePicker.todayInUtcMilliseconds() + val minLocalDateTime = + LocalDateTime.ofInstant( + Instant.ofEpochMilli(min), + ZoneId.of( + ZONE_ID_UTC.id, + ), + ) + val newMinLocalDateTime = minLocalDateTime.plusDays(1) + val selectedDateTimeMillis = + newMinLocalDateTime.atZone(ZoneId.of(ZONE_ID_UTC.id)).toInstant().toEpochMilli() + val max = (questionnaireViewItem.maxAnswerValue as? DateType)?.value?.time + val calendarConstraints = getCalenderConstraint(min, max) return MaterialDatePicker.Builder.datePicker() .setTitleText(R.string.select_date) - .setSelection(selectedDateMillis) - .setCalendarConstraints(getCalenderConstraint()) + .setSelection(selectedDateTimeMillis) + .setCalendarConstraints(calendarConstraints) .build() } - private fun getCalenderConstraint(): CalendarConstraints { - val min = (questionnaireViewItem.minAnswerValue as? DateType)?.value?.time - val max = (questionnaireViewItem.maxAnswerValue as? DateType)?.value?.time - + private fun getCalenderConstraint(min: Long?, max: Long?): CalendarConstraints { if (min != null && max != null && min > max) { throw IllegalArgumentException("minValue cannot be greater than maxValue") } From 5d36a33d567cf2964cf862f010a361fce96488f3 Mon Sep 17 00:00:00 2001 From: AdityaKBhadragond14 Date: Tue, 30 Apr 2024 13:00:27 +0530 Subject: [PATCH 2/4] Changed the min date to local date. --- .../factories/DatePickerViewHolderFactory.kt | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt index 06e04ef5bf..06c1fbb9b8 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt @@ -48,7 +48,6 @@ import com.google.android.material.textfield.TextInputLayout import java.text.ParseException import java.time.Instant import java.time.LocalDate -import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeParseException import java.util.Date @@ -155,26 +154,23 @@ internal object DatePickerViewHolderFactory : } private fun buildMaterialDatePicker(localDate: LocalDate?): MaterialDatePicker { - val min = - (questionnaireViewItem.minAnswerValue as? DateType)?.value?.time + val minDateInMillis = + (questionnaireViewItem.minAnswerValue as? DateType) + ?.value + ?.localDate + ?.atStartOfDay( + ZONE_ID_UTC, + ) + ?.toInstant() + ?.toEpochMilli() ?: localDate?.atStartOfDay(ZONE_ID_UTC)?.toInstant()?.toEpochMilli() ?: MaterialDatePicker.todayInUtcMilliseconds() - val minLocalDateTime = - LocalDateTime.ofInstant( - Instant.ofEpochMilli(min), - ZoneId.of( - ZONE_ID_UTC.id, - ), - ) - val newMinLocalDateTime = minLocalDateTime.plusDays(1) - val selectedDateTimeMillis = - newMinLocalDateTime.atZone(ZoneId.of(ZONE_ID_UTC.id)).toInstant().toEpochMilli() val max = (questionnaireViewItem.maxAnswerValue as? DateType)?.value?.time - val calendarConstraints = getCalenderConstraint(min, max) + val calendarConstraints = getCalenderConstraint(minDateInMillis, max) return MaterialDatePicker.Builder.datePicker() .setTitleText(R.string.select_date) - .setSelection(selectedDateTimeMillis) + .setSelection(minDateInMillis) .setCalendarConstraints(calendarConstraints) .build() } From ff33b098705df87532e61083fe60538a2061235f Mon Sep 17 00:00:00 2001 From: AdityaKBhadragond14 Date: Tue, 30 Apr 2024 14:54:55 +0530 Subject: [PATCH 3/4] renaming max variable. --- .../views/factories/DatePickerViewHolderFactory.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt index 06c1fbb9b8..cddac0dd4b 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt @@ -165,8 +165,8 @@ internal object DatePickerViewHolderFactory : ?.toEpochMilli() ?: localDate?.atStartOfDay(ZONE_ID_UTC)?.toInstant()?.toEpochMilli() ?: MaterialDatePicker.todayInUtcMilliseconds() - val max = (questionnaireViewItem.maxAnswerValue as? DateType)?.value?.time - val calendarConstraints = getCalenderConstraint(minDateInMillis, max) + val maxDateInMillis = (questionnaireViewItem.maxAnswerValue as? DateType)?.value?.time + val calendarConstraints = getCalenderConstraint(minDateInMillis, maxDateInMillis) return MaterialDatePicker.Builder.datePicker() .setTitleText(R.string.select_date) From c4d21728e61cccb813faf29631b37793fdd5bf6c Mon Sep 17 00:00:00 2001 From: AdityaKBhadragond14 Date: Thu, 6 Jun 2024 18:20:01 +0530 Subject: [PATCH 4/4] removed .setSelection for MaterialDatePicker and the Espresso test for the same. --- ...DatePickerViewHolderFactoryEspressoTest.kt | 106 ------------------ .../factories/DatePickerViewHolderFactory.kt | 1 - 2 files changed, 107 deletions(-) delete mode 100644 datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactoryEspressoTest.kt diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactoryEspressoTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactoryEspressoTest.kt deleted file mode 100644 index 737815c772..0000000000 --- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactoryEspressoTest.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2024 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.factories - -import android.view.View -import android.widget.FrameLayout -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions.click -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.test.TestActivity -import com.google.android.fhir.datacapture.test.utilities.clickIcon -import com.google.android.fhir.datacapture.validation.MAX_VALUE_EXTENSION_URL -import com.google.android.fhir.datacapture.validation.MIN_VALUE_EXTENSION_URL -import com.google.android.fhir.datacapture.validation.NotValidated -import com.google.android.fhir.datacapture.views.QuestionnaireViewItem -import org.hamcrest.CoreMatchers -import org.hl7.fhir.r4.model.DateType -import org.hl7.fhir.r4.model.Extension -import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent -import org.hl7.fhir.r4.model.QuestionnaireResponse -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class DatePickerViewHolderFactoryEspressoTest { - @Rule - @JvmField - var activityScenarioRule: ActivityScenarioRule = - ActivityScenarioRule(TestActivity::class.java) - - private lateinit var parent: FrameLayout - private lateinit var viewHolder: QuestionnaireItemViewHolder - - @Before - fun setUp() { - activityScenarioRule.scenario.onActivity { activity -> parent = FrameLayout(activity) } - viewHolder = DatePickerViewHolderFactory.create(parent) - setTestLayout(viewHolder.itemView) - } - - @Test - fun shouldSelectMinDateFromConstraint() { - val questionnaireItem = QuestionnaireItemComponent() - questionnaireItem.addExtension( - Extension().apply { - this.url = MIN_VALUE_EXTENSION_URL - setValue(DateType("2023-04-15")) - }, - ) - questionnaireItem.addExtension( - Extension().apply { - this.url = MAX_VALUE_EXTENSION_URL - setValue(DateType("2023-04-18")) - }, - ) - val questionnaireItemView = - QuestionnaireViewItem( - questionnaireItem, - QuestionnaireResponse.QuestionnaireResponseItemComponent(), - validationResult = NotValidated, - answersChangedCallback = { _, _, _, _ -> }, - ) - runOnUI { viewHolder.bind(questionnaireItemView) } - onView(withId(R.id.text_input_layout)).perform(clickIcon(true)) - onView(CoreMatchers.allOf(withText("OK"))) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(click()) - onView(withText("04/15/2023")).check(matches(isDisplayed())) - } - - /** Method to run code snippet on UI/main thread */ - private fun runOnUI(action: () -> Unit) { - activityScenarioRule.scenario.onActivity { activity -> action() } - } - - /** Method to set content view for test activity */ - private fun setTestLayout(view: View) { - activityScenarioRule.scenario.onActivity { activity -> activity.setContentView(view) } - InstrumentationRegistry.getInstrumentation().waitForIdleSync() - } -} diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt index cddac0dd4b..38a22cf456 100644 --- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt +++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/DatePickerViewHolderFactory.kt @@ -170,7 +170,6 @@ internal object DatePickerViewHolderFactory : return MaterialDatePicker.Builder.datePicker() .setTitleText(R.string.select_date) - .setSelection(minDateInMillis) .setCalendarConstraints(calendarConstraints) .build() }