@@ -19,29 +19,31 @@ package com.google.android.fhir.datacapture.test
1919import android.view.View
2020import android.widget.FrameLayout
2121import android.widget.TextView
22+ import androidx.compose.ui.semantics.Role
2223import androidx.compose.ui.semantics.SemanticsProperties
2324import androidx.compose.ui.test.SemanticsMatcher
2425import androidx.compose.ui.test.assert
2526import androidx.compose.ui.test.assertIsDisplayed
27+ import androidx.compose.ui.test.assertIsEnabled
2628import androidx.compose.ui.test.assertIsNotEnabled
2729import androidx.compose.ui.test.assertTextEquals
28- import androidx.compose.ui.test.junit4.createAndroidComposeRule
30+ import androidx.compose.ui.test.filterToOne
2931import androidx.compose.ui.test.hasAnyAncestor
3032import androidx.compose.ui.test.hasText
3133import androidx.compose.ui.test.isDialog
3234import androidx.compose.ui.test.junit4.createEmptyComposeRule
35+ import androidx.compose.ui.test.onChildren
3336import androidx.compose.ui.test.onNodeWithContentDescription
3437import androidx.compose.ui.test.onNodeWithTag
3538import androidx.compose.ui.test.onNodeWithText
3639import androidx.compose.ui.test.performClick
3740import androidx.compose.ui.test.performTextInput
41+ import androidx.compose.ui.test.performTextReplacement
3842import androidx.fragment.app.commitNow
3943import androidx.test.espresso.Espresso.onView
4044import androidx.test.espresso.action.ViewActions
4145import androidx.test.espresso.assertion.ViewAssertions
4246import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
43- import androidx.test.espresso.matcher.RootMatchers
44- import androidx.test.espresso.contrib.RecyclerViewActions
4547import androidx.test.espresso.matcher.ViewMatchers
4648import androidx.test.espresso.matcher.ViewMatchers.withId
4749import androidx.test.espresso.matcher.ViewMatchers.withText
@@ -55,16 +57,15 @@ import com.google.android.fhir.datacapture.QuestionnaireFragment
5557import com.google.android.fhir.datacapture.R
5658import com.google.android.fhir.datacapture.extensions.localDate
5759import com.google.android.fhir.datacapture.extensions.localDateTime
58- import com.google.android.fhir.datacapture.test.utilities.clickIcon
5960import com.google.android.fhir.datacapture.test.utilities.clickOnText
6061import com.google.android.fhir.datacapture.validation.Invalid
6162import com.google.android.fhir.datacapture.validation.QuestionnaireResponseValidator
6263import com.google.android.fhir.datacapture.validation.Valid
6364import com.google.android.fhir.datacapture.views.compose.DATE_TEXT_INPUT_FIELD
6465import com.google.android.fhir.datacapture.views.compose.EDIT_TEXT_FIELD_TEST_TAG
6566import com.google.android.fhir.datacapture.views.compose.HANDLE_INPUT_DEBOUNCE_TIME
67+ import com.google.android.fhir.datacapture.views.compose.TIME_PICKER_INPUT_FIELD
6668import com.google.android.material.progressindicator.LinearProgressIndicator
67- import com.google.android.material.textfield.TextInputLayout
6869import com.google.common.truth.Truth.assertThat
6970import java.math.BigDecimal
7071import java.time.LocalDate
@@ -231,57 +232,70 @@ class QuestionnaireUiEspressoTest {
231232 buildFragmentFromQuestionnaire(" /component_date_time_picker.json" )
232233
233234 // Add month and day. No need to add slashes as they are added automatically
234- onView(withId(R .id.date_input_edit_text))
235- .perform(ViewActions .click())
236- .perform(ViewActions .typeTextIntoFocusedView(" 0105" ))
235+ composeTestRule.onNodeWithTag(DATE_TEXT_INPUT_FIELD ).performTextReplacement(" 0105" )
237236
238- onView(withId(R .id.date_input_layout)).check { view, _ ->
239- val actualError = (view as TextInputLayout ).error
240- assertThat(actualError).isEqualTo(" Date format needs to be mm/dd/yyyy (e.g. 01/31/2023)" )
241- }
242- onView(withId(R .id.time_input_layout)).check { view, _ -> assertThat(view.isEnabled).isFalse() }
237+ composeTestRule
238+ .onNodeWithTag(DATE_TEXT_INPUT_FIELD )
239+ .assert (
240+ SemanticsMatcher .expectValue(
241+ SemanticsProperties .Error ,
242+ " Date format needs to be mm/dd/yyyy (e.g. 01/31/2023)" ,
243+ ),
244+ )
245+ composeTestRule.onNodeWithTag(TIME_PICKER_INPUT_FIELD ).assertIsNotEnabled()
243246 }
244247
245248 @Test
246249 fun dateTimePicker_shouldEnableTimePickerWithCorrectDate_butNotSaveInQuestionnaireResponse () {
247250 buildFragmentFromQuestionnaire(" /component_date_time_picker.json" )
248251
249- onView(withId(R .id.date_input_edit_text))
250- .perform(ViewActions .click())
251- .perform(ViewActions .typeTextIntoFocusedView(" 01052005" ))
252-
253- onView(withId(R .id.date_input_layout)).check { view, _ ->
254- val actualError = (view as TextInputLayout ).error
255- assertThat(actualError).isEqualTo(null )
256- }
252+ composeTestRule.onNodeWithTag(DATE_TEXT_INPUT_FIELD ).performTextReplacement(" 01052005" )
257253
258- onView(withId(R .id.time_input_layout)).check { view, _ -> assertThat(view.isEnabled).isTrue() }
254+ composeTestRule
255+ .onNodeWithTag(DATE_TEXT_INPUT_FIELD )
256+ .assert (
257+ SemanticsMatcher .keyNotDefined(
258+ SemanticsProperties .Error ,
259+ ),
260+ )
261+ composeTestRule.onNodeWithTag(TIME_PICKER_INPUT_FIELD ).assertIsEnabled()
259262
260- runBlocking {
261- assertThat(getQuestionnaireResponse().item.size).isEqualTo(1 )
262- assertThat(getQuestionnaireResponse().item.first().answer.size).isEqualTo(0 )
263- }
263+ val questionnaireResponse = runBlocking { getQuestionnaireResponse() }
264+ assertThat(questionnaireResponse.item.size).isEqualTo(1 )
265+ assertThat(questionnaireResponse.item.first().answer.size).isEqualTo(1 )
266+ val answer = questionnaireResponse.item.first().answer.first().valueDateTimeType
267+ assertThat(answer.localDateTime).isEqualTo(LocalDateTime .of(2005 , 1 , 5 , 0 , 0 ))
264268 }
265269
266270 @Test
267271 fun dateTimePicker_shouldSetAnswerWhenDateAndTimeAreFilled () {
268272 buildFragmentFromQuestionnaire(" /component_date_time_picker.json" )
269273
270- onView(withId(R .id.date_input_edit_text))
271- .perform(ViewActions .click())
272- .perform(ViewActions .typeTextIntoFocusedView(" 01052005" ))
274+ composeTestRule.onNodeWithTag(DATE_TEXT_INPUT_FIELD ).performTextReplacement(" 01052005" )
275+
276+ composeTestRule
277+ .onNodeWithTag(TIME_PICKER_INPUT_FIELD )
278+ .onChildren()
279+ .filterToOne(
280+ SemanticsMatcher .expectValue(SemanticsProperties .Role , Role .Button ),
281+ )
282+ .performClick()
283+
284+ composeTestRule.onNodeWithText(" AM" ).performClick()
285+ composeTestRule.onNodeWithContentDescription(" Select hour" , substring = true ).performClick()
286+ composeTestRule.onNodeWithContentDescription(" 6 o'clock" , substring = true ).performClick()
273287
274- onView(withId(R .id.time_input_layout)).perform(clickIcon(true ))
275- clickOnText(" AM" )
276- clickOnText(" 6" )
277- clickOnText(" 10" )
278- clickOnText(" OK" )
288+ composeTestRule.onNodeWithContentDescription(" Select minutes" , substring = true ).performClick()
289+ composeTestRule.onNodeWithContentDescription(" 10 minutes" , substring = true ).performClick()
279290
280- runBlocking {
281- val answer = getQuestionnaireResponse().item.first().answer.first().valueDateTimeType
282- // check Locale
283- assertThat(answer.localDateTime).isEqualTo(LocalDateTime .of(2005 , 1 , 5 , 6 , 10 ))
284- }
291+ composeTestRule.onNodeWithText(" OK" ).performClick()
292+ // Synchronize
293+ composeTestRule.waitForIdle()
294+
295+ val questionnaireResponse = runBlocking { getQuestionnaireResponse() }
296+ val answer = questionnaireResponse.item.first().answer.first().valueDateTimeType
297+ // check Locale
298+ assertThat(answer.localDateTime).isEqualTo(LocalDateTime .of(2005 , 1 , 5 , 6 , 10 ))
285299 }
286300
287301 @Test
@@ -649,8 +663,7 @@ class QuestionnaireUiEspressoTest {
649663 @Test
650664 fun test_repeated_group_is_added () {
651665 buildFragmentFromQuestionnaire(" /component_repeated_group.json" )
652- onView(withId(R .id.add_item_to_repeated_group))
653- .perform(ViewActions .click())
666+ onView(withId(R .id.add_item_to_repeated_group)).perform(ViewActions .click())
654667
655668 composeTestRule
656669 .onNodeWithTag(QuestionnaireFragment .QUESTIONNAIRE_EDIT_LIST )
@@ -660,8 +673,7 @@ class QuestionnaireUiEspressoTest {
660673 onView(withId(R .id.repeated_group_instance_header_title))
661674 .check(ViewAssertions .matches(ViewMatchers .isDisplayed()))
662675
663- onView(withText(R .string.delete))
664- .check(ViewAssertions .matches(ViewMatchers .isDisplayed()))
676+ onView(withText(R .string.delete)).check(ViewAssertions .matches(ViewMatchers .isDisplayed()))
665677 }
666678
667679 @Test
@@ -695,11 +707,9 @@ class QuestionnaireUiEspressoTest {
695707 onView(withId(R .id.repeated_group_instance_header_title))
696708 .check(ViewAssertions .matches(ViewMatchers .isDisplayed()))
697709
698- onView(withText(R .string.delete))
699- .perform(ViewActions .click())
710+ onView(withText(R .string.delete)).perform(ViewActions .click())
700711
701- onView(withText(R .id.repeated_group_instance_header_title))
702- .check(doesNotExist())
712+ onView(withText(R .id.repeated_group_instance_header_title)).check(doesNotExist())
703713 }
704714
705715 private fun buildFragmentFromQuestionnaire (
0 commit comments