11/*
2- * Copyright 2023-2024 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.
@@ -19,7 +19,15 @@ package com.google.android.fhir.datacapture.contrib.views
1919import android.view.View
2020import android.widget.FrameLayout
2121import android.widget.TextView
22- import androidx.test.annotation.UiThreadTest
22+ import androidx.compose.ui.test.IdlingResource
23+ import androidx.compose.ui.test.assertIsDisplayed
24+ import androidx.compose.ui.test.assertIsNotEnabled
25+ import androidx.compose.ui.test.assertTextEquals
26+ import androidx.compose.ui.test.junit4.createEmptyComposeRule
27+ import androidx.compose.ui.test.onNodeWithContentDescription
28+ import androidx.compose.ui.test.onNodeWithTag
29+ import androidx.compose.ui.test.onNodeWithText
30+ import androidx.compose.ui.test.performTextReplacement
2331import androidx.test.ext.junit.rules.ActivityScenarioRule
2432import androidx.test.ext.junit.runners.AndroidJUnit4
2533import androidx.test.platform.app.InstrumentationRegistry
@@ -30,42 +38,54 @@ import com.google.android.fhir.datacapture.test.TestActivity
3038import com.google.android.fhir.datacapture.validation.Invalid
3139import com.google.android.fhir.datacapture.validation.NotValidated
3240import com.google.android.fhir.datacapture.views.QuestionnaireViewItem
41+ import com.google.android.fhir.datacapture.views.compose.EDIT_TEXT_FIELD_TEST_TAG
3342import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolder
34- import com.google.android.material.textfield.TextInputEditText
35- import com.google.android.material.textfield.TextInputLayout
3643import com.google.common.truth.Truth.assertThat
3744import org.hl7.fhir.r4.model.IntegerType
3845import org.hl7.fhir.r4.model.Questionnaire
3946import org.hl7.fhir.r4.model.QuestionnaireResponse
4047import org.hl7.fhir.r4.model.StringType
48+ import org.junit.After
4149import org.junit.Before
42- import org.junit.Ignore
4350import org.junit.Rule
4451import org.junit.Test
4552import org.junit.runner.RunWith
4653
4754@RunWith(AndroidJUnit4 ::class )
4855class PhoneNumberViewHolderFactoryInstrumentedTest {
4956
50- @Rule
51- @JvmField
52- var activityScenarioRule: ActivityScenarioRule <TestActivity > =
57+ @get:Rule
58+ val activityScenarioRule: ActivityScenarioRule <TestActivity > =
5359 ActivityScenarioRule (TestActivity ::class .java)
5460
61+ @get:Rule val composeTestRule = createEmptyComposeRule()
62+
5563 private lateinit var parent: FrameLayout
5664 private lateinit var viewHolder: QuestionnaireItemViewHolder
57- private lateinit var questionnaireEditAdapter: QuestionnaireEditAdapter
65+
66+ private var pendingTextChange = 0
67+ private val handlingTextIdlingResource =
68+ object : IdlingResource {
69+ override val isIdleNow: Boolean
70+ get() = pendingTextChange == 0
71+ }
5872
5973 @Before
6074 fun setUp () {
6175 activityScenarioRule.scenario.onActivity { activity -> parent = FrameLayout (activity) }
6276 viewHolder = PhoneNumberViewHolderFactory .create(parent)
6377 setTestLayout(viewHolder.itemView)
64- questionnaireEditAdapter = QuestionnaireEditAdapter ()
78+ composeTestRule.registerIdlingResource(handlingTextIdlingResource)
79+ }
80+
81+ @After
82+ fun tearDown () {
83+ composeTestRule.unregisterIdlingResource(handlingTextIdlingResource)
6584 }
6685
6786 @Test
6887 fun createViewHolder_phoneNumberViewHolderFactory_returnsViewHolder () {
88+ val questionnaireEditAdapter = QuestionnaireEditAdapter ()
6989 val viewHolderFromAdapter =
7090 questionnaireEditAdapter.createViewHolder(
7191 parent,
@@ -75,16 +95,14 @@ class PhoneNumberViewHolderFactoryInstrumentedTest {
7595 )
7696 .viewType,
7797 ) as QuestionnaireEditAdapter .ViewHolder .QuestionHolder
98+
7899 assertThat(
79- viewHolderFromAdapter.holder.itemView
80- .findViewById<TextInputEditText >(R .id.text_input_edit_text)
81- .visibility,
100+ viewHolderFromAdapter.holder.itemView.visibility,
82101 )
83102 .isEqualTo(View .VISIBLE )
84103 }
85104
86105 @Test
87- @UiThreadTest
88106 fun shouldSetTextViewText () {
89107 viewHolder.bind(
90108 QuestionnaireViewItem (
@@ -94,13 +112,14 @@ class PhoneNumberViewHolderFactoryInstrumentedTest {
94112 answersChangedCallback = { _, _, _, _ -> },
95113 ),
96114 )
115+ // Synchronize
116+ composeTestRule.waitForIdle()
97117
98118 assertThat(viewHolder.itemView.findViewById<TextView >(R .id.question).text.toString())
99119 .isEqualTo(" Question?" )
100120 }
101121
102122 @Test
103- @UiThreadTest
104123 fun shouldSetInputText () {
105124 viewHolder.bind(
106125 QuestionnaireViewItem (
@@ -116,17 +135,10 @@ class PhoneNumberViewHolderFactoryInstrumentedTest {
116135 ),
117136 )
118137
119- assertThat(
120- viewHolder.itemView
121- .findViewById<TextInputEditText >(R .id.text_input_edit_text)
122- .text
123- .toString(),
124- )
125- .isEqualTo(" +12345678910" )
138+ composeTestRule.onNodeWithTag(EDIT_TEXT_FIELD_TEST_TAG ).assertTextEquals(" +12345678910" )
126139 }
127140
128141 @Test
129- @UiThreadTest
130142 fun shouldSetInputTextToEmpty () {
131143 viewHolder.bind(
132144 QuestionnaireViewItem (
@@ -150,36 +162,33 @@ class PhoneNumberViewHolderFactoryInstrumentedTest {
150162 ),
151163 )
152164
153- assertThat(
154- viewHolder.itemView
155- .findViewById<TextInputEditText >(R .id.text_input_edit_text)
156- .text
157- .toString(),
158- )
159- .isEqualTo(" " )
165+ composeTestRule.onNodeWithTag(EDIT_TEXT_FIELD_TEST_TAG ).assertTextEquals(" " )
160166 }
161167
162168 @Test
163- @UiThreadTest
164- @Ignore(" https://github.com/google/android-fhir/issues/1494" )
165169 fun shouldSetQuestionnaireResponseItemAnswer () {
170+ var answers: List <QuestionnaireResponse .QuestionnaireResponseItemAnswerComponent > = emptyList()
166171 val questionnaireViewItem =
167172 QuestionnaireViewItem (
168173 Questionnaire .QuestionnaireItemComponent (),
169174 QuestionnaireResponse .QuestionnaireResponseItemComponent (),
170175 validationResult = NotValidated ,
171- answersChangedCallback = { _, _, _, _ -> },
176+ answersChangedCallback = { _, _, newAnswers, _ ->
177+ answers = newAnswers
178+ pendingTextChange - = if (pendingTextChange > 0 ) 1 else 0
179+ },
172180 )
173181 viewHolder.bind(questionnaireViewItem)
174- viewHolder.itemView
175- .findViewById<TextInputEditText >(R .id.text_input_edit_text)
176- .setText(" +12345678910" )
177- assertThat(questionnaireViewItem.answers.single().valueStringType.value)
178- .isEqualTo(" +12345678910" )
182+ composeTestRule
183+ .onNodeWithTag(EDIT_TEXT_FIELD_TEST_TAG )
184+ .performTextReplacement(" +12345678910" )
185+ .also { pendingTextChange + = 1 }
186+
187+ composeTestRule.waitForIdle()
188+ assertThat(answers.single().valueStringType.value).isEqualTo(" +12345678910" )
179189 }
180190
181191 @Test
182- @UiThreadTest
183192 fun shouldSetQuestionnaireResponseItemAnswerToEmpty () {
184193 val questionnaireViewItem =
185194 QuestionnaireViewItem (
@@ -190,13 +199,12 @@ class PhoneNumberViewHolderFactoryInstrumentedTest {
190199 )
191200
192201 viewHolder.bind(questionnaireViewItem)
193- viewHolder.itemView.findViewById< TextInputEditText >( R .id.text_input_edit_text).setText (" " )
202+ composeTestRule.onNodeWithTag( EDIT_TEXT_FIELD_TEST_TAG ).performTextReplacement (" " )
194203
195204 assertThat(questionnaireViewItem.answers).isEmpty()
196205 }
197206
198207 @Test
199- @UiThreadTest
200208 fun displayValidationResult_noError_shouldShowNoErrorMessage () {
201209 viewHolder.bind(
202210 QuestionnaireViewItem (
@@ -218,12 +226,10 @@ class PhoneNumberViewHolderFactoryInstrumentedTest {
218226 ),
219227 )
220228
221- assertThat(viewHolder.itemView.findViewById<TextInputLayout >(R .id.text_input_layout).error)
222- .isNull()
229+ composeTestRule.onNodeWithContentDescription(" Error" ).assertDoesNotExist()
223230 }
224231
225232 @Test
226- @UiThreadTest
227233 fun displayValidationResult_error_shouldShowErrorMessage () {
228234 viewHolder.bind(
229235 QuestionnaireViewItem (
@@ -242,12 +248,14 @@ class PhoneNumberViewHolderFactoryInstrumentedTest {
242248 answersChangedCallback = { _, _, _, _ -> },
243249 ),
244250 )
245- assertThat(viewHolder.itemView.findViewById<TextInputLayout >(R .id.text_input_layout).error)
246- .isEqualTo(" The maximum number of characters that are permitted in the answer is: 10" )
251+
252+ composeTestRule.onNodeWithContentDescription(" Error" ).assertIsDisplayed()
253+ composeTestRule
254+ .onNodeWithText(" The maximum number of characters that are permitted in the answer is: 10" )
255+ .assertIsDisplayed()
247256 }
248257
249258 @Test
250- @UiThreadTest
251259 fun bind_readOnly_shouldDisableView () {
252260 viewHolder.bind(
253261 QuestionnaireViewItem (
@@ -257,11 +265,7 @@ class PhoneNumberViewHolderFactoryInstrumentedTest {
257265 answersChangedCallback = { _, _, _, _ -> },
258266 ),
259267 )
260-
261- assertThat(
262- viewHolder.itemView.findViewById<TextInputEditText >(R .id.text_input_edit_text).isEnabled,
263- )
264- .isFalse()
268+ composeTestRule.onNodeWithTag(EDIT_TEXT_FIELD_TEST_TAG ).assertIsNotEnabled()
265269 }
266270
267271 /* * Method to set content view for test activity */
0 commit comments