1
+ import 'dart:convert' ;
2
+
1
3
import 'package:checks/checks.dart' ;
4
+ import 'package:flutter/material.dart' ;
2
5
import 'package:flutter_test/flutter_test.dart' ;
6
+ import 'package:http/http.dart' as http;
7
+ import 'package:zulip/api/model/events.dart' ;
3
8
import 'package:zulip/api/model/model.dart' ;
9
+ import 'package:zulip/api/route/channels.dart' ;
10
+ import 'package:zulip/model/localizations.dart' ;
11
+ import 'package:zulip/model/store.dart' ;
4
12
import 'package:zulip/widgets/channel_list.dart' ;
13
+ import 'package:zulip/widgets/icons.dart' ;
5
14
15
+ import '../api/fake_api.dart' ;
6
16
import '../model/binding.dart' ;
7
17
import '../example_data.dart' as eg;
18
+ import '../stdlib_checks.dart' ;
8
19
import 'test_app.dart' ;
9
20
10
21
void main () {
11
22
TestZulipBinding .ensureInitialized ();
23
+ late FakeApiConnection connection;
24
+ late PerAccountStore store;
12
25
13
26
Future <void > setupChannelListPage (WidgetTester tester, {
14
27
required List <ZulipStream > streams,
@@ -18,8 +31,10 @@ void main() {
18
31
final initialSnapshot = eg.initialSnapshot (
19
32
subscriptions: subscriptions,
20
33
streams: streams,
21
- );
34
+ realmUsers : [eg.selfUser] );
22
35
await testBinding.globalStore.add (eg.selfAccount, initialSnapshot);
36
+ store = await testBinding.globalStore.perAccount (eg.selfAccount.id);
37
+ connection = store.connection as FakeApiConnection ;
23
38
24
39
await tester.pumpWidget (TestZulipApp (accountId: eg.selfAccount.id, child: const ChannelListPage ()));
25
40
@@ -67,4 +82,159 @@ void main() {
67
82
check (listedStreamNames (tester)).deepEquals (['a' , 'b' , 'c' ]);
68
83
});
69
84
});
85
+
86
+ group ('subscription toggle' , () {
87
+ final zulipLocalizations = GlobalLocalizations .zulipLocalizations;
88
+
89
+ Future <ZulipStream > prepareSingleStream (WidgetTester tester) async {
90
+ final stream = eg.stream ();
91
+ await setupChannelListPage (tester, streams: [stream], subscriptions: []);
92
+ return stream;
93
+ }
94
+
95
+ Future <void > tapSubscribeButton (WidgetTester tester) async {
96
+ await tester.tap (find.byIcon (Icons .add));
97
+ }
98
+
99
+ Future <void > waitAndCheckSnackbarIsShown (WidgetTester tester, String message, String channelName) async {
100
+ await tester.pump (Duration .zero);
101
+ await tester.pumpAndSettle ();
102
+ final richTextFinder = find.byWidgetPredicate (
103
+ (widget) => widget is RichText && widget.text.toPlainText ().contains (message));
104
+ check (richTextFinder.evaluate ()).single;
105
+ final richTextWidget = tester.widget <RichText >(richTextFinder);
106
+ check (richTextWidget.text.toPlainText ()).contains (message);
107
+ check (richTextWidget.text.toPlainText ()).contains (channelName);
108
+ check (find.descendant (
109
+ of: richTextFinder,
110
+ matching: find.byIcon (ZulipIcons .hash_sign),
111
+ ).evaluate ()).single;
112
+ }
113
+
114
+ testWidgets ('is affected by subscription events' , (WidgetTester tester) async {
115
+ final stream = await prepareSingleStream (tester);
116
+ connection.prepare (json: SubscribeToChannelsResult (
117
+ subscribed: {eg.selfUser.email: [stream.name]},
118
+ alreadySubscribed: {}).toJson ());
119
+
120
+ check (find.byIcon (Icons .add).evaluate ()).isNotEmpty ();
121
+
122
+ await store.handleEvent (SubscriptionAddEvent (id: 1 ,
123
+ subscriptions: [eg.subscription (stream)]));
124
+ await tester.pumpAndSettle ();
125
+
126
+ check (find.byIcon (Icons .add).evaluate ()).isEmpty ();
127
+
128
+ await store.handleEvent (SubscriptionRemoveEvent (id: 2 , streamIds: [stream.streamId]));
129
+ await tester.pumpAndSettle ();
130
+
131
+ check (find.byIcon (Icons .add).evaluate ()).isNotEmpty ();
132
+ });
133
+
134
+ testWidgets ('is disabled while loading' , (WidgetTester tester) async {
135
+ final stream = eg.stream ();
136
+ await setupChannelListPage (tester, streams: [stream], subscriptions: []);
137
+ connection.prepare (json: SubscribeToChannelsResult (
138
+ subscribed: {eg.selfUser.email: [stream.name]},
139
+ alreadySubscribed: {}).toJson ());
140
+ await tapSubscribeButton (tester);
141
+ await tester.pump ();
142
+
143
+ check (tester.widget <IconButton >(
144
+ find.byType (IconButton )).onPressed).isNull ();
145
+
146
+ await tester.pump (const Duration (seconds: 2 ));
147
+
148
+ check (tester.widget <IconButton >(
149
+ find.byType (IconButton )).onPressed).isNotNull ();
150
+ });
151
+
152
+ testWidgets ('is disabled while loading and enabled back when loading fails' , (WidgetTester tester) async {
153
+ final stream = eg.stream ();
154
+ await setupChannelListPage (tester, streams: [stream], subscriptions: []);
155
+ connection.prepare (exception: http.ClientException ('Oops' ), delay: const Duration (seconds: 2 ));
156
+ await tapSubscribeButton (tester);
157
+ await tester.pump ();
158
+
159
+ check (tester.widget <IconButton >(
160
+ find.byType (IconButton )).onPressed).isNull ();
161
+
162
+ await tester.pump (const Duration (seconds: 2 ));
163
+
164
+ check (tester.widget <IconButton >(
165
+ find.byType (IconButton )).onPressed).isNotNull ();
166
+ });
167
+
168
+ group ('subscribe' , () {
169
+ testWidgets ('is shown only for streams that user is not subscribed to' , (tester) async {
170
+ final streams = [eg.stream (), eg.stream (), eg.subscription (eg.stream ())];
171
+ final subscriptions = [streams[2 ] as Subscription ];
172
+ await setupChannelListPage (tester, streams: streams, subscriptions: subscriptions);
173
+
174
+ check (find.byIcon (Icons .add).evaluate ().length).equals (2 );
175
+ });
176
+
177
+ testWidgets ('smoke api' , (tester) async {
178
+ final stream = await prepareSingleStream (tester);
179
+ connection.prepare (json: SubscribeToChannelsResult (
180
+ subscribed: {eg.selfUser.email: [stream.name]},
181
+ alreadySubscribed: {}).toJson ());
182
+ await tapSubscribeButton (tester);
183
+
184
+ await tester.pump (Duration .zero);
185
+ await tester.pumpAndSettle ();
186
+ check (connection.lastRequest).isA< http.Request > ()
187
+ ..method.equals ('POST' )
188
+ ..url.path.equals ('/api/v1/users/me/subscriptions' )
189
+ ..bodyFields.deepEquals ({
190
+ 'subscriptions' : jsonEncode ([{'name' : stream.name}])
191
+ });
192
+ });
193
+
194
+ testWidgets ('shows a snackbar when subscription passes' , (WidgetTester tester) async {
195
+ final stream = await prepareSingleStream (tester);
196
+ connection.prepare (json: SubscribeToChannelsResult (
197
+ subscribed: {eg.selfUser.email: [stream.name]},
198
+ alreadySubscribed: {}).toJson ());
199
+ await tapSubscribeButton (tester);
200
+
201
+ await waitAndCheckSnackbarIsShown (tester,
202
+ zulipLocalizations.messageSubscribedToChannel, stream.name);
203
+ });
204
+
205
+ testWidgets ('shows a snackbar when already subscribed' , (WidgetTester tester) async {
206
+ final stream = await prepareSingleStream (tester);
207
+ connection.prepare (json: SubscribeToChannelsResult (
208
+ subscribed: {},
209
+ alreadySubscribed: {eg.selfUser.email: [stream.name]}).toJson ());
210
+ await tapSubscribeButton (tester);
211
+
212
+ await waitAndCheckSnackbarIsShown (tester,
213
+ zulipLocalizations.messageAlreadySubscribedToChannel, stream.name);
214
+ });
215
+
216
+ testWidgets ('shows a snackbar when subscription fails' , (WidgetTester tester) async {
217
+ final stream = await prepareSingleStream (tester);
218
+ connection.prepare (json: SubscribeToChannelsResult (
219
+ subscribed: {},
220
+ alreadySubscribed: {},
221
+ unauthorized: [stream.name]).toJson ());
222
+ await tapSubscribeButton (tester);
223
+
224
+ await waitAndCheckSnackbarIsShown (tester,
225
+ zulipLocalizations.errorFailedToSubscribeToChannel, stream.name);
226
+ });
227
+
228
+ testWidgets ('catch-all api errors' , (WidgetTester tester) async {
229
+ final stream = await prepareSingleStream (tester);
230
+ connection.prepare (exception: http.ClientException ('Oops' ));
231
+ await tapSubscribeButton (tester);
232
+ await tester.pump (Duration .zero);
233
+ await tester.pumpAndSettle ();
234
+
235
+ await waitAndCheckSnackbarIsShown (tester,
236
+ zulipLocalizations.errorFailedToSubscribeToChannel, stream.name);
237
+ });
238
+ });
239
+ });
70
240
}
0 commit comments