Skip to content

Commit dbe1cad

Browse files
committed
test_app: Assert the store has an Account for the passed accountId
This is nice and explicit compared to the behavior I got when I simply forgot to call `testBinding.globalStore.add` when working on some upcoming tests.
1 parent 12ce869 commit dbe1cad

File tree

2 files changed

+51
-7
lines changed

2 files changed

+51
-7
lines changed

test/widgets/actions_test.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ void main() {
4242
Future<void> prepare(WidgetTester tester, {
4343
UnreadMessagesSnapshot? unreadMsgs,
4444
String? ackedPushToken = '123',
45+
bool skipAssertAccountExists = false,
4546
}) async {
4647
addTearDown(testBinding.reset);
4748
final selfAccount = eg.selfAccount.copyWith(ackedPushToken: Value(ackedPushToken));
@@ -50,7 +51,9 @@ void main() {
5051
store = await testBinding.globalStore.perAccount(selfAccount.id);
5152
connection = store.connection as FakeApiConnection;
5253

53-
await tester.pumpWidget(TestZulipApp(accountId: selfAccount.id,
54+
await tester.pumpWidget(TestZulipApp(
55+
accountId: selfAccount.id,
56+
skipAssertAccountExists: skipAssertAccountExists,
5457
child: const Scaffold(body: Placeholder())));
5558
await tester.pump();
5659
context = tester.element(find.byType(Placeholder));
@@ -99,7 +102,7 @@ void main() {
99102

100103
group('logOutAccount', () {
101104
testWidgets('smoke', (tester) async {
102-
await prepare(tester);
105+
await prepare(tester, skipAssertAccountExists: true);
103106
check(testBinding.globalStore).accountIds.single.equals(eg.selfAccount.id);
104107
const unregisterDelay = Duration(seconds: 5);
105108
assert(unregisterDelay > TestGlobalStore.removeAccountDuration);
@@ -124,7 +127,7 @@ void main() {
124127
});
125128

126129
testWidgets('unregister request has an error', (tester) async {
127-
await prepare(tester);
130+
await prepare(tester, skipAssertAccountExists: true);
128131
check(testBinding.globalStore).accountIds.single.equals(eg.selfAccount.id);
129132
const unregisterDelay = Duration(seconds: 5);
130133
assert(unregisterDelay > TestGlobalStore.removeAccountDuration);

test/widgets/test_app.dart

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,29 @@ class TestZulipApp extends StatelessWidget {
99
const TestZulipApp({
1010
super.key,
1111
this.accountId,
12+
this.skipAssertAccountExists = false,
1213
this.navigatorObservers,
1314
this.child = const Placeholder(),
14-
});
15+
}) : assert(!skipAssertAccountExists || accountId != null);
1516

1617
final int? accountId;
1718

19+
/// Whether to proceed if [accountId] doesn't have an [Account] in the store.
20+
///
21+
/// If this widget's [GlobalStoreWidget] loads
22+
/// with no [Account] for [accountId],
23+
/// [build] will error unless this param is true.
24+
///
25+
/// Usually, this case is just a mistake;
26+
/// the caller either forgot to add the account to the store
27+
/// or they didn't want to simulate a per-account context in the first place.
28+
///
29+
/// Sometimes we want to simulate an account's UI
30+
/// just after the account is logged out (so is absent in the store)
31+
/// but before we tear down that UI.
32+
/// Pass true to silence the assertion in that case.
33+
final bool skipAssertAccountExists;
34+
1835
/// A list to pass through to [MaterialApp.navigatorObservers].
1936
final List<NavigatorObserver>? navigatorObservers;
2037

@@ -27,8 +44,31 @@ class TestZulipApp extends StatelessWidget {
2744

2845
@override
2946
Widget build(BuildContext context) {
30-
return GlobalStoreWidget(
31-
child: MaterialApp(
47+
return GlobalStoreWidget(child: Builder(builder: (context) {
48+
assert(() {
49+
if (accountId != null && !skipAssertAccountExists) {
50+
final account = GlobalStoreWidget.of(context).getAccount(accountId!);
51+
if (account == null) {
52+
throw FlutterError.fromParts([
53+
ErrorSummary(
54+
'TestZulipApp() was called with [accountId] but a corresponding '
55+
'Account was not found in the GlobalStore.'),
56+
ErrorHint(
57+
'If [child] needs per-account data, consider calling '
58+
'`testBinding.globalStore.add` before pumping `TestZulipApp`.'),
59+
ErrorHint(
60+
'If [child] is not specific to an account, omit [accountId].'),
61+
ErrorHint(
62+
'If you are testing behavior when an account is logged out, '
63+
'consider building ZulipApp instead of TestZulipApp, '
64+
'or pass `skipAssertAccountExists: true`.'),
65+
]);
66+
}
67+
}
68+
return true;
69+
}());
70+
71+
return MaterialApp(
3272
title: 'Zulip',
3373
localizationsDelegates: ZulipLocalizations.localizationsDelegates,
3474
supportedLocales: ZulipLocalizations.supportedLocales,
@@ -39,6 +79,7 @@ class TestZulipApp extends StatelessWidget {
3979
home: accountId != null
4080
? PerAccountStoreWidget(accountId: accountId!, child: child)
4181
: child,
42-
));
82+
);
83+
}));
4384
}
4485
}

0 commit comments

Comments
 (0)