Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions packages/stream_chat_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
- Fixed `StreamMessageComposer`'s send button looking enabled on an empty message when voice recording is off, and showing the microphone instead of send for a staged poll.
- Fixed the attachment picker showing an empty commands tab in channels without commands.
- Fixed dialogs and bottom sheets opened below `StreamChat` keeping the old colors when the app switched between light and dark mode.
- Fixed `StreamPollOptionVotesSheet` listing votes for other options and answers.
- Fixed `StreamPollCommentsSheet` listing votes for poll options as comments.

## 10.5.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,9 @@ class _StreamPollCommentsSheetState extends State<StreamPollCommentsSheet> {
}

void _initializeController() {
_controller = StreamPollVoteListController(
_controller = StreamPollVoteListController.forAnswers(
pollId: widget.poll.id,
channel: StreamChannel.of(context).channel,
filter: Filter.equal('is_answer', true),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ class _StreamPollOptionVotesSheetState extends State<StreamPollOptionVotesSheet>
}

void _initializeController() {
_controller = StreamPollVoteListController(
_controller = StreamPollVoteListController.forOption(
pollId: widget.poll.id,
channel: StreamChannel.of(context).channel,
filter: Filter.equal('option_id', widget.option.id!),
optionId: widget.option.id!,
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';

import '../mocks.dart';

const _pollId = 'poll-a';
const _pizza = PollOption(id: 'option-pizza', text: 'Pizza');

void main() {
setUpAll(() {
registerFallbackValue(const PaginationParams());
});

testWidgets('does not list a vote cast for an option', (tester) async {
final ben = User(id: 'ben', name: 'Ben');
final emit = await _pumpCommentsSheet(tester);

emit(
Event(
type: EventType.pollVoteCasted,
pollVote: PollVote(id: 'vote-ben', pollId: _pollId, optionId: _pizza.id, user: ben),
),
);
await tester.pumpAndSettle();

expect(find.text('Ben'), findsNothing);
});

testWidgets('lists an answer cast for the poll', (tester) async {
final ben = User(id: 'ben', name: 'Ben');
final emit = await _pumpCommentsSheet(tester);

emit(
Event(
type: EventType.pollAnswerCasted,
pollVote: PollVote(id: 'answer-ben', pollId: _pollId, answerText: 'Pasta!', user: ben),
),
);
await tester.pumpAndSettle();

expect(find.text('Pasta!'), findsOneWidget);
});
}

// Pumps the comments sheet with no comments loaded, and returns a function that emits a channel event to the sheet.
Future<void Function(Event event)> _pumpCommentsSheet(WidgetTester tester) async {
final events = StreamController<Event>.broadcast();
addTearDown(events.close);

final client = MockClient();
final clientState = MockClientState();
final channel = MockChannel(eventStream: events.stream);
final channelState = MockChannelState();

when(() => client.state).thenReturn(clientState);
when(() => clientState.currentUser).thenReturn(OwnUser(id: 'salvatore'));
when(() => clientState.currentUserStream).thenAnswer((_) => Stream.value(OwnUser(id: 'salvatore')));
when(() => channel.client).thenReturn(client);
when(() => channel.state).thenReturn(channelState);
when(
() => channel.queryPollVotes(
_pollId,
sort: any(named: 'sort'),
filter: any(named: 'filter'),
pagination: any(named: 'pagination'),
),
).thenAnswer(
(_) async => QueryPollVotesResponse()
..votes = []
..next = null,
);

await tester.pumpWidget(
MaterialApp(
home: StreamChat(
client: client,
child: StreamChannel.value(
channel: channel,
child: Scaffold(
body: StreamPollCommentsSheet(
poll: Poll(id: _pollId, name: 'Lunch: pizza or sushi?', options: const [_pizza]),
),
),
),
),
),
);
await tester.pumpAndSettle();

return events.add;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';

import '../mocks.dart';

const _pollId = 'poll-a';
const _pizza = PollOption(id: 'option-pizza', text: 'Pizza');
const _sushi = PollOption(id: 'option-sushi', text: 'Sushi');

void main() {
setUpAll(() {
registerFallbackValue(const PaginationParams());
});

testWidgets('does not list a vote cast for another option', (tester) async {
final ben = User(id: 'ben', name: 'Ben');
final emit = await _pumpOptionVotesSheet(tester, option: _pizza);

emit(Event(type: EventType.pollVoteCasted, pollVote: _voteBy(ben, _sushi)));
await tester.pumpAndSettle();

expect(find.text('Ben'), findsNothing);
});

testWidgets('stops listing a vote changed to another option', (tester) async {
final pizzaVote = _voteBy(User(id: 'ben', name: 'Ben'), _pizza);
final emit = await _pumpOptionVotesSheet(tester, option: _pizza, votes: [pizzaVote]);
expect(find.text('Ben'), findsOneWidget);

emit(
Event(
type: EventType.pollVoteChanged,
pollVote: pizzaVote.copyWith(optionId: _sushi.id),
),
);
await tester.pumpAndSettle();

expect(find.text('Ben'), findsNothing);
});
}

PollVote _voteBy(User user, PollOption option) {
return PollVote(id: 'vote-${user.id}', pollId: _pollId, optionId: option.id, user: user);
}

// Pumps the votes sheet for [option] with [votes] already loaded, and returns a function that emits a channel
// event to the sheet.
Future<void Function(Event event)> _pumpOptionVotesSheet(
WidgetTester tester, {
required PollOption option,
List<PollVote> votes = const [],
}) async {
final events = StreamController<Event>.broadcast();
addTearDown(events.close);

final client = MockClient();
final clientState = MockClientState();
final channel = MockChannel(eventStream: events.stream);
final channelState = MockChannelState();

when(() => client.state).thenReturn(clientState);
when(() => clientState.currentUser).thenReturn(OwnUser(id: 'salvatore'));
when(() => clientState.currentUserStream).thenAnswer((_) => Stream.value(OwnUser(id: 'salvatore')));
when(() => channel.client).thenReturn(client);
when(() => channel.state).thenReturn(channelState);
when(
() => channel.queryPollVotes(
_pollId,
sort: any(named: 'sort'),
filter: any(named: 'filter'),
pagination: any(named: 'pagination'),
),
).thenAnswer(
(_) async => QueryPollVotesResponse()
..votes = votes
..next = null,
);

await tester.pumpWidget(
MaterialApp(
home: StreamChat(
client: client,
child: StreamChannel.value(
channel: channel,
child: Scaffold(
body: StreamPollOptionVotesSheet(
poll: Poll(id: _pollId, name: 'Lunch: pizza or sushi?', options: const [_pizza, _sushi]),
option: option,
),
),
),
),
),
);
await tester.pumpAndSettle();

return events.add;
}
1 change: 1 addition & 0 deletions packages/stream_chat_flutter_core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
✅ Added

- Added `StreamMessageComposerController.validator` and `canSend`, which decide whether the message being composed can be sent, and `StreamMessageComposerController.defaultValidator`.
- Added `StreamPollVoteListController.forOption` and `StreamPollVoteListController.forAnswers`, which list only a poll's votes for one option or only its answers.

🔄 Changed

Expand Down
Loading
Loading