feat(flutter): update to work with latest Flutter stable release
fix(spelling): fixed various spelling errors fix(lint): fixed lint errors
This commit is contained in:
parent
ba28cc8f30
commit
f0f061b6d8
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -1,32 +1,34 @@
|
||||||
## [1.1.7+7] - 25/03/2019
|
## [1.1.8] - 08/05/2020
|
||||||
* Added the ability to test against an already running app; enabling you to debug a running application while it has tests executed against it. Setting the configuration property `runningAppProtocolEndpointUri` to the service protocol endpoint (found in stdout when an app has `--verbose` logging turrned on) will ensure that the existing app is connected to rather than starting a new instance of the app. NOTE: ensure the app you are trying to connect to calls `enableFlutterDriverExtension()` when it starts up otherwise the Flutter Driver will not be able to connect to it.
|
* Updated library to work with the new way the Flutter stable branch manages logging for Flutter driver
|
||||||
|
* Fixed spelling mistake of `targetAppWorkingDirectory` & `flutterDriverMaxConnectionAttempts` in `FlutterTestConfiguration`
|
||||||
|
* Added the ability to test against an already running app; enabling you to debug a running application while it has tests executed against it. Setting the configuration property `runningAppProtocolEndpointUri` to the service protocol endpoint (found in stdout when an app has `--verbose` logging turned on) will ensure that the existing app is connected to rather than starting a new instance of the app. NOTE: ensure the app you are trying to connect to calls `enableFlutterDriverExtension()` when it starts up otherwise the Flutter Driver will not be able to connect to it.
|
||||||
|
|
||||||
## [1.1.7+6] - 04/03/2019
|
## [1.1.7+6] - 04/03/2020
|
||||||
* Updated to latest Gherkin library (see https://github.com/jonsamwell/dart_gherkin/blob/master/CHANGELOG.md#117---04032020) - this includes a breaking change to the `Hook` inteface that will need to be updated if any of the `Scenerio` level methods are implemented
|
* Updated to latest Gherkin library (see https://github.com/jonsamwell/dart_gherkin/blob/master/CHANGELOG.md#117---04032020) - this includes a breaking change to the `Hook` inteface that will need to be updated if any of the `Scenerio` level methods are implemented
|
||||||
* Ensured the well known step `I tap the ".." button` scroll the element into view first
|
* Ensured the well known step `I tap the ".." button` scroll the element into view first
|
||||||
|
|
||||||
## [1.1.7+5] - 03/02/2019
|
## [1.1.7+5] - 03/02/2020
|
||||||
* Updated to latest Gherkin library (see https://github.com/jonsamwell/dart_gherkin/blob/master/CHANGELOG.md#1164---03022020)
|
* Updated to latest Gherkin library (see https://github.com/jonsamwell/dart_gherkin/blob/master/CHANGELOG.md#1164---03022020)
|
||||||
|
|
||||||
## [1.1.7+4] - 31/01/2019
|
## [1.1.7+4] - 31/01/2020
|
||||||
* Update check to determine if any devices are connected to run tests against
|
* Update check to determine if any devices are connected to run tests against
|
||||||
* When the flag `verboseFlutterProcessLogs` was true Flutter driver was preemptively connecting to the app when it was not ready
|
* When the flag `verboseFlutterProcessLogs` was true Flutter driver was preemptively connecting to the app when it was not ready
|
||||||
|
|
||||||
## [1.1.7+3] - 08/01/2019
|
## [1.1.7+3] - 08/01/2020
|
||||||
* Added retry logic to the Futter driver connect call to handle the seemingly random connection failures
|
* Added retry logic to the Futter driver connect call to handle the seemingly random connection failures
|
||||||
* Ensured `AttachScreenshotOnFailedStepHook` cannot throw an unhandled exception causing the test run to stop
|
* Ensured `AttachScreenshotOnFailedStepHook` cannot throw an unhandled exception causing the test run to stop
|
||||||
* Added new well known step `When I tap the back button` which finds and taps the default page back button
|
* Added new well known step `When I tap the back button` which finds and taps the default page back button
|
||||||
* Added a new well known step `Then I expect the widget 'notification' to be present within 2 seconds` which expects a widget with a given key to be present within n seconds
|
* Added a new well known step `Then I expect the widget 'notification' to be present within 2 seconds` which expects a widget with a given key to be present within n seconds
|
||||||
* Updated Gherkin library version
|
* Updated Gherkin library version
|
||||||
|
|
||||||
## [1.1.7+2] - 07/01/2019
|
## [1.1.7+2] - 07/01/2020
|
||||||
* Increased the Flutter driver reconnection delay to try and overcome some driver to app connection issues on slower machines
|
* Increased the Flutter driver reconnection delay to try and overcome some driver to app connection issues on slower machines
|
||||||
|
|
||||||
## [1.1.7+1] - 07/01/2019
|
## [1.1.7+1] - 07/01/2020
|
||||||
* Ensured when the Flutter driver is closed it cannot throw an unhandled exception causing the test run the stop
|
* Ensured when the Flutter driver is closed it cannot throw an unhandled exception causing the test run the stop
|
||||||
* Updated Gherkin library version
|
* Updated Gherkin library version
|
||||||
|
|
||||||
## [1.1.7] - 06/01/2019
|
## [1.1.7] - 06/01/2020
|
||||||
* `WhenFillFieldStep` Ensure widget is scrolled into view before setting it's value
|
* `WhenFillFieldStep` Ensure widget is scrolled into view before setting it's value
|
||||||
* Fixed lint warnings
|
* Fixed lint warnings
|
||||||
|
|
||||||
|
|
|
@ -981,7 +981,7 @@ After which the file will most likely look like this
|
||||||
|
|
||||||
#### Debugging the app under test
|
#### Debugging the app under test
|
||||||
|
|
||||||
Setting the configuration property `runningAppProtocolEndpointUri` to the service protocol endpoint (found in stdout when an app has `--verbose` logging turrned on) will ensure that the existing app is connected to rather than starting a new instance of the app.
|
Setting the configuration property `runningAppProtocolEndpointUri` to the service protocol endpoint (found in stdout when an app has `--verbose` logging turned on) will ensure that the existing app is connected to rather than starting a new instance of the app.
|
||||||
|
|
||||||
NOTE: ensure the app you are trying to connect to calls `enableFlutterDriverExtension()` when it starts up otherwise the Flutter Driver will not be able to connect to it.
|
NOTE: ensure the app you are trying to connect to calls `enableFlutterDriverExtension()` when it starts up otherwise the Flutter Driver will not be able to connect to it.
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ export "FLUTTER_APPLICATION_PATH=C:\github\flutter_gherkin\example"
|
||||||
export "FLUTTER_TARGET=lib\main.dart"
|
export "FLUTTER_TARGET=lib\main.dart"
|
||||||
export "FLUTTER_BUILD_DIR=build"
|
export "FLUTTER_BUILD_DIR=build"
|
||||||
export "SYMROOT=${SOURCE_ROOT}/../build\ios"
|
export "SYMROOT=${SOURCE_ROOT}/../build\ios"
|
||||||
|
export "OTHER_LDFLAGS=$(inherited) -framework Flutter"
|
||||||
export "FLUTTER_FRAMEWORK_DIR=C:\Google\flutter\bin\cache\artifacts\engine\ios"
|
export "FLUTTER_FRAMEWORK_DIR=C:\Google\flutter\bin\cache\artifacts\engine\ios"
|
||||||
export "FLUTTER_BUILD_NAME=1.0.0"
|
export "FLUTTER_BUILD_NAME=1.0.0"
|
||||||
export "FLUTTER_BUILD_NUMBER=1"
|
export "FLUTTER_BUILD_NUMBER=1"
|
||||||
|
|
|
@ -37,7 +37,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||||
title: Text(widget.title),
|
title: Text(widget.title),
|
||||||
),
|
),
|
||||||
drawer: Drawer(
|
drawer: Drawer(
|
||||||
key: const Key("drawer"),
|
key: const Key('drawer'),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
@ -81,7 +81,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||||
// to identify this specific Widget from inside our test suite and
|
// to identify this specific Widget from inside our test suite and
|
||||||
// read the text.
|
// read the text.
|
||||||
key: const Key('counter'),
|
key: const Key('counter'),
|
||||||
style: Theme.of(context).textTheme.display1,
|
style: Theme.of(context).textTheme.headline4,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -9,7 +9,7 @@ import 'steps/tap_button_n_times_step.dart';
|
||||||
|
|
||||||
Future<void> main() {
|
Future<void> main() {
|
||||||
final config = FlutterTestConfiguration()
|
final config = FlutterTestConfiguration()
|
||||||
..features = [Glob(r"features/**.feature")]
|
..features = [Glob('features//**.feature')]
|
||||||
..reporters = [
|
..reporters = [
|
||||||
ProgressReporter(),
|
ProgressReporter(),
|
||||||
TestRunSummaryReporter(),
|
TestRunSummaryReporter(),
|
||||||
|
@ -31,17 +31,18 @@ Future<void> main() {
|
||||||
..customStepParameterDefinitions = [
|
..customStepParameterDefinitions = [
|
||||||
ColourParameter(),
|
ColourParameter(),
|
||||||
]
|
]
|
||||||
..restartAppBetweenScenarios = false
|
..restartAppBetweenScenarios = true
|
||||||
..targetAppWorkingDirecotry = "../"
|
..targetAppWorkingDirectory = '../'
|
||||||
..targetAppPath = "test_driver/app.dart"
|
..targetAppPath = 'test_driver/app.dart'
|
||||||
// ..buildFlavor = "staging" // uncomment when using build flavor and check android/ios flavor setup see android file android\app\build.gradle
|
// ..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
|
||||||
// ..logFlutterProcessOutput = true // uncomment to see command invoked to start the flutter test app
|
// ..logFlutterProcessOutput = true // uncomment to see command invoked to start the flutter test app
|
||||||
// ..verboseFlutterProcessLogs = true // uncomment to see the verbose output from the Flutter process
|
// ..verboseFlutterProcessLogs = true // uncomment to see the verbose output from the Flutter process
|
||||||
// ..flutterBuildTimeout = Duration(minutes: 3) // uncomment to change the default period that flutter is expected to build and start the app within
|
// ..flutterBuildTimeout = Duration(minutes: 3) // uncomment to change the default period that flutter is expected to build and start the app within
|
||||||
..runningAppProtocolEndpointUri =
|
// ..runningAppProtocolEndpointUri =
|
||||||
'http://127.0.0.1:51540/bkegoer6eH8=/' // already running app observatory / service protocol uri (with enableFlutterDriverExtension method invoked) to test against if you use this set `restartAppBetweenScenarios` to false
|
// 'http://127.0.0.1:51540/bkegoer6eH8=/' // already running app observatory / service protocol uri (with enableFlutterDriverExtension method invoked) to test against if you use this set `restartAppBetweenScenarios` to false
|
||||||
..exitAfterTestRun = true; // set to false if debugging to exit cleanly
|
..exitAfterTestRun = true; // set to false if debugging to exit cleanly
|
||||||
|
|
||||||
return GherkinRunner().execute(config);
|
return GherkinRunner().execute(config);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,13 @@ class HookExample extends Hook {
|
||||||
/// Run before any scenario in a test run have executed
|
/// Run before any scenario in a test run have executed
|
||||||
@override
|
@override
|
||||||
Future<void> onBeforeRun(TestConfiguration config) async {
|
Future<void> onBeforeRun(TestConfiguration config) async {
|
||||||
print("before run hook");
|
print('before run hook');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run after all scenarios in a test run have completed
|
/// Run after all scenarios in a test run have completed
|
||||||
@override
|
@override
|
||||||
Future<void> onAfterRun(TestConfiguration config) async {
|
Future<void> onAfterRun(TestConfiguration config) async {
|
||||||
print("after run hook");
|
print('after run hook');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run before a scenario and it steps are executed
|
/// Run before a scenario and it steps are executed
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -4,13 +4,13 @@ enum Colour { red, green, blue }
|
||||||
|
|
||||||
class ColourParameter extends CustomParameter<Colour> {
|
class ColourParameter extends CustomParameter<Colour> {
|
||||||
ColourParameter()
|
ColourParameter()
|
||||||
: super("colour", RegExp(r"(red|green|blue)", caseSensitive: true), (c) {
|
: super('colour', RegExp(r'(red|green|blue)', caseSensitive: true), (c) {
|
||||||
switch (c.toLowerCase()) {
|
switch (c.toLowerCase()) {
|
||||||
case "red":
|
case 'red':
|
||||||
return Colour.red;
|
return Colour.red;
|
||||||
case "green":
|
case 'green':
|
||||||
return Colour.green;
|
return Colour.green;
|
||||||
case "blue":
|
case 'blue':
|
||||||
default:
|
default:
|
||||||
return Colour.blue;
|
return Colour.blue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,5 @@ class GivenIAddTheUsers extends Given1<Table> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(r"I add the users");
|
RegExp get pattern => RegExp(r'I add the users');
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,5 +8,5 @@ class GivenIPickAColour extends Given1<Colour> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(r"I pick the colour {colour}");
|
RegExp get pattern => RegExp(r'I pick the colour {colour}');
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,5 +15,5 @@ class GivenIProvideAComment extends Given2<String, String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(r"I provide the following {string} comment");
|
RegExp get pattern => RegExp(r'I provide the following {string} comment');
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,5 +15,5 @@ class TapButtonNTimesStep extends When2WithWorld<String, int, FlutterWorld> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(r"I tap the {string} button {int} times");
|
RegExp get pattern => RegExp(r'I tap the {string} button {int} times');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
library flutter_gherkin;
|
library flutter_gherkin;
|
||||||
|
|
||||||
// Flutter specific implementations
|
// Flutter specific implementations
|
||||||
export "src/flutter/flutter_world.dart";
|
export 'src/flutter/flutter_world.dart';
|
||||||
export "src/flutter/flutter_test_configuration.dart";
|
export 'src/flutter/flutter_test_configuration.dart';
|
||||||
export "src/flutter/utils/driver_utils.dart";
|
export 'src/flutter/utils/driver_utils.dart';
|
||||||
|
|
||||||
// Well known steps
|
// Well known steps
|
||||||
export 'src/flutter/steps/given_i_open_the_drawer_step.dart';
|
export 'src/flutter/steps/given_i_open_the_drawer_step.dart';
|
||||||
|
|
|
@ -4,45 +4,45 @@ import 'dart:io';
|
||||||
import 'package:gherkin/gherkin.dart';
|
import 'package:gherkin/gherkin.dart';
|
||||||
|
|
||||||
class FlutterRunProcessHandler extends ProcessHandler {
|
class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
static const String FAIL_COLOR = "\u001b[33;31m"; // red
|
static const String FAIL_COLOR = '\u001b[33;31m'; // red
|
||||||
static const String WARN_COLOR = "\u001b[33;10m"; // yellow
|
static const String WARN_COLOR = '\u001b[33;10m'; // yellow
|
||||||
static const String RESET_COLOR = "\u001b[33;0m";
|
static const String RESET_COLOR = '\u001b[33;0m';
|
||||||
|
|
||||||
// the flutter process usually outputs something like the below to indicate the app is ready to be connected to
|
// the flutter process usually outputs something like the below to indicate the app is ready to be connected to
|
||||||
// `An Observatory debugger and profiler on AOSP on IA Emulator is available at: http://127.0.0.1:51322/BI_fyYaeoCE=/`
|
// `An Observatory debugger and profiler on AOSP on IA Emulator is available at: http://127.0.0.1:51322/BI_fyYaeoCE=/`
|
||||||
static RegExp _observatoryDebuggerUriRegex = RegExp(
|
static final RegExp _observatoryDebuggerUriRegex = RegExp(
|
||||||
r"observatory (?:debugger|url) .* available .*[:]? (http[s]?:.*\/).*",
|
r'observatory (?:debugger|url) .* available .*[:]? (http[s]?:.*\/).*',
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
multiLine: false,
|
multiLine: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
static RegExp _noConnectedDeviceRegex = RegExp(
|
static final RegExp _noConnectedDeviceRegex = RegExp(
|
||||||
r"no connected device|no supported devices connected",
|
r'no connected device|no supported devices connected',
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
multiLine: false,
|
multiLine: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
static RegExp _moreThanOneDeviceConnectedDeviceRegex = RegExp(
|
static final RegExp _moreThanOneDeviceConnectedDeviceRegex = RegExp(
|
||||||
r"more than one device connected",
|
r'more than one device connected',
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
multiLine: false,
|
multiLine: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
static RegExp _errorMessageRegex = RegExp(
|
static final RegExp _errorMessageRegex = RegExp(
|
||||||
r"aborted|error|failure|unexpected|failed|exception",
|
r'aborted|error|failure|unexpected|failed|exception',
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
multiLine: false,
|
multiLine: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
static RegExp _restartedApplicationSuccessRegex = RegExp(
|
static final RegExp _restartedApplicationSuccessRegex = RegExp(
|
||||||
r"Restarted application (.*)ms.",
|
r'Restarted application (.*)ms.',
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
multiLine: false,
|
multiLine: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
Process _runningProcess;
|
Process _runningProcess;
|
||||||
Stream<String> _processStdoutStream;
|
Stream<String> _processStdoutStream;
|
||||||
List<StreamSubscription> _openSubscriptions = <StreamSubscription>[];
|
final List<StreamSubscription> _openSubscriptions = <StreamSubscription>[];
|
||||||
bool _buildApp = true;
|
bool _buildApp = true;
|
||||||
bool _logFlutterProcessOutput = false;
|
bool _logFlutterProcessOutput = false;
|
||||||
bool _verboseFlutterLogs = false;
|
bool _verboseFlutterLogs = false;
|
||||||
|
@ -81,28 +81,28 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
_buildApp = build;
|
_buildApp = build;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setVerboseFluterlogs(bool verbose) {
|
void setVerboseFlutterLogs(bool verbose) {
|
||||||
_verboseFlutterLogs = verbose;
|
_verboseFlutterLogs = verbose;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> run() async {
|
Future<void> run() async {
|
||||||
final arguments = ["run", "--target=$_appTarget"];
|
final arguments = ['run', '--target=$_appTarget'];
|
||||||
|
|
||||||
if (_buildApp == false) {
|
if (_buildApp == false) {
|
||||||
arguments.add("--no-build");
|
arguments.add('--no-build');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_buildFlavor != null && _buildFlavor.isNotEmpty) {
|
if (_buildFlavor != null && _buildFlavor.isNotEmpty) {
|
||||||
arguments.add("--flavor=$_buildFlavor");
|
arguments.add('--flavor=$_buildFlavor');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_deviceTargetId != null && _deviceTargetId.isNotEmpty) {
|
if (_deviceTargetId != null && _deviceTargetId.isNotEmpty) {
|
||||||
arguments.add("--device-id=$_deviceTargetId");
|
arguments.add('--device-id=$_deviceTargetId');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_verboseFlutterLogs) {
|
if (_verboseFlutterLogs) {
|
||||||
arguments.add("--verbose");
|
arguments.add('--verbose');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_logFlutterProcessOutput) {
|
if (_logFlutterProcessOutput) {
|
||||||
|
@ -112,7 +112,7 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
_runningProcess = await Process.start(
|
_runningProcess = await Process.start(
|
||||||
"flutter",
|
'flutter',
|
||||||
arguments,
|
arguments,
|
||||||
workingDirectory: _workingDirectory,
|
workingDirectory: _workingDirectory,
|
||||||
runInShell: true,
|
runInShell: true,
|
||||||
|
@ -126,21 +126,21 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
.where((event) => event.isNotEmpty)
|
.where((event) => event.isNotEmpty)
|
||||||
.listen((event) {
|
.listen((event) {
|
||||||
if (event.contains(_errorMessageRegex)) {
|
if (event.contains(_errorMessageRegex)) {
|
||||||
stderr.writeln("${FAIL_COLOR}Flutter build error: $event$RESET_COLOR");
|
stderr.writeln('${FAIL_COLOR}Flutter build error: $event$RESET_COLOR');
|
||||||
} else {
|
} else {
|
||||||
// This is most likely a depricated api usage warnings (from Gradle) and should not
|
// This is most likely a depricated api usage warnings (from Gradle) and should not
|
||||||
// cause the test run to fail.
|
// cause the test run to fail.
|
||||||
stdout.writeln("$WARN_COLOR$event$RESET_COLOR");
|
stdout.writeln('$WARN_COLOR$event$RESET_COLOR');
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> terminate() async {
|
Future<int> terminate() async {
|
||||||
int exitCode = -1;
|
var exitCode = -1;
|
||||||
_ensureRunningProcess();
|
_ensureRunningProcess();
|
||||||
if (_runningProcess != null) {
|
if (_runningProcess != null) {
|
||||||
_runningProcess.stdin.write("q");
|
_runningProcess.stdin.write('q');
|
||||||
_openSubscriptions.forEach((s) => s.cancel());
|
_openSubscriptions.forEach((s) => s.cancel());
|
||||||
_openSubscriptions.clear();
|
_openSubscriptions.clear();
|
||||||
exitCode = await _runningProcess.exitCode;
|
exitCode = await _runningProcess.exitCode;
|
||||||
|
@ -152,10 +152,10 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
|
|
||||||
Future<bool> restart({Duration timeout = const Duration(seconds: 90)}) async {
|
Future<bool> restart({Duration timeout = const Duration(seconds: 90)}) async {
|
||||||
_ensureRunningProcess();
|
_ensureRunningProcess();
|
||||||
_runningProcess.stdin.write("R");
|
_runningProcess.stdin.write('R');
|
||||||
await _waitForStdOutMessage(
|
await _waitForStdOutMessage(
|
||||||
_restartedApplicationSuccessRegex,
|
_restartedApplicationSuccessRegex,
|
||||||
"Timeout waiting for app restart",
|
'Timeout waiting for app restart',
|
||||||
timeout,
|
timeout,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
]) async {
|
]) async {
|
||||||
currentObservatoryUri = await _waitForStdOutMessage(
|
currentObservatoryUri = await _waitForStdOutMessage(
|
||||||
_observatoryDebuggerUriRegex,
|
_observatoryDebuggerUriRegex,
|
||||||
"Timeout while waiting for observatory debugger uri",
|
'Timeout while waiting for observatory debugger uri',
|
||||||
timeout,
|
timeout,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -208,12 +208,12 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
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');
|
||||||
}
|
}
|
||||||
} else if (_moreThanOneDeviceConnectedDeviceRegex.hasMatch(logLine)) {
|
} else if (_moreThanOneDeviceConnectedDeviceRegex.hasMatch(logLine)) {
|
||||||
sub?.cancel();
|
sub?.cancel();
|
||||||
if (!completer.isCompleted) {
|
if (!completer.isCompleted) {
|
||||||
stderr.writeln("$FAIL_COLOR$logLine$RESET_COLOR");
|
stderr.writeln('$FAIL_COLOR$logLine$RESET_COLOR');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -226,7 +226,7 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
void _ensureRunningProcess() {
|
void _ensureRunningProcess() {
|
||||||
if (_runningProcess == null) {
|
if (_runningProcess == null) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
"FlutterRunProcessHandler: flutter run process is not active");
|
'FlutterRunProcessHandler: flutter run process is not active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,15 +24,15 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
|
|
||||||
/// The target app to run the tests against
|
/// The target app to run the tests against
|
||||||
/// Defaults to "lib/test_driver/app.dart"
|
/// Defaults to "lib/test_driver/app.dart"
|
||||||
String targetAppPath = "lib/test_driver/app.dart";
|
String targetAppPath = 'lib/test_driver/app.dart';
|
||||||
|
|
||||||
/// Option to define the working directory for the process that runs the app under test (optional)
|
/// Option to define the working directory for the process that runs the app under test (optional)
|
||||||
/// Handy if your app is seperated from your tests as flutter needs to be able to find a pubspec file
|
/// Handy if your app is separated from your tests as flutter needs to be able to find a pubspec file
|
||||||
String targetAppWorkingDirecotry;
|
String targetAppWorkingDirectory;
|
||||||
|
|
||||||
/// The build flavor to run the tests against (optional)
|
/// The build flavor to run the tests against (optional)
|
||||||
/// Defaults to empty
|
/// Defaults to empty
|
||||||
String buildFlavor = "";
|
String buildFlavor = '';
|
||||||
|
|
||||||
/// If the application should be built prior to running the tests
|
/// If the application should be built prior to running the tests
|
||||||
/// Defaults to true
|
/// Defaults to true
|
||||||
|
@ -40,7 +40,7 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
|
|
||||||
/// 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 = '';
|
||||||
|
|
||||||
/// Logs Flutter process output to stdout
|
/// Logs Flutter process output to stdout
|
||||||
/// The Flutter process is use to start and driver the app under test.
|
/// The Flutter process is use to start and driver the app under test.
|
||||||
|
@ -64,7 +64,7 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
|
|
||||||
/// The maximum times the flutter driver can try and connect to the running app
|
/// The maximum times the flutter driver can try and connect to the running app
|
||||||
/// Defaults to 3
|
/// Defaults to 3
|
||||||
int flutterDriverMaxConnectionAttemps = 3;
|
int flutterDriverMaxConnectionAttempts = 3;
|
||||||
|
|
||||||
/// An observatory url that the test runner can connect to instead of creating a new running instance of the target application
|
/// An observatory url that the test runner can connect to instead of creating a new running instance of the target application
|
||||||
/// Url takes the form of `http://127.0.0.1:51540/EM72VtRsUV0=/` and usually printed to stdout in the form `Connecting to service protocol: http://127.0.0.1:51540/EM72VtRsUV0=/`
|
/// Url takes the form of `http://127.0.0.1:51540/EM72VtRsUV0=/` and usually printed to stdout in the form `Connecting to service protocol: http://127.0.0.1:51540/EM72VtRsUV0=/`
|
||||||
|
@ -84,7 +84,7 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
TestConfiguration config,
|
TestConfiguration config,
|
||||||
FlutterWorld world,
|
FlutterWorld world,
|
||||||
) async {
|
) async {
|
||||||
FlutterTestConfiguration flutterConfig = config as FlutterTestConfiguration;
|
var flutterConfig = config as FlutterTestConfiguration;
|
||||||
world = world ?? FlutterWorld();
|
world = world ?? FlutterWorld();
|
||||||
|
|
||||||
final driver = await createFlutterDriver(
|
final driver = await createFlutterDriver(
|
||||||
|
@ -116,6 +116,7 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
..addAll([
|
..addAll([
|
||||||
ThenExpectElementToHaveValue(),
|
ThenExpectElementToHaveValue(),
|
||||||
WhenTapWidget(),
|
WhenTapWidget(),
|
||||||
|
WhenTapWidgetWithoutScroll(),
|
||||||
WhenTapBackButtonWidget(),
|
WhenTapBackButtonWidget(),
|
||||||
GivenOpenDrawer(),
|
GivenOpenDrawer(),
|
||||||
WhenPauseStep(),
|
WhenPauseStep(),
|
||||||
|
@ -128,14 +129,14 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
Future<FlutterDriver> _attemptDriverConnection(
|
Future<FlutterDriver> _attemptDriverConnection(
|
||||||
String dartVmServiceUrl,
|
String dartVmServiceUrl,
|
||||||
int attempt,
|
int attempt,
|
||||||
int maxAttemps,
|
int maxAttempts,
|
||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
return await FlutterDriver.connect(
|
return await FlutterDriver.connect(
|
||||||
dartVmServiceUrl: dartVmServiceUrl,
|
dartVmServiceUrl: dartVmServiceUrl,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (attempt > maxAttemps) {
|
if (attempt > maxAttempts) {
|
||||||
rethrow;
|
rethrow;
|
||||||
} else {
|
} else {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -144,7 +145,7 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
return _attemptDriverConnection(
|
return _attemptDriverConnection(
|
||||||
dartVmServiceUrl,
|
dartVmServiceUrl,
|
||||||
attempt + 1,
|
attempt + 1,
|
||||||
maxAttemps,
|
maxAttempts,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,10 +69,10 @@ class FlutterAppRunnerHook extends Hook {
|
||||||
} else {
|
} else {
|
||||||
_flutterRunProcessHandler = FlutterRunProcessHandler()
|
_flutterRunProcessHandler = FlutterRunProcessHandler()
|
||||||
..setLogFlutterProcessOutput(config.logFlutterProcessOutput)
|
..setLogFlutterProcessOutput(config.logFlutterProcessOutput)
|
||||||
..setVerboseFluterlogs(config.verboseFlutterProcessLogs)
|
..setVerboseFlutterLogs(config.verboseFlutterProcessLogs)
|
||||||
..setApplicationTargetFile(config.targetAppPath)
|
..setApplicationTargetFile(config.targetAppPath)
|
||||||
..setDriverConnectionDelay(config.flutterDriverReconnectionDelay)
|
..setDriverConnectionDelay(config.flutterDriverReconnectionDelay)
|
||||||
..setWorkingDirectory(config.targetAppWorkingDirecotry)
|
..setWorkingDirectory(config.targetAppWorkingDirectory)
|
||||||
..setBuildRequired(haveRunFirstScenario ? false : config.build)
|
..setBuildRequired(haveRunFirstScenario ? false : config.build)
|
||||||
..setBuildFlavor(config.buildFlavor)
|
..setBuildFlavor(config.buildFlavor)
|
||||||
..setDeviceTargetId(config.targetDeviceId);
|
..setDeviceTargetId(config.targetDeviceId);
|
||||||
|
@ -88,7 +88,7 @@ class FlutterAppRunnerHook extends Hook {
|
||||||
|
|
||||||
Future<void> _terminateApp() async {
|
Future<void> _terminateApp() async {
|
||||||
if (_flutterRunProcessHandler != null) {
|
if (_flutterRunProcessHandler != null) {
|
||||||
stdout.writeln("Terminating Flutter app under test");
|
stdout.writeln('Terminating Flutter app under test');
|
||||||
await _flutterRunProcessHandler.terminate();
|
await _flutterRunProcessHandler.terminate();
|
||||||
_flutterRunProcessHandler = null;
|
_flutterRunProcessHandler = null;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ class FlutterAppRunnerHook extends Hook {
|
||||||
|
|
||||||
Future<void> _restartApp() async {
|
Future<void> _restartApp() async {
|
||||||
if (_flutterRunProcessHandler != null) {
|
if (_flutterRunProcessHandler != null) {
|
||||||
stdout.writeln("Restarting Flutter app under test");
|
stdout.writeln('Restarting Flutter app under test');
|
||||||
await _flutterRunProcessHandler.restart();
|
await _flutterRunProcessHandler.restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import 'dart:io';
|
||||||
import 'package:gherkin/gherkin.dart';
|
import 'package:gherkin/gherkin.dart';
|
||||||
import 'package:flutter_driver/flutter_driver.dart';
|
import 'package:flutter_driver/flutter_driver.dart';
|
||||||
|
|
||||||
|
enum _FlutterDriverMessageLogLevel { info, warning, error }
|
||||||
|
|
||||||
/// The Flutter driver helpfully logs ALL messages to the stderr output
|
/// The Flutter driver helpfully logs ALL messages to the stderr output
|
||||||
/// useless something is listening to the messages.
|
/// useless something is listening to the messages.
|
||||||
/// This reporter listens to the messages from the driver so nothing is logged
|
/// This reporter listens to the messages from the driver so nothing is logged
|
||||||
|
@ -15,7 +17,6 @@ class FlutterDriverReporter extends Reporter {
|
||||||
final bool logErrorMessages;
|
final bool logErrorMessages;
|
||||||
final bool logWarningMessages;
|
final bool logWarningMessages;
|
||||||
final bool logInfoMessages;
|
final bool logInfoMessages;
|
||||||
final List<StreamSubscription> subscriptions = List<StreamSubscription>();
|
|
||||||
|
|
||||||
FlutterDriverReporter({
|
FlutterDriverReporter({
|
||||||
this.logErrorMessages = true,
|
this.logErrorMessages = true,
|
||||||
|
@ -23,38 +24,37 @@ class FlutterDriverReporter extends Reporter {
|
||||||
this.logInfoMessages = false,
|
this.logInfoMessages = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
Future<void> onTestRunStarted() async {
|
Future<void> onTestRunStarted() async {
|
||||||
if (logInfoMessages) {
|
driverLog = _driverLogMessageHandler;
|
||||||
subscriptions.add(flutterDriverLog
|
|
||||||
.where((log) => _isLevel(log.level, [LogLevel.info, LogLevel.trace]))
|
|
||||||
.listen((log) {
|
|
||||||
stdout.writeln(log.toString());
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logWarningMessages) {
|
|
||||||
subscriptions.add(flutterDriverLog
|
|
||||||
.where((log) => _isLevel(log.level, [LogLevel.warning]))
|
|
||||||
.listen((log) {
|
|
||||||
stdout.writeln(log.toString());
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logErrorMessages) {
|
|
||||||
subscriptions.add(flutterDriverLog
|
|
||||||
.where(
|
|
||||||
(log) => _isLevel(log.level, [LogLevel.critical, LogLevel.error]))
|
|
||||||
.listen((log) {
|
|
||||||
stderr.writeln(log.toString());
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
subscriptions.forEach((s) => s.cancel());
|
driverLog = null;
|
||||||
subscriptions.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isLevel(LogLevel level, Iterable<LogLevel> levels) =>
|
void _driverLogMessageHandler(String source, String message) {
|
||||||
levels.contains(level);
|
final level = _getMessageLevel(message);
|
||||||
|
final log = '$source $message';
|
||||||
|
|
||||||
|
if (logWarningMessages && level == _FlutterDriverMessageLogLevel.warning) {
|
||||||
|
stdout.writeln(log);
|
||||||
|
} else if (logErrorMessages &&
|
||||||
|
level == _FlutterDriverMessageLogLevel.error) {
|
||||||
|
stderr.writeln(log);
|
||||||
|
} else {
|
||||||
|
stdout.writeln(log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_FlutterDriverMessageLogLevel _getMessageLevel(String message) {
|
||||||
|
if (message.toLowerCase().contains('warning')) {
|
||||||
|
return _FlutterDriverMessageLogLevel.warning;
|
||||||
|
} else if (message.toLowerCase().contains('error')) {
|
||||||
|
return _FlutterDriverMessageLogLevel.error;
|
||||||
|
} else {
|
||||||
|
return _FlutterDriverMessageLogLevel.info;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,22 +10,22 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `Given I open the drawer`
|
/// `Given I open the drawer`
|
||||||
class GivenOpenDrawer extends Given1WithWorld<String, FlutterWorld> {
|
class GivenOpenDrawer extends Given1WithWorld<String, FlutterWorld> {
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(r"I (open|close) the drawer");
|
RegExp get pattern => RegExp(r'I (open|close) the drawer');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> executeStep(String action) async {
|
Future<void> executeStep(String action) async {
|
||||||
final drawerFinder = find.byType("Drawer");
|
final drawerFinder = find.byType('Drawer');
|
||||||
final isOpen =
|
final isOpen =
|
||||||
await FlutterDriverUtils.isPresent(drawerFinder, world.driver);
|
await FlutterDriverUtils.isPresent(drawerFinder, world.driver);
|
||||||
// https://github.com/flutter/flutter/issues/9002#issuecomment-293660833
|
// https://github.com/flutter/flutter/issues/9002#issuecomment-293660833
|
||||||
if (isOpen && action == "close") {
|
if (isOpen && action == 'close') {
|
||||||
// Swipe to the left across the whole app to close the drawer
|
// Swipe to the left across the whole app to close the drawer
|
||||||
await world.driver
|
await world.driver
|
||||||
.scroll(drawerFinder, -300.0, 0.0, const Duration(milliseconds: 300));
|
.scroll(drawerFinder, -300.0, 0.0, const Duration(milliseconds: 300));
|
||||||
} else if (!isOpen && action == "open") {
|
} else if (!isOpen && action == 'open') {
|
||||||
await FlutterDriverUtils.tap(
|
await FlutterDriverUtils.tap(
|
||||||
world.driver,
|
world.driver,
|
||||||
find.byTooltip("Open navigation menu"),
|
find.byTooltip('Open navigation menu'),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,5 @@ class RestartAppStep extends WhenWithWorld<FlutterWorld> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(r"I restart the app");
|
RegExp get pattern => RegExp(r'I restart the app');
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
class ThenExpectElementToHaveValue
|
class ThenExpectElementToHaveValue
|
||||||
extends Then2WithWorld<String, String, FlutterWorld> {
|
extends Then2WithWorld<String, String, FlutterWorld> {
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(r"I expect the {string} to be {string}$");
|
RegExp get pattern => RegExp(r'I expect the {string} to be {string}$');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> executeStep(String key, String value) async {
|
Future<void> executeStep(String key, String value) async {
|
||||||
|
|
|
@ -16,7 +16,7 @@ class ThenExpectWidgetToBePresent
|
||||||
extends When2WithWorld<String, int, FlutterWorld> {
|
extends When2WithWorld<String, int, FlutterWorld> {
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(
|
RegExp get pattern => RegExp(
|
||||||
r"I expect the (?:button|element|label|icon|field|text|widget|dialog|popup) {string} to be present within {int} second(s)$");
|
r'I expect the (?:button|element|label|icon|field|text|widget|dialog|popup) {string} to be present within {int} second(s)$');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> executeStep(String key, int seconds) async {
|
Future<void> executeStep(String key, int seconds) async {
|
||||||
|
|
|
@ -22,5 +22,5 @@ class WhenFillFieldStep extends When2WithWorld<String, String, FlutterWorld> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(r"I fill the {string} field with {string}");
|
RegExp get pattern => RegExp(r'I fill the {string} field with {string}');
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,5 +18,5 @@ class WhenPauseStep extends When1<int> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(r"I pause for {int} second(s)");
|
RegExp get pattern => RegExp(r'I pause for {int} second(s)');
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `When I tap the back widget"`
|
/// `When I tap the back widget"`
|
||||||
class WhenTapBackButtonWidget extends WhenWithWorld<FlutterWorld> {
|
class WhenTapBackButtonWidget extends WhenWithWorld<FlutterWorld> {
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(r"I tap the back (?:button|element|widget)$");
|
RegExp get pattern => RegExp(r'I tap the back (?:button|element|widget)$');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> executeStep() async {
|
Future<void> executeStep() async {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
class WhenTapWidget extends When1WithWorld<String, FlutterWorld> {
|
class WhenTapWidget extends When1WithWorld<String, FlutterWorld> {
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(
|
RegExp get pattern => RegExp(
|
||||||
r"I tap the {string} (?:button|element|label|icon|field|text|widget)$");
|
r'I tap the {string} (?:button|element|label|icon|field|text|widget)$');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> executeStep(String key) async {
|
Future<void> executeStep(String key) async {
|
||||||
|
@ -37,3 +37,20 @@ class WhenTapWidget extends When1WithWorld<String, FlutterWorld> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WhenTapWidgetWithoutScroll extends When1WithWorld<String, FlutterWorld> {
|
||||||
|
@override
|
||||||
|
RegExp get pattern => RegExp(
|
||||||
|
r'I tap the {string} (?:button|element|label|icon|field|text|widget) without scrolling it into view$');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> executeStep(String key) async {
|
||||||
|
final finder = find.byValueKey(key);
|
||||||
|
|
||||||
|
await FlutterDriverUtils.tap(
|
||||||
|
world.driver,
|
||||||
|
finder,
|
||||||
|
timeout: timeout * .45,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -75,15 +75,15 @@ class FlutterDriverUtils {
|
||||||
/// condition does not return true with the timeout period.
|
/// condition does not return true with the timeout period.
|
||||||
static Future<void> waitUntil(
|
static Future<void> waitUntil(
|
||||||
FlutterDriver driver,
|
FlutterDriver driver,
|
||||||
Future<bool> condition(), {
|
Future<bool> Function() condition, {
|
||||||
Duration timeout = const Duration(seconds: 10),
|
Duration timeout = const Duration(seconds: 10),
|
||||||
Duration pollInterval = const Duration(milliseconds: 500),
|
Duration pollInterval = const Duration(milliseconds: 500),
|
||||||
}) async {
|
}) async {
|
||||||
return Future.microtask(() async {
|
return Future.microtask(() async {
|
||||||
final completer = Completer<void>();
|
final completer = Completer<void>();
|
||||||
int maxAttempts =
|
var maxAttempts =
|
||||||
(timeout.inMilliseconds / pollInterval.inMilliseconds).round();
|
(timeout.inMilliseconds / pollInterval.inMilliseconds).round();
|
||||||
int attempts = 0;
|
var attempts = 0;
|
||||||
|
|
||||||
while (attempts < maxAttempts) {
|
while (attempts < maxAttempts) {
|
||||||
final result = await condition();
|
final result = await condition();
|
||||||
|
|
85
pubspec.lock
85
pubspec.lock
|
@ -7,56 +7,56 @@ packages:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "3.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.39.4"
|
version: "0.39.8"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.11"
|
version: "2.0.13"
|
||||||
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.2"
|
version: "1.6.0"
|
||||||
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.4.0"
|
version: "2.4.1"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "2.0.0"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: charcode
|
name: charcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.2"
|
version: "1.1.3"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.14.11"
|
version: "1.14.12"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -77,7 +77,7 @@ packages:
|
||||||
name: crypto
|
name: crypto
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
csslib:
|
csslib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -139,7 +139,7 @@ packages:
|
||||||
name: http
|
name: http
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.0+4"
|
version: "0.12.1"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -160,21 +160,21 @@ packages:
|
||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.12"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.16.0"
|
version: "0.16.1"
|
||||||
io:
|
io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: io
|
name: io
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3"
|
version: "0.3.4"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -237,7 +237,7 @@ packages:
|
||||||
name: node_io
|
name: node_io
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1+2"
|
version: "1.1.0"
|
||||||
node_preamble:
|
node_preamble:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -251,14 +251,7 @@ packages:
|
||||||
name: package_config
|
name: package_config
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.2"
|
version: "1.9.3"
|
||||||
package_resolver:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_resolver
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.10"
|
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -272,7 +265,7 @@ packages:
|
||||||
name: pedantic
|
name: pedantic
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0+1"
|
version: "1.9.0"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -307,14 +300,14 @@ packages:
|
||||||
name: pub_semver
|
name: pub_semver
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.2"
|
version: "1.4.4"
|
||||||
quiver:
|
quiver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: quiver
|
name: quiver
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.1.3"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -328,7 +321,7 @@ packages:
|
||||||
name: shelf_packages_handler
|
name: shelf_packages_handler
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "2.0.0"
|
||||||
shelf_static:
|
shelf_static:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -354,7 +347,7 @@ packages:
|
||||||
name: source_map_stack_trace
|
name: source_map_stack_trace
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.5"
|
version: "2.0.0"
|
||||||
source_maps:
|
source_maps:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -368,7 +361,7 @@ packages:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.5"
|
version: "1.7.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -390,6 +383,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
|
sync_http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sync_http
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -403,21 +403,21 @@ packages:
|
||||||
name: test
|
name: test
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.4"
|
version: "1.14.3"
|
||||||
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.11"
|
version: "0.2.15"
|
||||||
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.15"
|
version: "0.3.4"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -438,7 +438,7 @@ packages:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "4.0.2"
|
||||||
vm_service_client:
|
vm_service_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -452,7 +452,7 @@ packages:
|
||||||
name: watcher
|
name: watcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.7+14"
|
version: "0.9.7+15"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -460,19 +460,34 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
webdriver:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: webdriver
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
webkit_inspection_protocol:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: webkit_inspection_protocol
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.3"
|
||||||
xml:
|
xml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xml
|
name: xml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.5.0"
|
version: "3.6.1"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: yaml
|
name: yaml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.7.0 <3.0.0"
|
dart: ">=2.7.0 <3.0.0"
|
||||||
|
flutter: ">=1.13.0"
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
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: 1.1.7+7
|
version: 1.1.8
|
||||||
homepage: https://github.com/jonsamwell/flutter_gherkin
|
homepage: https://github.com/jonsamwell/flutter_gherkin
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.1.0 <3.0.0"
|
sdk: ">=2.6.0 <3.0.0"
|
||||||
|
flutter: ">=1.13.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
|
|
|
@ -13,9 +13,9 @@ import 'package:test/test.dart';
|
||||||
import 'mocks/step_definition_mock.dart';
|
import 'mocks/step_definition_mock.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group("config", () {
|
group('config', () {
|
||||||
group("prepare", () {
|
group('prepare', () {
|
||||||
test("flutter app runner hook added", () {
|
test('flutter app runner hook added', () {
|
||||||
final config = FlutterTestConfiguration();
|
final config = FlutterTestConfiguration();
|
||||||
expect(config.hooks, isNull);
|
expect(config.hooks, isNull);
|
||||||
config.prepare();
|
config.prepare();
|
||||||
|
@ -24,36 +24,38 @@ void main() {
|
||||||
expect(config.hooks.elementAt(0), (x) => x is FlutterAppRunnerHook);
|
expect(config.hooks.elementAt(0), (x) => x is FlutterAppRunnerHook);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("common steps definition added", () {
|
test('common steps definition added', () {
|
||||||
final config = FlutterTestConfiguration();
|
final config = FlutterTestConfiguration();
|
||||||
expect(config.stepDefinitions, isNull);
|
expect(config.stepDefinitions, isNull);
|
||||||
|
|
||||||
config.prepare();
|
config.prepare();
|
||||||
expect(config.stepDefinitions, isNotNull);
|
expect(config.stepDefinitions, isNotNull);
|
||||||
expect(config.stepDefinitions.length, 8);
|
expect(config.stepDefinitions.length, 9);
|
||||||
expect(config.stepDefinitions.elementAt(0),
|
expect(config.stepDefinitions.elementAt(0),
|
||||||
(x) => x is ThenExpectElementToHaveValue);
|
(x) => x is ThenExpectElementToHaveValue);
|
||||||
expect(config.stepDefinitions.elementAt(1), (x) => x is WhenTapWidget);
|
expect(config.stepDefinitions.elementAt(1), (x) => x is WhenTapWidget);
|
||||||
expect(config.stepDefinitions.elementAt(2),
|
expect(config.stepDefinitions.elementAt(2),
|
||||||
|
(x) => x is WhenTapWidgetWithoutScroll);
|
||||||
|
expect(config.stepDefinitions.elementAt(3),
|
||||||
(x) => x is WhenTapBackButtonWidget);
|
(x) => x is WhenTapBackButtonWidget);
|
||||||
expect(
|
expect(
|
||||||
config.stepDefinitions.elementAt(3), (x) => x is GivenOpenDrawer);
|
config.stepDefinitions.elementAt(4), (x) => x is GivenOpenDrawer);
|
||||||
expect(config.stepDefinitions.elementAt(4), (x) => x is WhenPauseStep);
|
expect(config.stepDefinitions.elementAt(5), (x) => x is WhenPauseStep);
|
||||||
expect(
|
expect(
|
||||||
config.stepDefinitions.elementAt(5), (x) => x is WhenFillFieldStep);
|
config.stepDefinitions.elementAt(6), (x) => x is WhenFillFieldStep);
|
||||||
expect(config.stepDefinitions.elementAt(6),
|
expect(config.stepDefinitions.elementAt(7),
|
||||||
(x) => x is ThenExpectWidgetToBePresent);
|
(x) => x is ThenExpectWidgetToBePresent);
|
||||||
expect(config.stepDefinitions.elementAt(7), (x) => x is RestartAppStep);
|
expect(config.stepDefinitions.elementAt(8), (x) => x is RestartAppStep);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("common step definition added to existing steps", () {
|
test('common step definition added to existing steps', () {
|
||||||
final config = FlutterTestConfiguration()
|
final config = FlutterTestConfiguration()
|
||||||
..stepDefinitions = [MockStepDefinition()];
|
..stepDefinitions = [MockStepDefinition()];
|
||||||
expect(config.stepDefinitions.length, 1);
|
expect(config.stepDefinitions.length, 1);
|
||||||
|
|
||||||
config.prepare();
|
config.prepare();
|
||||||
expect(config.stepDefinitions, isNotNull);
|
expect(config.stepDefinitions, isNotNull);
|
||||||
expect(config.stepDefinitions.length, 9);
|
expect(config.stepDefinitions.length, 10);
|
||||||
expect(config.stepDefinitions.elementAt(0),
|
expect(config.stepDefinitions.elementAt(0),
|
||||||
(x) => x is MockStepDefinition);
|
(x) => x is MockStepDefinition);
|
||||||
expect(config.stepDefinitions.elementAt(1),
|
expect(config.stepDefinitions.elementAt(1),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:gherkin/gherkin.dart';
|
import 'package:gherkin/gherkin.dart';
|
||||||
|
|
||||||
typedef Future<void> OnRunCode(Iterable parameters);
|
typedef OnRunCode = Future<void> Function(Iterable parameters);
|
||||||
|
|
||||||
class MockStepDefinition extends StepDefinitionBase<World> {
|
class MockStepDefinition extends StepDefinitionBase<World> {
|
||||||
bool hasRun = false;
|
bool hasRun = false;
|
||||||
|
|
Loading…
Reference in New Issue