Skip to content

ci(repo): Add E2E reactions tests#2825

Open
testableapple wants to merge 21 commits into
masterfrom
ci/e2e-reactions-tests
Open

ci(repo): Add E2E reactions tests#2825
testableapple wants to merge 21 commits into
masterfrom
ci/e2e-reactions-tests

Conversation

@testableapple

@testableapple testableapple commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Resolve https://linear.app/stream/issue/FLU-604

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

Adds an end-to-end test suite for message reactions to the sample app's integration tests, plus the harness plumbing needed to run them reliably in CI.

New tests (integration_test/reactions_test.dart)

Covers the full reaction matrix against the mock server:

  • User adds / removes a reaction on their own message
  • User adds / removes a reaction on a participant's message
  • Participant adds / removes a reaction on the user's message
  • Participant adds / removes a reaction on their own message
  • Offline scenarios: reaction added/received while the client is offline, then synced on reconnect

Deterministic offline/online simulation

Real network toggling isn't reproducible in CI, so the harness drives connectivity itself:

  • StreamTestEnv.goOffline() / goOnline() simulate a full outage and recovery, then wait for the WebSocket to reach the expected ConnectionStatus.
  • A test-controlled debugConnectivityStream is injected into the StreamChat widget in place of the real connectivity_plus monitor (null in production).
  • A debugForceOffline flag + chatApiInterceptors reject every HTTP request while "offline", so the HTTP and WebSocket halves stay consistent.
  • Offline persistence is disabled under e2e — the shared, never-cleared SQLite DB leaked state across tests in a bundle and could throw an async FOREIGN KEY error that poisoned the test binding.

Failure artifacts & CI

  • Failed tests now attach screenshot, widget hierarchy, log, and a per-test video clip to Allure (support/failure_artifacts.dart, allure/allure.dart, Fastfile attach_videos lane).
  • e2e_test.yml / e2e_test_cron.yml gained an "Attach videos to failed tests" step (installs ffmpeg, runs the slicing lane on failure).
  • Added a skip parameter to the streamTest / streamTestWithEnv helpers.

Known issue

The "user adds a reaction while offline" test is currently skipped pending FLU-506.

Screenshots / Videos

N/A — CI-only integration tests.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added per-test screen recording for Android E2E and automatic video attachment to failed/broken results.
    • Enhanced Allure output with richer step markers and attachment capture, plus retry-selection markers.
    • Added deterministic E2E connectivity controls to reliably simulate offline/online states.
  • Bug Fixes
    • Improved failure diagnostics by capturing screenshots, widget hierarchies, and logs as artifacts.
    • Improved reaction test reliability via stronger long-press and negative-visibility synchronization helpers.
  • Tests
    • Expanded reaction integration coverage (including offline/online add/delete scenarios) and added optional skip support.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1f5b2006-97d4-48db-9a18-c5bcce7f7eb4

📥 Commits

Reviewing files that changed from the base of the PR and between 8256015 and 402f77d.

📒 Files selected for processing (1)
  • sample_app/fastlane/Fastfile
🚧 Files skipped from review as they are similar to previous changes (1)
  • sample_app/fastlane/Fastfile

📝 Walkthrough

Walkthrough

Android E2E tests now capture failure diagnostics and per-test videos, selectively retry failed cases, and attach recordings to Allure results. Integration tests add reaction scenarios and deterministic offline/online controls, while Allure supports structured steps and binary or text attachments.

Changes

E2E observability and reaction testing

