Skip to content

Commit 75769fb

Browse files
committed
Fix UIWorkflowTest text input and model loading issues
- Add testTag("chat_input_field") to BasicTextField for reliable test access - Create typeInChatInput() and clearChatInput() helper functions - Improve waitForModelLoaded() to detect both success and error states - Increase model loading timeout from 60s to 90s for slower devices - Update all tests to use testTag-based text input instead of text matching BasicTextField doesn't support performTextInput() without semantic actions, so using testTag allows Compose testing framework to properly interact with the text field.
1 parent 2a1ea9c commit 75769fb

2 files changed

Lines changed: 52 additions & 58 deletions

File tree

  • llm/android/LlamaDemo/app/src

llm/android/LlamaDemo/app/src/androidTest/java/com/example/executorchllamademo/UIWorkflowTest.kt

Lines changed: 48 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import androidx.compose.ui.test.hasText
1717
import androidx.compose.ui.test.junit4.createAndroidComposeRule
1818
import androidx.compose.ui.test.onAllNodesWithText
1919
import androidx.compose.ui.test.onNodeWithContentDescription
20+
import androidx.compose.ui.test.onNodeWithTag
2021
import androidx.compose.ui.test.onNodeWithText
2122
import androidx.compose.ui.test.performClick
2223
import androidx.compose.ui.test.performTextClearance
@@ -148,47 +149,51 @@ class UIWorkflowTest {
148149
}
149150

150151
/**
151-
* Waits for the model to be loaded by checking for "Successfully loaded model" message.
152+
* Waits for the model to be loaded by checking for success or error messages.
152153
*/
153154
private fun waitForModelLoaded(timeoutMs: Long = 60000): Boolean {
154155
val startTime = System.currentTimeMillis()
155156
while (System.currentTimeMillis() - startTime < timeoutMs) {
157+
composeTestRule.waitForIdle()
156158
try {
157-
composeTestRule.onNodeWithText("Successfully loaded model", substring = true)
159+
// Check for success message
160+
composeTestRule.onNodeWithText("Successfully loaded", substring = true)
158161
.assertExists()
162+
Log.i(TAG, "Model loaded successfully")
159163
return true
160164
} catch (e: AssertionError) {
161-
Thread.sleep(500)
165+
// Check for error to fail fast
166+
try {
167+
composeTestRule.onNodeWithText("Model Load failure", substring = true)
168+
.assertExists()
169+
Log.e(TAG, "Model load failed")
170+
return false
171+
} catch (e2: AssertionError) {
172+
// Neither success nor error, keep waiting
173+
}
162174
}
175+
Thread.sleep(1000)
163176
}
177+
Log.e(TAG, "Model loading timed out after ${timeoutMs}ms")
164178
return false
165179
}
166180

