navigator_test.dart 6.69 KB
Newer Older
Hixie's avatar
Hixie committed
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.

Adam Barth's avatar
Adam Barth committed
5
import 'package:flutter_test/flutter_test.dart';
Adam Barth's avatar
Adam Barth committed
6
import 'package:flutter/material.dart';
7

8
class FirstWidget extends StatelessWidget {
9
  @override
Adam Barth's avatar
Adam Barth committed
10
  Widget build(BuildContext context) {
11
  return new GestureDetector(
12
      onTap: () {
Hixie's avatar
Hixie committed
13
        Navigator.pushNamed(context, '/second');
14
      },
15 16
      child: new Container(
        decoration: new BoxDecoration(
17
          backgroundColor: new Color(0xFFFFFF00)
18
        ),
19
        child: new Text('X')
20 21 22 23 24
      )
    );
  }
}

25
class SecondWidget extends StatefulWidget {
26
  @override
27
  SecondWidgetState createState() => new SecondWidgetState();
Adam Barth's avatar
Adam Barth committed
28
}
29

30
class SecondWidgetState extends State<SecondWidget> {
31
  @override
Adam Barth's avatar
Adam Barth committed
32
  Widget build(BuildContext context) {
33
    return new GestureDetector(
Hixie's avatar
Hixie committed
34
      onTap: () => Navigator.pop(context),
35 36
      child: new Container(
        decoration: new BoxDecoration(
37
          backgroundColor: new Color(0xFFFF00FF)
38
        ),
39
        child: new Text('Y')
40 41 42 43 44
      )
    );
  }
}

45
typedef void ExceptionCallback(dynamic exception);
46

47 48
class ThirdWidget extends StatelessWidget {
  ThirdWidget({ this.targetKey, this.onException });
49 50 51 52

  final Key targetKey;
  final ExceptionCallback onException;

53
  @override
54 55 56 57 58
  Widget build(BuildContext context) {
    return new GestureDetector(
      key: targetKey,
      onTap: () {
        try {
59
          Navigator.of(context);
60 61 62 63 64 65 66 67 68
        } catch (e) {
          onException(e);
        }
      },
      behavior: HitTestBehavior.opaque
    );
  }
}

69
void main() {
70
  testWidgets('Can navigator navigate to and from a stateful widget', (WidgetTester tester) async {
71
    final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
72 73
      '/': (BuildContext context) => new FirstWidget(), // X
      '/second': (BuildContext context) => new SecondWidget(), // Y
74
    };
75

76
    await tester.pumpWidget(new MaterialApp(routes: routes));
77
    expect(find.text('X'), findsOneWidget);
78
    expect(find.text('Y', skipOffstage: false), findsNothing);
79

80
    await tester.tap(find.text('X'));
81 82 83
    await tester.pump();
    expect(find.text('X'), findsOneWidget);
    expect(find.text('Y', skipOffstage: false), isOffstage);
84

85
    await tester.pump(const Duration(milliseconds: 10));
86 87
    expect(find.text('X'), findsOneWidget);
    expect(find.text('Y'), findsOneWidget);
88

89
    await tester.pump(const Duration(milliseconds: 10));
90 91 92
    expect(find.text('X'), findsOneWidget);
    expect(find.text('Y'), findsOneWidget);

93
    await tester.pump(const Duration(milliseconds: 10));
94 95 96
    expect(find.text('X'), findsOneWidget);
    expect(find.text('Y'), findsOneWidget);

97
    await tester.pump(const Duration(seconds: 1));
98 99 100
    expect(find.text('X'), findsNothing);
    expect(find.text('X', skipOffstage: false), findsOneWidget);
    expect(find.text('Y'), findsOneWidget);
101

102
    await tester.tap(find.text('Y'));
103 104 105 106 107 108 109
    expect(find.text('X'), findsNothing);
    expect(find.text('Y'), findsOneWidget);

    await tester.pump();
    expect(find.text('X'), findsOneWidget);
    expect(find.text('Y'), findsOneWidget);

110
    await tester.pump(const Duration(milliseconds: 10));
111 112
    expect(find.text('X'), findsOneWidget);
    expect(find.text('Y'), findsOneWidget);
113

114
    await tester.pump(const Duration(seconds: 1));
115
    expect(find.text('X'), findsOneWidget);
116
    expect(find.text('Y', skipOffstage: false), findsNothing);
117
  });
118

119
  testWidgets('Navigator.of fails gracefully when not found in context', (WidgetTester tester) async {
120 121 122 123 124 125 126 127
    Key targetKey = new Key('foo');
    dynamic exception;
    Widget widget = new ThirdWidget(
      targetKey: targetKey,
      onException: (dynamic e) {
        exception = e;
      }
    );
128 129
    await tester.pumpWidget(widget);
    await tester.tap(find.byKey(targetKey));
130
    expect(exception, new isInstanceOf<FlutterError>());
131
    expect('$exception', startsWith('Navigator operation requested with a context'));
132
  });
133 134 135 136 137 138 139 140 141 142 143 144

  testWidgets('Missing settings in onGenerateRoute throws exception', (WidgetTester tester) async {
    await tester.pumpWidget(new Navigator(
      onGenerateRoute: (RouteSettings settings) {
        return new MaterialPageRoute<Null>(
          builder: (BuildContext context) => new Container()
        );
      }
    ));
    Object exception = tester.takeException();
    expect(exception is FlutterError, isTrue);
  });
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211

  testWidgets('Gestures between push and build are ignored', (WidgetTester tester) async {
    List<String> log = <String>[];
    final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
      '/': (BuildContext context) {
        return new Row(
          children: <Widget>[
            new GestureDetector(
              onTap: () {
                log.add('left');
                Navigator.pushNamed(context, '/second');
              },
              child: new Text('left')
            ),
            new GestureDetector(
              onTap: () { log.add('right'); },
              child: new Text('right')
            ),
          ]
        );
      },
      '/second': (BuildContext context) => new Container(),
    };
    await tester.pumpWidget(new MaterialApp(routes: routes));
    expect(log, isEmpty);
    await tester.tap(find.text('left'));
    expect(log, equals(<String>['left']));
    await tester.tap(find.text('right'));
    expect(log, equals(<String>['left']));
  });

  // This test doesn't work because the testing framework uses a fake version of
  // the pointer event dispatch loop.
  //
  // TODO(abarth): Test more of the real code and enable this test.
  // See https://github.com/flutter/flutter/issues/4771.
  //
  // testWidgets('Pending gestures are rejected', (WidgetTester tester) async {
  //   List<String> log = <String>[];
  //   final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
  //     '/': (BuildContext context) {
  //       return new Row(
  //         children: <Widget>[
  //           new GestureDetector(
  //             onTap: () {
  //               log.add('left');
  //               Navigator.pushNamed(context, '/second');
  //             },
  //             child: new Text('left')
  //           ),
  //           new GestureDetector(
  //             onTap: () { log.add('right'); },
  //             child: new Text('right')
  //           ),
  //         ]
  //       );
  //     },
  //     '/second': (BuildContext context) => new Container(),
  //   };
  //   await tester.pumpWidget(new MaterialApp(routes: routes));
  //   TestGesture gesture = await tester.startGesture(tester.getCenter(find.text('right')), pointer: 23);
  //   expect(log, isEmpty);
  //   await tester.tap(find.text('left'));
  //   expect(log, equals(<String>['left']));
  //   await gesture.up();
  //   expect(log, equals(<String>['left']));
  // });
212
}