routes_test.dart 13.2 KB
Newer Older
1 2 3 4
// 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.

5 6
import 'dart:collection';

7
import 'package:mockito/mockito.dart';
8 9 10 11 12
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';

final List<String> results = <String>[];

13
Set<TestRoute> routes = new HashSet<TestRoute>();
14

Hans Muller's avatar
Hans Muller committed
15
class TestRoute extends LocalHistoryRoute<String> {
16 17 18
  TestRoute(this.name);
  final String name;

19
  @override
20 21
  List<OverlayEntry> get overlayEntries => _entries;

22
  final List<OverlayEntry> _entries = <OverlayEntry>[];
23 24 25 26 27

  void log(String s) {
    results.add('$name: $s');
  }

28
  @override
Hixie's avatar
Hixie committed
29
  void install(OverlayEntry insertionPoint) {
30
    log('install');
31
    final OverlayEntry entry = new OverlayEntry(
32 33 34 35
      builder: (BuildContext context) => new Container(),
      opaque: true
    );
    _entries.add(entry);
Hixie's avatar
Hixie committed
36
    navigator.overlay?.insert(entry, above: insertionPoint);
37
    routes.add(this);
38
    super.install(insertionPoint);
39 40
  }

41
  @override
42
  TickerFuture didPush() {
43
    log('didPush');
44
    return super.didPush();
45 46
  }

47
  @override
48
  void didReplace(covariant TestRoute oldRoute) {
49
    log('didReplace ${oldRoute.name}');
50
    super.didReplace(oldRoute);
51 52
  }

53
  @override
54 55
  bool didPop(String result) {
    log('didPop $result');
56 57
    bool returnValue;
    if (returnValue = super.didPop(result))
58
      navigator.finalizeRoute(this);
59
    return returnValue;
60 61
  }

62
  @override
63
  void didPopNext(covariant TestRoute nextRoute) {
64
    log('didPopNext ${nextRoute.name}');
65
    super.didPopNext(nextRoute);
66 67
  }

68
  @override
69
  void didChangeNext(covariant TestRoute nextRoute) {
Hixie's avatar
Hixie committed
70
    log('didChangeNext ${nextRoute?.name}');
71
    super.didChangeNext(nextRoute);
72 73
  }

74
  @override
75 76
  void dispose() {
    log('dispose');
77 78
    for (OverlayEntry entry in _entries)
      entry.remove();
79
    _entries.clear();
80
    routes.remove(this);
81
    super.dispose();
82 83 84 85
  }

}

86
Future<Null> runNavigatorTest(
87 88
  WidgetTester tester,
  NavigatorState host,
89
  VoidCallback test,
90
  List<String> expectations
91
) async {
92
  expect(host, isNotNull);
93
  test();
94 95
  expect(results, equals(expectations));
  results.clear();
96
  await tester.pump();
97 98 99
}

void main() {
100
  testWidgets('Route settings', (WidgetTester tester) async {
101
    const RouteSettings settings = const RouteSettings(name: 'A');
102
    expect(settings, hasOneLineDescription);
103 104 105 106 107 108
    final RouteSettings settings2 = settings.copyWith(name: 'B');
    expect(settings2.name, 'B');
    expect(settings2.isInitialRoute, false);
    final RouteSettings settings3 = settings2.copyWith(isInitialRoute: true);
    expect(settings3.name, 'B');
    expect(settings3.isInitialRoute, true);
109 110
  });

111
  testWidgets('Route management - push, replace, pop', (WidgetTester tester) async {
112
    final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
113 114 115 116 117 118 119 120 121
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new Navigator(
          key: navigatorKey,
          onGenerateRoute: (_) => new TestRoute('initial'),
        ),
      ),
    );
122
    final NavigatorState host = navigatorKey.currentState;
123
    await runNavigatorTest(
124 125
      tester,
      host,
126
      () { },
127
      <String>[
128 129 130 131 132 133
        'initial: install',
        'initial: didPush',
        'initial: didChangeNext null',
      ]
    );
    TestRoute second;
134
    await runNavigatorTest(
135 136
      tester,
      host,
137
      () { host.push(second = new TestRoute('second')); },
138
      <String>[
139 140 141 142 143 144
        'second: install',
        'second: didPush',
        'second: didChangeNext null',
        'initial: didChangeNext second',
      ]
    );
145
    await runNavigatorTest(
146 147
      tester,
      host,
148
      () { host.push(new TestRoute('third')); },
149
      <String>[
150 151 152 153 154 155
        'third: install',
        'third: didPush',
        'third: didChangeNext null',
        'second: didChangeNext third',
      ]
    );
156
    await runNavigatorTest(
157 158
      tester,
      host,
159
      () { host.replace(oldRoute: second, newRoute: new TestRoute('two')); },
160
      <String>[
161 162 163 164 165 166 167
        'two: install',
        'two: didReplace second',
        'two: didChangeNext third',
        'initial: didChangeNext two',
        'second: dispose',
      ]
    );
168
    await runNavigatorTest(
169 170
      tester,
      host,
171
      () { host.pop('hello'); },
172
      <String>[
173 174 175 176 177
        'third: didPop hello',
        'third: dispose',
        'two: didPopNext third',
      ]
    );
178
    await runNavigatorTest(
179 180
      tester,
      host,
181
      () { host.pop('good bye'); },
182
      <String>[
183 184 185 186 187
        'two: didPop good bye',
        'two: dispose',
        'initial: didPopNext two',
      ]
    );
188
    await tester.pumpWidget(new Container());
189
    expect(results, equals(<String>['initial: dispose']));
190 191
    expect(routes.isEmpty, isTrue);
    results.clear();
192 193
  });