167181
/**
168-
* Waits for response to appear with minimum length.
182+
* Types text into the chat input field using testTag.
169183
*/
170-
private fun waitForResponseLength(minLength: Int, timeoutMs: Long): Boolean {
171-
val startTime = System.currentTimeMillis()
172-
while (System.currentTimeMillis() - startTime < timeoutMs) {
173-
// Check if there's any substantial text in the messages
174-
// This is a simplified check - Compose testing makes it harder to extract text length
175-
composeTestRule.waitForIdle()
176-
Thread.sleep(200)
184+
private fun typeInChatInput(text: String) {
185+
composeTestRule.onNodeWithTag("chat_input_field").performClick()
186+
composeTestRule.waitForIdle()
187+
composeTestRule.onNodeWithTag("chat_input_field").performTextInput(text)
188+
composeTestRule.waitForIdle()
189+
}
177190

178-
// Try to find nodes that might contain response
179-
try {
180-
val nodes = composeTestRule.onAllNodesWithText("", substring = true)
181-
.fetchSemanticsNodes()
182-
// If we have substantial content, return true
183-
if (nodes.size > 5) { // Rough heuristic for response
184-
return true
185-
}
186-
} catch (e: Exception) {
187-
// Continue waiting
188-
}
189-
Thread.sleep(300)
190-
}
191-
return false
191+
/**
192+
* Clears the chat input field.
193+
*/
194+
private fun clearChatInput() {
195+
composeTestRule.onNodeWithTag("chat_input_field").performTextClearance()
196+
composeTestRule.waitForIdle()
192197
}
193198

194199
/**
@@ -261,13 +266,11 @@ class UIWorkflowTest {
261266
assertTrue("Model should be selected successfully", loaded)
262267

263268
// Wait for model to load
264-
val modelLoaded = waitForModelLoaded(60000)
269+
val modelLoaded = waitForModelLoaded(90000)
265270
assertTrue("Model should be loaded successfully", modelLoaded)
266271

267-
// Type a message
268-
composeTestRule.onNodeWithText("Type a message...", substring = true).performClick()
269-
composeTestRule.onNodeWithText("Type a message...", substring = true).performTextInput("tell me a story")
270-
composeTestRule.waitForIdle()
272+
// Type a message using testTag
273+
typeInChatInput("tell me a story")
271274

272275
// Click send
273276
composeTestRule.onNodeWithContentDescription("Send").performClick()
@@ -293,14 +296,11 @@ class UIWorkflowTest {
293296
val loaded = loadModel()
294297
assertTrue("Model should be selected successfully", loaded)
295298

296-
val modelLoaded = waitForModelLoaded(60000)
299+
val modelLoaded = waitForModelLoaded(90000)
297300
assertTrue("Model should be loaded successfully", modelLoaded)
298301

299-
// Type a long prompt
300-
composeTestRule.onNodeWithText("Type a message...", substring = true).performClick()
301-
composeTestRule.onNodeWithText("Type a message...", substring = true)
302-
.performTextInput("Write a very long story about a brave knight who goes on an adventure")
303-
composeTestRule.waitForIdle()
302+
// Type a long prompt using testTag
303+
typeInChatInput("Write a very long story about a brave knight who goes on an adventure")
304304

305305
// Click send
306306
composeTestRule.onNodeWithContentDescription("Send").performClick()
@@ -333,14 +333,11 @@ class UIWorkflowTest {
333333
val loaded = loadModel()
334334
assertTrue("Model should be selected successfully", loaded)
335335

336-
val modelLoaded = waitForModelLoaded(60000)
336+
val modelLoaded = waitForModelLoaded(90000)
337337
assertTrue("Model should be loaded successfully", modelLoaded)
338338

339-
// Type a prompt
340-
composeTestRule.onNodeWithText("Type a message...", substring = true).performClick()
341-
composeTestRule.onNodeWithText("Type a message...", substring = true)
342-
.performTextInput("Write a detailed story")
343-
composeTestRule.waitForIdle()
339+
// Type a prompt using testTag
340+
typeInChatInput("Write a detailed story")
344341

345342
// Click send
346343
composeTestRule.onNodeWithContentDescription("Send").performClick()
@@ -366,23 +363,20 @@ class UIWorkflowTest {
366363
val loaded = loadModel()
367364
assertTrue("Model should be selected successfully", loaded)
368365

369-
val modelLoaded = waitForModelLoaded(60000)
366+
val modelLoaded = waitForModelLoaded(90000)
370367
assertTrue("Model should be loaded successfully", modelLoaded)
371368

372369
// Verify send button is disabled with empty input
373370
composeTestRule.onNodeWithContentDescription("Send").assertIsNotEnabled()
374371

375-
// Type some text
376-
composeTestRule.onNodeWithText("Type a message...", substring = true).performClick()
377-
composeTestRule.onNodeWithText("Type a message...", substring = true).performTextInput("hello")
378-
composeTestRule.waitForIdle()
372+
// Type some text using testTag
373+
typeInChatInput("hello")
379374

380375
// Verify send button is now enabled
381376
composeTestRule.onNodeWithContentDescription("Send").assertIsEnabled()
382377

383378
// Clear the text
384-
composeTestRule.onNode(hasText("hello")).performTextClearance()
385-
composeTestRule.waitForIdle()
379+
clearChatInput()
386380

387381
// Verify send button is disabled again
388382
composeTestRule.onNodeWithContentDescription("Send").assertIsNotEnabled()
@@ -522,24 +516,21 @@ class UIWorkflowTest {
522516
val loaded = loadModel()
523517
assertTrue("Model should be selected successfully", loaded)
524518

525-
val modelLoaded = waitForModelLoaded(60000)
519+
val modelLoaded = waitForModelLoaded(90000)
526520
assertTrue("Model should be loaded successfully", modelLoaded)
527521

528522
// Verify send button is disabled with empty input
529523
composeTestRule.onNodeWithContentDescription("Send").assertIsNotEnabled()
530524

531-
// Type only spaces
532-
composeTestRule.onNodeWithText("Type a message...", substring = true).performClick()
533-
composeTestRule.onNodeWithText("Type a message...", substring = true).performTextInput(" ")
534-
composeTestRule.waitForIdle()
525+
// Type only spaces using testTag
526+
typeInChatInput(" ")
535527

536528
// Verify send button is still disabled (whitespace only)
537529
composeTestRule.onNodeWithContentDescription("Send").assertIsNotEnabled()
538530

539531
// Clear and type actual text
540-
composeTestRule.onNode(hasText(" ")).performTextClearance()
541-
composeTestRule.onNodeWithText("Type a message...", substring = true).performTextInput("hello")
542-
composeTestRule.waitForIdle()
532+
clearChatInput()
533+
typeInChatInput("hello")
543534

544535
// Verify send button is now enabled
545536
composeTestRule.onNodeWithContentDescription("Send").assertIsEnabled()

llm/android/LlamaDemo/app/src/main/java/com/example/executorchllamademo/ui/components/ChatInput.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import androidx.compose.ui.draw.clip
3939
import androidx.compose.ui.graphics.Color
4040
import androidx.compose.ui.graphics.SolidColor
4141
import androidx.compose.ui.layout.ContentScale
42+
import androidx.compose.ui.platform.testTag
4243
import androidx.compose.ui.res.painterResource
4344
import androidx.compose.ui.text.TextStyle
4445
import androidx.compose.ui.unit.dp
@@ -173,7 +174,9 @@ fun ChatInput(
173174
BasicTextField(
174175
value = inputText,
175176
onValueChange = onInputTextChange,
176-
modifier = Modifier.fillMaxWidth(),
177+
modifier = Modifier
178+
.fillMaxWidth()
179+
.testTag("chat_input_field"),
177180
textStyle = TextStyle(
178181
fontSize = 14.sp,
179182
letterSpacing = 0.sp,

0 commit comments

Comments
 (0)