app_test.dart 10.4 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/cupertino.dart';
6 7
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
8
import 'package:flutter_test/flutter_test.dart';
9 10 11

void main() {
  testWidgets('Heroes work', (WidgetTester tester) async {
12
    await tester.pumpWidget(CupertinoApp(
13 14 15 16 17 18 19 20 21 22 23
      home: ListView(children: <Widget>[
        const Hero(tag: 'a', child: Text('foo')),
        Builder(builder: (BuildContext context) {
          return CupertinoButton(
            child: const Text('next'),
            onPressed: () {
              Navigator.push(
                context,
                CupertinoPageRoute<void>(builder: (BuildContext context) {
                  return const Hero(tag: 'a', child: Text('foo'));
                }),
24
              );
25 26 27 28
            },
          );
        }),
      ]),
29 30 31 32 33 34 35 36 37 38 39
    ));

    await tester.tap(find.text('next'));
    await tester.pump();
    await tester.pump(const Duration(milliseconds: 100));

    // During the hero transition, the hero widget is lifted off of both
    // page routes and exists as its own overlay on top of both routes.
    expect(find.widgetWithText(CupertinoPageRoute, 'foo'), findsNothing);
    expect(find.widgetWithText(Navigator, 'foo'), findsOneWidget);
  });
40 41 42 43 44 45 46 47

  testWidgets('Has default cupertino localizations', (WidgetTester tester) async {
    await tester.pumpWidget(
      CupertinoApp(
        home: Builder(
          builder: (BuildContext context) {
            return Column(
              children: <Widget>[
48 49
                Text(CupertinoLocalizations.of(context).selectAllButtonLabel),
                Text(CupertinoLocalizations.of(context).datePickerMediumDate(
50 51 52 53 54 55 56 57 58 59 60 61
                  DateTime(2018, 10, 4),
                )),
              ],
            );
          },
        ),
      ),
    );

    expect(find.text('Select All'), findsOneWidget);
    expect(find.text('Thu Oct 4 '), findsOneWidget);
  });
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

  testWidgets('Can use dynamic color', (WidgetTester tester) async {
    const CupertinoDynamicColor dynamicColor = CupertinoDynamicColor.withBrightness(
      color: Color(0xFF000000),
      darkColor: Color(0xFF000001),
    );
    await tester.pumpWidget(const CupertinoApp(
      theme: CupertinoThemeData(brightness: Brightness.light),
      color: dynamicColor,
      home: Placeholder(),
    ));

    expect(tester.widget<Title>(find.byType(Title)).color.value, 0xFF000000);

    await tester.pumpWidget(const CupertinoApp(
      theme: CupertinoThemeData(brightness: Brightness.dark),
      color: dynamicColor,
      home: Placeholder(),
    ));

    expect(tester.widget<Title>(find.byType(Title)).color.value, 0xFF000001);
  });
84 85 86 87 88 89 90 91 92 93 94 95 96

  testWidgets('Can customize initial routes', (WidgetTester tester) async {
    final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
    await tester.pumpWidget(
      CupertinoApp(
        navigatorKey: navigatorKey,
        onGenerateInitialRoutes: (String initialRoute) {
          expect(initialRoute, '/abc');
          return <Route<void>>[
            PageRouteBuilder<void>(
              pageBuilder: (
                BuildContext context,
                Animation<double> animation,
97 98
                Animation<double> secondaryAnimation,
              ) {
99
                return const Text('non-regular page one');
100
              },
101 102 103 104 105
            ),
            PageRouteBuilder<void>(
              pageBuilder: (
                BuildContext context,
                Animation<double> animation,
106 107
                Animation<double> secondaryAnimation,
              ) {
108
                return const Text('non-regular page two');
109
              },
110 111 112 113 114 115 116 117
            ),
          ];
        },
        initialRoute: '/abc',
        routes: <String, WidgetBuilder>{
          '/': (BuildContext context) => const Text('regular page one'),
          '/abc': (BuildContext context) => const Text('regular page two'),
        },
118
      ),
119 120 121 122 123
    );
    expect(find.text('non-regular page two'), findsOneWidget);
    expect(find.text('non-regular page one'), findsNothing);
    expect(find.text('regular page one'), findsNothing);
    expect(find.text('regular page two'), findsNothing);
124
    navigatorKey.currentState!.pop();
125 126 127 128 129 130
    await tester.pumpAndSettle();
    expect(find.text('non-regular page two'), findsNothing);
    expect(find.text('non-regular page one'), findsOneWidget);
    expect(find.text('regular page one'), findsNothing);
    expect(find.text('regular page two'), findsNothing);
  });
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

  testWidgets('CupertinoApp.navigatorKey can be updated', (WidgetTester tester) async {
    final GlobalKey<NavigatorState> key1 = GlobalKey<NavigatorState>();
    await tester.pumpWidget(CupertinoApp(
      navigatorKey: key1,
      home: const Placeholder(),
    ));
    expect(key1.currentState, isA<NavigatorState>());
    final GlobalKey<NavigatorState> key2 = GlobalKey<NavigatorState>();
    await tester.pumpWidget(CupertinoApp(
      navigatorKey: key2,
      home: const Placeholder(),
    ));
    expect(key2.currentState, isA<NavigatorState>());
    expect(key1.currentState, isNull);
  });
147 148 149 150 151 152 153 154 155

  testWidgets('CupertinoApp.router works', (WidgetTester tester) async {
    final PlatformRouteInformationProvider provider = PlatformRouteInformationProvider(
      initialRouteInformation: const RouteInformation(
        location: 'initial',
      ),
    );
    final SimpleNavigatorRouterDelegate delegate = SimpleNavigatorRouterDelegate(
      builder: (BuildContext context, RouteInformation information) {
156
        return Text(information.location!);
157 158 159 160 161 162
      },
      onPopPage: (Route<void> route, void result, SimpleNavigatorRouterDelegate delegate) {
        delegate.routeInformation = const RouteInformation(
          location: 'popped',
        );
        return route.didPop(result);
163
      },
164 165 166 167 168 169 170 171 172 173
    );
    await tester.pumpWidget(CupertinoApp.router(
      routeInformationProvider: provider,
      routeInformationParser: SimpleRouteInformationParser(),
      routerDelegate: delegate,
    ));
    expect(find.text('initial'), findsOneWidget);

    // Simulate android back button intent.
    final ByteData message = const JSONMethodCodec().encodeMethodCall(const MethodCall('popRoute'));
174
    await ServicesBinding.instance!.defaultBinaryMessenger.handlePlatformMessage('flutter/navigation', message, (_) { });
175 176 177
    await tester.pumpAndSettle();
    expect(find.text('popped'), findsOneWidget);
  });
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

  testWidgets('CupertinoApp has correct default ScrollBehavior', (WidgetTester tester) async {
    late BuildContext capturedContext;
    await tester.pumpWidget(
      CupertinoApp(
        home: Builder(
          builder: (BuildContext context) {
            capturedContext = context;
            return const Placeholder();
          },
        ),
      ),
    );
    expect(ScrollConfiguration.of(capturedContext).runtimeType, CupertinoScrollBehavior);
  });

  testWidgets('A ScrollBehavior can be set for CupertinoApp', (WidgetTester tester) async {
    late BuildContext capturedContext;
    await tester.pumpWidget(
      CupertinoApp(
198
        scrollBehavior: const MockScrollBehavior(),
199 200 201 202 203 204 205 206 207 208 209 210
        home: Builder(
          builder: (BuildContext context) {
            capturedContext = context;
            return const Placeholder();
          },
        ),
      ),
    );
    final ScrollBehavior scrollBehavior = ScrollConfiguration.of(capturedContext);
    expect(scrollBehavior.runtimeType, MockScrollBehavior);
    expect(scrollBehavior.getScrollPhysics(capturedContext).runtimeType, NeverScrollableScrollPhysics);
  });
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

  testWidgets('When `useInheritedMediaQuery` is true an existing MediaQuery is used if one is available', (WidgetTester tester) async {
    late BuildContext capturedContext;
    final UniqueKey uniqueKey = UniqueKey();
    await tester.pumpWidget(
      MediaQuery(
        key: uniqueKey,
        data: const MediaQueryData(),
        child: CupertinoApp(
          useInheritedMediaQuery: true,
          builder: (BuildContext context, Widget? child) {
            capturedContext = context;
            return const Placeholder();
          },
          color: const Color(0xFF123456),
        ),
      ),
    );
    expect(capturedContext.dependOnInheritedWidgetOfExactType<MediaQuery>()?.key, uniqueKey);
  });
231 232 233
}

