Skip to content

Commit a63f59f

Browse files
committed
Add tests for poll events
1 parent ab737f6 commit a63f59f

File tree

3 files changed

+244
-47
lines changed

3 files changed

+244
-47
lines changed

packages/stream_feeds/test/mocks.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class MockDefaultApi extends Mock implements api.DefaultApi {}
1616

1717
class MockWebSocketChannel extends Mock implements WebSocketChannel {}
1818

19+
class MockWebSocketSink extends Mock implements WebSocketSink {}
20+
1921
class FakeFeedsClient extends Fake implements StreamFeedsClientImpl {
2022
FakeFeedsClient({
2123
User? user,
Lines changed: 170 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
// ignore_for_file: avoid_redundant_argument_values
22

3+
import 'dart:async';
4+
import 'dart:convert';
5+
36
import 'package:mocktail/mocktail.dart';
47
import 'package:stream_feeds/src/client/feeds_client_impl.dart';
8+
import 'package:stream_feeds/src/state/activity_state.dart';
59
import 'package:stream_feeds/stream_feeds.dart';
610
import 'package:test/test.dart';
711

812
import '../mocks.dart';
13+
import '../ws_test_helpers.dart';
914

1015
void main() {
1116
late StreamFeedsClientImpl client;
@@ -18,7 +23,7 @@ void main() {
1823

1924
client = StreamFeedsClientImpl(
2025
apiKey: 'apiKey',
21-
user: const User(id: 'userId'),
26+
user: const User(id: 'luke_skywalker'),
2227
tokenProvider: TokenProvider.static(UserToken(testToken)),
2328
feedsRestApi: feedsApi,
2429
wsProvider: (options) => webSocketChannel,
@@ -33,7 +38,7 @@ void main() {
3338
test('fetch activity and comments', () async {
3439
const activityId = 'id';
3540
when(() => feedsApi.getActivity(id: activityId)).thenAnswer(
36-
(_) async => Result.success(defaultActivityResponse),
41+
(_) async => Result.success(createDefaultActivityResponse()),
3742
);
3843
when(
3944
() => feedsApi.getComments(
@@ -62,6 +67,91 @@ void main() {
6267
expect(result.getOrNull()?.id, 'id');
6368
});
6469
});
70+
71+
group('Poll events', () {
72+
late StreamController<Object> wsStreamController;
73+
late MockWebSocketSink webSocketSink;
74+
75+
setUp(() async {
76+
wsStreamController = StreamController<Object>();
77+
webSocketSink = MockWebSocketSink();
78+
WsTestConnection(
79+
wsStreamController: wsStreamController,
80+
webSocketSink: webSocketSink,
81+
webSocketChannel: webSocketChannel,
82+
).setUp();
83+
84+
await client.connect();
85+
});
86+
87+
tearDown(() async {
88+
await webSocketSink.close();
89+
await wsStreamController.close();
90+
});
91+
92+
void setupMockActivity({GetActivityResponse? activity}) {
93+
const activityId = 'id';
94+
when(() => feedsApi.getActivity(id: activityId)).thenAnswer(
95+
(_) async =>
96+
Result.success(activity ?? createDefaultActivityResponse()),
97+
);
98+
when(
99+
() => feedsApi.getComments(
100+
objectId: activityId,
101+
objectType: 'activity',
102+
depth: 3,
103+
),
104+
).thenAnswer(
105+
(_) async => const Result.success(defaultCommentsResponse),
106+
);
107+
}
108+
109+
test('poll vote casted', () async {
110+
final poll = createDefaultPollResponseData();
111+
final pollId = poll.id;
112+
final firstOptionId = poll.options.first.id;
113+
114+
setupMockActivity(
115+
activity: createDefaultActivityResponse(poll: poll),
116+
);
117+
118+
final activity = client.activity(
119+
activityId: 'id',
120+
fid: const FeedId(group: 'group', id: 'id'),
121+
);
122+
await activity.get();
123+
124+
expect(poll.voteCount, 0);
125+
126+
activity.notifier.stream.listen(
127+
expectAsync1(
128+
(event) {
129+
expect(event, isA<ActivityState>());
130+
expect(event.poll?.id, 'id');
131+
expect(event.poll?.voteCount, 1);
132+
},
133+
),
134+
);
135+
wsStreamController.add(
136+
jsonEncode(
137+
PollVoteCastedFeedEvent(
138+
createdAt: DateTime.now(),
139+
custom: {},
140+
fid: 'fid',
141+
poll: poll.copyWith(voteCount: 1),
142+
pollVote: PollVoteResponseData(
143+
createdAt: DateTime.now(),
144+
updatedAt: DateTime.now(),
145+
id: 'voteId1',
146+
optionId: firstOptionId,
147+
pollId: pollId,
148+
),
149+
type: 'feeds.poll.vote_casted',
150+
).toJson(),
151+
),
152+
);
153+
});
154+
});
65155
}
66156

67157
const defaultCommentsResponse = GetCommentsResponse(
@@ -71,50 +161,83 @@ const defaultCommentsResponse = GetCommentsResponse(
71161
duration: 'duration',
72162
);
73163

74-
final defaultActivityResponse = GetActivityResponse(
75-
activity: ActivityResponse(
76-
id: 'id',
77-
attachments: const [],
78-
bookmarkCount: 0,
79-
commentCount: 0,
80-
comments: const [],
81-
createdAt: DateTime(2021, 1, 1),
82-
custom: const {},
83-
feeds: const [],
84-
filterTags: const [],
85-
interestTags: const [],
86-
latestReactions: const [],
87-
mentionedUsers: const [],
88-
moderation: null,
89-
notificationContext: null,
90-
ownBookmarks: const [],
91-
ownReactions: const [],
92-
parent: null,
93-
poll: null,
94-
popularity: 0,
95-
reactionCount: 0,
96-
reactionGroups: const {},
97-
score: 0,
98-
searchData: const {},
99-
shareCount: 0,
100-
text: null,
101-
type: 'type',
102-
updatedAt: DateTime(2021, 2, 1),
103-
user: UserResponse(
164+
GetActivityResponse createDefaultActivityResponse({PollResponseData? poll}) =>
165+
GetActivityResponse(
166+
activity: ActivityResponse(
167+
id: 'id',
168+
attachments: const [],
169+
bookmarkCount: 0,
170+
commentCount: 0,
171+
comments: const [],
172+
createdAt: DateTime(2021, 1, 1),
173+
custom: const {},
174+
feeds: const [],
175+
filterTags: const [],
176+
interestTags: const [],
177+
latestReactions: const [],
178+
mentionedUsers: const [],
179+
moderation: null,
180+
notificationContext: null,
181+
ownBookmarks: const [],
182+
ownReactions: const [],
183+
parent: null,
184+
poll: poll,
185+
popularity: 0,
186+
reactionCount: 0,
187+
reactionGroups: const {},
188+
score: 0,
189+
searchData: const {},
190+
shareCount: 0,
191+
text: null,
192+
type: 'type',
193+
updatedAt: DateTime(2021, 2, 1),
194+
user: UserResponse(
195+
id: 'id',
196+
name: 'name',
197+
banned: false,
198+
blockedUserIds: const [],
199+
createdAt: DateTime(2021, 1, 1),
200+
custom: const {},
201+
language: 'language',
202+
online: false,
203+
role: 'role',
204+
teams: const [],
205+
updatedAt: DateTime(2021, 2, 1),
206+
),
207+
visibility: ActivityResponseVisibility.public,
208+
visibilityTag: null,
209+
),
210+
duration: 'duration',
211+
);
212+
213+
PollResponseData createDefaultPollResponseData() => PollResponseData(
104214
id: 'id',
105215
name: 'name',
106-
banned: false,
107-
blockedUserIds: const [],
108-
createdAt: DateTime(2021, 1, 1),
109-
custom: const {},
110-
language: 'language',
111-
online: false,
112-
role: 'role',
113-
teams: const [],
114-
updatedAt: DateTime(2021, 2, 1),
115-
),
116-
visibility: ActivityResponseVisibility.public,
117-
visibilityTag: null,
118-
),
119-
duration: 'duration',
120-
);
216+
allowAnswers: true,
217+
allowUserSuggestedOptions: true,
218+
answersCount: 0,
219+
createdAt: DateTime.now(),
220+
createdById: 'id',
221+
custom: {},
222+
description: 'description',
223+
enforceUniqueVote: true,
224+
latestAnswers: const [],
225+
latestVotesByOption: {},
226+
ownVotes: const [],
227+
updatedAt: DateTime.now(),
228+
voteCount: 0,
229+
voteCountsByOption: {},
230+
votingVisibility: 'visibility',
231+
options: const [
232+
PollOptionResponseData(
233+
id: 'id1',
234+
text: 'text1',
235+
custom: {},
236+
),
237+
PollOptionResponseData(
238+
id: 'id2',
239+
text: 'text2',
240+
custom: {},
241+
),
242+
],
243+
);
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import 'dart:async';
2+
import 'dart:convert';
3+
4+
import 'package:mocktail/mocktail.dart';
5+
import 'package:stream_feeds/src/ws/events/events.dart';
6+
import 'package:stream_feeds/stream_feeds.dart';
7+
8+
import 'mocks.dart';
9+
10+
class WsTestConnection {
11+
WsTestConnection({
12+
required this.wsStreamController,
13+
required this.webSocketSink,
14+
required this.webSocketChannel,
15+
});
16+
17+
StreamController<Object> wsStreamController;
18+
MockWebSocketSink webSocketSink;
19+
MockWebSocketChannel webSocketChannel;
20+
21+
void setUp() {
22+
final wsAuthenticationHelper = WsAuthenticationHelper(
23+
wsStreamController: wsStreamController,
24+
);
25+
26+
when(() => webSocketSink.add(any<Object>())).thenAnswer(
27+
wsAuthenticationHelper.handleAuthentication,
28+
);
29+
30+
when(() => webSocketChannel.ready).thenAnswer(
31+
(_) => Future.value(),
32+
);
33+
when(() => webSocketChannel.stream).thenAnswer(
34+
(_) => wsStreamController.stream,
35+
);
36+
37+
when(() => webSocketChannel.sink).thenAnswer(
38+
(_) => webSocketSink,
39+
);
40+
}
41+
}
42+
43+
class WsAuthenticationHelper {
44+
WsAuthenticationHelper({
45+
required this.wsStreamController,
46+
});
47+
48+
StreamController<Object> wsStreamController;
49+
50+
/// Returns true if the event is a [WsAuthMessageRequest].
51+
/// If it is, it will add a [HealthCheckEvent] to the stream.
52+
bool handleAuthentication(Invocation invocation) {
53+
final Map<String, dynamic> event =
54+
jsonDecode(invocation.positionalArguments.first as String);
55+
56+
if (event['token'] != null) {
57+
wsStreamController.add(
58+
jsonEncode(
59+
HealthCheckEvent(
60+
connectionId: 'connectionId',
61+
createdAt: DateTime.now(),
62+
custom: const {},
63+
type: 'health.check',
64+
).toJson(),
65+
),
66+
);
67+
return true;
68+
}
69+
70+
return false;
71+
}
72+
}

0 commit comments

Comments
 (0)