Skip to content

e2e: Fix the flaky test races and re-enable the composer giphy test#6570

Open
andremion wants to merge 8 commits into
developfrom
andrerego/and-1303-fix-flaky-e2e-tests
Open

e2e: Fix the flaky test races and re-enable the composer giphy test#6570
andremion wants to merge 8 commits into
developfrom
andrerego/and-1303-fix-flaky-e2e-tests

Conversation

@andremion

@andremion andremion commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Goal

Remove the two client-side race mechanisms behind the flaky e2e tests, harden the RetryRule screen recording, and re-enable the first of the ignored tests. Part of AND-1303; the remaining flake family (mock server behavior under WebSocket reconnects) is addressed in GetStream/stream-chat-test-mock-server#43.

Root causes

Diagnosed from the per-attempt artifacts (logcat, screenshot, hierarchy, step timings) that AND-1302's retry reporting attached to every flake of the 2026-07-13 nightly, plus the step durations of historic failures in TestOps:

Mechanism Signature Affected tests
Wait-once-read-once race in waitToAppear "timed out after 10000ms" that actually failed in 272 to 740ms, or StaleObjectException; screenshots show the correct app state threadIsNotLocked..., userAddsReactionWhileOffline
Same race via wait().isDisplayed() fast AssertionError (460 to 572ms) both deliveryStatusShown... tests
Mock server under reconnects (not client bugs) genuine 10s timeouts threadReplyIsRemovedEverywhere..., originalQuoteIsDeleted..., channelPreviewShowsPreviousMessage..., deletesReaction — tracked in the mock repo

