Skip to content

fix(llc, ui): Issues found by the e2e suites - #2874

Open
testableapple wants to merge 6 commits into
masterfrom
fix/e2e-tests-related-issues
Open

fix(llc, ui): Issues found by the e2e suites#2874
testableapple wants to merge 6 commits into
masterfrom
fix/e2e-tests-related-issues

Conversation

@testableapple

@testableapple testableapple commented Aug 7, 2026

Copy link
Copy Markdown
Member

🎯 Goal

Split out of #2869 — these are the SDK fixes the e2e suites uncovered, with their unit tests and changelog entries. #2869 keeps only the e2e suites and CI.

Based on master and reviewable on its own. #2869 picks these up by merging master once this lands; until then the e2e tests covering them fail there by design.

🛠 Implementation details

🟢 FLU-668ChannelClientState.updateThreadInfo no longer merges the parent message into a thread's reply list. A backend that returns the root alongside the replies made StreamMessageListView render it twice, since it also renders parentMessage in its own slot. This aligns the online path with the offline one, which already filtered on parent_id.

🟢 FLU-669 — the thread-replies footer under a message in the channel hardcoded '$replyCount replies', so a single reply read "1 replies" and the label was never localized. It now uses threadReplyCountText, which is translated in 11 locales and is what the two sibling call sites already used.

🟢 FLU-685 — after a channel was truncated, its row in the channel list still showed a timestamp next to "No messages yet". The tile read its subtitle from channelState.messages but its timestamp from channel.lastMessageAt, which truncate() never clears — and clearing it locally would not have held, since onChannelTruncated re-queries and the server keeps the field. ChannelLastMessageDate now derives the date from the message the preview actually shows, through a _LastMessageResolver mixin shared by all three preview widgets so they cannot drift apart again.

🟢 FLU-686 — surfaced while reviewing the fix above, not by a test: a channel-list row could preview another channel's last message. Rows are unkeyed, so a reorder rebinds the same State to a different channel, and the preserved last-known message that #2774 added (to keep the preview alive across a channel-state reload) was still used as a fallback — so an empty, not-yet-up-to-date channel showed the previous one's message. The resolver now drops the cache when it is rebound to a different ChannelClientState. Shipped in 10.2.0, so it is fixed here rather than introduced here.

🧪 Testing

Unit tests moved across with the fixes:

  • channel_test.dart.getReplies keeps the parent message out of the thread. 383 tests green in stream_chat.
  • stream_message_item_test.dart (new) — the thread-reply label is read from an injected Translations returning sentinels, so both the singular and the plural case prove the widget goes through the translations table. A hardcoded label passes a plural-only assertion, which is why the default-English variant of this test was not enough. A separate plain test() pins DefaultTranslations' own pluralization.
  • stream_channel_list_item_test.dartChannelLastMessageDate follows the previewed message (not lastMessageAt) and renders nothing when there is none; plus the rebind regression for FLU-686, which only catches the bug because the replacement channel is stubbed isUpToDate == false.

21 tests green in the two stream_chat_flutter files. Each fix was mutation-checked — reverting the production line fails the matching test.

The e2e tests that cover these live in #2869 and fail there until this lands and master is merged in; that is expected and called out in that PR. Nothing here depends on #2869.

☑️ Contributor Checklist

  • Changelog entries added (stream_chat, stream_chat_flutter)
  • Unit tests added
  • dart analyze --fatal-infos and dart format clean

Summary by CodeRabbit

  • Bug Fixes
    • Prevented thread parent messages from appearing twice when loading replies.
    • Localized and pluralized thread-reply counts.
    • Fixed stale channel previews and incorrect timestamps after channel updates or truncation.
    • Ensured dates reflect the latest previewable message and are cleared when no valid message exists.

@coderabbitai

coderabbitai Bot commented Aug 7, 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 Plus

Run ID: 04ebdd8a-fd6d-4705-976f-4998d1ae4e4f

📥 Commits

