@@ -4,36 +4,62 @@ import 'package:flutter/material.dart';
4
4
import 'package:flutter_checks/flutter_checks.dart' ;
5
5
import 'package:flutter_test/flutter_test.dart' ;
6
6
import 'package:zulip/model/settings.dart' ;
7
+ import 'package:zulip/widgets/page.dart' ;
7
8
import 'package:zulip/widgets/settings.dart' ;
9
+ import 'package:zulip/widgets/store.dart' ;
8
10
9
11
import '../flutter_checks.dart' ;
10
12
import '../model/binding.dart' ;
11
13
import '../model/store_checks.dart' ;
12
14
import '../example_data.dart' as eg;
15
+ import '../test_navigation.dart' ;
16
+ import 'checks.dart' ;
13
17
import 'test_app.dart' ;
14
18
15
19
void main () {
16
20
TestZulipBinding .ensureInitialized ();
17
21
22
+ late TestNavigatorObserver testNavObserver;
23
+ late Route <dynamic >? lastPushedRoute;
24
+ late Route <dynamic >? lastPoppedRoute;
25
+
18
26
Future <void > prepare (WidgetTester tester) async {
19
27
addTearDown (testBinding.reset);
20
28
29
+ testNavObserver = TestNavigatorObserver ()
30
+ ..onPushed = ((route, _) => lastPushedRoute = route)
31
+ ..onPopped = ((route, _) => lastPoppedRoute = route);
32
+ lastPushedRoute = null ;
33
+ lastPoppedRoute = null ;
34
+
21
35
await testBinding.globalStore.add (eg.selfAccount, eg.initialSnapshot ());
22
36
await tester.pumpWidget (TestZulipApp (
23
37
accountId: eg.selfAccount.id,
38
+ navigatorObservers: [testNavObserver],
24
39
child: SettingsPage ()));
25
40
await tester.pump ();
26
41
await tester.pump ();
27
42
}
28
43
44
+ void checkTileOnSettingsPage (WidgetTester tester, {
45
+ required String expectedTitle,
46
+ required String expectedSubtitle,
47
+ }) {
48
+ check (find.descendant (of: find.widgetWithText (ListTile , expectedTitle),
49
+ matching: find.text (expectedSubtitle))).findsOne ();
50
+ }
51
+
29
52
Finder findRadioListTileWithTitle <T >(String title) => find.ancestor (
30
53
of: find.text (title),
31
54
matching: find.byType (RadioListTile <T >));
32
55
33
- void checkRadioButtonAppearsChecked <T >(WidgetTester tester, String title, bool expectedIsChecked) {
56
+ void checkRadioButtonAppearsChecked <T >(WidgetTester tester,
57
+ String title, bool expectedIsChecked, {String ? subtitle}) {
34
58
check (tester.semantics.find (findRadioListTileWithTitle <T >(title)))
35
59
.containsSemantics (
36
- label: title,
60
+ label: subtitle == null
61
+ ? title
62
+ : '$title \n $subtitle ' ,
37
63
isInMutuallyExclusiveGroup: true ,
38
64
hasCheckedState: true , isChecked: expectedIsChecked);
39
65
}
@@ -134,7 +160,140 @@ void main() {
134
160
}, variant: TargetPlatformVariant ({TargetPlatform .android, TargetPlatform .iOS}));
135
161
});
136
162
137
- // TODO(#1571): test visitFirstUnread setting UI
163
+ group ('VisitFirstUnreadSetting' , () {
164
+ String settingTitle (VisitFirstUnreadSetting setting) => switch (setting) {
165
+ VisitFirstUnreadSetting .always => 'First unread message' ,
166
+ VisitFirstUnreadSetting .conversations => 'First unread message in conversation views, newest message elsewhere' ,
167
+ VisitFirstUnreadSetting .never => 'Newest message' ,
168
+ };
169
+
170
+ void checkPage (WidgetTester tester, {
171
+ required VisitFirstUnreadSetting expectedSetting,
172
+ }) {
173
+ for (final setting in VisitFirstUnreadSetting .values) {
174
+ final thisSettingTitle = settingTitle (setting);
175
+ checkRadioButtonAppearsChecked <VisitFirstUnreadSetting >(tester,
176
+ thisSettingTitle, setting == expectedSetting);
177
+ }
178
+ }
179
+
180
+ testWidgets ('smoke' , (tester) async {
181
+ await prepare (tester);
182
+
183
+ // "conversations" is the default, and it appears in the SettingsPage
184
+ // (as the setting tile's subtitle)
185
+ check (GlobalStoreWidget .settingsOf (tester.element (find.byType (SettingsPage ))))
186
+ .visitFirstUnread.equals (VisitFirstUnreadSetting .conversations);
187
+ checkTileOnSettingsPage (tester,
188
+ expectedTitle: 'Open message feeds at' ,
189
+ expectedSubtitle: settingTitle (VisitFirstUnreadSetting .conversations));
190
+
191
+ await tester.tap (find.text ('Open message feeds at' ));
192
+ await tester.pump ();
193
+ check (lastPushedRoute).isA <MaterialWidgetRoute >()
194
+ .page.isA <VisitFirstUnreadSettingPage >();
195
+ await tester.pump ((lastPushedRoute as TransitionRoute ).transitionDuration);
196
+ checkPage (tester, expectedSetting: VisitFirstUnreadSetting .conversations);
197
+
198
+ await tester.tap (findRadioListTileWithTitle <VisitFirstUnreadSetting >(
199
+ settingTitle (VisitFirstUnreadSetting .always)));
200
+ await tester.pump ();
201
+ checkPage (tester, expectedSetting: VisitFirstUnreadSetting .always);
202
+
203
+ await tester.tap (findRadioListTileWithTitle <VisitFirstUnreadSetting >(
204
+ settingTitle (VisitFirstUnreadSetting .conversations)));
205
+ await tester.pump ();
206
+ checkPage (tester, expectedSetting: VisitFirstUnreadSetting .conversations);
207
+
208
+ await tester.tap (findRadioListTileWithTitle <VisitFirstUnreadSetting >(
209
+ settingTitle (VisitFirstUnreadSetting .never)));
210
+ await tester.pump ();
211
+ checkPage (tester, expectedSetting: VisitFirstUnreadSetting .never);
212
+
213
+ await tester.tap (find.backButton ());
214
+ check (lastPoppedRoute).isA <MaterialWidgetRoute >()
215
+ .page.isA <VisitFirstUnreadSettingPage >();
216
+ await tester.pump ((lastPoppedRoute as TransitionRoute ).reverseTransitionDuration);
217
+ check (GlobalStoreWidget .settingsOf (tester.element (find.byType (SettingsPage ))))
218
+ .visitFirstUnread.equals (VisitFirstUnreadSetting .never);
219
+
220
+ checkTileOnSettingsPage (tester,
221
+ expectedTitle: 'Open message feeds at' ,
222
+ expectedSubtitle: settingTitle (VisitFirstUnreadSetting .never));
223
+ });
224
+ });
225
+
226
+ group ('MarkReadOnScrollSetting' , () {
227
+ String settingTitle (MarkReadOnScrollSetting setting) => switch (setting) {
228
+ MarkReadOnScrollSetting .always => 'Always' ,
229
+ MarkReadOnScrollSetting .conversations => 'Only in conversation views' ,
230
+ MarkReadOnScrollSetting .never => 'Never' ,
231
+ };
232
+
233
+ String ? settingSubtitle (MarkReadOnScrollSetting setting) => switch (setting) {
234
+ MarkReadOnScrollSetting .always => null ,
235
+ MarkReadOnScrollSetting .conversations =>
236
+ 'Messages will be automatically marked as read only when viewing a single topic or direct message conversation.' ,
237
+ MarkReadOnScrollSetting .never => null ,
238
+ };
239
+
240
+ void checkPage (WidgetTester tester, {
241
+ required MarkReadOnScrollSetting expectedSetting,
242
+ }) {
243
+ for (final setting in MarkReadOnScrollSetting .values) {
244
+ final thisSettingTitle = settingTitle (setting);
245
+ checkRadioButtonAppearsChecked <MarkReadOnScrollSetting >(tester,
246
+ thisSettingTitle,
247
+ setting == expectedSetting,
248
+ subtitle: settingSubtitle (setting));
249
+ }
250
+ }
251
+
252
+ testWidgets ('smoke' , (tester) async {
253
+ await prepare (tester);
254
+
255
+ // "conversations" is the default, and it appears in the SettingsPage
256
+ // (as the setting tile's subtitle)
257
+ check (GlobalStoreWidget .settingsOf (tester.element (find.byType (SettingsPage ))))
258
+ .markReadOnScroll.equals (MarkReadOnScrollSetting .conversations);
259
+ checkTileOnSettingsPage (tester,
260
+ expectedTitle: 'Mark messages as read on scroll' ,
261
+ expectedSubtitle: settingTitle (MarkReadOnScrollSetting .conversations));
262
+
263
+ await tester.tap (find.text ('Mark messages as read on scroll' ));
264
+ await tester.pump ();
265
+ check (lastPushedRoute).isA <MaterialWidgetRoute >()
266
+ .page.isA <MarkReadOnScrollSettingPage >();
267
+ await tester.pump ((lastPushedRoute as TransitionRoute ).transitionDuration);
268
+ checkPage (tester, expectedSetting: MarkReadOnScrollSetting .conversations);
269
+
270
+ await tester.tap (findRadioListTileWithTitle <MarkReadOnScrollSetting >(
271
+ settingTitle (MarkReadOnScrollSetting .always)));
272
+ await tester.pump ();
273
+ checkPage (tester, expectedSetting: MarkReadOnScrollSetting .always);
274
+
275
+ await tester.tap (findRadioListTileWithTitle <MarkReadOnScrollSetting >(
276
+ settingTitle (MarkReadOnScrollSetting .conversations)));
277
+ await tester.pump ();
278
+ checkPage (tester, expectedSetting: MarkReadOnScrollSetting .conversations);
279
+
280
+ await tester.tap (findRadioListTileWithTitle <MarkReadOnScrollSetting >(
281
+ settingTitle (MarkReadOnScrollSetting .never)));
282
+ await tester.pump ();
283
+ checkPage (tester, expectedSetting: MarkReadOnScrollSetting .never);
284
+
285
+ await tester.tap (find.byType (BackButton ));
286
+ check (lastPoppedRoute).isA <MaterialWidgetRoute >()
287
+ .page.isA <MarkReadOnScrollSettingPage >();
288
+ await tester.pump ((lastPoppedRoute as TransitionRoute ).reverseTransitionDuration);
289
+ check (GlobalStoreWidget .settingsOf (tester.element (find.byType (SettingsPage ))))
290
+ .markReadOnScroll.equals (MarkReadOnScrollSetting .never);
291
+
292
+ checkTileOnSettingsPage (tester,
293
+ expectedTitle: 'Mark messages as read on scroll' ,
294
+ expectedSubtitle: settingTitle (MarkReadOnScrollSetting .never));
295
+ });
296
+ });
138
297
139
298
// TODO maybe test GlobalSettingType.experimentalFeatureFlag settings
140
299
// Or maybe not; after all, it's a developer-facing feature, so
0 commit comments