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 @@ -3,6 +3,8 @@
🐞 Fixed

- Fixed `StreamMessageInput` polling the audio recorder ten times a second while no recording is in progress, including when voice recording is disabled.
- Fixed `StreamPollOptionVotesDialog` listing votes for other options and answers.
- Fixed `StreamPollCommentsDialog` listing votes for poll options as comments.

## 9.31.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,9 @@ class _StreamPollCommentsDialogState extends State<StreamPollCommentsDialog> {
}

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 @@ -91,10 +91,10 @@ class _StreamPollOptionVotesDialogState
}

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,104 @@
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 _pumpCommentsDialog(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 _pumpCommentsDialog(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 dialog with no comments loaded, and returns a function
// that emits a channel event to the dialog.
Future<void Function(Event event)> _pumpCommentsDialog(
WidgetTester tester) async {
final events = StreamController<Event>.broadcast();
addTearDown(events.close);

final client = MockClient();
final clientState = MockClientState();
final channel = MockChannel();
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.on).thenAnswer((_) => events.stream);
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: StreamPollCommentsDialog(
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,110 @@
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 _pumpOptionVotesDialog(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 _pumpOptionVotesDialog(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 dialog for [option] with [votes] already loaded, and returns
// a function that emits a channel event to the dialog.
Future<void Function(Event event)> _pumpOptionVotesDialog(
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();
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.on).thenAnswer((_) => events.stream);
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: StreamPollOptionVotesDialog(
poll: Poll(
id: _pollId,
name: 'Lunch: pizza or sushi?',
options: const [_pizza, _sushi]),
option: option,
pollVotesCount: null,
),
),
),
),
),
);
await tester.pumpAndSettle();

return events.add;
}
4 changes: 4 additions & 0 deletions packages/stream_chat_flutter_core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## Upcoming

✅ Added

- Added `StreamPollVoteListController.forOption` and `StreamPollVoteListController.forAnswers`, which list only a poll's votes for one option or only its answers.

🐞 Fixed

- Fixed `StreamDraftListController.deleteDraft` removing the wrong draft.
Expand Down
Loading
Loading