Layer / File(s) Summary
Allure steps and failure artifacts
sample_app/integration_test/allure/allure.dart, sample_app/integration_test/support/*, sample_app/integration_test/message_list_test.dart
Allure steps use explicit lifecycle markers, tests emit status markers and attachments, and failures capture screenshots, widget hierarchies, and logs.
Reaction test flows and UI helpers
sample_app/integration_test/mock_server/*, sample_app/integration_test/pages/*, sample_app/integration_test/robots/*, sample_app/integration_test/reactions_test.dart
Reaction types include emoji metadata, robot and page helpers support reaction actions and assertions, and scenarios cover reaction visibility, deletion, and persistence.
Deterministic offline and online test control
sample_app/lib/app.dart, sample_app/lib/auth/auth_controller.dart, sample_app/integration_test/support/stream_test_env.dart
E2E tests inject connectivity events, simulate offline API requests, wait for WebSocket state changes, and reset connectivity controls during teardown.
Failure-video recording and attachment
sample_app/fastlane/*, .github/workflows/e2e_test*.yml
Android attempts are recorded and selectively retried; failed-test videos are clipped, added to Allure results, and attached by failure-handling workflow steps.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant E2ETest
  participant Allure
  participant Fastlane
  participant CI
  E2ETest->>Allure: Emit test result and status markers
  E2ETest->>Fastlane: Save recording manifest
  CI->>Fastlane: Run attach_videos after failure
  Fastlane->>Allure: Add clipped videos to failed results
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main addition of end-to-end reaction tests, even though the PR also includes CI and failure-diagnostics support.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/e2e-reactions-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.

@testableapple
testableapple force-pushed the ci/e2e-reactions-tests branch from 6186245 to d212baf Compare July 17, 2026 14:47
@testableapple
testableapple force-pushed the ci/e2e-reactions-tests branch from d212baf to 08bb32a Compare July 17, 2026 14:49
@testableapple
testableapple marked this pull request as ready for review July 21, 2026 11:38
@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.35%. Comparing base (4dee989) to head (402f77d).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2825   +/-   ##
=======================================
  Coverage   71.35%   71.35%           
=======================================
  Files         430      430           
  Lines       26945    26945           
=======================================
  Hits        19226    19226           
  Misses       7719     7719           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
sample_app/integration_test/support/stream_test_case.dart (1)

18-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Skip reason is silently discarded.

skip is accepted as a descriptive String? but converted to skip: skip != null for testWidgets, which only accepts bool?. Since the callback body (where the reason could otherwise be logged) never runs for skipped tests, the actual reason never surfaces anywhere — not in CI output, not in Allure. Consider folding the reason into the test description so it's visible in test run output.

💡 Suggested fix
   IntegrationTestWidgetsFlutterBinding.ensureInitialized();

-  testWidgets(description, skip: skip != null, (tester) async {
+  final effectiveDescription = skip != null ? '$description (skipped: $skip)' : description;
+  testWidgets(effectiveDescription, skip: skip != null, (tester) async {

"@isTest void testWidgets( String description, WidgetTesterCallback callback, { bool? skip, test_package.Timeout? timeout" confirms skip on testWidgets is strictly bool?, so a reason string can't be passed through directly — please double check there isn't already a downstream consumer (e.g. Fastlane's retry parser) that expects the raw reason.

Also applies to: 74-79

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sample_app/integration_test/support/stream_test_case.dart` around lines 18 -
27, The streamTest helper discards the descriptive skip reason by passing only a
boolean to testWidgets. Update streamTest so when skip is non-null, the reason
is incorporated into the test description supplied to testWidgets, while
preserving the original description when no skip reason exists and continuing to
mark the test as skipped.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sample_app/fastlane/Fastfile`:
- Around line 196-227: Update the retry loop around start_screen_recording, the
videos[test_name] assignment, and failed-result attachment so each attempt
preserves its own recording file and metadata instead of overwriting the fixed
test-name path. Ensure failed Allure results are matched to the per-attempt
recording whose start/end time window contains their timestamps, including when
later retries narrow the test set or pass.
- Around line 24-38: Update start_screen_recording to pass an explicit
--time-limit value to the adb shell screenrecord command, using a duration
sufficient for the longest supported test run. Preserve the existing
Android-only behavior, device serial handling, output path, and process
metadata.

---

Nitpick comments:
In `@sample_app/integration_test/support/stream_test_case.dart`:
- Around line 18-27: The streamTest helper discards the descriptive skip reason
by passing only a boolean to testWidgets. Update streamTest so when skip is
non-null, the reason is incorporated into the test description supplied to
testWidgets, while preserving the original description when no skip reason
exists and continuing to mark the test as skipped.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 660b22e3-96f8-44af-a713-8dc8c7d8ca10

📥 Commits

Reviewing files that changed from the base of the PR and between 4dee989 and 9d776d5.

📒 Files selected for processing (19)
  • .github/workflows/e2e_test.yml
  • .github/workflows/e2e_test_cron.yml
  • sample_app/fastlane/Allurefile
  • sample_app/fastlane/Fastfile
  • sample_app/integration_test/allure/allure.dart
  • sample_app/integration_test/message_list_test.dart
  • sample_app/integration_test/mock_server/data_types.dart
  • sample_app/integration_test/pages/message_list_page.dart
  • sample_app/integration_test/reactions_test.dart
  • sample_app/integration_test/robots/participant_robot.dart
  • sample_app/integration_test/robots/user_robot.dart
  • sample_app/integration_test/robots/user_robot_message_list_asserts.dart
  • sample_app/integration_test/support/failure_artifacts.dart
  • sample_app/integration_test/support/step.dart
  • sample_app/integration_test/support/stream_test_case.dart
  • sample_app/integration_test/support/stream_test_env.dart
  • sample_app/integration_test/support/widget_test_extensions.dart
  • sample_app/lib/app.dart
  • sample_app/lib/auth/auth_controller.dart

Comment thread sample_app/fastlane/Fastfile
Comment thread sample_app/fastlane/Fastfile Outdated
/// Only the methods currently used by the ported suites are mirrored here; add
/// more from [ParticipantRobot] as new suites need to chain them.
extension ParticipantRobotChain on Future<ParticipantRobot> {
Future<ParticipantRobot> readMessage() async => (await this).readMessage();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can also use .then instead of awaiting first. Not sure if the syntax is correct.

Suggested change
Future<ParticipantRobot> readMessage() async => (await this).readMessage();
Future<ParticipantRobot> readMessage() async => .then((it)=>it.readMessage());

/// native robots (`userRobot.login().openChannel()`) instead of a block of
/// sequential `await`s. Each method mirrors the instance method above.
extension UserRobotChain on Future<UserRobot> {
Future<UserRobot> login([

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// Chainable counterparts to [UserRobotMessageListAsserts], so an assertion can
/// follow a fluent action chain (`userRobot.addReaction(x).assertReaction(...)`).
extension UserRobotMessageListAssertsChain on Future<UserRobot> {
Future<UserRobot> assertMessage(String text) async => (await this).assertMessage(text);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xsahil03x xsahil03x left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added minor comments, Otherwise LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants