Skip to content

Commit 1ff9acf

Browse files
gnpricechrisbobbe
authored andcommitted
nav [nfc]: Make navigation more cleanly testable, with WidgetRoute
As discussed when we added this first test of this kind: #249 (comment)
1 parent 4bcbffd commit 1ff9acf

File tree

5 files changed

+64
-7
lines changed

5 files changed

+64
-7
lines changed

lib/widgets/message_list.dart

+2-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ class MessageListPage extends StatefulWidget {
2020
const MessageListPage({super.key, required this.narrow});
2121

2222
static Route<void> buildRoute({required BuildContext context, required Narrow narrow}) {
23-
return MaterialAccountPageRoute(context: context,
24-
settings: RouteSettings(name: 'message_list', arguments: narrow), // for testing
25-
builder: (context) => MessageListPage(narrow: narrow));
23+
return MaterialAccountWidgetRoute(context: context,
24+
page: MessageListPage(narrow: narrow));
2625
}
2726

2827
/// A [ComposeBoxController], if this [MessageListPage] offers a compose box.

lib/widgets/page.dart

+41
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,30 @@ import 'package:flutter/material.dart';
33

44
import 'store.dart';
55

6+
/// A page route that always builds the same widget.
7+
///
8+
/// This is useful for making the route more transparent for a test to inspect.
9+
abstract class WidgetRoute<T> extends PageRoute<T> {
10+
/// The widget that this page route always builds.
11+
Widget get page;
12+
}
13+
14+
/// A [MaterialPageRoute] that always builds the same widget.
15+
///
16+
/// This is useful for making the route more transparent for a test to inspect.
17+
class MaterialWidgetRoute<T> extends MaterialPageRoute<T> implements WidgetRoute<T> {
18+
MaterialWidgetRoute({
19+
required this.page,
20+
super.settings,
21+
super.maintainState,
22+
super.fullscreenDialog,
23+
super.allowSnapshotting,
24+
}) : super(builder: (context) => page);
25+
26+
@override
27+
final Widget page;
28+
}
29+
630
mixin AccountPageRouteMixin<T> on PageRoute<T> {
731
int get accountId;
832

@@ -28,6 +52,23 @@ class MaterialAccountPageRoute<T> extends MaterialPageRoute<T> with AccountPageR
2852
final int accountId;
2953
}
3054

55+
/// A [MaterialAccountPageRoute] that always builds the same widget.
56+
///
57+
/// This is useful for making the route more transparent for a test to inspect.
58+
class MaterialAccountWidgetRoute<T> extends MaterialAccountPageRoute<T> implements WidgetRoute<T> {
59+
MaterialAccountWidgetRoute({
60+
required super.context,
61+
required this.page,
62+
super.settings,
63+
super.maintainState,
64+
super.fullscreenDialog,
65+
super.allowSnapshotting,
66+
}) : super(builder: (context) => page);
67+
68+
@override
69+
final Widget page;
70+
}
71+
3172
class AccountPageRouteBuilder<T> extends PageRouteBuilder<T> with AccountPageRouteMixin<T> {
3273
AccountPageRouteBuilder({
3374
required BuildContext context,

test/widgets/message_list_checks.dart

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import 'package:checks/checks.dart';
2+
import 'package:zulip/model/narrow.dart';
3+
import 'package:zulip/widgets/message_list.dart';
4+
5+
extension MessageListPageChecks on Subject<MessageListPage> {
6+
Subject<Narrow> get narrow => has((x) => x.narrow, 'narrow');
7+
}

test/widgets/page_checks.dart

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import 'package:checks/checks.dart';
2+
import 'package:flutter/widgets.dart';
3+
import 'package:zulip/widgets/page.dart';
4+
5+
extension WidgetRouteChecks on Subject<WidgetRoute> {
6+
Subject<Widget> get page => has((x) => x.page, 'page');
7+
}

test/widgets/recent_dm_conversations_test.dart

+7-4
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@ import 'package:zulip/api/model/model.dart';
77
import 'package:zulip/model/narrow.dart';
88
import 'package:zulip/widgets/content.dart';
99
import 'package:zulip/widgets/icons.dart';
10+
import 'package:zulip/widgets/message_list.dart';
11+
import 'package:zulip/widgets/page.dart';
1012
import 'package:zulip/widgets/recent_dm_conversations.dart';
1113
import 'package:zulip/widgets/store.dart';
1214

1315
import '../example_data.dart' as eg;
14-
import '../flutter_checks.dart';
1516
import '../model/binding.dart';
1617
import '../model/test_store.dart';
1718
import '../test_navigation.dart';
1819
import 'content_checks.dart';
20+
import 'message_list_checks.dart';
21+
import 'page_checks.dart';
1922

2023
Future<void> setupPage(WidgetTester tester, {
2124
required List<DmMessage> dmMessages,
@@ -275,9 +278,9 @@ void main() {
275278
await tester.tap(find.byType(RecentDmConversationsItem));
276279
// no `tester.pump`, to avoid having to mock API response for [MessageListPage]
277280

278-
check(pushedRoutes).last.settings
279-
..name.equals('message_list')
280-
..arguments.equals(expectedNarrow);
281+
check(pushedRoutes).last.isA<WidgetRoute>().page
282+
.isA<MessageListPage>()
283+
.narrow.equals(expectedNarrow);
281284
}
282285

283286
testWidgets('1:1', (WidgetTester tester) async {

0 commit comments

Comments
 (0)