Merge pull request #1424 from abarth/consolidate_tests

Consolidate widget tests
This commit is contained in:
Adam Barth 2015-10-01 08:30:20 -07:00
commit 43190374af
38 changed files with 118 additions and 309 deletions

View File

@ -10,7 +10,6 @@ library animation;
export 'src/animation/animated_simulation.dart'; export 'src/animation/animated_simulation.dart';
export 'src/animation/animated_value.dart'; export 'src/animation/animated_value.dart';
export 'src/animation/animation_performance.dart'; export 'src/animation/animation_performance.dart';
export 'src/animation/clamped_simulation.dart';
export 'src/animation/curves.dart'; export 'src/animation/curves.dart';
export 'src/animation/forces.dart'; export 'src/animation/forces.dart';
export 'src/animation/scheduler.dart'; export 'src/animation/scheduler.dart';

View File

@ -1,42 +0,0 @@
import 'package:sky/src/fn3.dart';
import 'package:test/test.dart';
import 'widget_tester.dart';
void main() {
test('Events bubble up the tree', () {
WidgetTester tester = new WidgetTester();
List<String> log = new List<String>();
tester.pumpFrame(
new Listener(
onPointerDown: (_) {
log.add('top');
},
child: new Listener(
onPointerDown: (_) {
log.add('middle');
},
child: new DecoratedBox(
decoration: const BoxDecoration(),
child: new Listener(
onPointerDown: (_) {
log.add('bottom');
},
child: new Text('X')
)
)
)
)
);
tester.tap(tester.findText('X'));
expect(log, equals([
'bottom',
'middle',
'top',
]));
});
}

View File

@ -1,164 +0,0 @@
import 'dart:sky' as sky;
import 'package:sky/animation.dart';
import 'package:sky/rendering.dart';
import 'package:sky/src/fn3.dart';
import '../engine/mock_events.dart';
class RootComponent extends StatefulComponent {
RootComponentState createState() => new RootComponentState();
}
class RootComponentState extends State<RootComponent> {
Widget _child = new DecoratedBox(decoration: new BoxDecoration());
Widget get child => _child;
void set child(Widget value) {
if (value != _child) {
setState(() {
_child = value;
});
}
}
Widget build(BuildContext context) => child;
}
class WidgetTester {
// See thttps://github.com/flutter/engine/issues/1084 regarding frameTimeMs vs FakeAsync
void pumpFrame(Widget widget, [ double frameTimeMs = 0.0 ]) {
runApp(widget);
scheduler.beginFrame(frameTimeMs);
}
void pumpFrameWithoutChange([ double frameTimeMs = 0.0 ]) {
scheduler.beginFrame(frameTimeMs);
}
void reset() {
runApp(new Container());
scheduler.beginFrame(0.0);
}
List<Layer> _layers(Layer layer) {
List<Layer> result = [layer];
if (layer is ContainerLayer) {
ContainerLayer root = layer;
Layer child = root.firstChild;
while(child != null) {
result.addAll(_layers(child));
child = child.nextSibling;
}
}
return result;
}
List<Layer> get layers => _layers(FlutterBinding.instance.renderView.layer);
void walkElements(ElementVisitor visitor) {
void walk(Element element) {
visitor(element);
element.visitChildren(walk);
}
WidgetFlutterBinding.instance.renderViewElement.visitChildren(walk);
}
Element findElement(bool predicate(Element element)) {
try {
walkElements((Element element) {
if (predicate(element))
throw element;
});
} on Element catch (e) {
return e;
}
return null;
}
Element findElementByKey(Key key) {
return findElement((Element element) => element.widget.key == key);
}
Element findText(String text) {
return findElement((Element element) {
return element.widget is Text && element.widget.data == text;
});
}
State findStateOfType(Type type) {
StatefulComponentElement element = findElement((Element element) {
return element is StatefulComponentElement && element.state.runtimeType == type;
});
return element?.state;
}
State findStateByConfig(Widget config) {
StatefulComponentElement element = findElement((Element element) {
return element is StatefulComponentElement && element.state.config == config;
});
return element?.state;
}
Point getCenter(Element element) {
return _getElementPoint(element, (Size size) => size.center(Point.origin));
}
Point getTopLeft(Element element) {
return _getElementPoint(element, (_) => Point.origin);
}
Point getTopRight(Element element) {
return _getElementPoint(element, (Size size) => size.topRight(Point.origin));
}
Point getBottomLeft(Element element) {
return _getElementPoint(element, (Size size) => size.bottomLeft(Point.origin));
}
Point getBottomRight(Element element) {
return _getElementPoint(element, (Size size) => size.bottomRight(Point.origin));
}
Point _getElementPoint(Element element, Function sizeToPoint) {
assert(element != null);
RenderBox box = element.renderObject as RenderBox;
assert(box != null);
return box.localToGlobal(sizeToPoint(box.size));
}
void tap(Element element, { int pointer: 1 }) {
tapAt(getCenter(element), pointer: pointer);
}
void tapAt(Point location, { int pointer: 1 }) {
HitTestResult result = _hitTest(location);
TestPointer p = new TestPointer(pointer);
_dispatchEvent(p.down(location), result);
_dispatchEvent(p.up(), result);
}
void scroll(Element element, Offset offset, { int pointer: 1 }) {
Point startLocation = getCenter(element);
Point endLocation = startLocation + offset;
TestPointer p = new TestPointer(pointer);
// Events for the entire press-drag-release gesture are dispatched
// to the widgets "hit" by the pointer down event.
HitTestResult result = _hitTest(startLocation);
_dispatchEvent(p.down(startLocation), result);
_dispatchEvent(p.move(endLocation), result);
_dispatchEvent(p.up(), result);
}
void dispatchEvent(sky.Event event, Point location) {
_dispatchEvent(event, _hitTest(location));
}
HitTestResult _hitTest(Point location) => WidgetFlutterBinding.instance.hitTest(location);
void _dispatchEvent(sky.Event event, HitTestResult result) {
WidgetFlutterBinding.instance.dispatchEvent(event, result);
}
}

