Skip to content

Commit d527337

Browse files
committed
Migrate DateTimePicker widget with both date and time components
1 parent ff18e30 commit d527337

File tree

10 files changed

+558
-260
lines changed

10 files changed

+558
-260
lines changed

datacapture/src/androidTest/java/com/google/android/fhir/datacapture/test/QuestionnaireUiEspressoTest.kt

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import androidx.compose.ui.semantics.SemanticsProperties
2323
import androidx.compose.ui.test.SemanticsMatcher
2424
import androidx.compose.ui.test.assert
2525
import androidx.compose.ui.test.assertIsDisplayed
26+
import androidx.compose.ui.test.assertIsEnabled
2627
import androidx.compose.ui.test.assertIsNotEnabled
2728
import androidx.compose.ui.test.assertTextEquals
2829
import androidx.compose.ui.test.hasAnyAncestor
@@ -34,6 +35,8 @@ import androidx.compose.ui.test.onNodeWithTag
3435
import androidx.compose.ui.test.onNodeWithText
3536
import androidx.compose.ui.test.performClick
3637
import androidx.compose.ui.test.performTextInput
38+
import androidx.compose.ui.test.performTextReplacement
39+
import androidx.compose.ui.test.printToLog
3740
import androidx.fragment.app.commitNow
3841
import androidx.recyclerview.widget.RecyclerView
3942
import androidx.recyclerview.widget.RecyclerView.ViewHolder
@@ -65,8 +68,8 @@ import com.google.android.fhir.datacapture.validation.Valid
6568
import com.google.android.fhir.datacapture.views.compose.DATE_TEXT_INPUT_FIELD
6669
import com.google.android.fhir.datacapture.views.compose.EDIT_TEXT_FIELD_TEST_TAG
6770
import com.google.android.fhir.datacapture.views.compose.HANDLE_INPUT_DEBOUNCE_TIME
71+
import com.google.android.fhir.datacapture.views.compose.TIME_PICKER_INPUT_FIELD
6872
import com.google.android.material.progressindicator.LinearProgressIndicator
69-
import com.google.android.material.textfield.TextInputLayout
7073
import com.google.common.truth.Truth.assertThat
7174
import java.math.BigDecimal
7275
import java.time.LocalDate
@@ -232,57 +235,58 @@ class QuestionnaireUiEspressoTest {
232235
buildFragmentFromQuestionnaire("/component_date_time_picker.json")
233236

234237
// Add month and day. No need to add slashes as they are added automatically
235-
onView(withId(R.id.date_input_edit_text))
236-
.perform(ViewActions.click())
237-
.perform(ViewActions.typeTextIntoFocusedView("0105"))
238+
composeTestRule.onNodeWithTag(DATE_TEXT_INPUT_FIELD).performTextReplacement("0105")
238239

239-
onView(withId(R.id.date_input_layout)).check { view, _ ->
240-
val actualError = (view as TextInputLayout).error
241-
assertThat(actualError).isEqualTo("Date format needs to be mm/dd/yyyy (e.g. 01/31/2023)")
242-
}
243-
onView(withId(R.id.time_input_layout)).check { view, _ -> assertThat(view.isEnabled).isFalse() }
240+
composeTestRule
241+
.onNodeWithTag(DATE_TEXT_INPUT_FIELD)
242+
.assert(
243+
SemanticsMatcher.expectValue(
244+
SemanticsProperties.Error,
245+
"Date format needs to be mm/dd/yyyy (e.g. 01/31/2023)",
246+
),
247+
)
248+
composeTestRule.onNodeWithTag(TIME_PICKER_INPUT_FIELD).assertIsNotEnabled()
244249
}
245250

246251
@Test
247252
fun dateTimePicker_shouldEnableTimePickerWithCorrectDate_butNotSaveInQuestionnaireResponse() {
248253
buildFragmentFromQuestionnaire("/component_date_time_picker.json")
249254

250-
onView(withId(R.id.date_input_edit_text))
251-
.perform(ViewActions.click())
252-
.perform(ViewActions.typeTextIntoFocusedView("01052005"))
253-
254-
onView(withId(R.id.date_input_layout)).check { view, _ ->
255-
val actualError = (view as TextInputLayout).error
256-
assertThat(actualError).isEqualTo(null)
257-
}
255+
composeTestRule.onNodeWithTag(DATE_TEXT_INPUT_FIELD).performTextReplacement("01052005")
258256

259-
onView(withId(R.id.time_input_layout)).check { view, _ -> assertThat(view.isEnabled).isTrue() }
257+
composeTestRule
258+
.onNodeWithTag(DATE_TEXT_INPUT_FIELD)
259+
.assert(
260+
SemanticsMatcher.keyNotDefined(
261+
SemanticsProperties.Error,
262+
),
263+
)
264+
composeTestRule.onNodeWithTag(TIME_PICKER_INPUT_FIELD).assertIsEnabled()
260265

261-
runBlocking {
262-
assertThat(getQuestionnaireResponse().item.size).isEqualTo(1)
263-
assertThat(getQuestionnaireResponse().item.first().answer.size).isEqualTo(0)
264-
}
266+
val questionnaireResponse = runBlocking { getQuestionnaireResponse() }
267+
assertThat(questionnaireResponse.item.size).isEqualTo(1)
268+
assertThat(questionnaireResponse.item.first().answer.size).isEqualTo(1)
269+
val answer = questionnaireResponse.item.first().answer.first().valueDateTimeType
270+
assertThat(answer.localDateTime).isEqualTo(LocalDateTime.of(2005, 1, 5, 0, 0))
265271
}
266272

