diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index 2d4a9cd0e32..486875814c3 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -151,7 +151,7 @@ abstract class GlobalKey> extends Key { void _register(Element element) { assert(() { if (_registry.containsKey(this)) { - int oldCount = _debugDuplicates.putIfAbsent(this, () => 1); + final int oldCount = _debugDuplicates.putIfAbsent(this, () => 1); assert(oldCount >= 1); _debugDuplicates[this] = oldCount + 1; } @@ -163,7 +163,7 @@ abstract class GlobalKey> extends Key { void _unregister(Element element) { assert(() { if (_registry.containsKey(this) && _debugDuplicates.containsKey(this)) { - int oldCount = _debugDuplicates[this]; + final int oldCount = _debugDuplicates[this]; assert(oldCount >= 2); if (oldCount == 2) { _debugDuplicates.remove(this); @@ -257,17 +257,19 @@ abstract class GlobalKey> extends Key { for (GlobalKey key in _removedKeys) { if (!_registry.containsKey(key) && _removeListeners.containsKey(key)) { Set localListeners = new HashSet.from(_removeListeners[key]); - for (GlobalKeyRemoveListener listener in localListeners) - listener(key); + for (GlobalKeyRemoveListener listener in localListeners) { + try { + listener(key); + } catch (e, stack) { + _debugReportException('while notifying GlobalKey listener', e, stack); + } + } } } - } catch (e, stack) { - _debugReportException('while notifying GlobalKey listeners', e, stack); } finally { _removedKeys.clear(); } } - } /// A global key with a debugging label. diff --git a/packages/flutter/lib/src/widgets/gesture_detector.dart b/packages/flutter/lib/src/widgets/gesture_detector.dart index 9986bdfece2..4e4d8912cc8 100644 --- a/packages/flutter/lib/src/widgets/gesture_detector.dart +++ b/packages/flutter/lib/src/widgets/gesture_detector.dart @@ -525,7 +525,6 @@ class _GestureSemantics extends SingleChildRenderObjectWidget { return; } } - assert(false); } void _handleVerticalDragUpdate(DragUpdateDetails updateDetails) { @@ -553,7 +552,6 @@ class _GestureSemantics extends SingleChildRenderObjectWidget { return; } } - assert(false); } @override diff --git a/packages/flutter/lib/src/widgets/media_query.dart b/packages/flutter/lib/src/widgets/media_query.dart index e418f85ad57..4f82b9e012e 100644 --- a/packages/flutter/lib/src/widgets/media_query.dart +++ b/packages/flutter/lib/src/widgets/media_query.dart @@ -72,6 +72,20 @@ class MediaQueryData { return size.width > size.height ? Orientation.landscape : Orientation.portrait; } + MediaQueryData copyWith({ + Size size, + double devicePixelRatio, + double textScaleFactor, + EdgeInsets padding, + }) { + return new MediaQueryData( + size: size ?? this.size, + devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio, + textScaleFactor: textScaleFactor ?? this.textScaleFactor, + padding: padding ?? this.padding, + ); + } + @override bool operator ==(Object other) { if (other.runtimeType != runtimeType) diff --git a/packages/flutter/test/animation/animation_controller_test.dart b/packages/flutter/test/animation/animation_controller_test.dart index 675ff601f31..e376f3e74b4 100644 --- a/packages/flutter/test/animation/animation_controller_test.dart +++ b/packages/flutter/test/animation/animation_controller_test.dart @@ -6,8 +6,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/animation.dart'; import 'package:flutter/widgets.dart'; -import 'animation_tester.dart'; - void main() { setUp(() { WidgetsFlutterBinding.ensureInitialized(); @@ -206,17 +204,17 @@ void main() { duration: const Duration(milliseconds: 100), vsync: const TestVSync(), ); - expect(controller.toString(), hasOneLineDescription); + expect(controller, hasOneLineDescription); controller.forward(); WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 10)); WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 20)); - expect(controller.toString(), hasOneLineDescription); + expect(controller, hasOneLineDescription); WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 30)); - expect(controller.toString(), hasOneLineDescription); + expect(controller, hasOneLineDescription); controller.reverse(); WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 40)); WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 50)); - expect(controller.toString(), hasOneLineDescription); + expect(controller, hasOneLineDescription); controller.stop(); }); diff --git a/packages/flutter/test/animation/animations_test.dart b/packages/flutter/test/animation/animations_test.dart index 062edcb95b2..0afa3c779d4 100644 --- a/packages/flutter/test/animation/animations_test.dart +++ b/packages/flutter/test/animation/animations_test.dart @@ -6,8 +6,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/animation.dart'; import 'package:flutter/widgets.dart'; -import 'animation_tester.dart'; - void main() { setUp(() { WidgetsFlutterBinding.ensureInitialized(); @@ -15,16 +13,16 @@ void main() { }); test('toString control test', () { - expect(kAlwaysCompleteAnimation.toString(), hasOneLineDescription); - expect(kAlwaysDismissedAnimation.toString(), hasOneLineDescription); - expect(const AlwaysStoppedAnimation(0.5).toString(), hasOneLineDescription); + expect(kAlwaysCompleteAnimation, hasOneLineDescription); + expect(kAlwaysDismissedAnimation, hasOneLineDescription); + expect(const AlwaysStoppedAnimation(0.5), hasOneLineDescription); CurvedAnimation curvedAnimation = new CurvedAnimation( parent: kAlwaysDismissedAnimation, curve: Curves.ease ); - expect(curvedAnimation.toString(), hasOneLineDescription); + expect(curvedAnimation, hasOneLineDescription); curvedAnimation.reverseCurve = Curves.elasticOut; - expect(curvedAnimation.toString(), hasOneLineDescription); + expect(curvedAnimation, hasOneLineDescription); AnimationController controller = new AnimationController( duration: const Duration(milliseconds: 500), vsync: const TestVSync(), @@ -37,7 +35,7 @@ void main() { curve: Curves.ease, reverseCurve: Curves.elasticOut ); - expect(curvedAnimation.toString(), hasOneLineDescription); + expect(curvedAnimation, hasOneLineDescription); controller.stop(); }); @@ -45,9 +43,9 @@ void main() { ProxyAnimation animation = new ProxyAnimation(); expect(animation.value, 0.0); expect(animation.status, AnimationStatus.dismissed); - expect(animation.toString(), hasOneLineDescription); + expect(animation, hasOneLineDescription); animation.parent = kAlwaysDismissedAnimation; - expect(animation.toString(), hasOneLineDescription); + expect(animation, hasOneLineDescription); }); test('ProxyAnimation set parent generates value changed', () { @@ -88,7 +86,7 @@ void main() { expect(didReceiveCallback, isFalse); controller.value = 0.7; expect(didReceiveCallback, isFalse); - expect(animation.toString(), hasOneLineDescription); + expect(animation, hasOneLineDescription); }); test('TrainHoppingAnimation', () { @@ -107,11 +105,11 @@ void main() { }); expect(didSwitchTrains, isFalse); expect(animation.value, 0.5); - expect(animation.toString(), hasOneLineDescription); + expect(animation, hasOneLineDescription); nextTrain.value = 0.25; expect(didSwitchTrains, isTrue); expect(animation.value, 0.25); - expect(animation.toString(), hasOneLineDescription); + expect(animation, hasOneLineDescription); expect(animation.toString(), contains('no next')); }); } diff --git a/packages/flutter/test/animation/tween_test.dart b/packages/flutter/test/animation/tween_test.dart index 10ef4a4b8e2..bca2d5c2d0e 100644 --- a/packages/flutter/test/animation/tween_test.dart +++ b/packages/flutter/test/animation/tween_test.dart @@ -6,8 +6,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/animation.dart'; import 'package:flutter/widgets.dart'; -import 'animation_tester.dart'; - void main() { test('Can chain tweens', () { Tween tween = new Tween(begin: 0.30, end: 0.50); diff --git a/packages/flutter/test/painting/colors_test.dart b/packages/flutter/test/painting/colors_test.dart index b6ca3f4ec32..0aeb8decaf1 100644 --- a/packages/flutter/test/painting/colors_test.dart +++ b/packages/flutter/test/painting/colors_test.dart @@ -9,7 +9,7 @@ void main() { test('HSVColor control test', () { const HSVColor color = const HSVColor.fromAHSV(0.7, 28.0, 0.3, 0.6); - expect(color.toString(), hasOneLineDescription); + expect(color, hasOneLineDescription); expect(color.hashCode, equals(new HSVColor.fromAHSV(0.7, 28.0, 0.3, 0.6).hashCode)); expect(color.withAlpha(0.8), const HSVColor.fromAHSV(0.8, 28.0, 0.3, 0.6)); diff --git a/packages/flutter/test/painting/edge_insets_test.dart b/packages/flutter/test/painting/edge_insets_test.dart index 159d4de1531..0893108262b 100644 --- a/packages/flutter/test/painting/edge_insets_test.dart +++ b/packages/flutter/test/painting/edge_insets_test.dart @@ -9,7 +9,7 @@ void main() { test('EdgeInsets control test', () { const EdgeInsets insets = const EdgeInsets.fromLTRB(5.0, 7.0, 11.0, 13.0); - expect(insets.toString(), hasOneLineDescription); + expect(insets, hasOneLineDescription); expect(insets.hashCode, equals(new EdgeInsets.fromLTRB(5.0, 7.0, 11.0, 13.0).hashCode)); expect(insets.topLeft, const Offset(5.0, 7.0)); diff --git a/packages/flutter/test/widgets/framework_test.dart b/packages/flutter/test/widgets/framework_test.dart new file mode 100644 index 00000000000..559c078faf9 --- /dev/null +++ b/packages/flutter/test/widgets/framework_test.dart @@ -0,0 +1,81 @@ +// 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 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/widgets.dart'; + +void main() { + testWidgets('UniqueKey control test', (WidgetTester tester) async { + Key key = new UniqueKey(); + expect(key, hasOneLineDescription); + expect(key, isNot(equals(new UniqueKey()))); + }); + + testWidgets('ObjectKey control test', (WidgetTester tester) async { + Object a = new Object(); + Object b = new Object(); + Key keyA = new ObjectKey(a); + Key keyA2 = new ObjectKey(a); + Key keyB = new ObjectKey(b); + + expect(keyA, hasOneLineDescription); + expect(keyA, equals(keyA2)); + expect(keyA.hashCode, equals(keyA2.hashCode)); + expect(keyA, isNot(equals(keyB))); + }); + + testWidgets('GlobalKey duplication', (WidgetTester tester) async { + Key key = new GlobalKey(debugLabel: 'problematic'); + + await tester.pumpWidget(new Stack( + children: [ + new Container( + key: const ValueKey(1), + ), + new Container( + key: const ValueKey(2), + ), + new Container( + key: key + ), + ], + )); + + await tester.pumpWidget(new Stack( + children: [ + new Container( + key: const ValueKey(1), + child: new SizedBox(key: key), + ), + new Container( + key: const ValueKey(2), + child: new Placeholder(key: key), + ), + ], + )); + + expect(tester.takeException(), isNotNull); + }); + + testWidgets('GlobalKey notification exception handling', (WidgetTester tester) async { + GlobalKey key = new GlobalKey(); + + await tester.pumpWidget(new Container(key: key)); + + GlobalKey.registerRemoveListener(key, (GlobalKey key) { + throw new Exception('Misbehaving listener'); + }); + + bool didReceiveCallback = false; + GlobalKey.registerRemoveListener(key, (GlobalKey key) { + expect(didReceiveCallback, isFalse); + didReceiveCallback = true; + }); + + await tester.pumpWidget(new Placeholder()); + + expect(tester.takeException(), isNotNull); + expect(didReceiveCallback, isTrue); + }); +} diff --git a/packages/flutter/test/widgets/media_query_test.dart b/packages/flutter/test/widgets/media_query_test.dart index bba1c7ab4bf..f64ab50357e 100644 --- a/packages/flutter/test/widgets/media_query_test.dart +++ b/packages/flutter/test/widgets/media_query_test.dart @@ -14,7 +14,10 @@ void main() { await tester.pumpWidget( new Builder( builder: (BuildContext context) { - size = MediaQuery.of(context).size; + final MediaQueryData data = MediaQuery.of(context); + expect(data, hasOneLineDescription); + expect(data.hashCode, equals(data.copyWith().hashCode)); + size = data.size; return new Container(); } ) diff --git a/packages/flutter/test/widgets/performance_overlay_test.dart b/packages/flutter/test/widgets/performance_overlay_test.dart new file mode 100644 index 00000000000..047d5c96d21 --- /dev/null +++ b/packages/flutter/test/widgets/performance_overlay_test.dart @@ -0,0 +1,13 @@ +// Copyright 2017 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 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/widgets.dart'; + +void main() { + testWidgets('Performance overlay smoke test', (WidgetTester tester) async { + await tester.pumpWidget(new PerformanceOverlay()); + await tester.pumpWidget(new PerformanceOverlay.allEnabled()); + }); +} diff --git a/packages/flutter/test/widgets/placeholder_test.dart b/packages/flutter/test/widgets/placeholder_test.dart new file mode 100644 index 00000000000..40a1fc81192 --- /dev/null +++ b/packages/flutter/test/widgets/placeholder_test.dart @@ -0,0 +1,22 @@ +// Copyright 2017 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 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/widgets.dart'; + +void main() { + testWidgets('Placeholder control test', (WidgetTester tester) async { + GlobalKey key = new GlobalKey(); + + await tester.pumpWidget(new Placeholder(key: key)); + + key.currentState.child = new Text('target'); + + expect(find.text('target'), findsNothing); + + await tester.pump(); + + expect(find.text('target'), findsOneWidget); + }); +} diff --git a/packages/flutter/test/widgets/raw_keyboard_listener_test.dart b/packages/flutter/test/widgets/raw_keyboard_listener_test.dart index 6976a1c25c7..a4248ebc226 100644 --- a/packages/flutter/test/widgets/raw_keyboard_listener_test.dart +++ b/packages/flutter/test/widgets/raw_keyboard_listener_test.dart @@ -18,6 +18,7 @@ void sendFakeKeyEvent(Map data) { void main() { testWidgets('Can dispose without keyboard', (WidgetTester tester) async { + await tester.pumpWidget(new RawKeyboardListener(child: new Container())); await tester.pumpWidget(new RawKeyboardListener(child: new Container())); await tester.pumpWidget(new Container()); }); diff --git a/packages/flutter/test/widgets/routes_test.dart b/packages/flutter/test/widgets/routes_test.dart index 32f7d34e059..2d3a30b7939 100644 --- a/packages/flutter/test/widgets/routes_test.dart +++ b/packages/flutter/test/widgets/routes_test.dart @@ -96,6 +96,11 @@ Future runNavigatorTest( } void main() { + testWidgets('Route settings', (WidgetTester tester) async { + RouteSettings settings = const RouteSettings(name: 'A'); + expect(settings, hasOneLineDescription); + }); + testWidgets('Route management - push, replace, pop', (WidgetTester tester) async { GlobalKey navigatorKey = new GlobalKey(); await tester.pumpWidget(new Navigator( @@ -320,6 +325,7 @@ void main() { 'B: didChangeNext C', ] ); + expect(routeC.isActive, isTrue); TestRoute routeB; await runNavigatorTest( tester, diff --git a/packages/flutter/test/widgets/semantics_debugger_test.dart b/packages/flutter/test/widgets/semantics_debugger_test.dart index 0511bcbb9f9..246582e2cb1 100644 --- a/packages/flutter/test/widgets/semantics_debugger_test.dart +++ b/packages/flutter/test/widgets/semantics_debugger_test.dart @@ -14,13 +14,13 @@ void main() { children: [ new Semantics(), new Semantics( - container: true + container: true, ), new Semantics( - label: 'label' + label: 'label', ), - ] - ) + ], + ), ); await tester.pumpWidget( @@ -29,20 +29,21 @@ void main() { children: [ new Semantics(), new Semantics( - container: true + container: true, ), new Semantics( - label: 'label' + label: 'label', ), - ] - ) - ) + ], + ), + ), ); expect(true, isTrue); // expect that we reach here without crashing }); - testWidgets('SemanticsDebugger reparents subtree', (WidgetTester tester) async { + testWidgets('SemanticsDebugger reparents subtree', + (WidgetTester tester) async { GlobalKey key = new GlobalKey(); await tester.pumpWidget( @@ -51,12 +52,16 @@ void main() { children: [ new Semantics(label: 'label1'), new Positioned( - key: key, left: 0.0, top: 0.0, width: 100.0, height: 100.0, - child: new Semantics(label: 'label2') + key: key, + left: 0.0, + top: 0.0, + width: 100.0, + height: 100.0, + child: new Semantics(label: 'label2'), ), - ] - ) - ) + ], + ), + ), ); await tester.pumpWidget( @@ -69,16 +74,20 @@ void main() { child: new Stack( children: [ new Positioned( - key: key, left: 0.0, top: 0.0, width: 100.0, height: 100.0, - child: new Semantics(label: 'label2') + key: key, + left: 0.0, + top: 0.0, + width: 100.0, + height: 100.0, + child: new Semantics(label: 'label2'), ), new Semantics(label: 'label3'), - ] - ) - ) - ] - ) - ) + ], + ), + ), + ], + ), + ), ); await tester.pumpWidget( @@ -91,19 +100,180 @@ void main() { child: new Stack( children: [ new Positioned( - key: key, left: 0.0, top: 0.0, width: 100.0, height: 100.0, - child: new Semantics(label: 'label2') - ), + key: key, + left: 0.0, + top: 0.0, + width: 100.0, + height: 100.0, + child: new Semantics(label: 'label2')), new Semantics(label: 'label3'), new Semantics(label: 'label4'), - ] - ) - ) - ] - ) - ) + ], + ), + ), + ], + ), + ), ); expect(tester.takeException(), isNull); }); + + testWidgets('SemanticsDebugger interaction test', + (WidgetTester tester) async { + final List log = []; + + await tester.pumpWidget( + new SemanticsDebugger( + child: new Material( + child: new Block(children: [ + new RaisedButton( + onPressed: () { + log.add('top'); + }, + child: new Text('TOP'), + ), + new RaisedButton( + onPressed: () { + log.add('bottom'); + }, + child: new Text('BOTTOM'), + ), + ]), + ), + ), + ); + + await tester.tap(find.text('TOP')); + expect(log, equals(['top'])); + log.clear(); + + await tester.tap(find.text('BOTTOM')); + expect(log, equals(['bottom'])); + log.clear(); + }); + + testWidgets('SemanticsDebugger scroll test', (WidgetTester tester) async { + Key childKey = new UniqueKey(); + + await tester.pumpWidget( + new SemanticsDebugger( + child: new Block( + children: [ + new Container( + key: childKey, + height: 5000.0, + decoration: + new BoxDecoration(backgroundColor: Colors.green[500]), + ), + ], + ), + ), + ); + + expect(tester.getTopLeft(find.byKey(childKey)).y, equals(0.0)); + + await tester.fling(find.byType(Block), const Offset(0.0, -200.0), 200.0); + await tester.pump(); + + expect(tester.getTopLeft(find.byKey(childKey)).y, equals(-480.0)); + + await tester.fling(find.byType(Block), const Offset(200.0, 0.0), 200.0); + await tester.pump(); + + expect(tester.getTopLeft(find.byKey(childKey)).y, equals(-480.0)); + + await tester.fling(find.byType(Block), const Offset(-200.0, 0.0), 200.0); + await tester.pump(); + + expect(tester.getTopLeft(find.byKey(childKey)).y, equals(-480.0)); + + await tester.fling(find.byType(Block), const Offset(0.0, 200.0), 200.0); + await tester.pump(); + + expect(tester.getTopLeft(find.byKey(childKey)).y, equals(0.0)); + }); + + testWidgets('SemanticsDebugger long press', (WidgetTester tester) async { + bool didLongPress = false; + + await tester.pumpWidget( + new SemanticsDebugger( + child: new GestureDetector( + onLongPress: () { + expect(didLongPress, isFalse); + didLongPress = true; + }, + child: new Text('target'), + ), + ), + ); + + await tester.longPress(find.text('target')); + expect(didLongPress, isTrue); + }); + + testWidgets('SemanticsDebugger slider', (WidgetTester tester) async { + double value = 0.75; + + await tester.pumpWidget( + new SemanticsDebugger( + child: new Material( + child: new Center( + child: new Slider( + value: value, + onChanged: (double newValue) { + value = newValue; + }, + ), + ), + ), + ), + ); + + await tester.fling(find.byType(Slider), const Offset(-100.0, 0.0), 100.0); + expect(value, equals(0.65)); + }); + + testWidgets('SemanticsDebugger checkbox', (WidgetTester tester) async { + Key keyTop = new UniqueKey(); + Key keyBottom = new UniqueKey(); + + bool valueTop = false; + bool valueBottom = true; + + await tester.pumpWidget( + new SemanticsDebugger( + child: new Material( + child: new Block( + children: [ + new Checkbox( + key: keyTop, + value: valueTop, + onChanged: (bool newValue) { + valueTop = newValue; + }, + ), + new Checkbox( + key: keyBottom, + value: valueBottom, + onChanged: null, + ), + ], + ), + ), + ), + ); + + await tester.tap(find.byKey(keyTop)); + + expect(valueTop, isTrue); + valueTop = false; + expect(valueTop, isFalse); + + await tester.tap(find.byKey(keyBottom)); + + expect(valueTop, isFalse); + expect(valueTop, isFalse); + }); } diff --git a/packages/flutter/test/widgets/status_transitions_test.dart b/packages/flutter/test/widgets/status_transitions_test.dart new file mode 100644 index 00000000000..419d05056d2 --- /dev/null +++ b/packages/flutter/test/widgets/status_transitions_test.dart @@ -0,0 +1,88 @@ +// Copyright 2017 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 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/widgets.dart'; + +class TestStatusTransitionWidget extends StatusTransitionWidget { + TestStatusTransitionWidget({ + Key key, + this.builder, + Animation animation, + }) : super(key: key, animation: animation); + + final WidgetBuilder builder; + + @override + Widget build(BuildContext context) => builder(context); +} + +void main() { + testWidgets('Status transition control test', (WidgetTester tester) async { + bool didBuild = false; + final AnimationController controller = new AnimationController( + duration: const Duration(seconds: 1), + vsync: const TestVSync(), + ); + + await tester.pumpWidget(new TestStatusTransitionWidget( + animation: controller, + builder: (BuildContext context) { + expect(didBuild, isFalse); + didBuild = true; + return new Container(); + }, + )); + + expect(didBuild, isTrue); + didBuild = false; + + controller.forward(); + + expect(didBuild, isFalse); + await tester.pump(); + expect(didBuild, isTrue); + didBuild = false; + await tester.pump(const Duration(milliseconds: 100)); + expect(didBuild, isFalse); + await tester.pump(const Duration(milliseconds: 850)); + expect(didBuild, isFalse); + await tester.pump(const Duration(milliseconds: 100)); + expect(didBuild, isTrue); + didBuild = false; + controller.forward(); + await tester.pump(const Duration(milliseconds: 100)); + expect(didBuild, isFalse); + controller.stop(); + await tester.pump(const Duration(milliseconds: 100)); + expect(didBuild, isFalse); + + final AnimationController anotherController = new AnimationController( + duration: const Duration(seconds: 1), + vsync: const TestVSync(), + ); + + await tester.pumpWidget(new TestStatusTransitionWidget( + animation: anotherController, + builder: (BuildContext context) { + expect(didBuild, isFalse); + didBuild = true; + return new Container(); + }, + )); + + expect(didBuild, isTrue); + didBuild = false; + controller.reverse(); + await tester.pump(const Duration(milliseconds: 100)); + expect(didBuild, isFalse); + anotherController.forward(); + await tester.pump(const Duration(milliseconds: 100)); + expect(didBuild, isTrue); + didBuild = false; + + controller.stop(); + anotherController.stop(); + }); +} diff --git a/packages/flutter_test/lib/flutter_test.dart b/packages/flutter_test/lib/flutter_test.dart index 159f08a3c41..f4079b23beb 100644 --- a/packages/flutter_test/lib/flutter_test.dart +++ b/packages/flutter_test/lib/flutter_test.dart @@ -15,4 +15,5 @@ export 'src/matchers.dart'; export 'src/test_async_utils.dart'; export 'src/stack_manipulation.dart'; export 'src/test_pointer.dart'; +export 'src/test_vsync.dart'; export 'src/widget_tester.dart'; diff --git a/packages/flutter/test/animation/animation_tester.dart b/packages/flutter_test/lib/src/test_vsync.dart similarity index 62% rename from packages/flutter/test/animation/animation_tester.dart rename to packages/flutter_test/lib/src/test_vsync.dart index 348f473c760..80d88fe96ae 100644 --- a/packages/flutter/test/animation/animation_tester.dart +++ b/packages/flutter_test/lib/src/test_vsync.dart @@ -4,7 +4,12 @@ import 'package:flutter/scheduler.dart'; +/// A [TickerProvider] that creates a standalone ticker. +/// +/// Useful in tests that create an [AnimationController] outside of the widget +/// tree. class TestVSync implements TickerProvider { + /// Creates a ticker provider that creates standalone tickers. const TestVSync(); @override