Implementation

  • waitToAppear (both overloads) polls findObject at the existing 50ms interval within the same timeout budget and absorbs StaleObjectException between polls.
  • Visibility asserts go through a new selector-level waitDisplayed predicate: the find-and-check happens inside the poll, so asserts never hold a UiObject2 across reads (a held node can be recycled by recomposition before visibleCenter is read, which CI demonstrated after the first two fixes). assertMessageDeliveryStatus and assertMessageFailedIcon route through the same helper (the READ branch keeps its 30s timeout).
  • RetryRule: the recording file is named by methodName (the display name's parentheses can break when the recording commands go through the device shell), and stopRecordingSafely makes sure a recording infrastructure failure cannot change a test result or skip the failure reporting.
  • test_userObservesAnimatedGiphy_afterAddingGiphyThroughComposerMenu re-enabled: its blocking ticket (AND-1018, composer redesign) shipped in January. The flow is adapted to the redesigned composer (typing / opens the command suggestion list; the dedicated commands button no longer exists).
  • The shell-based UiDevice.typeText is removed: on this uiautomator version the shell command's wrapping quotes reach the input field as literal text, and nothing uses it after the flow fix.

Testing

All on a local emulator against the mock server:

  1. MessageListTests#test_threadIsNotLocked_afterParentMessageDeletedByParticipant (the race-prone test): 3/3 green, first attempt each time.
  2. MessageDeliveryStatusTests#test_singleCheckmarkShown_whenMessageIsSent and #test_deliveryStatusShownForTheLastMessageInGroup: green through the new assert routing.
  3. GiphyTests#test_userObservesAnimatedGiphy_afterAddingGiphyThroughComposerMenu (re-enabled): 2/2 green on the adapted flow.

To reproduce any of them: ./gradlew :stream-chat-android-compose-sample:connectedE2eDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=<class>#<method> with the mock server driver running.

No UI changes.

Summary by CodeRabbit

  • New Features

    • Composer command suggestions can now be opened by typing / in the message field.
    • Giphy commands can be selected and inserted directly from the suggestion list.
  • Bug Fixes

    • Improved reliability when waiting for messages, delivery statuses, and error indicators.
    • Reduced UI automation failures caused by stale or changing screen elements.
    • Improved handling of test recordings so recording issues do not obscure test results.
  • Tests

    • Re-enabled coverage for animated Giphy messages added through composer commands.

waitToAppear waited for hasObject once and then read findObject once. Under active recomposition the node the wait just saw can be recycled before the single read reaches it, failing in a few hundred ms with a misleading timeout message; the failure screenshots show the app in the correct state. Both overloads now poll at the existing 50ms interval within the same timeout budget and absorb StaleObjectException between polls.
assertMessageDeliveryStatus and assertMessageFailedIcon used wait().isDisplayed(), the same wait-once-read-once race in a second shape; the historic failures of test_deliveryStatusShownForTheLastMessageInGroup died in 460-572ms. assertVisibility gains a timeout parameter (the READ branch keeps its 30s) and these asserts go through it. On a genuine timeout they now surface as IllegalStateException (Allure broken) instead of AssertionError (failed).
Back-ports from stream-video-android PR 1746: the recording file uses methodName (displayName's parentheses break when recording commands go through the device shell, which depends on the uiautomator version), and a recording stop failure logs and skips the video attachment instead of replacing the test result or skipping the failure reporting.
test_userObservesAnimatedGiphy_afterAddingGiphyThroughComposerMenu was ignored pending the composer redesign (AND-1018), which shipped in January. The redesign removed the dedicated commands button, so the flow now types '/' to open the command suggestion list. Text entry uses the direct UiObject2.typeText: the shell-based input leaks its wrapping quotes into the field on this uiautomator version (the composer literally contained '/'), which also silently affected the command branch of uploadGiphy. The dead Stream_ComposerCommandsButton selector is removed.
No usages remain, and on this uiautomator version the shell command's wrapping quotes reach the input field as literal text. Direct UiObject2.typeText is the working path everywhere.
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled, or the PR is bot-authored.
  • An issue is linked (Linear ticket or GitHub issue), or the PR is bot-authored.

🎉 Great job! This PR is ready for review.

@andremion andremion added the pr:ci CI changes label Jul 13, 2026
@andremion

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

Copy link
Copy Markdown
Contributor

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.94 MB 5.94 MB 0.00 MB 🟢
stream-chat-android-ui-components 11.21 MB 11.21 MB 0.00 MB 🟢
stream-chat-android-compose 12.68 MB 12.68 MB 0.00 MB 🟢

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The E2E test updates replace the composer command button flow with slash-triggered suggestions, standardize message visibility waits, harden retry screen recording, move text entry to UiObject2, and add polling for stale UI selectors.

Changes

E2E test behavior

Layer / File(s) Summary
Composer command Giphy flow
stream-chat-android-compose-sample/src/androidTestE2eDebug/kotlin/io/getstream/chat/android/compose/pages/MessageListPage.kt, stream-chat-android-compose-sample/src/androidTestE2eDebug/kotlin/io/getstream/chat/android/compose/robots/UserRobot.kt, stream-chat-android-compose-sample/src/androidTestE2eDebug/kotlin/io/getstream/chat/android/compose/tests/GiphyTests.kt, stream-chat-android-e2e-test/src/main/kotlin/io/getstream/chat/android/e2e/test/uiautomator/Actions.kt
Composer commands open from /, Giphy input uses the full command, text entry targets UiObject2, and the animated Giphy test is enabled.
Message visibility assertions
stream-chat-android-compose-sample/src/androidTestE2eDebug/kotlin/io/getstream/chat/android/compose/robots/UserRobotMessageListAsserts.kt
Message delivery and failure-icon checks use configurable visibility assertions and bounded timeouts.
Retry recording and UI polling
stream-chat-android-e2e-test/src/main/kotlin/io/getstream/chat/android/e2e/test/rules/RetryRule.kt, stream-chat-android-e2e-test/src/main/kotlin/io/getstream/chat/android/e2e/test/uiautomator/Wait.kt
Retry recording shutdown is safely handled, attachments require successful stopping, and selector waits retry through stale-object reads.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: pr:test, pr:improvement

Suggested reviewers: testableapple

Poem

I’m a bunny with tests in my paws,
Slash commands now hop without flaws.
Giphy springs bright, waits gently align,
Recordings stop safely, right on time.
UI hares through stale screens with cheer!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: fixing flaky e2e test races and re-enabling the composer Giphy test.
Description check ✅ Passed It includes Goal, Implementation, and Testing; only non-critical template sections like UI Changes, checklists, and GIF are omitted.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch andrerego/and-1303-fix-flaky-e2e-tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The command branch of uploadGiphy tapped the send button with a one-shot findObject right after setting the input text. The send button only renders once the input is non-empty, and on a slow CI emulator the recomposition can still be in flight, so the read returned null and all three attempts failed. tapOnSendButton already waits and is what every other send path uses.
Selecting a command suggestion activates command mode, where the composer's trailing button is the save button instead of the send button. Whether the suggestion tap lands depends on the animation state (CI disables animations, local emulators usually do not), so the giphy command flow saw the send button locally and the save button on CI. With the sample's configuration both buttons build the same message, so the robot taps whichever the composer settled on.
A node returned by waitToAppear can be recycled by recomposition before the caller reads visibleCenter, so assertTrue(waitToAppear().isDisplayed()) still threw StaleObjectException after the wait itself was fixed. waitDisplayed polls find-and-check as one operation and reports the outcome, so asserts never hold a node across reads. All 14 assert sites move to it; assertGiphyImage's stale-retry wrapper becomes redundant. waitToAppear stays for interaction flows that need the node.
@sonarqubecloud

Copy link
Copy Markdown

@andremion andremion marked this pull request as ready for review July 13, 2026 16:03
@andremion andremion requested a review from a team as a code owner July 13, 2026 16:03
@andremion andremion enabled auto-merge (squash) July 13, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:ci CI changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant