diff --git a/invoiceninja.iml b/invoiceninja.iml index e5c837191..b906a1e50 100644 --- a/invoiceninja.iml +++ b/invoiceninja.iml @@ -4,7 +4,7 @@ - + diff --git a/lib/ui/auth/login.dart b/lib/ui/auth/login.dart index 7f86b9457..b0a3218fc 100644 --- a/lib/ui/auth/login.dart +++ b/lib/ui/auth/login.dart @@ -20,7 +20,7 @@ class Login extends StatelessWidget { static final GlobalKey _formKey = GlobalKey(); static final GlobalKey> _emailKey = - GlobalKey>(); + GlobalKey>(debugLabel: 'email'); static final GlobalKey> _passwordKey = GlobalKey>(); static final GlobalKey> _urlKey = diff --git a/lib/ui/product/product_list_vm.dart b/lib/ui/product/product_list_vm.dart index f5af02052..a2f7a2150 100644 --- a/lib/ui/product/product_list_vm.dart +++ b/lib/ui/product/product_list_vm.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_redux/flutter_redux.dart'; +import 'package:invoiceninja/ui/app/snackbar_row.dart'; +import 'package:invoiceninja/utils/localization.dart'; import 'package:redux/redux.dart'; import 'package:invoiceninja/data/models/models.dart'; import 'package:invoiceninja/ui/product/product_list.dart'; @@ -51,8 +53,6 @@ class ProductListVM { Future _handleRefresh(BuildContext context) { final Completer completer = new Completer(); store.dispatch(LoadProductsAction(completer)); - return completer.future; - /* return completer.future.then((_) { Scaffold.of(context).showSnackBar(SnackBar( content: SnackBarRow( @@ -60,7 +60,6 @@ class ProductListVM { ), duration: Duration(seconds: 3))); }); - */ } return ProductListVM( diff --git a/pubspec.lock b/pubspec.lock index a9666a2a4..bb5eae256 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -141,6 +141,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.14" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.0" fixnum: dependency: transitive description: @@ -153,6 +160,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_driver: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" flutter_localizations: dependency: "direct main" description: flutter @@ -166,7 +178,7 @@ packages: source: hosted version: "0.5.0" flutter_test: - dependency: "direct dev" + dependency: transitive description: flutter source: sdk version: "0.0.0" @@ -254,6 +266,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.1" + json_rpc_2: + dependency: transitive + description: + name: json_rpc_2 + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" kernel: dependency: transitive description: @@ -511,6 +530,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.6" + vm_service_client: + dependency: transitive + description: + name: vm_service_client + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.4+1" watcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e1466ff64..5a7a867e4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,8 +18,10 @@ dependencies: dev_dependencies: - flutter_test: + flutter_driver: sdk: flutter + #flutter_test: + # sdk: flutter build_runner: ^0.8.9 built_value_generator: "^5.4.5" diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index d6d9cca4b..000000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -// This is a basic Flutter widget test. -// To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter -// provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to -// find child widgets in the widget tree, read text, and verify that the values of widget properties -// are correct. - -//import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - /* - await tester.pumpWidget(new MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - */ - }); -} diff --git a/test_driver/widget.dart b/test_driver/widget.dart new file mode 100644 index 000000000..0648770ae --- /dev/null +++ b/test_driver/widget.dart @@ -0,0 +1,94 @@ +import 'package:flutter_driver/driver_extension.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:redux/redux.dart'; +import 'package:invoiceninja/routes.dart'; +import 'package:invoiceninja/ui/auth/login_vm.dart'; +import 'package:invoiceninja/ui/dashboard/dashboard.dart'; +import 'package:invoiceninja/ui/product/product_screen.dart'; +import 'package:invoiceninja/redux/app/app_reducer.dart'; +import 'package:invoiceninja/redux/app/app_state.dart'; +import 'package:invoiceninja/redux/auth/auth_middleware.dart'; +import 'package:invoiceninja/redux/auth/auth_actions.dart'; +import 'package:invoiceninja/redux/dashboard/dashboard_actions.dart'; +import 'package:invoiceninja/redux/dashboard/dashboard_middleware.dart'; +import 'package:invoiceninja/redux/product/product_actions.dart'; +import 'package:invoiceninja/redux/product/product_middleware.dart'; +import 'package:invoiceninja/utils/localization.dart'; +//import 'package:redux_logging/redux_logging.dart'; + +void main() { + enableFlutterDriverExtension(); + + final store = Store(appReducer, + initialState: AppState(), + middleware: [] + ..addAll(createStoreAuthMiddleware()) + ..addAll(createStoreDashboardMiddleware()) + ..addAll(createStoreProductsMiddleware()) + ..addAll([ + //LoggingMiddleware.printer(), + ])); + + runApp(new InvoiceNinjaApp(store: store)); +} + +class InvoiceNinjaApp extends StatefulWidget { + final Store store; + + InvoiceNinjaApp({Key key, this.store}) : super(key: key); + + @override + _InvoiceNinjaAppState createState() => new _InvoiceNinjaAppState(); +} + +class _InvoiceNinjaAppState extends State { + @override + Widget build(BuildContext context) { + return new StoreProvider( + store: widget.store, + child: new MaterialApp( + debugShowCheckedModeBanner: false, + localizationsDelegates: [ + const AppLocalizationsDelegate(), + GlobalMaterialLocalizations.delegate, + ], + theme: ThemeData().copyWith( + primaryColor: const Color(0xFF117cc1), + primaryColorDark: const Color(0xFF005090), + primaryColorLight: const Color(0xFF5dabf4), + ), + /* + theme: ThemeData( + brightness: Brightness.dark, + accentColor: Colors.lightBlueAccent, + ), + */ + title: 'Invoice Ninja', + routes: { + AppRoutes.login: (context) { + StoreProvider.of(context).dispatch(LoadUserLogin()); + return LoginVM(); + }, + AppRoutes.dashboard: (context) { + StoreProvider.of(context).dispatch(LoadDashboardAction()); + return Dashboard(); + }, + AppRoutes.products: (context) { + if (StoreProvider + .of(context) + .state + .productState() + .isStale()) { + StoreProvider + .of(context) + .dispatch(LoadProductsAction()); + } + return ProductScreen(); + }, + }, + ), + ); + } +} diff --git a/test_driver/widget_test.dart b/test_driver/widget_test.dart new file mode 100644 index 000000000..353c73381 --- /dev/null +++ b/test_driver/widget_test.dart @@ -0,0 +1,71 @@ +import 'dart:async'; + +// Imports the Flutter Driver API +import 'package:flutter_driver/flutter_driver.dart'; +import 'package:test/test.dart'; +//import 'package:invoiceninja/ui/auth/login.dart'; + +void main() { + group('scrolling performance test', () { + FlutterDriver driver; + + setUpAll(() async { + // Connects to the app + driver = await FlutterDriver.connect(); + }); + + tearDownAll(() async { + if (driver != null) { + // Closes the connection + driver.close(); + } + }); + + test('measure', () async { + // Record the performance timeline of things that happen inside the closure + Timeline timeline = await driver.traceAction(() async { + + //SerializableFinder email = find.byValueKey('email'); + + final SerializableFinder email = find.byTooltip('Email'); + expect(email, isNotNull); + await driver.waitFor(email); + await driver.enterText('test'); + + /* + // Find the scrollable user list + SerializableFinder userList = find.byValueKey('user-list'); + + // Scroll down 5 times + for (int i = 0; i < 5; i++) { + // Scroll 300 pixels down, for 300 millis + await driver.scroll( + userList, 0.0, -300.0, new Duration(milliseconds: 300)); + + // Emulate a user's finger taking its time to go back to the original + // position before the next scroll + await new Future.delayed(new Duration(milliseconds: 500)); + } + + // Scroll up 5 times + for (int i = 0; i < 5; i++) { + await driver.scroll( + userList, 0.0, 300.0, new Duration(milliseconds: 300)); + await new Future.delayed(new Duration(milliseconds: 500)); + } + */ + }); + + // The `timeline` object contains all the performance data recorded during + // the scrolling session. It can be digested into a handful of useful + // aggregate numbers, such as "average frame build time". + TimelineSummary summary = new TimelineSummary.summarize(timeline); + + // The following line saves the timeline summary to a JSON file. + summary.writeSummaryToFile('scrolling_performance', pretty: true); + + // The following line saves the raw timeline data as JSON. + summary.writeTimelineToFile('scrolling_performance', pretty: true); + }); + }); +} \ No newline at end of file