routes_test.dart 13.5 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
import 'package:flutter_test/flutter_test.dart';
9
import 'package:flutter/widgets.dart';
10 11 12

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(Route<dynamic> oldRoute) {
49
    expect(oldRoute, isInstanceOf<TestRoute>());
50 51 52
    final TestRoute castRoute = oldRoute;
    log('didReplace ${castRoute.name}');
    super.didReplace(castRoute);
53 54
  }

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

64
  @override
65
  void didPopNext(Route<dynamic> nextRoute) {
66
    expect(nextRoute, isInstanceOf<TestRoute>());
67 68 69
    final TestRoute castRoute = nextRoute;
    log('didPopNext ${castRoute.name}');
    super.didPopNext(castRoute);
70 71
  }

72
  @override
73
  void didChangeNext(Route<dynamic> nextRoute) {
74
    expect(nextRoute, anyOf(isNull, isInstanceOf<TestRoute>()));
75 76 77
    final TestRoute castRoute = nextRoute;
    log('didChangeNext ${castRoute?.name}');
    super.didChangeNext(castRoute);
78 79
  }

80
  @override
81 82
  void dispose() {
    log('dispose');
83 84
    for (OverlayEntry entry in _entries)
      entry.remove();
85
    _entries.clear();
86
    routes.remove(this);
87
    super.dispose();
88 89 90 91
  }

}

92
Future<Null> runNavigatorTest(
93 94
  WidgetTester tester,
  NavigatorState host,
95
  VoidCallback test,
96
  List<String> expectations
97
) async {
98
  expect(host, isNotNull);
99
  test();
100 101
  expect(results, equals(expectations));
  results.clear();
102
  await tester.pump();
103 104 105
}

void main() {
106
  testWidgets('Route settings', (WidgetTester tester) async {
107
    const RouteSettings settings = RouteSettings(name: 'A');
108
    expect(settings, hasOneLineDescription);
109 110 111 112 113 114
    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);
115 116
  });

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

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

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

  testWidgets('Route localHistory - popUntil', (WidgetTester tester) async {
387
    final TestRoute routeA = new TestRoute('A');
Hans Muller's avatar
Hans Muller committed
388 389 390 391 392 393
    routeA.addLocalHistoryEntry(new LocalHistoryEntry(
      onRemove: () { routeA.log('onRemove 0'); }
    ));
    routeA.addLocalHistoryEntry(new LocalHistoryEntry(
      onRemove: () { routeA.log('onRemove 1'); }
    ));
394
    final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
395 396 397 398 399 400 401 402 403
    await tester.pumpWidget(
      new Directionality(
        textDirection: TextDirection.ltr,
        child: new Navigator(
          key: navigatorKey,
          onGenerateRoute: (_) => routeA
        ),
      ),
    );
404
    final NavigatorState host = navigatorKey.currentState;
Hans Muller's avatar
Hans Muller committed
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
    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>[
      ]
    );
  });
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 456 457 458 459 460 461

  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);
    });
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486

    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);
    });
487
  });
488
}
489 490 491 492 493 494

class MockPageRoute extends Mock implements PageRoute<dynamic> { }

class MockRoute extends Mock implements Route<dynamic> { }

class MockRouteAware extends Mock implements RouteAware { }