| // 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'; |
| |
| import 'package:flutter/widgets.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'binding.dart'; |
| import 'finders.dart'; |
| import 'controller.dart'; |
| |
| /// Signature for callback to [testWidgets] and [benchmarkWidgets]. |
| typedef Future<Null> WidgetTesterCallback(WidgetTester widgetTester); |
| |
| /// Runs the [callback] inside the Flutter test environment. |
| /// |
| /// Use this function for testing custom [StatelessWidget]s and |
| /// [StatefulWidget]s. |
| /// |
| /// The callback can be asynchronous (using `async`/`await` or |
| /// using explicit [Future]s). |
| /// |
| /// This function uses the [test] function in the test package to |
| /// register the given callback as a test. The callback, when run, |
| /// will be given a new instance of [WidgetTester]. The [find] object |
| /// provides convenient widget [Finder]s for use with the |
| /// [WidgetTester]. |
| /// |
| /// Example: |
| /// |
| /// testWidgets('MyWidget', (WidgetTester tester) { |
| /// tester.pumpWidget(new MyWidget()); |
| /// tester.tap(find.text('Save')); |
| /// expect(tester, hasWidget(find.text('Success'))); |
| /// }); |
| void testWidgets(String description, WidgetTesterCallback callback, { |
| Timeout timeout: const Timeout(const Duration(seconds: 5)), |
| bool skip |
| }) { |
| TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); |
| WidgetTester tester = new WidgetTester._(binding); |
| group('-', () { |
| setUp(binding.preTest); |
| test(description, () => binding.runTest(() => callback(tester)), skip: skip); |
| tearDown(binding.postTest); |
| }, timeout: timeout); |
| } |
| |
| /// Runs the [callback] inside the Flutter benchmark environment. |
| /// |
| /// Use this function for benchmarking custom [StatelessWidget]s and |
| /// [StatefulWidget]s when you want to be able to use features from |
| /// [TestWidgetsFlutterBinding]. The callback, when run, will be given |
| /// a new instance of [WidgetTester]. The [find] object provides |
| /// convenient widget [Finder]s for use with the [WidgetTester]. |
| /// |
| /// The callback can be asynchronous (using `async`/`await` or using |
| /// explicit [Future]s). If it is, then [benchmarkWidgets] will return |
| /// a [Future] that completes when the callback's does. Otherwise, it |
| /// will return a widget that is always complete. |
| /// |
| /// Benchmarks must not be run in checked mode. To avoid this, this |
| /// function will assert if it is run in checked mode. |
| /// |
| /// Example: |
| /// |
| /// main() async { |
| /// await benchmarkWidgets((WidgetTester tester) { |
| /// tester.pumpWidget(new MyWidget()); |
| /// final Stopwatch timer = new Stopwatch()..start(); |
| /// for (int index = 0; index < 10000; index += 1) { |
| /// tester.tap(find.text('Tap me')); |
| /// tester.pump(); |
| /// } |
| /// timer.stop(); |
| /// print('Time taken: ${timer.elapsedMilliseconds}ms'); |
| /// }); |
| /// exit(0); |
| /// } |
| Future<Null> benchmarkWidgets(WidgetTesterCallback callback) { |
| assert(false); // Don't run benchmarks in checked mode. |
| TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); |
| WidgetTester tester = new WidgetTester._(binding); |
| return binding.runTest(() => callback(tester)) ?? new Future<Null>.value(); |
| } |
| |
| /// Class that programmatically interacts with widgets and the test environment. |
| class WidgetTester extends WidgetController { |
| WidgetTester._(TestWidgetsFlutterBinding binding) : super(binding); |
| |
| /// The binding instance used by the testing framework. |
| @override |
| TestWidgetsFlutterBinding get binding => super.binding; |
| |
| /// Renders the UI from the given [widget]. |
| /// |
| /// Calls [runApp] with the given widget, then triggers a frame sequence and |
| /// flushes microtasks, by calling [pump] with the same duration (if any). |
| /// The supplied [EnginePhase] is the final phase reached during the pump pass; |
| /// if not supplied, the whole pass is executed. |
| void pumpWidget(Widget widget, [ Duration duration, EnginePhase phase ]) { |
| runApp(widget); |
| binding.pump(duration, phase); |
| } |
| |
| /// Triggers a sequence of frames for [duration] amount of time. |
| /// |
| /// This is a convenience function that just calls |
| /// [TestWidgetsFlutterBinding.pump]. |
| void pump([ Duration duration, EnginePhase phase ]) { |
| binding.pump(duration, phase); |
| } |
| |
| /// Returns the exception most recently caught by the Flutter framework. |
| /// |
| /// See [TestWidgetsFlutterBinding.takeException] for details. |
| dynamic takeException() { |
| return binding.takeException(); |
| } |
| |
| /// Runs all remaining microtasks, including those scheduled as a result of |
| /// running them, until there are no more microtasks scheduled. |
| /// |
| /// Does not run timers. May result in an infinite loop or run out of memory |
| /// if microtasks continue to recursively schedule new microtasks. |
| void flushMicrotasks() { |
| binding.fakeAsync.flushMicrotasks(); |
| } |
| } |