From cc10eec02a7c7f6c2cc69618430122101c8be473 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 16 Jun 2023 16:37:27 +0200 Subject: [PATCH 1/2] feat: replace asserts with exceptions To make it possible to catch attempts navigating past boundaries. --- lib/src/controller.dart | 35 +++++++++++++++++++++++------------ lib/src/exception.dart | 8 ++++++++ lib/src/wizard.dart | 1 + lib/wizard_router.dart | 1 + test/wizard_router_test.dart | 16 ++++++++-------- 5 files changed, 41 insertions(+), 20 deletions(-) create mode 100644 lib/src/exception.dart diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 6fed1c7..fa4a7ac 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -49,8 +49,10 @@ class WizardController extends SafeChangeNotifier { /// Requests the wizard to show the first page. void home() { - assert(state.length > 1, - '`Wizard.home()` called from the first route ${state.last.name}'); + if (state.length <= 1) { + throw WizardException( + '`Wizard.home()` called from the first route ${state.last.name}'); + } _updateState((state) { final copy = List.of(state); @@ -61,14 +63,18 @@ class WizardController extends SafeChangeNotifier { /// Requests the wizard to show the previous page. Optionally, `result` can be /// returned to the previous page. void back([T? result]) async { - assert(state.length > 1, - '`Wizard.back()` called from the first route ${state.last.name}'); + if (state.length <= 1) { + throw WizardException( + '`Wizard.back()` called from the first route ${state.last.name}'); + } // go back to a specific route, or pick the previous route on the list final previous = await routes[currentRoute]!.onBack?.call(state.last); if (previous != null) { - assert(routes.keys.contains(previous), - '`Wizard.routes` is missing route \'$previous\'.'); + if (!routes.keys.contains(previous)) { + throw WizardException( + '`Wizard.routes` is missing route \'$previous\'.'); + } } final start = previous != null @@ -116,14 +122,17 @@ class WizardController extends SafeChangeNotifier { String nextRoute() { final routeNames = routes.keys.toList(); final index = routeNames.indexOf(previous.name!); - assert(index < routeNames.length - 1, - '`Wizard.next()` called from the last route ${previous.name}.'); + if (index == routeNames.length - 1) { + throw WizardException( + '`Wizard.next()` called from the last route ${previous.name}.'); + } return routeNames[index + 1]; } final name = await onNext() ?? nextRoute(); - assert(routes.keys.contains(name), - '`Wizard.routes` is missing route \'$name\'.'); + if (!routes.keys.contains(name)) { + throw WizardException('`Wizard.routes` is missing route \'$name\'.'); + } return WizardRouteSettings(name: name, arguments: arguments); } @@ -146,8 +155,10 @@ class WizardController extends SafeChangeNotifier { /// Requests the wizard to jump to a specific page. Optionally, `arguments` /// can be passed to the page. Future jump(String route, {Object? arguments}) async { - assert(routes.keys.contains(route), - '`Wizard.jump()` called with an unknown route $route.'); + if (!routes.keys.contains(route)) { + throw WizardException( + '`Wizard.jump()` called with an unknown route $route.'); + } final settings = await _loadRoute(route, (name) async { return WizardRouteSettings(name: name, arguments: arguments); }); diff --git a/lib/src/exception.dart b/lib/src/exception.dart new file mode 100644 index 0000000..63ca204 --- /dev/null +++ b/lib/src/exception.dart @@ -0,0 +1,8 @@ +class WizardException implements Exception { + const WizardException(this.message); + + final String message; + + @override + String toString() => message; +} diff --git a/lib/src/wizard.dart b/lib/src/wizard.dart index 61a63e6..04cd532 100644 --- a/lib/src/wizard.dart +++ b/lib/src/wizard.dart @@ -5,6 +5,7 @@ import 'package:flow_builder/flow_builder.dart'; import 'package:flutter/material.dart'; import 'package:safe_change_notifier/safe_change_notifier.dart'; +import 'exception.dart'; import 'route.dart'; import 'scope.dart'; import 'settings.dart'; diff --git a/lib/wizard_router.dart b/lib/wizard_router.dart index 0a911fa..18717c5 100644 --- a/lib/wizard_router.dart +++ b/lib/wizard_router.dart @@ -7,6 +7,7 @@ /// ![wizard_router](https://github.com/ubuntu-flutter-community/wizard_router/raw/main/images/wizard_router.png) library wizard_router; +export 'src/exception.dart'; export 'src/route.dart'; export 'src/scope.dart'; export 'src/wizard.dart'; diff --git a/test/wizard_router_test.dart b/test/wizard_router_test.dart index d86f254..2944550 100644 --- a/test/wizard_router_test.dart +++ b/test/wizard_router_test.dart @@ -186,13 +186,13 @@ void main() { await tester.pumpAndSettle(); wizard.next(); await tester.pumpAndSettle(); - await expectLater(wizard.next, throwsAssertionError); + await expectLater(wizard.next, throwsA(isA())); wizard.back(); await tester.pumpAndSettle(); wizard.back(); await tester.pumpAndSettle(); - await expectLater(wizard.back, throwsAssertionError); + await expectLater(wizard.back, throwsA(isA())); }); testWidgets('route conditions', (tester) async { @@ -305,7 +305,7 @@ void main() { final firstWizardScope = Wizard.of(tester.element(firstPage)); nextRoute = 'unknown'; - await expectLater(firstWizardScope.next, throwsAssertionError); + await expectLater(firstWizardScope.next, throwsA(isA())); nextRoute = Routes.second; firstWizardScope.next(); @@ -313,7 +313,7 @@ void main() { final secondWizardScope = Wizard.of(tester.element(secondPage)); backRoute = 'invalid'; - await expectLater(secondWizardScope.back, throwsAssertionError); + await expectLater(secondWizardScope.back, throwsA(isA())); }); testWidgets('pass arguments', (tester) async { @@ -354,7 +354,7 @@ void main() { final wizard = Wizard.of(tester.element(firstPage)); // 1st -> home - await expectLater(wizard.home, throwsAssertionError); + await expectLater(wizard.home, throwsA(isA())); // 2nd -> home wizard.next(); @@ -426,7 +426,7 @@ void main() { expect(secondWizardScope.hasNext, isTrue); // 2nd -> 1st - await expectLater(secondWizardScope.back, throwsAssertionError); + await expectLater(secondWizardScope.back, throwsA(isA())); // 2nd -> 3rd secondWizardScope.replace(); @@ -444,7 +444,7 @@ void main() { expect(thirdWizardScope.hasNext, isFalse); // 3rd -> 2nd - await expectLater(thirdWizardScope.back, throwsAssertionError); + await expectLater(thirdWizardScope.back, throwsA(isA())); }); testWidgets('jump', (tester) async { @@ -843,7 +843,7 @@ void main() { expect(root3Scope.hasPrevious, isTrue); expect(root3Scope.hasNext, isTrue); - await expectLater(nested3Scope.next, throwsAssertionError); + await expectLater(nested3Scope.next, throwsA(isA())); root3Scope.next(); await tester.pumpAndSettle(); From a16ffdf80c30fbd3a7fee8e8cfec4cb1c911c349 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 16 Jun 2023 16:46:53 +0200 Subject: [PATCH 2/2] add missing test for jump() exception --- test/wizard_router_test.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/wizard_router_test.dart b/test/wizard_router_test.dart index 2944550..74e3fb2 100644 --- a/test/wizard_router_test.dart +++ b/test/wizard_router_test.dart @@ -490,6 +490,10 @@ void main() { expect(firstPage, findsOneWidget); expect(secondPage, findsNothing); expect(thirdPage, findsNothing); + + // unknown + await expectLater( + firstWizardScope.jump('/unknown'), throwsA(isA())); }); testWidgets('has next or previous', (tester) async {