Skip to content

Commit 11a626c

Browse files
committed
settings test: Add widget tests for latest settings
"Open message feeds at" and "Mark messages as read on scroll". Related: #1571 Related: #1583
1 parent 01477ca commit 11a626c

File tree

2 files changed

+165
-3
lines changed

2 files changed

+165
-3
lines changed

test/model/store_checks.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ extension GlobalSettingsStoreChecks on Subject<GlobalSettingsStore> {
3232
Subject<BrowserPreference?> get browserPreference => has((x) => x.browserPreference, 'browserPreference');
3333
Subject<BrowserPreference> get effectiveBrowserPreference => has((x) => x.effectiveBrowserPreference, 'effectiveBrowserPreference');
3434
Subject<UrlLaunchMode> getUrlLaunchMode(Uri url) => has((x) => x.getUrlLaunchMode(url), 'getUrlLaunchMode');
35+
Subject<VisitFirstUnreadSetting> get visitFirstUnread => has((x) => x.visitFirstUnread, 'visitFirstUnread');
36+
Subject<MarkReadOnScrollSetting> get markReadOnScroll => has((x) => x.markReadOnScroll, 'markReadOnScroll');
3537
Subject<bool> getBool(BoolGlobalSetting setting) => has((x) => x.getBool(setting), 'getBool(${setting.name}');
3638
}
3739

test/widgets/settings_test.dart

Lines changed: 163 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,68 @@
11
import 'package:checks/checks.dart';
22
import 'package:flutter/foundation.dart';
33
import 'package:flutter/material.dart';
4+
import 'package:flutter_checks/flutter_checks.dart';
45
import 'package:flutter_test/flutter_test.dart';
56
import 'package:legacy_checks/legacy_checks.dart';
67
import 'package:zulip/model/settings.dart';
8+
import 'package:zulip/widgets/page.dart';
79
import 'package:zulip/widgets/settings.dart';
10+
import 'package:zulip/widgets/store.dart';
811

912
import '../flutter_checks.dart';
1013
import '../model/binding.dart';
1114
import '../model/store_checks.dart';
1215
import '../example_data.dart' as eg;
16+
import '../test_navigation.dart';
17+
import 'page_checks.dart';
1318
import 'test_app.dart';
1419