Reviewing files that changed from the base of the PR and between 02d97a7 and b683cf2.

📒 Files selected for processing (1)
  • packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_item.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_item.dart

📝 Walkthrough

Walkthrough

The PR filters parent messages from online thread replies, localizes thread reply labels, and aligns channel-list dates and text with the resolved preview message.

Changes

Thread reply state

Layer / File(s) Summary
Filter parent messages
packages/stream_chat/lib/src/client/channel.dart, packages/stream_chat/test/src/client/channel_test.dart, packages/stream_chat/CHANGELOG.md
updateThreadInfo excludes the thread parent from stored replies. Regression tests verify that actual replies remain. The changelog records the fix.

Localized reply labels

Layer / File(s) Summary
Render localized reply counts
packages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dart, packages/stream_chat_flutter/test/src/message_widget/stream_message_item_test.dart
Thread reply indicators use localized singular and plural translations. Tests cover translated labels, zero replies, and default translations.

Channel-list preview rendering

Layer / File(s) Summary
Resolve channel preview messages
packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_item.dart, packages/stream_chat_flutter/test/src/scroll_view/channel_scroll_view/stream_channel_list_item_test.dart, packages/stream_chat_flutter/CHANGELOG.md
Date, subtitle, and text widgets share _LastMessageResolver. The resolver filters invalid messages, handles incomplete channel state, and clears cached data after channel rebinding. Tests cover preview dates, filtering, empty states, and rebinding. The changelog records the fixes.

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

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 identifies the pull request as fixes for issues found by the end-to-end test suites.
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 fix/e2e-tests-related-issues

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.

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.24%. Comparing base (4b4c2ae) to head (b683cf2).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2874      +/-   ##
==========================================
+ Coverage   73.18%   73.24%   +0.06%     
==========================================
  Files         429      429              
  Lines       27724    27735      +11     
==========================================
+ Hits        20289    20314      +25     
+ Misses       7435     7421      -14     

☔ 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.

@testableapple
testableapple changed the base branch from ci/e2e-tests to master August 7, 2026 16:21
@testableapple
testableapple changed the base branch from master to ci/e2e-tests August 7, 2026 16:25
A backend that returns the root alongside the replies made
ChannelClientState.threads hold it too, so StreamMessageListView
rendered it twice. Aligns the online path with the offline one, which
already filtered on parent_id.

Linear: FLU-668
The footer hardcoded '$replyCount replies', so a single reply read
"1 replies" and the label was never translated. It now uses
threadReplyCountText, like the two sibling call sites.

Covered by widget tests that inject a translation returning sentinels,
so both the singular and the plural case prove the label goes through
the translations table.

Linear: FLU-669
The tile read its subtitle from channelState.messages but its timestamp
from channel.lastMessageAt, which truncate() never clears, so a
truncated channel showed a date next to "No messages yet".
ChannelLastMessageDate now derives the date from the message the preview
actually shows, via a _LastMessageResolver mixin shared by all three
preview widgets so they cannot drift apart again.

Linear: FLU-685
Channel-list rows are unkeyed, so a reorder rebinds the same State to a
different channel. The preserved last-known message (added in #2774 to
keep the preview alive across a channel-state reload) was still used as
a fallback, so an empty, not-yet-up-to-date channel previewed the
previous channel's message. The resolver now drops the cache when it
sees a different ChannelClientState.

Linear: FLU-686
@testableapple
testableapple force-pushed the fix/e2e-tests-related-issues branch from 6ee0e3b to f6e1faa Compare August 7, 2026 16:31
@testableapple
testableapple changed the base branch from ci/e2e-tests to master August 7, 2026 16:31

@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: 4

🧹 Nitpick comments (1)
packages/stream_chat/test/src/client/channel_test.dart (1)

4121-4124: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert every retained reply.

The current assertions check only the count and parent absence. They pass if one reply is dropped and another reply is duplicated. Assert the exact reply ID set to prove that filtering removes only the parent.

