async_test.dart 16.7 KB
Newer Older
1 2 3 4 5 6
// Copyright 2015 The Chromium 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 'dart:async';

7 8 9
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';

10
void main() {
11
  Widget snapshotText(BuildContext context, AsyncSnapshot<String> snapshot) {
12
    return Text(snapshot.toString(), textDirection: TextDirection.ltr);
13
  }
14 15 16
  group('AsyncSnapshot', () {
    test('requiring data succeeds if data is present', () {
      expect(
17
        const AsyncSnapshot<String>.withData(ConnectionState.done, 'hello').requireData,
18 19 20 21 22
        'hello',
      );
    });
    test('requiring data fails if there is an error', () {
      expect(
23
        () => const AsyncSnapshot<String>.withError(ConnectionState.done, 'error').requireData,
24 25 26 27 28
        throwsA(equals('error')),
      );
    });
    test('requiring data fails if snapshot has neither data nor error', () {
      expect(
29
        () => const AsyncSnapshot<String>.nothing().requireData,
30 31 32 33
        throwsStateError,
      );
    });
  });
34 35
  group('Async smoke tests', () {
    testWidgets('FutureBuilder', (WidgetTester tester) async {
36 37
      await tester.pumpWidget(FutureBuilder<String>(
        future: Future<String>.value('hello'),
38 39 40 41 42
        builder: snapshotText
      ));
      await eventFiring(tester);
    });
    testWidgets('StreamBuilder', (WidgetTester tester) async {
43 44
      await tester.pumpWidget(StreamBuilder<String>(
        stream: Stream<String>.fromIterable(<String>['hello', 'world']),
45 46 47 48 49
        builder: snapshotText
      ));
      await eventFiring(tester);
    });
    testWidgets('StreamFold', (WidgetTester tester) async {
50 51
      await tester.pumpWidget(StringCollector(
        stream: Stream<String>.fromIterable(<String>['hello', 'world'])
52 53 54 55 56 57
      ));
      await eventFiring(tester);
    });
  });
  group('FutureBuilder', () {
    testWidgets('gracefully handles transition from null future', (WidgetTester tester) async {
58 59
      final GlobalKey key = GlobalKey();
      await tester.pumpWidget(FutureBuilder<String>(
60 61
        key: key, future: null, builder: snapshotText
      ));
62
      expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
63 64
      final Completer<String> completer = Completer<String>();
      await tester.pumpWidget(FutureBuilder<String>(
65 66
        key: key, future: completer.future, builder: snapshotText
      ));
67
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
68 69
    });
    testWidgets('gracefully handles transition to null future', (WidgetTester tester) async {
70 71 72
      final GlobalKey key = GlobalKey();
      final Completer<String> completer = Completer<String>();
      await tester.pumpWidget(FutureBuilder<String>(
73 74
        key: key, future: completer.future, builder: snapshotText
      ));
75
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
76
      await tester.pumpWidget(FutureBuilder<String>(
77 78
        key: key, future: null, builder: snapshotText
      ));
79
      expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
80 81
      completer.complete('hello');
      await eventFiring(tester);
82
      expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
83 84
    });
    testWidgets('gracefully handles transition to other future', (WidgetTester tester) async {
85 86 87 88
      final GlobalKey key = GlobalKey();
      final Completer<String> completerA = Completer<String>();
      final Completer<String> completerB = Completer<String>();
      await tester.pumpWidget(FutureBuilder<String>(
89 90
        key: key, future: completerA.future, builder: snapshotText
      ));
91
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
92
      await tester.pumpWidget(FutureBuilder<String>(
93 94
        key: key, future: completerB.future, builder: snapshotText
      ));
95
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
96 97 98
      completerB.complete('B');
      completerA.complete('A');
      await eventFiring(tester);
99
      expect(find.text('AsyncSnapshot<String>(ConnectionState.done, B, null)'), findsOneWidget);
100 101
    });
    testWidgets('tracks life-cycle of Future to success', (WidgetTester tester) async {
102 103
      final Completer<String> completer = Completer<String>();
      await tester.pumpWidget(FutureBuilder<String>(
104 105
        future: completer.future, builder: snapshotText
      ));
106
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
107 108
      completer.complete('hello');
      await eventFiring(tester);
109
      expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null)'), findsOneWidget);
110 111
    });
    testWidgets('tracks life-cycle of Future to error', (WidgetTester tester) async {
112 113
      final Completer<String> completer = Completer<String>();
      await tester.pumpWidget(FutureBuilder<String>(
114 115
        future: completer.future, builder: snapshotText
      ));
116
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
117 118
      completer.completeError('bad');
      await eventFiring(tester);
119
      expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad)'), findsOneWidget);
120
    });
121
    testWidgets('runs the builder using given initial data', (WidgetTester tester) async {
122 123
      final GlobalKey key = GlobalKey();
      await tester.pumpWidget(FutureBuilder<String>(
124 125 126 127 128
        key: key,
        future: null,
        builder: snapshotText,
        initialData: 'I',
      ));
129
      expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsOneWidget);
130 131
    });
    testWidgets('ignores initialData when reconfiguring', (WidgetTester tester) async {
132 133
      final GlobalKey key = GlobalKey();
      await tester.pumpWidget(FutureBuilder<String>(
134 135 136 137 138
        key: key,
        future: null,
        builder: snapshotText,
        initialData: 'I',
      ));
139
      expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsOneWidget);
140 141
      final Completer<String> completer = Completer<String>();
      await tester.pumpWidget(FutureBuilder<String>(
142 143 144 145 146
        key: key,
        future: completer.future,
        builder: snapshotText,
        initialData: 'Ignored',
      ));
147
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsOneWidget);
148
    });
149 150 151
  });
  group('StreamBuilder', () {
    testWidgets('gracefully handles transition from null stream', (WidgetTester tester) async {
152 153
      final GlobalKey key = GlobalKey();
      await tester.pumpWidget(StreamBuilder<String>(
154 155
        key: key, stream: null, builder: snapshotText
      ));
156
      expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
157 158
      final StreamController<String> controller = StreamController<String>();
      await tester.pumpWidget(StreamBuilder<String>(
159 160
        key: key, stream: controller.stream, builder: snapshotText
      ));
161
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
162 163
    });
    testWidgets('gracefully handles transition to null stream', (WidgetTester tester) async {
164 165 166
      final GlobalKey key = GlobalKey();
      final StreamController<String> controller = StreamController<String>();
      await tester.pumpWidget(StreamBuilder<String>(
167 168
        key: key, stream: controller.stream, builder: snapshotText
      ));
169
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
170
      await tester.pumpWidget(StreamBuilder<String>(
171 172
        key: key, stream: null, builder: snapshotText
      ));
173
      expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
174 175
    });
    testWidgets('gracefully handles transition to other stream', (WidgetTester tester) async {
176 177 178 179
      final GlobalKey key = GlobalKey();
      final StreamController<String> controllerA = StreamController<String>();
      final StreamController<String> controllerB = StreamController<String>();
      await tester.pumpWidget(StreamBuilder<String>(
180 181
        key: key, stream: controllerA.stream, builder: snapshotText
      ));
182
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
183
      await tester.pumpWidget(StreamBuilder<String>(
184 185 186 187 188
        key: key, stream: controllerB.stream, builder: snapshotText
      ));
      controllerB.add('B');
      controllerA.add('A');
      await eventFiring(tester);
189
      expect(find.text('AsyncSnapshot<String>(ConnectionState.active, B, null)'), findsOneWidget);
190 191
    });
    testWidgets('tracks events and errors of stream until completion', (WidgetTester tester) async {
192 193 194
      final GlobalKey key = GlobalKey();
      final StreamController<String> controller = StreamController<String>();
      await tester.pumpWidget(StreamBuilder<String>(
195 196
        key: key, stream: controller.stream, builder: snapshotText
      ));
197
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
198 199 200
      controller.add('1');
      controller.add('2');
      await eventFiring(tester);
201
      expect(find.text('AsyncSnapshot<String>(ConnectionState.active, 2, null)'), findsOneWidget);
202 203 204
      controller.add('3');
      controller.addError('bad');
      await eventFiring(tester);
205
      expect(find.text('AsyncSnapshot<String>(ConnectionState.active, null, bad)'), findsOneWidget);
206 207 208
      controller.add('4');
      controller.close();
      await eventFiring(tester);
209
      expect(find.text('AsyncSnapshot<String>(ConnectionState.done, 4, null)'), findsOneWidget);
210
    });
211
    testWidgets('runs the builder using given initial data', (WidgetTester tester) async {
212 213
      final StreamController<String> controller = StreamController<String>();
      await tester.pumpWidget(StreamBuilder<String>(
214 215 216 217
        stream: controller.stream,
        builder: snapshotText,
        initialData: 'I',
      ));
218
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsOneWidget);
219 220
    });
    testWidgets('ignores initialData when reconfiguring', (WidgetTester tester) async {
221 222
      final GlobalKey key = GlobalKey();
      await tester.pumpWidget(StreamBuilder<String>(
223 224 225 226 227
        key: key,
        stream: null,
        builder: snapshotText,
        initialData: 'I',
      ));
228
      expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsOneWidget);
229 230
      final StreamController<String> controller = StreamController<String>();
      await tester.pumpWidget(StreamBuilder<String>(
231 232 233 234 235
        key: key,
        stream: controller.stream,
        builder: snapshotText,
        initialData: 'Ignored',
      ));
236
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsOneWidget);
237
    });
238 239 240
  });
  group('FutureBuilder and StreamBuilder behave identically on Stream from Future', () {
    testWidgets('when completing with data', (WidgetTester tester) async {
241 242 243 244
      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),
245
      ]));
246
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsNWidgets(2));
247 248
      completer.complete('hello');
      await eventFiring(tester);
249
      expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null)'), findsNWidgets(2));
250 251
    });
    testWidgets('when completing with error', (WidgetTester tester) async {
252 253 254 255
      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),
256
      ]));
257
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsNWidgets(2));
258 259
      completer.completeError('bad');
      await eventFiring(tester);
260
      expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad)'), findsNWidgets(2));
261 262
    });
    testWidgets('when Future is null', (WidgetTester tester) async {
263 264 265
      await tester.pumpWidget(Column(children: <Widget>[
        FutureBuilder<String>(future: null, builder: snapshotText),
        StreamBuilder<String>(stream: null, builder: snapshotText),
266
      ]));
267
      expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsNWidgets(2));
268
    });
269
    testWidgets('when initialData is used with null Future and Stream', (WidgetTester tester) async {
270 271 272
      await tester.pumpWidget(Column(children: <Widget>[
        FutureBuilder<String>(future: null, builder: snapshotText, initialData: 'I'),
        StreamBuilder<String>(stream: null, builder: snapshotText, initialData: 'I'),
273
      ]));
274
      expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsNWidgets(2));
275 276
    });
    testWidgets('when using initialData and completing with data', (WidgetTester tester) async {
277 278 279 280
      final Completer<String> completer = Completer<String>();
      await tester.pumpWidget(Column(children: <Widget>[
        FutureBuilder<String>(future: completer.future, builder: snapshotText, initialData: 'I'),
        StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText, initialData: 'I'),
281
      ]));
282
      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsNWidgets(2));
283 284
      completer.complete('hello');
      await eventFiring(tester);
285
      expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null)'), findsNWidgets(2));
286
    });
287 288 289
  });
  group('StreamBuilderBase', () {
    testWidgets('gracefully handles transition from null stream', (WidgetTester tester) async {
290 291
      final GlobalKey key = GlobalKey();
      await tester.pumpWidget(StringCollector(key: key, stream: null));
292
      expect(find.text(''), findsOneWidget);
293 294
      final StreamController<String> controller = StreamController<String>();
      await tester.pumpWidget(StringCollector(key: key, stream: controller.stream));
295 296 297
      expect(find.text('conn'), findsOneWidget);
    });
    testWidgets('gracefully handles transition to null stream', (WidgetTester tester) async {
298 299 300
      final GlobalKey key = GlobalKey();
      final StreamController<String> controller = StreamController<String>();
      await tester.pumpWidget(StringCollector(key: key, stream: controller.stream));
301
      expect(find.text('conn'), findsOneWidget);
302
      await tester.pumpWidget(StringCollector(key: key, stream: null));
303 304 305
      expect(find.text('conn, disc'), findsOneWidget);
    });
    testWidgets('gracefully handles transition to other stream', (WidgetTester tester) async {
306 307 308 309 310
      final GlobalKey key = GlobalKey();
      final StreamController<String> controllerA = StreamController<String>();
      final StreamController<String> controllerB = StreamController<String>();
      await tester.pumpWidget(StringCollector(key: key, stream: controllerA.stream));
      await tester.pumpWidget(StringCollector(key: key, stream: controllerB.stream));
311 312 313 314 315 316
      controllerA.add('A');
      controllerB.add('B');
      await eventFiring(tester);
      expect(find.text('conn, disc, conn, data:B'), findsOneWidget);
    });
    testWidgets('tracks events and errors until completion', (WidgetTester tester) async {
317 318 319
      final GlobalKey key = GlobalKey();
      final StreamController<String> controller = StreamController<String>();
      await tester.pumpWidget(StringCollector(key: key, stream: controller.stream));
320 321 322 323 324 325 326 327 328 329
      controller.add('1');
      controller.addError('bad');
      controller.add('2');
      controller.close();
      await eventFiring(tester);
      expect(find.text('conn, data:1, error:bad, data:2, done'), findsOneWidget);
    });
  });
}

330
Future<void> eventFiring(WidgetTester tester) async {
331
  await tester.pump(Duration.zero);
332 333 334
}

class StringCollector extends StreamBuilderBase<String, List<String>> {
335
  const StringCollector({ Key key, Stream<String> stream }) : super(key: key, stream: stream);
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355

  @override
  List<String> initial() => <String>[];

  @override
  List<String> afterConnected(List<String> current) => current..add('conn');

  @override
  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');

  @override
  List<String> afterDone(List<String> current) => current..add('done');

  @override
  List<String> afterDisconnected(List<String> current) => current..add('disc');

  @override
356
  Widget build(BuildContext context, List<String> currentSummary) => Text(currentSummary.join(', '), textDirection: TextDirection.ltr);
357
}