1520
void main() {
1621
TestZulipBinding.ensureInitialized();
1722

23+
late TestNavigatorObserver testNavObserver;
24+
late Route<dynamic>? lastPushedRoute;
25+
late Route<dynamic>? lastPoppedRoute;
26+
1827
Future<void> prepare(WidgetTester tester) async {
1928
addTearDown(testBinding.reset);
2029

30+
testNavObserver = TestNavigatorObserver()
31+
..onPushed = ((route, _) => lastPushedRoute = route)
32+
..onPopped = ((route, _) => lastPoppedRoute = route);
33+
lastPushedRoute = null;
34+
lastPoppedRoute = null;
35+
2136
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
2237
await tester.pumpWidget(TestZulipApp(
2338
accountId: eg.selfAccount.id,
39+
navigatorObservers: [testNavObserver],
2440
child: SettingsPage()));
2541
await tester.pump();
2642
await tester.pump();
2743
}
2844

45+
void checkTileOnSettingsPage(WidgetTester tester, {
46+
required String expectedTitle,
47+
required String expectedSubtitle,
48+
}) {
49+
check(find.descendant(of: find.widgetWithText(ListTile, expectedTitle),
50+
matching: find.text(expectedSubtitle))).findsOne();
51+
}
52+
2953
Finder findRadioListTileWithTitle<T>(String title) => find.ancestor(
3054
of: find.text(title),
3155
matching: find.byType(RadioListTile<T>));
3256

33-
void checkRadioButtonAppearsChecked<T>(WidgetTester tester, String title, bool expectedIsChecked) {
57+
void checkRadioButtonAppearsChecked<T>(WidgetTester tester,
58+
String title, bool expectedIsChecked, {String? subtitle}) {
3459
check(tester.semantics.find(findRadioListTileWithTitle<T>(title)))
3560
// `.legacyMatcher()` wrapper just because flutter_checks's
3661
// `matchesSemantics` doesn't yet have a `hasSelectedState` param
3762
.legacyMatcher(matchesSemantics(
38-
label: title,
63+
label: subtitle == null
64+
? title
65+
: '$title\n$subtitle',
3966
isFocusable: true,
4067
hasEnabledState: true, isEnabled: true,
4168
isInMutuallyExclusiveGroup: true,
@@ -141,7 +168,140 @@ void main() {
141168
}, variant: TargetPlatformVariant({TargetPlatform.android, TargetPlatform.iOS}));
142169
});
143170

144-
// TODO(#1571): test visitFirstUnread setting UI
171+
group('VisitFirstUnreadSetting', () {
172+
String settingTitle(VisitFirstUnreadSetting setting) => switch (setting) {
173+
VisitFirstUnreadSetting.always => 'First unread message',
174+
VisitFirstUnreadSetting.conversations => 'First unread message in conversation views, newest message elsewhere',
175+
VisitFirstUnreadSetting.never => 'Newest message',
176+
};
177+
178+
void checkPage(WidgetTester tester, {
179+
required VisitFirstUnreadSetting expectedSetting,
180+
}) {
181+
for (final setting in VisitFirstUnreadSetting.values) {
182+
final thisSettingTitle = settingTitle(setting);
183+
checkRadioButtonAppearsChecked<VisitFirstUnreadSetting>(tester,
184+
thisSettingTitle, setting == expectedSetting);
185+
}
186+
}
187+
188+
testWidgets('smoke', (tester) async {
189+
await prepare(tester);
190+
191+
// "conversations" is the default, and it appears in the SettingsPage
192+
// (as the setting tile's subtitle)
193+
check(GlobalStoreWidget.settingsOf(tester.element(find.byType(SettingsPage))))
194+
.visitFirstUnread.equals(VisitFirstUnreadSetting.conversations);
195+
checkTileOnSettingsPage(tester,
196+
expectedTitle: 'Open message feeds at',
197+
expectedSubtitle: settingTitle(VisitFirstUnreadSetting.conversations));
198+
199+
await tester.tap(find.text('Open message feeds at'));
200+
await tester.pump();
201+
check(lastPushedRoute).isA<MaterialWidgetRoute>()
202+
.page.isA<VisitFirstUnreadSettingPage>();
203+
await tester.pump((lastPushedRoute as TransitionRoute).transitionDuration);
204+
checkPage(tester, expectedSetting: VisitFirstUnreadSetting.conversations);
205+
206+
await tester.tap(findRadioListTileWithTitle<VisitFirstUnreadSetting>(
207+
settingTitle(VisitFirstUnreadSetting.always)));
208+
await tester.pump();
209+
checkPage(tester, expectedSetting: VisitFirstUnreadSetting.always);
210+
211+
await tester.tap(findRadioListTileWithTitle<VisitFirstUnreadSetting>(
212+
settingTitle(VisitFirstUnreadSetting.conversations)));
213+
await tester.pump();
214+
checkPage(tester, expectedSetting: VisitFirstUnreadSetting.conversations);
215+
216+
await tester.tap(findRadioListTileWithTitle<VisitFirstUnreadSetting>(
217+
settingTitle(VisitFirstUnreadSetting.never)));
218+
await tester.pump();
219+
checkPage(tester, expectedSetting: VisitFirstUnreadSetting.never);
220+
221+
await tester.tap(find.backButton());
222+
check(lastPoppedRoute).isA<MaterialWidgetRoute>()
223+
.page.isA<VisitFirstUnreadSettingPage>();
224+
await tester.pump((lastPoppedRoute as TransitionRoute).reverseTransitionDuration);
225+
check(GlobalStoreWidget.settingsOf(tester.element(find.byType(SettingsPage))))
226+
.visitFirstUnread.equals(VisitFirstUnreadSetting.never);
227+
228+
checkTileOnSettingsPage(tester,
229+
expectedTitle: 'Open message feeds at',
230+
expectedSubtitle: settingTitle(VisitFirstUnreadSetting.never));
231+
});
232+
});
233+
234+
group('MarkReadOnScrollSetting', () {
235+
String settingTitle(MarkReadOnScrollSetting setting) => switch (setting) {
236+
MarkReadOnScrollSetting.always => 'Always',
237+
MarkReadOnScrollSetting.conversations => 'Only in conversation views',
238+
MarkReadOnScrollSetting.never => 'Never',
239+
};
240+
241+
String? settingSubtitle(MarkReadOnScrollSetting setting) => switch (setting) {
242+
MarkReadOnScrollSetting.always => null,
243+
MarkReadOnScrollSetting.conversations =>
244+
'Messages will be automatically marked as read only when viewing a single topic or direct message conversation.',
245+
MarkReadOnScrollSetting.never => null,
246+
};
247+
248+
void checkPage(WidgetTester tester, {
249+
required MarkReadOnScrollSetting expectedSetting,
250+
}) {
251+
for (final setting in MarkReadOnScrollSetting.values) {
252+
final thisSettingTitle = settingTitle(setting);
253+
checkRadioButtonAppearsChecked<MarkReadOnScrollSetting>(tester,
254+
thisSettingTitle,
255+
setting == expectedSetting,
256+
subtitle: settingSubtitle(setting));
257+
}
258+
}
259+
260+
testWidgets('smoke', (tester) async {
261+
await prepare(tester);
262+
263+
// "conversations" is the default, and it appears in the SettingsPage
264+
// (as the setting tile's subtitle)
265+
check(GlobalStoreWidget.settingsOf(tester.element(find.byType(SettingsPage))))
266+
.markReadOnScroll.equals(MarkReadOnScrollSetting.conversations);
267+
checkTileOnSettingsPage(tester,
268+
expectedTitle: 'Mark messages as read on scroll',
269+
expectedSubtitle: settingTitle(MarkReadOnScrollSetting.conversations));
270+
271+
await tester.tap(find.text('Mark messages as read on scroll'));
272+
await tester.pump();
273+
check(lastPushedRoute).isA<MaterialWidgetRoute>()
274+
.page.isA<MarkReadOnScrollSettingPage>();
275+
await tester.pump((lastPushedRoute as TransitionRoute).transitionDuration);
276+
checkPage(tester, expectedSetting: MarkReadOnScrollSetting.conversations);
277+
278+
await tester.tap(findRadioListTileWithTitle<MarkReadOnScrollSetting>(
279+
settingTitle(MarkReadOnScrollSetting.always)));
280+
await tester.pump();
281+
checkPage(tester, expectedSetting: MarkReadOnScrollSetting.always);
282+
283+
await tester.tap(findRadioListTileWithTitle<MarkReadOnScrollSetting>(
284+
settingTitle(MarkReadOnScrollSetting.conversations)));
285+
await tester.pump();
286+
checkPage(tester, expectedSetting: MarkReadOnScrollSetting.conversations);
287+
288+
await tester.tap(findRadioListTileWithTitle<MarkReadOnScrollSetting>(
289+
settingTitle(MarkReadOnScrollSetting.never)));
290+
await tester.pump();
291+
checkPage(tester, expectedSetting: MarkReadOnScrollSetting.never);
292+
293+
await tester.tap(find.byType(BackButton));
294+
check(lastPoppedRoute).isA<MaterialWidgetRoute>()
295+
.page.isA<MarkReadOnScrollSettingPage>();
296+
await tester.pump((lastPoppedRoute as TransitionRoute).reverseTransitionDuration);
297+
check(GlobalStoreWidget.settingsOf(tester.element(find.byType(SettingsPage))))
298+
.markReadOnScroll.equals(MarkReadOnScrollSetting.never);
299+
300+
checkTileOnSettingsPage(tester,
301+
expectedTitle: 'Mark messages as read on scroll',
302+
expectedSubtitle: settingTitle(MarkReadOnScrollSetting.never));
303+
});
304+
});
145305

146306
// TODO maybe test GlobalSettingType.experimentalFeatureFlag settings
147307
// Or maybe not; after all, it's a developer-facing feature, so

0 commit comments

Comments
 (0)