267273
@Test
268274
fun dateTimePicker_shouldSetAnswerWhenDateAndTimeAreFilled() {
269275
buildFragmentFromQuestionnaire("/component_date_time_picker.json")
270276

271-
onView(withId(R.id.date_input_edit_text))
272-
.perform(ViewActions.click())
273-
.perform(ViewActions.typeTextIntoFocusedView("01052005"))
277+
composeTestRule.onNodeWithTag(DATE_TEXT_INPUT_FIELD).performTextReplacement("01052005")
274278

279+
composeTestRule.onNodeWithTag(TIME_PICKER_INPUT_FIELD).printToLog("Timer!")
275280
onView(withId(R.id.time_input_layout)).perform(clickIcon(true))
276281
clickOnText("AM")
277282
clickOnText("6")
278283
clickOnText("10")
279284
clickOnText("OK")
280285

281-
runBlocking {
282-
val answer = getQuestionnaireResponse().item.first().answer.first().valueDateTimeType
283-
// check Locale
284-
assertThat(answer.localDateTime).isEqualTo(LocalDateTime.of(2005, 1, 5, 6, 10))
285-
}
286+
val questionnaireResponse = runBlocking { getQuestionnaireResponse() }
287+
val answer = questionnaireResponse.item.first().answer.first().valueDateTimeType
288+
// check Locale
289+
assertThat(answer.localDateTime).isEqualTo(LocalDateTime.of(2005, 1, 5, 6, 10))
286290
}
287291

288292
@Test

datacapture/src/androidTest/java/com/google/android/fhir/datacapture/test/views/DateTimePickerViewHolderFactoryEspressoTest.kt

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023 Google LLC
2+
* Copyright 2023-2025 Google LLC
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,8 +16,8 @@
1616

1717
package com.google.android.fhir.datacapture.test.views
1818

19-
import android.view.View
2019
import android.widget.FrameLayout
20+
import androidx.compose.ui.test.junit4.createEmptyComposeRule
2121
import androidx.test.espresso.Espresso.onView
2222
import androidx.test.espresso.action.ViewActions
2323
import androidx.test.espresso.assertion.ViewAssertions.matches
@@ -51,14 +51,17 @@ class DateTimePickerViewHolderFactoryEspressoTest {
5151
var activityScenarioRule: ActivityScenarioRule<TestActivity> =
5252
ActivityScenarioRule(TestActivity::class.java)
5353

54-
private lateinit var parent: FrameLayout
54+
@get:Rule val composeTestRule = createEmptyComposeRule()
55+
5556
private lateinit var viewHolder: QuestionnaireItemViewHolder
5657

5758
@Before
5859
fun setup() {
59-
activityScenarioRule.scenario.onActivity { activity -> parent = FrameLayout(activity) }
60-
viewHolder = DateTimePickerViewHolderFactory.create(parent)
61-
setTestLayout(viewHolder.itemView)
60+
activityScenarioRule.scenario.onActivity { activity ->
61+
viewHolder = DateTimePickerViewHolderFactory.create(FrameLayout(activity))
62+
activity.setContentView(viewHolder.itemView)
63+
}
64+
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
6265
}
6366

6467
@Test
@@ -71,7 +74,7 @@ class DateTimePickerViewHolderFactoryEspressoTest {
7174
answersChangedCallback = { _, _, _, _ -> },
7275
)
7376

74-
runOnUI { viewHolder.bind(questionnaireItemView) }
77+
viewHolder.bind(questionnaireItemView)
7578
onView(withId(R.id.date_input_layout)).perform(clickIcon(true))
7679
onView(allOf(withText("OK")))
7780
.inRoot(isDialog())
@@ -94,7 +97,7 @@ class DateTimePickerViewHolderFactoryEspressoTest {
9497
answersChangedCallback = { _, _, _, _ -> },
9598
)
9699

97-
runOnUI { viewHolder.bind(questionnaireItemView) }
100+
viewHolder.bind(questionnaireItemView)
98101
onView(withId(R.id.date_input_layout)).perform(clickIcon(true))
99102
onView(allOf(withText("OK")))
100103
.inRoot(isDialog())
@@ -106,15 +109,4 @@ class DateTimePickerViewHolderFactoryEspressoTest {
106109
.inRoot(isDialog())
107110
.check(matches(isDisplayed()))
108111
}
109-
110-
/** Method to run code snippet on UI/main thread */
111-
private fun runOnUI(action: () -> Unit) {
112-
activityScenarioRule.scenario.onActivity { activity -> action() }
113-
}
114-
115-
/** Method to set content view for test activity */
116-
private fun setTestLayout(view: View) {
117-
activityScenarioRule.scenario.onActivity { activity -> activity.setContentView(view) }
118-
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
119-
}
120112
}

0 commit comments

Comments
 (0)