194
  testWidgets('Route management - push, remove, pop', (WidgetTester tester) async {
195
    final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
196 197 198 199 200 201 202 203 204
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new Navigator(
          key: navigatorKey,
          onGenerateRoute: (_) => new TestRoute('first')
        ),
      ),
    );
205
    final NavigatorState host = navigatorKey.currentState;
206
    await runNavigatorTest(
207 208
      tester,
      host,
209
      () { },
210
      <String>[
211 212 213 214 215 216
        'first: install',
        'first: didPush',
        'first: didChangeNext null',
      ]
    );
    TestRoute second;
217
    await runNavigatorTest(
218 219
      tester,
      host,
220
      () { host.push(second = new TestRoute('second')); },
221
      <String>[
222 223 224 225 226 227
        'second: install',
        'second: didPush',
        'second: didChangeNext null',
        'first: didChangeNext second',
      ]
    );
228
    await runNavigatorTest(
229 230
      tester,
      host,
231
      () { host.push(new TestRoute('third')); },
232
      <String>[
233 234 235 236 237 238
        'third: install',
        'third: didPush',
        'third: didChangeNext null',
        'second: didChangeNext third',
      ]
    );
239
    await runNavigatorTest(
240 241
      tester,
      host,
242
      () { host.removeRouteBelow(second); },
243
      <String>[
244 245 246
        'first: dispose',
      ]
    );
247
    await runNavigatorTest(
248 249
      tester,
      host,
250
      () { host.pop('good bye'); },
251
      <String>[
252 253 254 255 256
        'third: didPop good bye',
        'third: dispose',
        'second: didPopNext third',
      ]
    );
257
    await runNavigatorTest(
258 259
      tester,
      host,
260
      () { host.push(new TestRoute('three')); },
261
      <String>[
262 263 264 265 266 267 268
        'three: install',
        'three: didPush',
        'three: didChangeNext null',
        'second: didChangeNext three',
      ]
    );
    TestRoute four;
269
    await runNavigatorTest(
270 271
      tester,
      host,
272
      () { host.push(four = new TestRoute('four')); },
273
      <String>[
274 275 276 277 278 279
        'four: install',
        'four: didPush',
        'four: didChangeNext null',
        'three: didChangeNext four',
      ]
    );
280
    await runNavigatorTest(
281 282
      tester,
      host,
283
      () { host.removeRouteBelow(four); },
284
      <String>[
285 286 287 288
        'second: didChangeNext four',
        'three: dispose',
      ]
    );
289
    await runNavigatorTest(
290 291
      tester,
      host,
292
      () { host.pop('the end'); },
293
      <String>[
294 295 296 297 298
        'four: didPop the end',
        'four: dispose',
        'second: didPopNext four',
      ]
    );
299
    await tester.pumpWidget(new Container());
300
    expect(results, equals(<String>['second: dispose']));
301 302
    expect(routes.isEmpty, isTrue);
    results.clear();
303 304
  });

305
  testWidgets('Route management - push, replace, popUntil', (WidgetTester tester) async {
306
    final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
307 308 309 310 311 312 313 314 315
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new Navigator(
          key: navigatorKey,
          onGenerateRoute: (_) => new TestRoute('A')
        ),
      ),
    );
316
    final NavigatorState host = navigatorKey.currentState;
317
    await runNavigatorTest(
318 319
      tester,
      host,
320
      () { },
321
      <String>[
322 323 324 325 326
        'A: install',
        'A: didPush',
        'A: didChangeNext null',
      ]
    );
327
    await runNavigatorTest(
328 329
      tester,
      host,
330
      () { host.push(new TestRoute('B')); },
331
      <String>[
332 333 334 335 336 337 338
        'B: install',
        'B: didPush',
        'B: didChangeNext null',
        'A: didChangeNext B',
      ]
    );
    TestRoute routeC;
339
    await runNavigatorTest(
340 341
      tester,
      host,
342
      () { host.push(routeC = new TestRoute('C')); },
343
      <String>[
344 345 346 347 348 349
        'C: install',
        'C: didPush',
        'C: didChangeNext null',
        'B: didChangeNext C',
      ]
    );
350
    expect(routeC.isActive, isTrue);
351
    TestRoute routeB;
352
    await runNavigatorTest(
353 354
      tester,
      host,
355
      () { host.replaceRouteBelow(anchorRoute: routeC, newRoute: routeB = new TestRoute('b')); },
356
      <String>[
357 358 359 360 361 362 363
        'b: install',
        'b: didReplace B',
        'b: didChangeNext C',
        'A: didChangeNext b',
        'B: dispose',
      ]
    );
