Add stackTrace to AsyncSnapshot (#69507)
This commit is contained in:
parent
0d7270ba0c
commit
85f0aea87e
@ -75,10 +75,11 @@ abstract class StreamBuilderBase<T, S> extends StatefulWidget {
|
||||
/// is combined with the new data item in the fold computation.
|
||||
S afterData(S current, T data);
|
||||
|
||||
/// Returns an updated version of the [current] summary following an error.
|
||||
/// Returns an updated version of the [current] summary following an error
|
||||
/// with a stack trace.
|
||||
///
|
||||
/// The default implementation returns [current] as is.
|
||||
S afterError(S current, Object error) => current;
|
||||
S afterError(S current, Object error, StackTrace stackTrace) => current;
|
||||
|
||||
/// Returns an updated version of the [current] summary following stream
|
||||
/// termination.
|
||||
@ -138,9 +139,9 @@ class _StreamBuilderBaseState<T, S> extends State<StreamBuilderBase<T, S>> {
|
||||
setState(() {
|
||||
_summary = widget.afterData(_summary, data);
|
||||
});
|
||||
}, onError: (Object error) {
|
||||
}, onError: (Object error, StackTrace stackTrace) {
|
||||
setState(() {
|
||||
_summary = widget.afterError(_summary, error);
|
||||
_summary = widget.afterError(_summary, error, stackTrace);
|
||||
});
|
||||
}, onDone: () {
|
||||
setState(() {
|
||||
@ -204,22 +205,31 @@ enum ConnectionState {
|
||||
@immutable
|
||||
class AsyncSnapshot<T> {
|
||||
/// Creates an [AsyncSnapshot] with the specified [connectionState],
|
||||
/// and optionally either [data] or [error] (but not both).
|
||||
const AsyncSnapshot._(this.connectionState, this.data, this.error)
|
||||
/// and optionally either [data] or [error] with an optional [stackTrace]
|
||||
/// (but not both data and error).
|
||||
const AsyncSnapshot._(this.connectionState, this.data, this.error, this.stackTrace)
|
||||
: assert(connectionState != null),
|
||||
assert(!(data != null && error != null));
|
||||
assert(!(data != null && error != null)),
|
||||
assert(stackTrace == null || error != null);
|
||||
|
||||
/// Creates an [AsyncSnapshot] in [ConnectionState.none] with null data and error.
|
||||
const AsyncSnapshot.nothing() : this._(ConnectionState.none, null, null);
|
||||
const AsyncSnapshot.nothing() : this._(ConnectionState.none, null, null, null);
|
||||
|
||||
/// Creates an [AsyncSnapshot] in [ConnectionState.waiting] with null data and error.
|
||||
const AsyncSnapshot.waiting() : this._(ConnectionState.waiting, null, null);
|
||||
const AsyncSnapshot.waiting() : this._(ConnectionState.waiting, null, null, null);
|
||||
|
||||
/// Creates an [AsyncSnapshot] in the specified [state] and with the specified [data].
|
||||
const AsyncSnapshot.withData(ConnectionState state, T data): this._(state, data, null);
|
||||
const AsyncSnapshot.withData(ConnectionState state, T data): this._(state, data, null, null);
|
||||
|
||||
/// Creates an [AsyncSnapshot] in the specified [state] and with the specified [error].
|
||||
const AsyncSnapshot.withError(ConnectionState state, Object error) : this._(state, null, error);
|
||||
/// Creates an [AsyncSnapshot] in the specified [state] with the specified [error]
|
||||
/// and a [stackTrace].
|
||||
///
|
||||
/// If no [stackTrace] is explicitly specified, [StackTrace.empty] will be used instead.
|
||||
const AsyncSnapshot.withError(
|
||||
ConnectionState state,
|
||||
Object error, [
|
||||
StackTrace stackTrace = StackTrace.empty,
|
||||
]) : this._(state, null, error, stackTrace);
|
||||
|
||||
/// Current state of connection to the asynchronous computation.
|
||||
final ConnectionState connectionState;
|
||||
@ -254,11 +264,20 @@ class AsyncSnapshot<T> {
|
||||
/// If [data] is not null, this will be null.
|
||||
final Object? error;
|
||||
|
||||
/// The latest stack trace object received by the asynchronous computation.
|
||||
///
|
||||
/// This will not be null iff [error] is not null. Consequently, [stackTrace]
|
||||
/// will be non-null when [hasError] is true.
|
||||
///
|
||||
/// However, even when not null, [stackTrace] might be empty. The stack trace
|
||||
/// is empty when there is an error but no stack trace has been provided.
|
||||
final StackTrace? stackTrace;
|
||||
|
||||
/// Returns a snapshot like this one, but in the specified [state].
|
||||
///
|
||||
/// The [data] and [error] fields persist unmodified, even if the new state is
|
||||
/// [ConnectionState.none].
|
||||
AsyncSnapshot<T> inState(ConnectionState state) => AsyncSnapshot<T>._(state, data, error);
|
||||
/// The [data], [error], and [stackTrace] fields persist unmodified, even if
|
||||
/// the new state is [ConnectionState.none].
|
||||
AsyncSnapshot<T> inState(ConnectionState state) => AsyncSnapshot<T>._(state, data, error, stackTrace);
|
||||
|
||||
/// Returns whether this snapshot contains a non-null [data] value.
|
||||
///
|
||||
@ -275,7 +294,7 @@ class AsyncSnapshot<T> {
|
||||
bool get hasError => error != null;
|
||||
|
||||
@override
|
||||
String toString() => '${objectRuntimeType(this, 'AsyncSnapshot')}($connectionState, $data, $error)';
|
||||
String toString() => '${objectRuntimeType(this, 'AsyncSnapshot')}($connectionState, $data, $error, $stackTrace)';
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
@ -284,7 +303,8 @@ class AsyncSnapshot<T> {
|
||||
return other is AsyncSnapshot<T>
|
||||
&& other.connectionState == connectionState
|
||||
&& other.data == data
|
||||
&& other.error == error;
|
||||
&& other.error == error
|
||||
&& other.stackTrace == stackTrace;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -318,12 +338,12 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
|
||||
/// of the following snapshots that includes the last one (the one with
|
||||
/// ConnectionState.done):
|
||||
///
|
||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.waiting, null)`
|
||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.active, 0)`
|
||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.active, 1)`
|
||||
/// * `AsyncSnapshot<int>.withData(ConnectionState.waiting, null)`
|
||||
/// * `AsyncSnapshot<int>.withData(ConnectionState.active, 0)`
|
||||
/// * `AsyncSnapshot<int>.withData(ConnectionState.active, 1)`
|
||||
/// * ...
|
||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.active, 9)`
|
||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.done, 9)`
|
||||
/// * `AsyncSnapshot<int>.withData(ConnectionState.active, 9)`
|
||||
/// * `AsyncSnapshot<int>.withData(ConnectionState.done, 9)`
|
||||
///
|
||||
/// The actual sequence of invocations of the [builder] depends on the relative
|
||||
/// timing of events produced by the stream and the build rate of the Flutter
|
||||
@ -332,15 +352,15 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
|
||||
/// Changing the [StreamBuilder] configuration to another stream during event
|
||||
/// generation introduces snapshot pairs of the form:
|
||||
///
|
||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.none, 5)`
|
||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.waiting, 5)`
|
||||
/// * `AsyncSnapshot<int>.withData(ConnectionState.none, 5)`
|
||||
/// * `AsyncSnapshot<int>.withData(ConnectionState.waiting, 5)`
|
||||
///
|
||||
/// The latter will be produced only when the new stream is non-null, and the
|
||||
/// former only when the old stream is non-null.
|
||||
///
|
||||
/// The stream may produce errors, resulting in snapshots of the form:
|
||||
///
|
||||
/// * `new AsyncSnapshot<int>.withError(ConnectionState.active, 'some error')`
|
||||
/// * `AsyncSnapshot<int>.withError(ConnectionState.active, 'some error', someStackTrace)`
|
||||
///
|
||||
/// The data and error fields of snapshots produced are only changed when the
|
||||
/// state is `ConnectionState.active`.
|
||||
@ -386,7 +406,11 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
|
||||
/// Padding(
|
||||
/// padding: const EdgeInsets.only(top: 16),
|
||||
/// child: Text('Error: ${snapshot.error}'),
|
||||
/// )
|
||||
/// ),
|
||||
/// Padding(
|
||||
/// padding: const EdgeInsets.only(top: 8),
|
||||
/// child: Text('Stack trace: ${snapshot.stackTrace}'),
|
||||
/// ),
|
||||
/// ];
|
||||
/// } else {
|
||||
/// switch (snapshot.connectionState) {
|
||||
@ -511,8 +535,8 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
|
||||
}
|
||||
|
||||
@override
|
||||
AsyncSnapshot<T> afterError(AsyncSnapshot<T> current, Object error) {
|
||||
return AsyncSnapshot<T>.withError(ConnectionState.active, error);
|
||||
AsyncSnapshot<T> afterError(AsyncSnapshot<T> current, Object error, StackTrace stackTrace) {
|
||||
return AsyncSnapshot<T>.withError(ConnectionState.active, error, stackTrace);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -559,14 +583,14 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
|
||||
/// is null, the [builder] will be called with either both or only the latter of
|
||||
/// the following snapshots:
|
||||
///
|
||||
/// * `new AsyncSnapshot<String>.withData(ConnectionState.waiting, null)`
|
||||
/// * `new AsyncSnapshot<String>.withData(ConnectionState.done, 'some data')`
|
||||
/// * `AsyncSnapshot<String>.withData(ConnectionState.waiting, null)`
|
||||
/// * `AsyncSnapshot<String>.withData(ConnectionState.done, 'some data')`
|
||||
///
|
||||
/// If that same future instead completed with an error, the [builder] would be
|
||||
/// called with either both or only the latter of:
|
||||
///
|
||||
/// * `new AsyncSnapshot<String>.withData(ConnectionState.waiting, null)`
|
||||
/// * `new AsyncSnapshot<String>.withError(ConnectionState.done, 'some error')`
|
||||
/// * `AsyncSnapshot<String>.withData(ConnectionState.waiting, null)`
|
||||
/// * `AsyncSnapshot<String>.withError(ConnectionState.done, 'some error', someStackTrace)`
|
||||
///
|
||||
/// The initial snapshot data can be controlled by specifying [initialData]. You
|
||||
/// would use this facility to ensure that if the [builder] is invoked before
|
||||
@ -579,8 +603,8 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
|
||||
/// old future has already completed successfully with data as above, changing
|
||||
/// configuration to a new future results in snapshot pairs of the form:
|
||||
///
|
||||
/// * `new AsyncSnapshot<String>.withData(ConnectionState.none, 'data of first future')`
|
||||
/// * `new AsyncSnapshot<String>.withData(ConnectionState.waiting, 'data of second future')`
|
||||
/// * `AsyncSnapshot<String>.withData(ConnectionState.none, 'data of first future')`
|
||||
/// * `AsyncSnapshot<String>.withData(ConnectionState.waiting, 'data of second future')`
|
||||
///
|
||||
/// In general, the latter will be produced only when the new future is
|
||||
/// non-null, and the former only when the old future is non-null.
|
||||
@ -768,10 +792,10 @@ class _FutureBuilderState<T> extends State<FutureBuilder<T>> {
|
||||
_snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data);
|
||||
});
|
||||
}
|
||||
}, onError: (Object error) {
|
||||
}, onError: (Object error, StackTrace stackTrace) {
|
||||
if (_activeCallbackIdentity == callbackIdentity) {
|
||||
setState(() {
|
||||
_snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error);
|
||||
_snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error, stackTrace);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -34,9 +34,20 @@ void main() {
|
||||
expect(const AsyncSnapshot<int>.nothing().connectionState, ConnectionState.none);
|
||||
expect(const AsyncSnapshot<int>.nothing().data, isNull);
|
||||
expect(const AsyncSnapshot<int>.nothing().error, isNull);
|
||||
expect(const AsyncSnapshot<int>.nothing().stackTrace, isNull);
|
||||
expect(const AsyncSnapshot<int>.waiting().connectionState, ConnectionState.waiting);
|
||||
expect(const AsyncSnapshot<int>.waiting().data, isNull);
|
||||
expect(const AsyncSnapshot<int>.waiting().error, isNull);
|
||||
expect(const AsyncSnapshot<int>.waiting().stackTrace, isNull);
|
||||
});
|
||||
test('withError uses empty stack trace if no stack trace is specified', () {
|
||||
// We need to store the error as a local variable in order for the
|
||||
// equality check on the error to be true.
|
||||
final Error error = Error();
|
||||
expect(
|
||||
AsyncSnapshot<int>.withError(ConnectionState.done, error),
|
||||
AsyncSnapshot<int>.withError(
|
||||
ConnectionState.done, error, StackTrace.empty));
|
||||
});
|
||||
});
|
||||
group('Async smoke tests', () {
|
||||
@ -67,12 +78,12 @@ void main() {
|
||||
await tester.pumpWidget(FutureBuilder<String>(
|
||||
key: key, future: null, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsOneWidget);
|
||||
final Completer<String> completer = Completer<String>();
|
||||
await tester.pumpWidget(FutureBuilder<String>(
|
||||
key: key, future: completer.future, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||
});
|
||||
testWidgets('gracefully handles transition to null future', (WidgetTester tester) async {
|
||||
final GlobalKey key = GlobalKey();
|
||||
@ -80,14 +91,14 @@ void main() {
|
||||
await tester.pumpWidget(FutureBuilder<String>(
|
||||
key: key, future: completer.future, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||
await tester.pumpWidget(FutureBuilder<String>(
|
||||
key: key, future: null, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsOneWidget);
|
||||
completer.complete('hello');
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsOneWidget);
|
||||
});
|
||||
testWidgets('gracefully handles transition to other future', (WidgetTester tester) async {
|
||||
final GlobalKey key = GlobalKey();
|
||||
@ -96,35 +107,35 @@ void main() {
|
||||
await tester.pumpWidget(FutureBuilder<String>(
|
||||
key: key, future: completerA.future, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||
await tester.pumpWidget(FutureBuilder<String>(
|
||||
key: key, future: completerB.future, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||
completerB.complete('B');
|
||||
completerA.complete('A');
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, B, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, B, null, null)'), findsOneWidget);
|
||||
});
|
||||
testWidgets('tracks life-cycle of Future to success', (WidgetTester tester) async {
|
||||
final Completer<String> completer = Completer<String>();
|
||||
await tester.pumpWidget(FutureBuilder<String>(
|
||||
future: completer.future, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||
completer.complete('hello');
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null, null)'), findsOneWidget);
|
||||
});
|
||||
testWidgets('tracks life-cycle of Future to error', (WidgetTester tester) async {
|
||||
final Completer<String> completer = Completer<String>();
|
||||
await tester.pumpWidget(FutureBuilder<String>(
|
||||
future: completer.future, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
||||
completer.completeError('bad');
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||
completer.completeError('bad', StackTrace.fromString('trace'));
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad, trace)'), findsOneWidget);
|
||||
});
|
||||
testWidgets('runs the builder using given initial data', (WidgetTester tester) async {
|
||||
final GlobalKey key = GlobalKey();
|
||||
@ -134,7 +145,7 @@ void main() {
|
||||
builder: snapshotText,
|
||||
initialData: 'I',
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null, null)'), findsOneWidget);
|
||||
});
|
||||
testWidgets('ignores initialData when reconfiguring', (WidgetTester tester) async {
|
||||
final GlobalKey key = GlobalKey();
|
||||
@ -144,7 +155,7 @@ void main() {
|
||||
builder: snapshotText,
|
||||
initialData: 'I',
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null, null)'), findsOneWidget);
|
||||
final Completer<String> completer = Completer<String>();
|
||||
await tester.pumpWidget(FutureBuilder<String>(
|
||||
key: key,
|
||||
@ -152,7 +163,7 @@ void main() {
|
||||
builder: snapshotText,
|
||||
initialData: 'Ignored',
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null, null)'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
group('StreamBuilder', () {
|
||||
@ -161,12 +172,12 @@ void main() {
|
||||
await tester.pumpWidget(StreamBuilder<String>(
|
||||
key: key, stream: null, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsOneWidget);
|
||||
final StreamController<String> controller = StreamController<String>();
|
||||
await tester.pumpWidget(StreamBuilder<String>(
|
||||
key: key, stream: controller.stream, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||
});
|
||||
testWidgets('gracefully handles transition to null stream', (WidgetTester tester) async {
|
||||
final GlobalKey key = GlobalKey();
|
||||
@ -174,11 +185,11 @@ void main() {
|
||||
await tester.pumpWidget(StreamBuilder<String>(
|
||||
key: key, stream: controller.stream, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||
await tester.pumpWidget(StreamBuilder<String>(
|
||||
key: key, stream: null, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsOneWidget);
|
||||
});
|
||||
testWidgets('gracefully handles transition to other stream', (WidgetTester tester) async {
|
||||
final GlobalKey key = GlobalKey();
|
||||
@ -187,14 +198,14 @@ void main() {
|
||||
await tester.pumpWidget(StreamBuilder<String>(
|
||||
key: key, stream: controllerA.stream, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||
await tester.pumpWidget(StreamBuilder<String>(
|
||||
key: key, stream: controllerB.stream, builder: snapshotText,
|
||||
));
|
||||
controllerB.add('B');
|
||||
controllerA.add('A');
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, B, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, B, null, null)'), findsOneWidget);
|
||||
});
|
||||
testWidgets('tracks events and errors of stream until completion', (WidgetTester tester) async {
|
||||
final GlobalKey key = GlobalKey();
|
||||
@ -202,19 +213,19 @@ void main() {
|
||||
await tester.pumpWidget(StreamBuilder<String>(
|
||||
key: key, stream: controller.stream, builder: snapshotText,
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||
controller.add('1');
|
||||
controller.add('2');
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, 2, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, 2, null, null)'), findsOneWidget);
|
||||
controller.add('3');
|
||||
controller.addError('bad');
|
||||
controller.addError('bad', StackTrace.fromString('trace'));
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, null, bad)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, null, bad, trace)'), findsOneWidget);
|
||||
controller.add('4');
|
||||
controller.close();
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, 4, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, 4, null, null)'), findsOneWidget);
|
||||
});
|
||||
testWidgets('runs the builder using given initial data', (WidgetTester tester) async {
|
||||
final StreamController<String> controller = StreamController<String>();
|
||||
@ -223,7 +234,7 @@ void main() {
|
||||
builder: snapshotText,
|
||||
initialData: 'I',
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null, null)'), findsOneWidget);
|
||||
});
|
||||
testWidgets('ignores initialData when reconfiguring', (WidgetTester tester) async {
|
||||
final GlobalKey key = GlobalKey();
|
||||
@ -233,7 +244,7 @@ void main() {
|
||||
builder: snapshotText,
|
||||
initialData: 'I',
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null, null)'), findsOneWidget);
|
||||
final StreamController<String> controller = StreamController<String>();
|
||||
await tester.pumpWidget(StreamBuilder<String>(
|
||||
key: key,
|
||||
@ -241,7 +252,7 @@ void main() {
|
||||
builder: snapshotText,
|
||||
initialData: 'Ignored',
|
||||
));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsOneWidget);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null, null)'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
group('FutureBuilder and StreamBuilder behave identically on Stream from Future', () {
|
||||
@ -251,35 +262,46 @@ void main() {
|
||||
FutureBuilder<String>(future: completer.future, builder: snapshotText),
|
||||
StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText),
|
||||
]));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsNWidgets(2));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsNWidgets(2));
|
||||
completer.complete('hello');
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null)'), findsNWidgets(2));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null, null)'), findsNWidgets(2));
|
||||
});
|
||||
testWidgets('when completing with error', (WidgetTester tester) async {
|
||||
testWidgets('when completing with error and with empty stack trace', (WidgetTester tester) async {
|
||||
final Completer<String> completer = Completer<String>();
|
||||
await tester.pumpWidget(Column(children: <Widget>[
|
||||
FutureBuilder<String>(future: completer.future, builder: snapshotText),
|
||||
StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText),
|
||||
]));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsNWidgets(2));
|
||||
completer.completeError('bad');
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsNWidgets(2));
|
||||
completer.completeError('bad', StackTrace.empty);
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad)'), findsNWidgets(2));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad, )'), findsNWidgets(2));
|
||||
});
|
||||
testWidgets('when completing with error and with stack trace', (WidgetTester tester) async {
|
||||
final Completer<String> completer = Completer<String>();
|
||||
await tester.pumpWidget(Column(children: <Widget>[
|
||||
FutureBuilder<String>(future: completer.future, builder: snapshotText),
|
||||
StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText),
|
||||
]));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsNWidgets(2));
|
||||
completer.completeError('bad', StackTrace.fromString('trace'));
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad, trace)'), findsNWidgets(2));
|
||||
});
|
||||
testWidgets('when Future is null', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(Column(children: <Widget>[
|
||||
FutureBuilder<String>(future: null, builder: snapshotText),
|
||||
StreamBuilder<String>(stream: null, builder: snapshotText),
|
||||
]));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsNWidgets(2));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsNWidgets(2));
|
||||
});
|
||||
testWidgets('when initialData is used with null Future and Stream', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(Column(children: <Widget>[
|
||||
FutureBuilder<String>(future: null, builder: snapshotText, initialData: 'I'),
|
||||
StreamBuilder<String>(stream: null, builder: snapshotText, initialData: 'I'),
|
||||
]));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsNWidgets(2));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null, null)'), findsNWidgets(2));
|
||||
});
|
||||
testWidgets('when using initialData and completing with data', (WidgetTester tester) async {
|
||||
final Completer<String> completer = Completer<String>();
|
||||
@ -287,10 +309,10 @@ void main() {
|
||||
FutureBuilder<String>(future: completer.future, builder: snapshotText, initialData: 'I'),
|
||||
StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText, initialData: 'I'),
|
||||
]));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsNWidgets(2));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null, null)'), findsNWidgets(2));
|
||||
completer.complete('hello');
|
||||
await eventFiring(tester);
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null)'), findsNWidgets(2));
|
||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null, null)'), findsNWidgets(2));
|
||||
});
|
||||
});
|
||||
group('StreamBuilderBase', () {
|
||||
@ -326,11 +348,11 @@ void main() {
|
||||
final StreamController<String> controller = StreamController<String>();
|
||||
await tester.pumpWidget(StringCollector(key: key, stream: controller.stream));
|
||||
controller.add('1');
|
||||
controller.addError('bad');
|
||||
controller.addError('bad', StackTrace.fromString('trace'));
|
||||
controller.add('2');
|
||||
controller.close();
|
||||
await eventFiring(tester);
|
||||
expect(find.text('conn, data:1, error:bad, data:2, done'), findsOneWidget);
|
||||
expect(find.text('conn, data:1, error:bad stackTrace:trace, data:2, done'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -352,7 +374,7 @@ class StringCollector extends StreamBuilderBase<String, List<String>> {
|
||||
List<String> afterData(List<String> current, String data) => current..add('data:$data');
|
||||
|
||||
@override
|
||||
List<String> afterError(List<String> current, dynamic error) => current..add('error:$error');
|
||||
List<String> afterError(List<String> current, dynamic error, StackTrace stackTrace) => current..add('error:$error stackTrace:$stackTrace');
|
||||
|
||||
@override
|
||||
List<String> afterDone(List<String> current) => current..add('done');
|
||||
|
Loading…
x
Reference in New Issue
Block a user