reader/flutter: WIP flutter test setup.

Adds basic UI testing example. Pending test support will be uploaded in a new
patch to this CL once a flutter profile has been added.

Change-Id: I4b02021b4a66884671b5e8652746c6bd52f952e2
diff --git a/flutter/Makefile b/flutter/Makefile
index dd3dee9..628a177 100644
--- a/flutter/Makefile
+++ b/flutter/Makefile
@@ -18,7 +18,8 @@
 
 .PHONY: analyze
 analyze:
-	dartanalyzer lib/main.dart
+	@echo "=== Analyzing Dart code ==="
+	dartanalyzer lib/main.dart --lints --fatal-hints --fatal-warnings
 
 .PHONY: fmt
 fmt: packages
@@ -27,3 +28,11 @@
 doc:
 	dartdoc
 	@touch $@
+
+# TODO(jasoncampbell): this task needs to use the jiri profile for flutter.
+# NOTE: $FLUTTER_ENGINE is automatically picked up by flutter but is explcitly
+# set here for calrity until the correct path can be set using a jiri profile.
+.PHONY: test
+test: analyze
+	@echo "=== Running tests ==="
+	flutter test --engine-src-path $(FLUTTER_ENGINE)/src
diff --git a/flutter/README.md b/flutter/README.md
index 8effe7a..bef43c1 100644
--- a/flutter/README.md
+++ b/flutter/README.md
@@ -6,3 +6,14 @@
 
 For help getting started with Flutter, view our online
 [documentation](http://flutter.io/).
+
+
+# Development
+
+## Testing
+
+TODO(jasoncampbell): Add docs for running tests.
+
+### Mac
+
+https://github.com/flutter/engine/blob/master/CONTRIBUTING.md
diff --git a/flutter/lib/components/flutter_demo.dart b/flutter/lib/components/flutter_demo.dart
new file mode 100644
index 0000000..a06cd66
--- /dev/null
+++ b/flutter/lib/components/flutter_demo.dart
@@ -0,0 +1,33 @@
+// Copyright 2016 The Vanadium 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/material.dart';
+
+class FlutterDemo extends StatefulComponent {
+  FlutterDemo({Key key}): super(key: key);
+
+  FlutterDemoState createState() => new FlutterDemoState();
+}
+
+class FlutterDemoState extends State<FlutterDemo> {
+  int counter = 0;
+
+  void incrementCounter() {
+    setState(() {
+      counter++;
+    });
+  }
+
+  Widget build(BuildContext context) {
+    return new Scaffold(
+        toolBar: new ToolBar(center: new Text('Flutter Demo')),
+        body: new Center(
+            child: new Text(
+                'Button tapped $counter time${ counter == 1 ? '' : 's' }.')),
+        floatingActionButton: new FloatingActionButton(
+            onPressed: incrementCounter,
+            tooltip: 'Increment',
+            child: new Icon(icon: Icons.add)));
+  }
+}
diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart
index 6625aa4..2a10be3 100644
--- a/flutter/lib/main.dart
+++ b/flutter/lib/main.dart
@@ -3,6 +3,7 @@
 // license that can be found in the LICENSE file.
 
 import 'package:flutter/material.dart';
+import 'components/flutter_demo.dart';
 
 void main() {
   runApp(new MaterialApp(
@@ -11,29 +12,3 @@
         '/': (RouteArguments args) => new FlutterDemo()
       }));
 }
-
-class FlutterDemo extends StatefulComponent {
-  _FlutterDemoState createState() => new _FlutterDemoState();
-}
-
-class _FlutterDemoState extends State<FlutterDemo> {
-  int _counter = 0;
-
-  void _incrementCounter() {
-    setState(() {
-      _counter++;
-    });
-  }
-
-  Widget build(BuildContext context) {
-    return new Scaffold(
-        toolBar: new ToolBar(center: new Text('Flutter Demo')),
-        body: new Center(
-            child: new Text(
-                'Button tapped $_counter time${ _counter == 1 ? '' : 's' }.')),
-        floatingActionButton: new FloatingActionButton(
-            onPressed: _incrementCounter,
-            tooltip: 'Increment',
-            child: new Icon(icon: 'content/add')));
-  }
-}
diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml
index df057b1..89098ec 100644
--- a/flutter/pubspec.yaml
+++ b/flutter/pubspec.yaml
@@ -3,3 +3,7 @@
 dependencies:
   flutter:
     path: ../../../../../flutter/packages/flutter
+dev_dependencies:
+  test: ^0.12.6
+  flutter_test:
+    path: ../../../../../flutter/packages/flutter_test
diff --git a/flutter/test/flutter_demo_test.dart b/flutter/test/flutter_demo_test.dart
new file mode 100644
index 0000000..d2c890b
--- /dev/null
+++ b/flutter/test/flutter_demo_test.dart
@@ -0,0 +1,57 @@
+// Copyright 2016 The Vanadium 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:test/test.dart";
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:reader/components/flutter_demo.dart';
+
+void main() {
+  test("Example State test", () {
+    testWidgets((WidgetTester tester) {
+      GlobalKey key = new GlobalKey();
+      tester.pumpWidget(new MaterialApp(
+          title: 'Test App',
+          routes: <String, RouteBuilder>{
+            '/': (RouteArguments args) => new FlutterDemo(key: key)
+          }));
+
+      // Test State.
+      StatefulComponentElement element = tester.findElementByKey(key);
+      FlutterDemoState state = element.state;
+
+      expect(tester.findText("Flutter Demo"), isNotNull);
+      expect(tester.findText("Button tapped 0 times."), isNotNull);
+
+      state.incrementCounter();
+      // Advance the flutter framework to the next tick.
+      tester.pump();
+
+      expect(state.counter, equals(1));
+      expect(tester.findText("Button tapped 1 time."), isNotNull);
+
+      // NOTE: There is an animation segue for the floating action button in
+      // the material scaffold. The FAB is not tappable during this initial
+      // segue, the FAB will not be responsive to tapping until the animation
+      // ends at 400ms.
+      //
+      // SEE: https://git.io/vaLPb
+      tester.pump(new Duration(milliseconds: 400));
+
+      // Test Widget input and rendering.
+      StatefulComponentElement fab = tester.findElement((Element element) {
+        return element.widget is FloatingActionButton;
+      });
+
+      expect(fab, isNotNull);
+
+      tester.tap(fab);
+      tester.pump();
+
+      expect(state.counter, equals(2));
+      expect(tester.findText("Button tapped 2 times."), isNotNull);
+    });
+  });
+}