364
    await runNavigatorTest(
365 366
      tester,
      host,
Hans Muller's avatar
Hans Muller committed
367
      () { host.popUntil((Route<dynamic> route) => route == routeB); },
368
      <String>[
369 370 371 372 373
        'C: didPop null',
        'C: dispose',
        'b: didPopNext C',
      ]
    );
374
    await tester.pumpWidget(new Container());
375
    expect(results, equals(<String>['A: dispose', 'b: dispose']));
376 377
    expect(routes.isEmpty, isTrue);
    results.clear();
378
  });
Hans Muller's avatar
Hans Muller committed
379 380

  testWidgets('Route localHistory - popUntil', (WidgetTester tester) async {
381
    final TestRoute routeA = new TestRoute('A');
Hans Muller's avatar
Hans Muller committed
382 383 384 385 386 387
    routeA.addLocalHistoryEntry(new LocalHistoryEntry(
      onRemove: () { routeA.log('onRemove 0'); }
    ));
    routeA.addLocalHistoryEntry(new LocalHistoryEntry(
      onRemove: () { routeA.log('onRemove 1'); }
    ));
388
    final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
389 390 391 392 393 394 395 396 397
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new Navigator(
          key: navigatorKey,
          onGenerateRoute: (_) => routeA
        ),
      ),
    );
398
    final NavigatorState host = navigatorKey.currentState;
Hans Muller's avatar
Hans Muller committed
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
    await runNavigatorTest(
      tester,
      host,
      () { host.popUntil((Route<dynamic> route) => !route.willHandlePopInternally); },
      <String>[
        'A: install',
        'A: didPush',
        'A: didChangeNext null',
        'A: didPop null',
        'A: onRemove 1',
        'A: didPop null',
        'A: onRemove 0',
      ]
    );

    await runNavigatorTest(
      tester,
      host,
      () { host.popUntil((Route<dynamic> route) => !route.willHandlePopInternally); },
      <String>[
      ]
    );
  });
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455

  group('PageRouteObserver', () {
    test('calls correct listeners', () {
      final RouteObserver<PageRoute<dynamic>> observer = new RouteObserver<PageRoute<dynamic>>();
      final RouteAware pageRouteAware1 = new MockRouteAware();
      final MockPageRoute route1 = new MockPageRoute();
      observer.subscribe(pageRouteAware1, route1);
      verify(pageRouteAware1.didPush()).called(1);

      final RouteAware pageRouteAware2 = new MockRouteAware();
      final MockPageRoute route2 = new MockPageRoute();
      observer.didPush(route2, route1);
      verify(pageRouteAware1.didPushNext()).called(1);

      observer.subscribe(pageRouteAware2, route2);
      verify(pageRouteAware2.didPush()).called(1);

      observer.didPop(route2, route1);
      verify(pageRouteAware2.didPop()).called(1);
      verify(pageRouteAware1.didPopNext()).called(1);
    });

    test('does not call listeners for non-PageRoute', () {
      final RouteObserver<PageRoute<dynamic>> observer = new RouteObserver<PageRoute<dynamic>>();
      final RouteAware pageRouteAware = new MockRouteAware();
      final MockPageRoute pageRoute = new MockPageRoute();
      final MockRoute route = new MockRoute();
      observer.subscribe(pageRouteAware, pageRoute);
      verify(pageRouteAware.didPush());

      observer.didPush(route, pageRoute);
      observer.didPop(route, pageRoute);
      verifyNoMoreInteractions(pageRouteAware);
    });
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480

    test('does not call listeners when already subscribed', () {
      final RouteObserver<PageRoute<dynamic>> observer = new RouteObserver<PageRoute<dynamic>>();
      final RouteAware pageRouteAware = new MockRouteAware();
      final MockPageRoute pageRoute = new MockPageRoute();
      observer.subscribe(pageRouteAware, pageRoute);
      observer.subscribe(pageRouteAware, pageRoute);
      verify(pageRouteAware.didPush()).called(1);
    });

    test('does not call listeners when unsubscribed', () {
      final RouteObserver<PageRoute<dynamic>> observer = new RouteObserver<PageRoute<dynamic>>();
      final RouteAware pageRouteAware = new MockRouteAware();
      final MockPageRoute pageRoute = new MockPageRoute();
      final MockPageRoute nextPageRoute = new MockPageRoute();
      observer.subscribe(pageRouteAware, pageRoute);
      observer.subscribe(pageRouteAware, nextPageRoute);
      verify(pageRouteAware.didPush()).called(2);

      observer.unsubscribe(pageRouteAware);

      observer.didPush(nextPageRoute, pageRoute);
      observer.didPop(nextPageRoute, pageRoute);
      verifyNoMoreInteractions(pageRouteAware);
    });
481
  });
482
}
483 484 485 486 487 488

class MockPageRoute extends Mock implements PageRoute<dynamic> { }

class MockRoute extends Mock implements Route<dynamic> { }

class MockRouteAware extends Mock implements RouteAware { }