feat(runner): app is not hot reloaded to increase testing speed

feat(build): no-build and flavor options now work
chore(lib): upgraded to v1 of dart_gherkin
This commit is contained in:
Jon Samwell 2019-06-05 11:00:41 +10:00
parent 6705a5f8f5
commit fe20dd9cdc
18 changed files with 138 additions and 128 deletions

View File

@ -1,3 +1,9 @@
## [1.0.0] - 05/06/2019
* Huge speed improvement when running tests by hot reloading (which clears the state) rather than restarting the app
* Added flag to determine if the application should be built prior to running tests
* Merged PR which allows for build flavor and device id to be specified thanks to @iqbalmineraltown for the PR
* Updated to latest v1 dart_gherkin lib
## [0.0.14] - 23/04/2019 ## [0.0.14] - 23/04/2019
* Updated to rely on the abstracted Gherkin library 'https://github.com/jonsamwell/dart_gherkin' which now includes a JsonReporter * Updated to rely on the abstracted Gherkin library 'https://github.com/jonsamwell/dart_gherkin' which now includes a JsonReporter
* Updated docs * Updated docs

View File

@ -38,6 +38,9 @@ Available as a Dart package https://pub.dartlang.org/packages/flutter_gherkin
- [exitAfterTestRun](#exitaftertestrun) - [exitAfterTestRun](#exitaftertestrun)
- [Flutter specific configuration options](#flutter-specific-configuration-options) - [Flutter specific configuration options](#flutter-specific-configuration-options)
- [restartAppBetweenScenarios](#restartappbetweenscenarios) - [restartAppBetweenScenarios](#restartappbetweenscenarios)
- [build](#build)
- [buildFlavor](#buildFlavor)
- [targetDeviceId](#targetDeviceId)
- [targetAppPath](#targetapppath) - [targetAppPath](#targetapppath)
- [Features Files](#features-files) - [Features Files](#features-files)
- [Steps Definitions](#steps-definitions) - [Steps Definitions](#steps-definitions)
@ -343,13 +346,19 @@ The `FlutterTestConfiguration` will automatically create some default Flutter op
Defaults to `true`. Defaults to `true`.
To avoid tests starting on an app changed by a previous test it is suggested that the Flutter application under test be restarted between each scenario. While this will increase the execution time slightly it will limit tests failing because they run against an app changed by a previous test. Note in more complex application it may also be necessary to use the `AfterScenario` hook to reset the application to a base state a test can run on. Logging out for example if restarting an application will present a lock screen etc. To avoid tests starting on an app changed by a previous test it is suggested that the Flutter application under test be restarted between each scenario. While this will increase the execution time slightly it will limit tests failing because they run against an app changed by a previous test. Note in more complex application it may also be necessary to use the `AfterScenario` hook to reset the application to a base state a test can run on. Logging out for example if restarting an application will present a lock screen etc. This now performs a hot reload of the application which resets the state and drastically reduces the time to run the tests.
#### targetAppPath #### targetAppPath
Defaults to `lib/test_driver/app.dart` Defaults to `lib/test_driver/app.dart`
This should point to the *testable* application that enables the Flutter driver extensions and thus is able to be automated. This application wil be started when the test run in started and restarted if the `restartAppBetweenScenarios` configuration property is set to true. This should point to the *testable* application that enables the Flutter driver extensions and thus is able to be automated. This application wil be started when the test run is started and restarted if the `restartAppBetweenScenarios` configuration property is set to true.
#### build
Defaults to `true`
This optional argument lets you specify if the target application should be built prior to running the first test. This defaults to `true`
#### buildFlavor #### buildFlavor

View File

@ -3,82 +3,12 @@
# see https://github.com/dart-lang/pedantic#enabled-lints. # see https://github.com/dart-lang/pedantic#enabled-lints.
include: package:pedantic/analysis_options.yaml include: package:pedantic/analysis_options.yaml
analyzer:
errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning
# treat missing returns as a warning (not a hint)
missing_return: warning
# allow having TODOs in the code
todo: ignore
exclude:
# - path/to/excluded/files/**
# For lint rules and documentation, see http://dart-lang.github.io/linter/lints. # For lint rules and documentation, see http://dart-lang.github.io/linter/lints.
linter: # Uncomment to specify additional rules.
rules: # linter:
# these rules are documented on and in the same order as # rules:
# the Dart Lint rules page to make maintenance easier # - camel_case_types
# http://dart-lang.github.io/linter/lints/
# === error rules === # analyzer:
- avoid_empty_else # exclude:
- avoid_slow_async_io # - path/to/excluded/files/**
- cancel_subscriptions
- close_sinks
- control_flow_in_finally
- empty_statements
- hash_and_equals
- iterable_contains_unrelated_type
- list_remove_unrelated_type
- no_adjacent_strings_in_list
- no_duplicate_case_values
- test_types_in_equals
- throw_in_finally
- unrelated_type_equality_checks
- valid_regexps
# === style rules ===
- always_declare_return_types
- always_require_non_null_named_parameters
# - always_specify_types
- annotate_overrides
# - avoid_as
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_return_types_on_setters
- await_only_futures
- camel_case_types
# - directives_ordering
# - empty_catches
- empty_constructor_bodies
- implementation_imports
- library_names
- library_prefixes
- non_constant_identifier_names
- overridden_fields
- package_api_docs
- package_prefixed_library_names
- prefer_adjacent_string_concatenation
- prefer_collection_literals
- prefer_const_constructors
- prefer_contains
- prefer_equal_for_default_values
- prefer_final_locals
- prefer_initializing_formals
- prefer_is_empty
- prefer_is_not_empty
- prefer_void_to_null
- slash_for_doc_comments
# - sort_constructors_first
- sort_unnamed_constructors_first
- type_init_formals
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_getters_setters
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_statements
- unnecessary_this
- use_rethrow_when_possible

View File

@ -17,5 +17,6 @@ This will run the features files found in the folder `test_driver/features` agai
To debug this example and step through the library code. To debug this example and step through the library code.
1. Set a break point in `test_driver/app_test.dart` 1. Set a break point in `test_driver/app_test.dart`
2. If you are in VsCode you will simply be able to select `Debug example` from the dropdown in the `debugging tab` as the `launch.json` has been configured. 2. Set `exitAfterTestRun` on the configuration to false to ensure exiting cleanly as the IDE will handle exiting
3. If you are in VsCode you will simply be able to select `Debug example` from the dropdown in the `debugging tab` as the `launch.json` has been configured.
- otherwise you will need to run a debugging session against `test_driver/app_test.dart`. - otherwise you will need to run a debugging session against `test_driver/app_test.dart`.

View File

@ -49,13 +49,16 @@ android {
} }
} }
/// uncomment to see example of using flavor /// uncomment to see example of using flavors
// flavorDimensions "env" // flavorDimensions "env"
// productFlavors { // productFlavors {
// staging { // staging {
// dimension "env" // dimension "env"
// applicationIdSuffix ".dev" // applicationIdSuffix ".dev"
// versionNameSuffix "-dev"
// }
// production {
// dimension "env"
// } // }
// } // }
} }

View File

@ -17,7 +17,7 @@ dependencies:
sdk: flutter sdk: flutter
path: ^1.6.2 path: ^1.6.2
glob: ^1.1.7 glob: ^1.1.7
gherkin: ^0.0.4 gherkin: ^1.0.0
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
@ -32,7 +32,6 @@ dev_dependencies:
flutter_gherkin: flutter_gherkin:
path: ../ path: ../
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec # following page: https://www.dartlang.org/tools/pub/pubspec

1
example/report.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,4 @@
import 'package:example/main.dart'; import 'package:example/main.dart' as app;
import 'package:flutter/widgets.dart';
import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_driver/driver_extension.dart';
void main() { void main() {
@ -8,5 +7,5 @@ void main() {
// Call the `main()` function of your app or call `runApp` with any widget you // Call the `main()` function of your app or call `runApp` with any widget you
// are interested in testing. // are interested in testing.
runApp(MyApp()); app.main();
} }

View File

@ -20,7 +20,7 @@ Future<void> main() {
..customStepParameterDefinitions = [ColourParameter()] ..customStepParameterDefinitions = [ColourParameter()]
..restartAppBetweenScenarios = true ..restartAppBetweenScenarios = true
..targetAppPath = "test_driver/app.dart" ..targetAppPath = "test_driver/app.dart"
// ..buildFlavor = "staging" // uncomment when using build flavor and check android/ios flavor setup // ..buildFlavor = "staging" // uncomment when using build flavor and check android/ios flavor setup see android file android\app\build.gradle
// ..targetDeviceId = "all" // uncomment to run tests on all connected devices or set specific device target id // ..targetDeviceId = "all" // uncomment to run tests on all connected devices or set specific device target id
// ..tagExpression = "@smoke" // uncomment to see an example of running scenarios based on tag expressions // ..tagExpression = "@smoke" // uncomment to see an example of running scenarios based on tag expressions
..exitAfterTestRun = true; // set to false if debugging to exit cleanly ..exitAfterTestRun = true; // set to false if debugging to exit cleanly

View File

@ -12,7 +12,7 @@ import 'package:gherkin/gherkin.dart';
class GivenIAddTheUsers extends Given1<Table> { class GivenIAddTheUsers extends Given1<Table> {
@override @override
Future<void> executeStep(Table dataTable) async { Future<void> executeStep(Table dataTable) async {
// TODO: implement executeStep // implement executeStep
for (var row in dataTable.rows) { for (var row in dataTable.rows) {
// do something with row // do something with row
row.columns.forEach((columnValue) => print(columnValue)); row.columns.forEach((columnValue) => print(columnValue));

View File

@ -11,7 +11,7 @@ import 'package:gherkin/gherkin.dart';
class GivenIProvideAComment extends Given2<String, String> { class GivenIProvideAComment extends Given2<String, String> {
@override @override
Future<void> executeStep(String commentType, String comment) async { Future<void> executeStep(String commentType, String comment) async {
// TODO: implement executeStep // implement executeStep
} }
@override @override

View File

@ -14,11 +14,18 @@ class FlutterRunProcessHandler extends ProcessHandler {
static RegExp _noConnectedDeviceRegex = static RegExp _noConnectedDeviceRegex =
RegExp(r"no connected device", caseSensitive: false, multiLine: false); RegExp(r"no connected device", caseSensitive: false, multiLine: false);
static RegExp _restartedApplicationSuccessRegex = RegExp(
r"Restarted application (.*)ms.",
caseSensitive: false,
multiLine: false);
Process _runningProcess; Process _runningProcess;
Stream<String> _processStdoutStream; Stream<String> _processStdoutStream;
List<StreamSubscription> _openSubscriptions = <StreamSubscription>[]; List<StreamSubscription> _openSubscriptions = <StreamSubscription>[];
String _appTarget;
String _workingDirectory; String _workingDirectory;
String _appTarget;
bool _buildApp = true;
String _buildFlavor; String _buildFlavor;
String _deviceTargetId; String _deviceTargetId;
@ -38,13 +45,22 @@ class FlutterRunProcessHandler extends ProcessHandler {
_deviceTargetId = deviceTargetId; _deviceTargetId = deviceTargetId;
} }
void setBuildRequired(bool build) {
_buildApp = build;
}
@override @override
Future<void> run() async { Future<void> run() async {
final arguments = ["run", "--target=$_appTarget"]; final arguments = ["run", "--target=$_appTarget"];
if (_buildApp == false) {
arguments.add("--no-build");
}
if (_buildFlavor.isNotEmpty) { if (_buildFlavor.isNotEmpty) {
arguments.add("--flavor=$_buildFlavor"); arguments.add("--flavor=$_buildFlavor");
} }
if (_deviceTargetId.isNotEmpty) { if (_deviceTargetId.isNotEmpty) {
arguments.add("--device-id=$_deviceTargetId"); arguments.add("--device-id=$_deviceTargetId");
} }
@ -75,28 +91,41 @@ class FlutterRunProcessHandler extends ProcessHandler {
return exitCode; return exitCode;
} }
Future<String> waitForObservatoryDebuggerUri( @override
[Duration timeout = const Duration(seconds: 60)]) { Future restart() async {
_ensureRunningProcess();
_runningProcess.stdin.write("R");
return _waitForStdOutMessage(
_restartedApplicationSuccessRegex, "Timeout waiting for app restart");
}
Future<String> waitForObservatoryDebuggerUri() => _waitForStdOutMessage(
_observatoryDebuggerUriRegex,
"Timeout while waiting for observatory debugger uri");
Future<String> _waitForStdOutMessage(RegExp matcher, String timeoutMessage,
[Duration timeout = const Duration(seconds: 90)]) {
_ensureRunningProcess(); _ensureRunningProcess();
final completer = Completer<String>(); final completer = Completer<String>();
StreamSubscription sub; StreamSubscription sub;
sub = _processStdoutStream.timeout(timeout, onTimeout: (_) { sub = _processStdoutStream.timeout(timeout, onTimeout: (_) {
sub?.cancel(); sub?.cancel();
if (!completer.isCompleted) if (!completer.isCompleted) {
completer.completeError(TimeoutException( completer.completeError(TimeoutException(timeoutMessage, timeout));
"Timeout while wait for observatory debugger uri", timeout)); }
}).listen((logLine) { }).listen((logLine) {
// stdout.write(logLine); // stdout.write(logLine);
if (_observatoryDebuggerUriRegex.hasMatch(logLine)) { if (matcher.hasMatch(logLine)) {
sub?.cancel(); sub?.cancel();
if (!completer.isCompleted) if (!completer.isCompleted) {
completer.complete( completer.complete(matcher.firstMatch(logLine).group(1));
_observatoryDebuggerUriRegex.firstMatch(logLine).group(1)); }
} else if (_noConnectedDeviceRegex.hasMatch(logLine)) { } else if (_noConnectedDeviceRegex.hasMatch(logLine)) {
sub?.cancel(); sub?.cancel();
if (!completer.isCompleted) if (!completer.isCompleted) {
stderr.writeln( stderr.writeln(
"${FAIL_COLOR}No connected devices found to run app on and tests against$RESET_COLOR"); "${FAIL_COLOR}No connected devices found to run app on and tests against$RESET_COLOR");
}
} }
}, cancelOnError: true); }, cancelOnError: true);

View File

@ -26,6 +26,10 @@ class FlutterTestConfiguration extends TestConfiguration {
/// Defaults to empty /// Defaults to empty
String buildFlavor = ""; String buildFlavor = "";
/// If the application should be built prior to running the tests
/// Defaults to true
bool build = true;
/// The target device id to run the tests against when multiple devices detected /// The target device id to run the tests against when multiple devices detected
/// Defaults to empty /// Defaults to empty
String targetDeviceId = ""; String targetDeviceId = "";
@ -35,11 +39,11 @@ class FlutterTestConfiguration extends TestConfiguration {
Future<FlutterDriver> createFlutterDriver([String dartVmServiceUrl]) async { Future<FlutterDriver> createFlutterDriver([String dartVmServiceUrl]) async {
dartVmServiceUrl = (dartVmServiceUrl ?? _observatoryDebuggerUri) ?? dartVmServiceUrl = (dartVmServiceUrl ?? _observatoryDebuggerUri) ??
Platform.environment['VM_SERVICE_URL']; Platform.environment['VM_SERVICE_URL'];
final driver = await FlutterDriver.connect(
return await FlutterDriver.connect(
dartVmServiceUrl: dartVmServiceUrl, dartVmServiceUrl: dartVmServiceUrl,
logCommunicationToFile: false, logCommunicationToFile: false,
printCommunication: false); printCommunication: false);
return driver;
} }
Future<FlutterWorld> createFlutterWorld( Future<FlutterWorld> createFlutterWorld(

View File

@ -11,7 +11,7 @@ class FlutterWorld extends World {
} }
@override @override
void dispose() { void dispose() async {
_driver.close(); await _driver?.close();
} }
} }

View File

@ -37,13 +37,15 @@ class FlutterAppRunnerHook extends Hook {
haveRunFirstScenario = true; haveRunFirstScenario = true;
if (_flutterAppProcess != null && if (_flutterAppProcess != null &&
flutterConfig.restartAppBetweenScenarios) { flutterConfig.restartAppBetweenScenarios) {
await _terminateApp(); await _restartApp();
} }
} }
Future<void> _runApp(FlutterTestConfiguration config) async { Future<void> _runApp(FlutterTestConfiguration config) async {
_flutterAppProcess = FlutterRunProcessHandler(); _flutterAppProcess = FlutterRunProcessHandler();
_flutterAppProcess.setApplicationTargetFile(config.targetAppPath); _flutterAppProcess.setApplicationTargetFile(config.targetAppPath);
_flutterAppProcess
.setBuildRequired(haveRunFirstScenario ? false : config.build);
_flutterAppProcess.setBuildFlavor(config.buildFlavor); _flutterAppProcess.setBuildFlavor(config.buildFlavor);
_flutterAppProcess.setDeviceTargetId(config.targetDeviceId); _flutterAppProcess.setDeviceTargetId(config.targetDeviceId);
stdout.writeln( stdout.writeln(
@ -62,6 +64,16 @@ class FlutterAppRunnerHook extends Hook {
} }
} }
Future<void> _restartApp() async {
if (_flutterAppProcess != null) {
stdout.writeln("Restarting Flutter app under test");
await _flutterAppProcess.restart();
// it seems we need a small delay here otherwise the flutter driver fails to
// consistently connect
await Future.delayed(Duration(seconds: 1));
}
}
FlutterTestConfiguration _castConfig(TestConfiguration config) => FlutterTestConfiguration _castConfig(TestConfiguration config) =>
config as FlutterTestConfiguration; config as FlutterTestConfiguration;
} }

3
pre-publish-checks.cmd Normal file
View File

@ -0,0 +1,3 @@
CALL "C:\Google\flutter\bin\cache\dart-sdk\bin\dartanalyzer" --options analysis_options.yaml .
CALL "C:\Google\flutter\bin\cache\dart-sdk\bin\dartfmt" . -w
CALL flutter test

View File

@ -7,21 +7,21 @@ packages:
name: analyzer name: analyzer
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.35.4" version: "0.36.3"
args: args:
dependency: transitive dependency: transitive
description: description:
name: args name: args
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.5.1" version: "1.5.2"
async: async:
dependency: transitive dependency: transitive
description: description:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.2.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -57,13 +57,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.6" version: "2.0.6"
csslib:
dependency: transitive
description:
name: csslib
url: "https://pub.dartlang.org"
source: hosted
version: "0.16.0"
file: file:
dependency: transitive dependency: transitive
description: description:
name: file name: file
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.0.7" version: "5.0.8"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -85,7 +92,7 @@ packages:
name: front_end name: front_end
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.14" version: "0.1.18"
fuchsia_remote_debug_protocol: fuchsia_remote_debug_protocol:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -97,7 +104,7 @@ packages:
name: gherkin name: gherkin
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.0.4" version: "1.0.0"
glob: glob:
dependency: "direct main" dependency: "direct main"
description: description:
@ -105,6 +112,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.7" version: "1.1.7"
html:
dependency: transitive
description:
name: html
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.0+2"
http: http:
dependency: transitive dependency: transitive
description: description:
@ -118,7 +132,7 @@ packages:
name: http_multi_server name: http_multi_server
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.5" version: "2.0.6"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
@ -153,14 +167,14 @@ packages:
name: json_rpc_2 name: json_rpc_2
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.10" version: "2.1.0"
kernel: kernel:
dependency: transitive dependency: transitive
description: description:
name: kernel name: kernel
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.14" version: "0.3.18"
matcher: matcher:
dependency: "direct main" dependency: "direct main"
description: description:
@ -181,7 +195,7 @@ packages:
name: mime name: mime
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.6+2" version: "0.9.6+3"
multi_server_socket: multi_server_socket:
dependency: transitive dependency: transitive
description: description:
@ -223,7 +237,7 @@ packages:
name: pedantic name: pedantic
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.5.0" version: "1.7.0"
platform: platform:
dependency: transitive dependency: transitive
description: description:
@ -258,7 +272,7 @@ packages:
name: quiver name: quiver
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.3"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
@ -347,21 +361,21 @@ packages:
name: test name: test
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.6.1" version: "1.6.4"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.4" version: "0.2.6"
test_core: test_core:
dependency: transitive dependency: transitive
description: description:
name: test_core name: test_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.3" version: "0.2.6"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -382,7 +396,7 @@ packages:
name: vm_service_client name: vm_service_client
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.6+1" version: "0.2.6+2"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:
@ -396,7 +410,7 @@ packages:
name: web_socket_channel name: web_socket_channel
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.12" version: "1.0.13"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:

View File

@ -1,6 +1,6 @@
name: flutter_gherkin name: flutter_gherkin
description: A Gherkin / Cucumber parser and test runner for Dart and Flutter description: A Gherkin / Cucumber parser and test runner for Dart and Flutter
version: 0.0.14 version: 1.0.0
author: Jon Samwell <jonsamwell@gmail.com> author: Jon Samwell <jonsamwell@gmail.com>
homepage: https://github.com/jonsamwell/flutter_gherkin homepage: https://github.com/jonsamwell/flutter_gherkin
@ -12,10 +12,10 @@ dependencies:
sdk: flutter sdk: flutter
path: ^1.6.2 path: ^1.6.2
glob: ^1.1.7 glob: ^1.1.7
test: ^1.4.0 test: ^1.6.4
matcher: ^0.12.3+1 matcher: ^0.12.5
pedantic: ^1.4.0 pedantic: ^1.5.0
gherkin: ^0.0.4 gherkin: ^1.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: