@@ -19,12 +19,19 @@ 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.SemanticsProperties
23+ import androidx.compose.ui.test.SemanticsMatcher
24+ import androidx.compose.ui.test.assert
2225import androidx.compose.ui.test.assertIsDisplayed
2326import androidx.compose.ui.test.assertTextEquals
27+ import androidx.compose.ui.test.hasAnyAncestor
28+ import androidx.compose.ui.test.hasText
29+ import androidx.compose.ui.test.isDialog
2430import androidx.compose.ui.test.junit4.createEmptyComposeRule
2531import androidx.compose.ui.test.onNodeWithContentDescription
2632import androidx.compose.ui.test.onNodeWithTag
2733import androidx.compose.ui.test.onNodeWithText
34+ import androidx.compose.ui.test.performClick
2835import androidx.compose.ui.test.performTextInput
2936import androidx.fragment.app.commitNow
3037import androidx.recyclerview.widget.RecyclerView
@@ -35,30 +42,29 @@ import androidx.test.espresso.ViewAction
3542import androidx.test.espresso.action.ViewActions
3643import androidx.test.espresso.assertion.ViewAssertions
3744import androidx.test.espresso.contrib.RecyclerViewActions
38- import androidx.test.espresso.matcher.RootMatchers
3945import androidx.test.espresso.matcher.ViewMatchers
4046import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
4147import androidx.test.espresso.matcher.ViewMatchers.withId
4248import androidx.test.espresso.matcher.ViewMatchers.withText
4349import androidx.test.ext.junit.rules.ActivityScenarioRule
4450import androidx.test.ext.junit.runners.AndroidJUnit4
45- import androidx.test.filters.SdkSuppress
4651import androidx.test.platform.app.InstrumentationRegistry
4752import ca.uhn.fhir.context.FhirContext
4853import ca.uhn.fhir.context.FhirVersionEnum
4954import ca.uhn.fhir.parser.IParser
5055import com.google.android.fhir.datacapture.QuestionnaireFragment
56+ import com.google.android.fhir.datacapture.R
57+ import com.google.android.fhir.datacapture.extensions.localDate
58+ import com.google.android.fhir.datacapture.extensions.localDateTime
5159import com.google.android.fhir.datacapture.test.utilities.clickIcon
5260import com.google.android.fhir.datacapture.test.utilities.clickOnText
5361import com.google.android.fhir.datacapture.validation.Invalid
5462import com.google.android.fhir.datacapture.validation.QuestionnaireResponseValidator
5563import com.google.android.fhir.datacapture.validation.Valid
64+ import com.google.android.fhir.datacapture.views.compose.DATE_TEXT_INPUT_FIELD
5665import com.google.android.fhir.datacapture.views.compose.EDIT_TEXT_FIELD_TEST_TAG
5766import com.google.android.fhir.datacapture.views.compose.HANDLE_INPUT_DEBOUNCE_TIME
58- import com.google.android.fhir.datacapture.views.factories.localDate
59- import com.google.android.fhir.datacapture.views.factories.localDateTime
6067import com.google.android.material.progressindicator.LinearProgressIndicator
61- import com.google.android.material.textfield.TextInputEditText
6268import com.google.android.material.textfield.TextInputLayout
6369import com.google.common.truth.Truth.assertThat
6470import java.math.BigDecimal
@@ -284,28 +290,25 @@ class QuestionnaireUiEspressoTest {
284290 buildFragmentFromQuestionnaire(" /component_date_picker.json" )
285291
286292 // Add month and day. No need to add slashes as they are added automatically
287- onView(withId(R .id.text_input_edit_text))
288- .perform(ViewActions .click())
289- .perform(ViewActions .typeTextIntoFocusedView(" 0105" ))
290-
291- onView(withId(R .id.text_input_layout)).check { view, _ ->
292- val actualError = (view as TextInputLayout ).error
293- assertThat(actualError).isEqualTo(" Date format needs to be mm/dd/yyyy (e.g. 01/31/2023)" )
294- }
293+ composeTestRule.onNodeWithTag(DATE_TEXT_INPUT_FIELD ).performTextInput(" 0105" )
294+ composeTestRule
295+ .onNodeWithTag(DATE_TEXT_INPUT_FIELD )
296+ .assert (
297+ SemanticsMatcher .expectValue(
298+ SemanticsProperties .Error ,
299+ " Date format needs to be mm/dd/yyyy (e.g. 01/31/2023)" ,
300+ ),
301+ )
295302 }
296303
297304 @Test
298305 fun datePicker_shouldSaveInQuestionnaireResponseWhenCorrectDateEntered () {
299306 buildFragmentFromQuestionnaire(" /component_date_picker.json" )
300307
301- onView(withId(R .id.text_input_edit_text))
302- .perform(ViewActions .click())
303- .perform(ViewActions .typeTextIntoFocusedView(" 01052005" ))
304-
305- onView(withId(R .id.text_input_layout)).check { view, _ ->
306- val actualError = (view as TextInputLayout ).error
307- assertThat(actualError).isEqualTo(null )
308- }
308+ composeTestRule.onNodeWithTag(DATE_TEXT_INPUT_FIELD ).performTextInput(" 01052005" )
309+ composeTestRule
310+ .onNodeWithTag(DATE_TEXT_INPUT_FIELD )
311+ .assert (SemanticsMatcher .keyNotDefined(SemanticsProperties .Error ))
309312
310313 runBlocking {
311314 val answer = getQuestionnaireResponse().item.first().answer.first().valueDateType
@@ -337,11 +340,14 @@ class QuestionnaireUiEspressoTest {
337340 }
338341
339342 buildFragmentFromQuestionnaire(questionnaire)
340- onView(withId(R .id.text_input_layout)).perform(clickIcon(true ))
341- onView(CoreMatchers .allOf(ViewMatchers .withText(" OK" )))
342- .inRoot(RootMatchers .isDialog())
343- .check(ViewAssertions .matches(ViewMatchers .isDisplayed()))
344- .perform(ViewActions .click())
343+ composeTestRule
344+ .onNodeWithContentDescription(context.getString(R .string.select_date))
345+ .performClick()
346+ composeTestRule
347+ .onNode(hasText(" OK" ) and hasAnyAncestor(isDialog()))
348+ .assertIsDisplayed()
349+ .performClick()
350+ composeTestRule.waitForIdle() // Synchronize
345351
346352 val today = DateTimeType .today().valueAsString
347353
@@ -384,11 +390,14 @@ class QuestionnaireUiEspressoTest {
384390 }
385391
386392 buildFragmentFromQuestionnaire(questionnaire)
387- onView(withId(R .id.text_input_layout)).perform(clickIcon(true ))
388- onView(CoreMatchers .allOf(ViewMatchers .withText(" OK" )))
389- .inRoot(RootMatchers .isDialog())
390- .check(ViewAssertions .matches(ViewMatchers .isDisplayed()))
391- .perform(ViewActions .click())
393+ composeTestRule
394+ .onNodeWithContentDescription(context.getString(R .string.select_date))
395+ .performClick()
396+ composeTestRule
397+ .onNode(hasText(" OK" ) and hasAnyAncestor(isDialog()))
398+ .assertIsDisplayed()
399+ .performClick()
400+ composeTestRule.waitForIdle() // Synchronize
392401
393402 val maxDateAllowed = maxDate.valueAsString
394403
@@ -431,11 +440,14 @@ class QuestionnaireUiEspressoTest {
431440 }
432441
433442 buildFragmentFromQuestionnaire(questionnaire)
434- onView(withId(R .id.text_input_layout)).perform(clickIcon(true ))
435- onView(CoreMatchers .allOf(ViewMatchers .withText(" OK" )))
436- .inRoot(RootMatchers .isDialog())
437- .check(ViewAssertions .matches(ViewMatchers .isDisplayed()))
438- .perform(ViewActions .click())
443+ composeTestRule
444+ .onNodeWithContentDescription(context.getString(R .string.select_date))
445+ .performClick()
446+ composeTestRule
447+ .onNode(hasText(" OK" ) and hasAnyAncestor(isDialog()))
448+ .assertIsDisplayed()
449+ .performClick()
450+ composeTestRule.waitForIdle() // Synchronize
439451
440452 val minDateAllowed = minDate.valueAsString
441453
@@ -481,12 +493,14 @@ class QuestionnaireUiEspressoTest {
481493 buildFragmentFromQuestionnaire(questionnaire)
482494 val exception =
483495 Assert .assertThrows(IllegalArgumentException ::class .java) {
484- onView(withId(com.google.android.fhir.datacapture.R .id.text_input_layout))
485- .perform(clickIcon(true ))
486- onView(CoreMatchers .allOf(ViewMatchers .withText(" OK" )))
487- .inRoot(RootMatchers .isDialog())
488- .check(ViewAssertions .matches(ViewMatchers .isDisplayed()))
489- .perform(ViewActions .click())
496+ composeTestRule
497+ .onNodeWithContentDescription(context.getString(R .string.select_date))
498+ .performClick()
499+ composeTestRule
500+ .onNode(hasText(" OK" ) and hasAnyAncestor(isDialog()))
501+ .assertIsDisplayed()
502+ .performClick()
503+ composeTestRule.waitForIdle() // Synchronize
490504 }
491505 assertThat(exception.message).isEqualTo(" minValue cannot be greater than maxValue" )
492506 }
@@ -550,19 +564,21 @@ class QuestionnaireUiEspressoTest {
550564 }
551565
552566 @Test
553- @SdkSuppress(minSdkVersion = 33 )
554567 fun clearAllAnswers_shouldClearDraftAnswer () {
555568 val questionnaireFragment = buildFragmentFromQuestionnaire(" /component_date_picker.json" )
556569 // Add month and day. No need to add slashes as they are added automatically
557- onView(withId(R .id.text_input_edit_text))
558- .perform(ViewActions .click())
559- .perform(ViewActions .typeTextIntoFocusedView(" 0105" ))
570+ composeTestRule
571+ .onNodeWithTag(DATE_TEXT_INPUT_FIELD , useUnmergedTree = true )
572+ .performTextInput(" 0105" )
573+ composeTestRule
574+ .onNodeWithTag(DATE_TEXT_INPUT_FIELD , useUnmergedTree = true )
575+ .assertTextEquals(" 01/05/" )
560576
561577 questionnaireFragment.clearAllAnswers()
562578
563- onView(withId( R .id.text_input_edit_text)).check { view, _ ->
564- assertThat((view as TextInputEditText ).text.toString()).isEmpty( )
565- }
579+ composeTestRule
580+ .onNodeWithTag( DATE_TEXT_INPUT_FIELD , useUnmergedTree = true )
581+ .assertTextEquals( " " )
566582 }
567583
568584 @Test
@@ -724,7 +740,7 @@ class QuestionnaireUiEspressoTest {
724740 activityScenarioRule.scenario.onActivity { activity ->
725741 activity.supportFragmentManager.commitNow {
726742 setReorderingAllowed(true )
727- add(R .id.container_holder, fragment)
743+ add(com.google.android.fhir.datacapture.test. R .id.container_holder, fragment)
728744 }
729745 }
730746 }
@@ -742,7 +758,7 @@ class QuestionnaireUiEspressoTest {
742758 activityScenarioRule.scenario.onActivity { activity ->
743759 activity.supportFragmentManager.commitNow {
744760 setReorderingAllowed(true )
745- add(R .id.container_holder, questionnaireFragment)
761+ add(com.google.android.fhir.datacapture.test. R .id.container_holder, questionnaireFragment)
746762 }
747763 }
748764 }
@@ -754,8 +770,9 @@ class QuestionnaireUiEspressoTest {
754770 var testQuestionnaireFragment: QuestionnaireFragment ? = null
755771 activityScenarioRule.scenario.onActivity { activity ->
756772 testQuestionnaireFragment =
757- activity.supportFragmentManager.findFragmentById(R .id.container_holder)
758- as QuestionnaireFragment
773+ activity.supportFragmentManager.findFragmentById(
774+ com.google.android.fhir.datacapture.test.R .id.container_holder,
775+ ) as QuestionnaireFragment
759776 }
760777 return testQuestionnaireFragment!! .getQuestionnaireResponse()
761778 }
0 commit comments