Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5fd710f
test(e2e): add e2e_app, port native e2e suites, one app process per C…
testableapple Oct 23, 2026
0b8aa6d
chore(repo): ignore e2e mock server, Allure results and allurectl in …
testableapple Oct 23, 2026
b4979e4
test(repo): link every e2e test to its Allure TestOps case
testableapple Oct 23, 2026
b71afe2
Merge remote-tracking branch 'origin/master' into test/e2e-native-parity
testableapple Oct 23, 2026
b03e78e
test(repo): unmount the e2e app before resetting the client, verify c…
testableapple Oct 23, 2026
3bb8bd3
ci(repo): run PR e2e on API 36, use a preinstalled NDK, retry failed …
testableapple Oct 23, 2026
e7510b3
test(repo): retype into a field that ignored the text
testableapple Oct 23, 2026
1e75428
test(repo): keep the copied message id in the e2e app, bound the vid…
testableapple Oct 23, 2026
4b7a5fa
Merge branch 'master' into test/e2e-native-parity
testableapple Oct 23, 2026
8d9cef2
test(repo): scroll until a message is on screen, stop when the emulat…
testableapple Oct 23, 2026
0e4da80
ci(repo): give the e2e app's Gradle the sample app's memory settings
testableapple Oct 23, 2026
4a66a48
Bump timeout
testableapple Oct 24, 2026
ab90f31
test(repo): restart a dead emulator during the e2e run
testableapple Oct 24, 2026
c883599
test(repo): time-limit e2e runs so a dead emulator can't hang the job
testableapple Oct 24, 2026
87586b9
fix(repo): look up timeout through sh so the lane runs on Linux
testableapple Oct 24, 2026
d42a93a
ci(repo): split the e2e coverage nightly into 4 batches
testableapple Oct 25, 2026
8e73290
ci(repo): give the e2e runners memory headroom
testableapple Oct 25, 2026
b6acb2a
test(repo): retry the tap that opens the image gallery
testableapple Oct 25, 2026
ae9cd85
fix(ui): don't save a draft into a disposed channel
xsahil03x Oct 26, 2026
fd65291
Merge remote-tracking branch 'origin/test/e2e-native-parity' into sah…
xsahil03x Oct 26, 2026
d0d5fad
test(repo): assert the token date and signature policy (#3123)
xsahil03x Oct 27, 2026
bdf15fd
fix(ui): keep the footer of a sent message before a failed one (#3122)
xsahil03x Oct 27, 2026
dc51a77
Merge remote-tracking branch 'origin/test/e2e-native-parity' into sah…
xsahil03x Oct 27, 2026
226bce3
Merge remote-tracking branch 'origin/master' into sahil/flu-1101-the-…
xsahil03x Oct 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/stream_chat_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

🐞 Fixed

- Fixed `StreamMessageComposer` throwing when its channel is disposed while it still holds unsent text, for example when the user is disconnected with a channel page open.
- Fixed a message that failed to send, update or delete hiding the timestamp and delivery status of the message before it.
- Fixed a voice recording sent as a reply without text showing inside a filled container instead of directly on the message bubble.
- Fixed a mention rendering as another mentioned user's shorter mention followed by leftover text, such as `@Anna` next to `@Ann`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1730,6 +1730,9 @@ class DefaultStreamMessageComposerState extends State<DefaultStreamMessageCompos
final channel = StreamChannel.maybeOf(context)?.channel;
if (channel == null) return;

// A channel that isn't initialized yet or was disposed can't hold a draft.
if (channel.state == null) return;

final message = _effectiveController.message;
final isMessageValid = _effectiveController.validator.call(message);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void main() {

// Stands up a composer with the given controller and a draft stream the
// test can drive on demand.
Future<void> pumpComposer(
Future<MockChannel> pumpComposer(
WidgetTester tester, {
required StreamMessageComposerController composerController,
required Stream<Draft?> draftStream,
Expand Down Expand Up @@ -72,6 +72,8 @@ void main() {
// Let the post-frame callback fire and set up the draft stream listener
// before the test starts emitting events.
await tester.pumpAndSettle();

return channel;
}

group('draft stream', () {
Expand Down Expand Up @@ -198,4 +200,41 @@ void main() {
expect(composerController.text, isEmpty);
});
});

group('when the composer is removed with unsent text', () {
testWidgets('saves the text as a draft', (tester) async {
final composerController = StreamMessageComposerController()..text = 'unsent';
addTearDown(composerController.dispose);

final channel = await pumpComposer(
tester,
composerController: composerController,
draftStream: const Stream.empty(),
);

await tester.pumpWidget(const SizedBox());

verify(() => channel.createDraft(any())).called(1);
});

testWidgets('does not throw once the channel is disposed', (tester) async {
final composerController = StreamMessageComposerController()..text = 'unsent';
addTearDown(composerController.dispose);

final channel = await pumpComposer(
tester,
composerController: composerController,
draftStream: const Stream.empty(),
);

// A disposed channel has no state, and throws on any call that needs it.
when(() => channel.state).thenReturn(null);
when(() => channel.createDraft(any())).thenThrow(StateError('disposed'));

await tester.pumpWidget(const SizedBox());

expect(tester.takeException(), isNull);
verifyNever(() => channel.createDraft(any()));
});
});
}
Loading