@@ -59,45 +59,49 @@ void main() {
59
59
expect (renderCount, equals (1 ));
60
60
});
61
61
62
- testWidgets ("Observer.withBuiltChild's child doesn't re-render" , (tester) async {
62
+ testWidgets ("Observer.withBuiltChild's child doesn't re-render" ,
63
+ (tester) async {
63
64
final message = Observable ('Click' );
64
- final key1 = UniqueKey ();
65
- final key2 = UniqueKey ();
66
- final key3 = UniqueKey ();
67
-
68
- await tester.pumpWidget (
69
- MaterialApp (
70
- home: Observer .withBuiltChild (
71
- builderWithChild: (context, child) {
72
- return Column (
73
- children: [
74
- ElevatedButton (onPressed: () => message.value = 'Clicked' , child: Container ()),
75
- Text (message.value, key: key1),
76
- child! ,
77
- Builder (
78
- builder: (context) {
79
- return Text (message.value, key: key3);
80
- }
81
- ),
82
- ],
83
- );
84
- },
85
- child: Text (message.value, key: key2),
86
- ),
87
- ),
88
- );
89
-
90
- expect (tester.widget <Text >(find.byKey (key1)).data, equals ('Click' ));
91
- expect (tester.widget <Text >(find.byKey (key2)).data, equals ('Click' ));
92
- expect (tester.widget <Text >(find.byKey (key3)).data, equals ('Click' ));
93
-
94
- await tester.tap (find.byType (ElevatedButton ));
95
- expect (message.value, equals ('Clicked' ));
96
-
97
- await tester.pump ();
98
- expect (tester.widget <Text >(find.byKey (key1)).data, equals ('Clicked' )); // Observer rebuilt the Text1
99
- expect (tester.widget <Text >(find.byKey (key2)).data, equals ('Click' )); // child Text2 did not change
100
- expect (tester.widget <Text >(find.byKey (key3)).data, equals ('Clicked' )); // Builder does not preserve from rebuild
65
+ final key1 = UniqueKey ();
66
+ final key2 = UniqueKey ();
67
+ final key3 = UniqueKey ();
68
+
69
+ await tester.pumpWidget (
70
+ MaterialApp (
71
+ home: Observer .withBuiltChild (
72
+ builderWithChild: (context, child) {
73
+ return Column (
74
+ children: [
75
+ ElevatedButton (
76
+ onPressed: () => message.value = 'Clicked' ,
77
+ child: Container ()),
78
+ Text (message.value, key: key1),
79
+ child! ,
80
+ Builder (builder: (context) {
81
+ return Text (message.value, key: key3);
82
+ }),
83
+ ],
84
+ );
85
+ },
86
+ child: Text (message.value, key: key2),
87
+ ),
88
+ ),
89
+ );
90
+
91
+ expect (tester.widget <Text >(find.byKey (key1)).data, equals ('Click' ));
92
+ expect (tester.widget <Text >(find.byKey (key2)).data, equals ('Click' ));
93
+ expect (tester.widget <Text >(find.byKey (key3)).data, equals ('Click' ));
94
+
95
+ await tester.tap (find.byType (ElevatedButton ));
96
+ expect (message.value, equals ('Clicked' ));
97
+
98
+ await tester.pump ();
99
+ expect (tester.widget <Text >(find.byKey (key1)).data,
100
+ equals ('Clicked' )); // Observer rebuilt the Text1
101
+ expect (tester.widget <Text >(find.byKey (key2)).data,
102
+ equals ('Click' )); // child Text2 did not change
103
+ expect (tester.widget <Text >(find.byKey (key3)).data,
104
+ equals ('Clicked' )); // Builder does not preserve from rebuild
101
105
});
102
106
103
107
testWidgets ('Observer build should call reaction.track' , (tester) async {
@@ -172,6 +176,34 @@ void main() {
172
176
expect (exception, isInstanceOf <MobXCaughtException >());
173
177
});
174
178
179
+ testWidgets (
180
+ 'Observer should print full stacktrace of error coming from computed' ,
181
+ (tester) async {
182
+ late StackTrace stackTrace;
183
+ final errorWrapper = await _testThrowingObserverWithStackTrace (
184
+ tester,
185
+ firstError: true ,
186
+ actionThrows: (tester) async {
187
+ await tester.pumpWidget (
188
+ Observer (
189
+ builder: (context) {
190
+ Computed (() {
191
+ try {
192
+ throw Exception ();
193
+ } on Exception catch (e, st) {
194
+ stackTrace = st;
195
+ rethrow ;
196
+ }
197
+ }).value;
198
+ },
199
+ ),
200
+ );
201
+ },
202
+ );
203
+ expect (errorWrapper.stackTrace, stackTrace);
204
+ },
205
+ );
206
+
175
207
testWidgets ('Observer unmount should dispose Reaction' , (tester) async {
176
208
final mock = MockReaction ();
177
209
when (() => mock.hasObservables).thenReturn (true );
@@ -341,18 +373,45 @@ Future<MobXCaughtException> _testThrowingObserver(
341
373
WidgetTester tester,
342
374
Object errorToThrow,
343
375
) async {
344
- late Object exception;
376
+ return (await _testThrowingObserverWithStackTrace (
377
+ tester,
378
+ actionThrows: (tester) async {
379
+ final count = Observable (0 );
380
+ await tester.pumpWidget (FlutterErrorThrowingObserver (
381
+ errorToThrow: errorToThrow,
382
+ builder: (context) => Text (count.value.toString ()),
383
+ ));
384
+ count.value++ ;
385
+ },
386
+ ))
387
+ .exception as MobXCaughtException ;
388
+ }
389
+
390
+ class _ErrorWrapper {
391
+ final Object exception;
392
+ final StackTrace ? stackTrace;
393
+
394
+ _ErrorWrapper ({required this .exception, required this .stackTrace});
395
+ }
396
+
397
+ Future <_ErrorWrapper > _testThrowingObserverWithStackTrace (
398
+ WidgetTester tester, {
399
+ required Future <void > Function (WidgetTester tester) actionThrows,
400
+ bool firstError = false ,
401
+ }) async {
402
+ _ErrorWrapper ? errorWrapper;
345
403
final prevOnError = FlutterError .onError;
346
- FlutterError .onError = (details) => exception = details.exception;
404
+ FlutterError .onError = (details) {
405
+ if (firstError && errorWrapper != null ) {
406
+ return ;
407
+ }
408
+ errorWrapper =
409
+ _ErrorWrapper (exception: details.exception, stackTrace: details.stack);
410
+ };
347
411
348
412
try {
349
- final count = Observable (0 );
350
- await tester.pumpWidget (FlutterErrorThrowingObserver (
351
- errorToThrow: errorToThrow,
352
- builder: (context) => Text (count.value.toString ()),
353
- ));
354
- count.value++ ;
355
- return exception as MobXCaughtException ;
413
+ await actionThrows (tester);
414
+ return errorWrapper! ;
356
415
} finally {
357
416
FlutterError .onError = prevOnError;
358
417
}
0 commit comments