class MockScrollBehavior extends ScrollBehavior {
234 235
  const MockScrollBehavior();

236 237
  @override
  ScrollPhysics getScrollPhysics(BuildContext context) => const NeverScrollableScrollPhysics();
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
}

typedef SimpleRouterDelegateBuilder = Widget Function(BuildContext, RouteInformation);
typedef SimpleNavigatorRouterDelegatePopPage<T> = bool Function(Route<T> route, T result, SimpleNavigatorRouterDelegate delegate);

class SimpleRouteInformationParser extends RouteInformationParser<RouteInformation> {
  SimpleRouteInformationParser();

  @override
  Future<RouteInformation> parseRouteInformation(RouteInformation information) {
    return SynchronousFuture<RouteInformation>(information);
  }

  @override
  RouteInformation restoreRouteInformation(RouteInformation configuration) {
    return configuration;
  }
}

class SimpleNavigatorRouterDelegate extends RouterDelegate<RouteInformation> with PopNavigatorRouterDelegateMixin<RouteInformation>, ChangeNotifier {
  SimpleNavigatorRouterDelegate({
259
    required this.builder,
260 261 262 263 264 265 266
    this.onPopPage,
  });

  @override
  GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  RouteInformation get routeInformation => _routeInformation;
267
  late RouteInformation _routeInformation;
268 269 270 271 272 273
  set routeInformation(RouteInformation newValue) {
    _routeInformation = newValue;
    notifyListeners();
  }

  SimpleRouterDelegateBuilder builder;
274
  SimpleNavigatorRouterDelegatePopPage<void>? onPopPage;
275 276 277 278 279 280 281 282

  @override
  Future<void> setNewRoutePath(RouteInformation configuration) {
    _routeInformation = configuration;
    return SynchronousFuture<void>(null);
  }

  bool _handlePopPage(Route<void> route, void data) {
283
    return onPopPage!(route, data, this);
284 285 286 287 288 289 290 291 292 293
  }

  @override
  Widget build(BuildContext context) {
    return Navigator(
      key: navigatorKey,
      onPopPage: _handlePopPage,
      pages: <Page<void>>[
        // We need at least two pages for the pop to propagate through.
        // Otherwise, the navigator will bubble the pop to the system navigator.
294 295
        const CupertinoPage<void>(
          child:  Text('base'),
296 297
        ),
        CupertinoPage<void>(
298
          key: ValueKey<String?>(routeInformation.location),
299
          child: builder(context, routeInformation),
300
        ),
301 302 303
      ],
    );
  }
304
}