| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| void main() { |
| testWidgets('ErrorWidget displays actual error when throwing during build', (WidgetTester tester) async { |
| final Key container = UniqueKey(); |
| const String errorText = 'Oh no, there was a crash!!1'; |
| |
| await tester.pumpWidget( |
| Container( |
| key: container, |
| color: Colors.red, |
| padding: const EdgeInsets.all(10), |
| child: Builder( |
| builder: (BuildContext context) { |
| throw UnsupportedError(errorText); |
| }, |
| ), |
| ), |
| ); |
| |
| expect( |
| tester.takeException(), |
| isA<UnsupportedError>().having((UnsupportedError error) => error.message, 'message', contains(errorText)), |
| ); |
| |
| final ErrorWidget errorWidget = tester.widget(find.byType(ErrorWidget)); |
| expect(errorWidget.message, contains(errorText)); |
| |
| // Failure in one widget shouldn't ripple through the entire tree and effect |
| // ancestors. Those should still be in the tree. |
| expect(find.byKey(container), findsOneWidget); |
| }); |
| |
| testWidgets('when constructing an ErrorWidget due to a build failure throws an error, fail gracefully', (WidgetTester tester) async { |
| final Key container = UniqueKey(); |
| await tester.pumpWidget( |
| Container( |
| key: container, |
| color: Colors.red, |
| padding: const EdgeInsets.all(10), |
| // This widget throws during build, which causes the construction of an |
| // ErrorWidget with the build error. However, during construction of |
| // that ErrorWidget, another error is thrown. |
| child: const MyDoubleThrowingWidget(), |
| ), |
| ); |
| |
| expect( |
| tester.takeException(), |
| isA<UnsupportedError>().having((UnsupportedError error) => error.message, 'message', contains(MyThrowingElement.debugFillPropertiesErrorMessage)), |
| ); |
| |
| final ErrorWidget errorWidget = tester.widget(find.byType(ErrorWidget)); |
| expect(errorWidget.message, contains(MyThrowingElement.debugFillPropertiesErrorMessage)); |
| |
| // Failure in one widget shouldn't ripple through the entire tree and effect |
| // ancestors. Those should still be in the tree. |
| expect(find.byKey(container), findsOneWidget); |
| }); |
| } |
| |
| // This widget throws during its regular build and then again when the |
| // ErrorWidget is constructed, which calls MyThrowingElement.debugFillProperties. |
| class MyDoubleThrowingWidget extends StatelessWidget { |
| const MyDoubleThrowingWidget({super.key}); |
| |
| @override |
| StatelessElement createElement() => MyThrowingElement(this); |
| |
| @override |
| Widget build(BuildContext context) { |
| throw UnsupportedError('You cannot build me!'); |
| } |
| } |
| |
| class MyThrowingElement extends StatelessElement { |
| MyThrowingElement(super.widget); |
| |
| static const String debugFillPropertiesErrorMessage = 'Crash during debugFillProperties'; |
| |
| @override |
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
| super.debugFillProperties(properties); |
| throw UnsupportedError(debugFillPropertiesErrorMessage); |
| } |
| } |