View File

@ -1,7 +1,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
void main() { void main() {
test('Align smoke test', () { test('Align smoke test', () {

View File

@ -2,7 +2,7 @@ import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../engine/mock_events.dart'; import '../engine/mock_events.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
final Key blockKey = new Key('test'); final Key blockKey = new Key('test');

View File

@ -2,7 +2,7 @@ import 'package:sky/material.dart';
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
void main() { void main() {
test('Circles can have uniform borders', () { test('Circles can have uniform borders', () {

View File

@ -1,8 +1,8 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
import '../fn3/test_widgets.dart'; import 'test_widgets.dart';
class ProbeWidget extends StatefulComponent { class ProbeWidget extends StatefulComponent {
ProbeWidgetState createState() => new ProbeWidgetState(); ProbeWidgetState createState() => new ProbeWidgetState();

View File

@ -1,7 +1,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
void main() { void main() {
test('Can be placed in an infinte box', () { test('Can be placed in an infinte box', () {

View File

@ -1,7 +1,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
void main() { void main() {
test('Can select a day', () { test('Can select a day', () {

View File

@ -3,7 +3,7 @@ import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../engine/mock_events.dart'; import '../engine/mock_events.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
const double itemExtent = 100.0; const double itemExtent = 100.0;
ScrollDirection scrollDirection = ScrollDirection.vertical; ScrollDirection scrollDirection = ScrollDirection.vertical;

View File

@ -2,7 +2,7 @@ import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../engine/mock_events.dart'; import '../engine/mock_events.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
void main() { void main() {
test('Drag and drop - control test', () { test('Drag and drop - control test', () {

View File

@ -1,7 +1,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
class Item { class Item {
GlobalKey key1 = new GlobalKey(); GlobalKey key1 = new GlobalKey();

View File

@ -1,7 +1,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
void main() { void main() {
test('Can hit test flex children of stacks', () { test('Can hit test flex children of stacks', () {

View File

@ -2,7 +2,7 @@ import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../engine/mock_events.dart'; import '../engine/mock_events.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
void main() { void main() {
test('Uncontested scrolls start immediately', () { test('Uncontested scrolls start immediately', () {

View File

@ -5,7 +5,7 @@ import 'package:sky/services.dart';
import 'package:sky/widgets_next.dart'; import 'package:sky/widgets_next.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
import '../services/mock_services.dart'; import '../services/mock_services.dart';
class MockKeyboard implements KeyboardService { class MockKeyboard implements KeyboardService {

View File

@ -1,7 +1,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
void main() { void main() {
test('Events bubble up the tree', () { test('Events bubble up the tree', () {

View File

@ -2,7 +2,7 @@ import 'package:sky/animation.dart';
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
class FirstComponent extends StatelessComponent { class FirstComponent extends StatelessComponent {
FirstComponent(this.navigator); FirstComponent(this.navigator);

View File

@ -2,7 +2,7 @@ import 'package:quiver/testing/async.dart';
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
const Size pageSize = const Size(800.0, 600.0); const Size pageSize = const Size(800.0, 600.0);
const List<int> pages = const <int>[0, 1, 2, 3, 4, 5]; const List<int> pages = const <int>[0, 1, 2, 3, 4, 5];

View File

@ -3,7 +3,7 @@ import 'package:sky/rendering.dart';
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
void main() { void main() {
test('LinearProgressIndicator changes when its value changes', () { test('LinearProgressIndicator changes when its value changes', () {

View File

@ -2,7 +2,7 @@ import 'package:sky/rendering.dart';
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
const List<int> items = const <int>[0, 1, 2, 3, 4, 5]; const List<int> items = const <int>[0, 1, 2, 3, 4, 5];
List<int> tapped = <int>[]; List<int> tapped = <int>[];

View File

@ -2,7 +2,7 @@ import 'package:sky/rendering.dart';
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
const List<int> items = const <int>[0, 1, 2, 3, 4, 5]; const List<int> items = const <int>[0, 1, 2, 3, 4, 5];

View File

@ -1,7 +1,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
const List<int> items = const <int>[0, 1, 2, 3, 4, 5]; const List<int> items = const <int>[0, 1, 2, 3, 4, 5];

View File

@ -5,7 +5,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
ChangerState changer; ChangerState changer;

View File

@ -2,7 +2,7 @@ import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../engine/mock_events.dart'; import '../engine/mock_events.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
class Inside extends StatefulComponent { class Inside extends StatefulComponent {
InsideState createState() => new InsideState(); InsideState createState() => new InsideState();

View File

@ -8,7 +8,7 @@ import 'package:quiver/testing/async.dart';
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
const double itemExtent = 200.0; const double itemExtent = 200.0;
ScrollDirection scrollDirection = ScrollDirection.vertical; ScrollDirection scrollDirection = ScrollDirection.vertical;

View File

@ -1,7 +1,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
void main() { void main() {
test('Can change position data', () { test('Can change position data', () {

View File

@ -1,7 +1,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
class InnerComponent extends StatefulComponent { class InnerComponent extends StatefulComponent {
InnerComponent({ Key key }) : super(key: key); InnerComponent({ Key key }) : super(key: key);

View File

@ -1,7 +1,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
class TestWidget extends StatefulComponent { class TestWidget extends StatefulComponent {
TestWidget({ this.child, this.persistentState, this.syncedState }); TestWidget({ this.child, this.persistentState, this.syncedState });

View File

@ -1,7 +1,7 @@
import 'package:sky/src/fn3.dart'; import 'package:sky/src/fn3.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import '../fn3/widget_tester.dart'; import 'widget_tester.dart';
void main() { void main() {
test('Transform origin', () { test('Transform origin', () {

View File

@ -2,36 +2,44 @@ import 'dart:sky' as sky;
import 'package:sky/animation.dart'; import 'package:sky/animation.dart';
import 'package:sky/rendering.dart'; import 'package:sky/rendering.dart';
import 'package:sky/widgets.dart'; import 'package:sky/src/fn3.dart';
import '../engine/mock_events.dart'; import '../engine/mock_events.dart';
typedef Widget WidgetBuilder(); class RootComponent extends StatefulComponent {
RootComponentState createState() => new RootComponentState();
}
class TestApp extends App { class RootComponentState extends State<RootComponent> {
Widget _child = new DecoratedBox(decoration: new BoxDecoration());
WidgetBuilder _builder; Widget get child => _child;
void set builder (WidgetBuilder value) { void set child(Widget value) {
if (value != _child) {
setState(() { setState(() {
_builder = value; _child = value;
}); });
} }
Widget build() {
if (_builder != null)
return _builder();
return new Container();
} }
Widget build(BuildContext context) => child;
} }
class WidgetTester { class WidgetTester {
WidgetTester() {
_app = new TestApp(); // See thttps://github.com/flutter/engine/issues/1084 regarding frameTimeMs vs FakeAsync
runApp(_app);
scheduler.beginFrame(0.0); // to initialise the app void pumpFrame(Widget widget, [ double frameTimeMs = 0.0 ]) {
runApp(widget);
scheduler.beginFrame(frameTimeMs);
} }
TestApp _app; void pumpFrameWithoutChange([ double frameTimeMs = 0.0 ]) {
scheduler.beginFrame(frameTimeMs);
}
void reset() {
runApp(new Container());
scheduler.beginFrame(0.0);
}
List<Layer> _layers(Layer layer) { List<Layer> _layers(Layer layer) {
List<Layer> result = [layer]; List<Layer> result = [layer];
@ -47,70 +55,81 @@ class WidgetTester {
} }
List<Layer> get layers => _layers(FlutterBinding.instance.renderView.layer); List<Layer> get layers => _layers(FlutterBinding.instance.renderView.layer);
void walkWidgets(WidgetTreeWalker walker) {
void walk(Widget widget) { void walkElements(ElementVisitor visitor) {
walker(widget); void walk(Element element) {
widget.walkChildren(walk); visitor(element);
element.visitChildren(walk);
}
WidgetFlutterBinding.instance.renderViewElement.visitChildren(walk);
} }
_app.walkChildren(walk); Element findElement(bool predicate(Element element)) {
}
Widget findWidget(bool predicate(Widget widget)) {
try { try {
walkWidgets((Widget widget) { walkElements((Element element) {
if (predicate(widget)) if (predicate(element))
throw widget; throw element;
}); });
} catch (e) { } on Element catch (e) {
if (e is Widget)
return e; return e;
rethrow;
} }
return null; return null;
} }
Text findText(String text) { Element findElementByKey(Key key) {
return findWidget((Widget widget) { return findElement((Element element) => element.widget.key == key);
return widget is Text && widget.data == text; }
Element findText(String text) {
return findElement((Element element) {
return element.widget is Text && element.widget.data == text;
}); });
} }
Point _getWidgetPoint(Widget widget, Function sizeToPoint) { State findStateOfType(Type type) {
assert(widget != null); StatefulComponentElement element = findElement((Element element) {
RenderBox box = widget.renderObject as RenderBox; return element is StatefulComponentElement && element.state.runtimeType == type;
});
return element?.state;
}
State findStateByConfig(Widget config) {
StatefulComponentElement element = findElement((Element element) {
return element is StatefulComponentElement && element.state.config == config;
});
return element?.state;
}
Point getCenter(Element element) {
return _getElementPoint(element, (Size size) => size.center(Point.origin));
}
Point getTopLeft(Element element) {
return _getElementPoint(element, (_) => Point.origin);
}
Point getTopRight(Element element) {
return _getElementPoint(element, (Size size) => size.topRight(Point.origin));
}
Point getBottomLeft(Element element) {
return _getElementPoint(element, (Size size) => size.bottomLeft(Point.origin));
}
Point getBottomRight(Element element) {
return _getElementPoint(element, (Size size) => size.bottomRight(Point.origin));
}
Point _getElementPoint(Element element, Function sizeToPoint) {
assert(element != null);
RenderBox box = element.renderObject as RenderBox;
assert(box != null); assert(box != null);
return box.localToGlobal(sizeToPoint(box.size)); return box.localToGlobal(sizeToPoint(box.size));
} }
Point getCenter(Widget widget) {
return _getWidgetPoint(widget, (Size size) => size.center(Point.origin));
}
Point getTopLeft(Widget widget) { void tap(Element element, { int pointer: 1 }) {
return _getWidgetPoint(widget, (_) => Point.origin); tapAt(getCenter(element), pointer: pointer);
}
Point getTopRight(Widget widget) {
return _getWidgetPoint(widget, (Size size) => size.topRight(Point.origin));
}
Point getBottomLeft(Widget widget) {
return _getWidgetPoint(widget, (Size size) => size.bottomLeft(Point.origin));
}
Point getBottomRight(Widget widget) {
return _getWidgetPoint(widget, (Size size) => size.bottomRight(Point.origin));
}
HitTestResult _hitTest(Point location) => FlutterBinding.instance.hitTest(location);
void _dispatchEvent(sky.Event event, HitTestResult result) {
FlutterBinding.instance.dispatchEvent(event, result);
}
void tap(Widget widget, { int pointer: 1 }) {
tapAt(getCenter(widget), pointer: pointer);
} }
void tapAt(Point location, { int pointer: 1 }) { void tapAt(Point location, { int pointer: 1 }) {
@ -120,8 +139,8 @@ class WidgetTester {
_dispatchEvent(p.up(), result); _dispatchEvent(p.up(), result);
} }
void scroll(Widget widget, Offset offset, { int pointer: 1 }) { void scroll(Element element, Offset offset, { int pointer: 1 }) {
Point startLocation = getCenter(widget); Point startLocation = getCenter(element);
Point endLocation = startLocation + offset; Point endLocation = startLocation + offset;
TestPointer p = new TestPointer(pointer); TestPointer p = new TestPointer(pointer);
// Events for the entire press-drag-release gesture are dispatched // Events for the entire press-drag-release gesture are dispatched
@ -136,13 +155,10 @@ class WidgetTester {
_dispatchEvent(event, _hitTest(location)); _dispatchEvent(event, _hitTest(location));
} }
void pumpFrame(WidgetBuilder builder, [double frameTimeMs = 0.0]) { HitTestResult _hitTest(Point location) => WidgetFlutterBinding.instance.hitTest(location);
_app.builder = builder;
scheduler.beginFrame(frameTimeMs);
}
void pumpFrameWithoutChange([double frameTimeMs = 0.0]) { void _dispatchEvent(sky.Event event, HitTestResult result) {
scheduler.beginFrame(frameTimeMs); WidgetFlutterBinding.instance.dispatchEvent(event, result);
} }
} }