Proposed test assertion
       expect(threadMessages, isNotNull);
       expect(threadMessages!.length, messages.length - 1);
-      expect(threadMessages.any((it) => it.id == parentId), isFalse);
+      final replyIds = threadMessages.map((it) => it.id).toSet();
+      expect(replyIds, hasLength(3));
+      expect(
+        replyIds.containsAll([
+          'test-message-id-0',
+          'test-message-id-1',
+          'test-message-id-2',
+        ]),
+        isTrue,
+      );
🤖 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 `@packages/stream_chat/test/src/client/channel_test.dart` around lines 4121 -
4124, Strengthen the assertions in the threadMessages test by verifying the
exact set of retained reply IDs, not just the count and parent exclusion. Derive
the expected IDs from messages after removing parentId, then compare them with
the IDs in threadMessages while preserving the existing parent-absence check.
🤖 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
`@packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_item.dart`:
- Around line 493-499: Update the documentation for ChannelLastMessageDate to
state that it uses the latest previewable sent message, excluding drafts, and
renders no date when the preview is a draft or no eligible message exists;
remove the claim that it always reads the same message as the preview subtitle.
- Around line 805-806: Validate the cached _currentLastMessage against the
active predicate before using it in the !channelState.isUpToDate fallback within
the surrounding message-selection logic. Clear or exclude the cached value when
it fails predicate, while preserving the latest-message selection for valid
cached messages; add a widget test covering a predicate change while isUpToDate
remains false.

In
`@packages/stream_chat_flutter/test/src/message_widget/stream_message_item_test.dart`:
- Around line 99-100: Update the test assertions near the existing
singular/plural checks to also verify that
DefaultTranslations.instance.threadReplyCountText(0) is not rendered. Keep the
fake translation absence assertions, and specifically assert findsNothing for
the default zero-count reply label.

In `@packages/stream_chat/lib/src/client/channel.dart`:
- Around line 3927-3931: Update the thread merge flow around
_mergeMessagesIntoExisting to remove any cached parent message from
threadMessages before merging. Continue filtering parentId from incoming
messages, and preserve all non-parent cached and incoming thread messages.

---

Nitpick comments:
In `@packages/stream_chat/test/src/client/channel_test.dart`:
- Around line 4121-4124: Strengthen the assertions in the threadMessages test by
verifying the exact set of retained reply IDs, not just the count and parent
exclusion. Derive the expected IDs from messages after removing parentId, then
compare them with the IDs in threadMessages while preserving the existing
parent-absence check.
🪄 Autofix

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 Plus

Run ID: 5be7224a-e90d-4eb5-85ff-7ec457170cc8

📥 Commits

Reviewing files that changed from the base of the PR and between 4b4c2ae and f6e1faa.

📒 Files selected for processing (8)
  • packages/stream_chat/CHANGELOG.md
  • packages/stream_chat/lib/src/client/channel.dart
  • packages/stream_chat/test/src/client/channel_test.dart
  • packages/stream_chat_flutter/CHANGELOG.md
  • packages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dart
  • packages/stream_chat_flutter/lib/src/scroll_view/channel_scroll_view/stream_channel_list_item.dart
  • packages/stream_chat_flutter/test/src/message_widget/stream_message_item_test.dart
  • packages/stream_chat_flutter/test/src/scroll_view/channel_scroll_view/stream_channel_list_item_test.dart

Comment thread packages/stream_chat/lib/src/client/channel.dart
- Drop the cached preview message when the active predicate stops
  accepting it, so a changed filter cannot resurrect it as the
  not-up-to-date fallback.
- Correct the ChannelLastMessageDate doc: it dates the latest sent
  message and ignores drafts, so it does not always match the subtitle.
- Assert the default "0 replies" label is absent in the no-replies test.
The summary still said it dates "the message shown in the channel
preview", which is the claim the body paragraph was corrected for: with
a draft, the preview shows the draft and this dates the last sent
message.
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.

1 participant