Commit 43190374 authored by Adam Barth's avatar Adam Barth

Merge pull request #1424 from abarth/consolidate_tests

Consolidate widget tests
parents b05d42c3 45251598
...@@ -10,7 +10,6 @@ library animation; ...@@ -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';
......
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',
]));
});
}
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);
}
}
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', () {
......
...@@ -2,7 +2,7 @@ import 'package:sky/src/fn3.dart'; ...@@ -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');
......
...@@ -2,7 +2,7 @@ import 'package:sky/material.dart'; ...@@ -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', () {
......
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();
......
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', () {
......
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', () {
......
...@@ -3,7 +3,7 @@ import 'package:sky/src/fn3.dart'; ...@@ -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;
......
...@@ -2,7 +2,7 @@ import 'package:sky/src/fn3.dart'; ...@@ -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', () {
......
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();
......
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', () {
......
...@@ -2,7 +2,7 @@ import 'package:sky/src/fn3.dart'; ...@@ -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', () {
......
...@@ -5,7 +5,7 @@ import 'package:sky/services.dart'; ...@@ -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 {
......
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', () {
......
...@@ -2,7 +2,7 @@ import 'package:sky/animation.dart'; ...@@ -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);
......
...@@ -2,7 +2,7 @@ import 'package:quiver/testing/async.dart'; ...@@ -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];
......
...@@ -3,7 +3,7 @@ import 'package:sky/rendering.dart'; ...@@ -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', () {
......
...@@ -2,7 +2,7 @@ import 'package:sky/rendering.dart'; ...@@ -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>[];
......
...@@ -2,7 +2,7 @@ import 'package:sky/rendering.dart'; ...@@ -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];
......
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];
......
...@@ -5,7 +5,7 @@ ...@@ -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;
......
...@@ -2,7 +2,7 @@ import 'package:sky/src/fn3.dart'; ...@@ -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();
......
...@@ -8,7 +8,7 @@ import 'package:quiver/testing/async.dart'; ...@@ -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;
......
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', () {
......
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);
......
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 });
......
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', () {
......
...@@ -2,36 +2,44 @@ import 'dart:sky' as sky; ...@@ -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 { }
WidgetBuilder _builder;
void set builder (WidgetBuilder value) {
setState(() {
_builder = value;
});
}
Widget build() { class RootComponentState extends State<RootComponent> {
if (_builder != null) Widget _child = new DecoratedBox(decoration: new BoxDecoration());
return _builder(); Widget get child => _child;
return new Container(); void set child(Widget value) {
if (value != _child) {
setState(() {
_child = value;
});
}
} }
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);
}
void pumpFrameWithoutChange([ double frameTimeMs = 0.0 ]) {
scheduler.beginFrame(frameTimeMs);
} }
TestApp _app; 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 { ...@@ -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) {
walker(widget);
widget.walkChildren(walk);
}
_app.walkChildren(walk); void walkElements(ElementVisitor visitor) {
void walk(Element element) {
visitor(element);
element.visitChildren(walk);
}
WidgetFlutterBinding.instance.renderViewElement.visitChildren(walk);
} }
Widget findWidget(bool predicate(Widget widget)) { Element findElement(bool predicate(Element element)) {
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;
assert(box != null); });
return box.localToGlobal(sizeToPoint(box.size)); return element?.state;
} }
Point getCenter(Widget widget) { State findStateByConfig(Widget config) {
return _getWidgetPoint(widget, (Size size) => size.center(Point.origin)); StatefulComponentElement element = findElement((Element element) {
return element is StatefulComponentElement && element.state.config == config;
});
return element?.state;
} }
Point getTopLeft(Widget widget) { Point getCenter(Element element) {
return _getWidgetPoint(widget, (_) => Point.origin); return _getElementPoint(element, (Size size) => size.center(Point.origin));
} }
Point getTopRight(Widget widget) { Point getTopLeft(Element element) {
return _getWidgetPoint(widget, (Size size) => size.topRight(Point.origin)); return _getElementPoint(element, (_) => Point.origin);
} }
Point getBottomLeft(Widget widget) { Point getTopRight(Element element) {
return _getWidgetPoint(widget, (Size size) => size.bottomLeft(Point.origin)); return _getElementPoint(element, (Size size) => size.topRight(Point.origin));
} }
Point getBottomRight(Widget widget) { Point getBottomLeft(Element element) {
return _getWidgetPoint(widget, (Size size) => size.bottomRight(Point.origin)); return _getElementPoint(element, (Size size) => size.bottomLeft(Point.origin));
} }
HitTestResult _hitTest(Point location) => FlutterBinding.instance.hitTest(location); Point getBottomRight(Element element) {
return _getElementPoint(element, (Size size) => size.bottomRight(Point.origin));
}
void _dispatchEvent(sky.Event event, HitTestResult result) { Point _getElementPoint(Element element, Function sizeToPoint) {
FlutterBinding.instance.dispatchEvent(event, result); assert(element != null);
RenderBox box = element.renderObject as RenderBox;
assert(box != null);
return box.localToGlobal(sizeToPoint(box.size));
} }
void tap(Widget widget, { int pointer: 1 }) {
tapAt(getCenter(widget), pointer: pointer); void tap(Element element, { int pointer: 1 }) {
tapAt(getCenter(element), pointer: pointer);
} }
void tapAt(Point location, { int pointer: 1 }) { void tapAt(Point location, { int pointer: 1 }) {
...@@ -120,8 +139,8 @@ class WidgetTester { ...@@ -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 { ...@@ -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);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment