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
|
||||
* 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.
|
||||
## [1.1.8] - 08/05/2020
|
||||
* 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
|
||||
* 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)
|
||||
|
||||
## [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
|
||||
* 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
|
||||
* 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 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
|
||||
|
||||
## [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
|
||||
|
||||
## [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
|
||||
* 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
|
||||
* Fixed lint warnings
|
||||
|
||||
|
|
|
@ -981,7 +981,7 @@ After which the file will most likely look like this
|
|||
|
||||
#### 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.
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ export "FLUTTER_APPLICATION_PATH=C:\github\flutter_gherkin\example"
|
|||
export "FLUTTER_TARGET=lib\main.dart"
|
||||
export "FLUTTER_BUILD_DIR=build"
|
||||
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_BUILD_NAME=1.0.0"
|
||||
export "FLUTTER_BUILD_NUMBER=1"
|
||||
|
|
|
@ -37,7 +37,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||
title: Text(widget.title),
|
||||
),
|
||||
drawer: Drawer(
|
||||
key: const Key("drawer"),
|
||||
key: const Key('drawer'),
|
||||
child: ListView(
|
||||
padding: EdgeInsets.zero,
|
||||
children: <Widget>[
|
||||
|
@ -81,7 +81,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||
// to identify this specific Widget from inside our test suite and
|
||||
// read the text.
|
||||
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() {
|
||||
final config = FlutterTestConfiguration()
|
||||
..features = [Glob(r"features/**.feature")]
|
||||
..features = [Glob('features//**.feature')]
|
||||
..reporters = [
|
||||
ProgressReporter(),
|
||||
TestRunSummaryReporter(),
|
||||
|
@ -31,17 +31,18 @@ Future<void> main() {
|
|||
..customStepParameterDefinitions = [
|
||||
ColourParameter(),
|
||||
]
|
||||
..restartAppBetweenScenarios = false
|
||||
..targetAppWorkingDirecotry = "../"
|
||||
..targetAppPath = "test_driver/app.dart"
|
||||
..restartAppBetweenScenarios = true
|
||||
..targetAppWorkingDirectory = '../'
|
||||
..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
|
||||
// ..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
|
||||
// ..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
|
||||
// ..flutterBuildTimeout = Duration(minutes: 3) // uncomment to change the default period that flutter is expected to build and start the app within
|
||||
..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
|
||||
// ..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
|
||||
..exitAfterTestRun = true; // set to false if debugging to exit cleanly
|
||||
|
||||
return GherkinRunner().execute(config);
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ class HookExample extends Hook {
|
|||
/// Run before any scenario in a test run have executed
|
||||
@override
|
||||
Future<void> onBeforeRun(TestConfiguration config) async {
|
||||
print("before run hook");
|
||||
print('before run hook');
|
||||
}
|
||||
|
||||
/// Run after all scenarios in a test run have completed
|
||||
@override
|
||||
Future<void> onAfterRun(TestConfiguration config) async {
|
||||
print("after run hook");
|
||||
print('after run hook');
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
ColourParameter()
|
||||
: super("colour", RegExp(r"(red|green|blue)", caseSensitive: true), (c) {
|
||||
: super('colour', RegExp(r'(red|green|blue)', caseSensitive: true), (c) {
|
||||
switch (c.toLowerCase()) {
|
||||
case "red":
|
||||
case 'red':
|
||||
return Colour.red;
|
||||
case "green":
|
||||
case 'green':
|
||||
return Colour.green;
|
||||
case "blue":
|
||||
case 'blue':
|
||||
default:
|
||||
return Colour.blue;
|
||||
}
|
||||
|
|
|
@ -20,5 +20,5 @@ class GivenIAddTheUsers extends Given1<Table> {
|
|||
}
|
||||
|
||||
@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
|
||||
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
|
||||
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
|
||||
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;
|
||||
|
||||
// Flutter specific implementations
|
||||
export "src/flutter/flutter_world.dart";
|
||||
export "src/flutter/flutter_test_configuration.dart";
|
||||
export "src/flutter/utils/driver_utils.dart";
|
||||
export 'src/flutter/flutter_world.dart';
|
||||
export 'src/flutter/flutter_test_configuration.dart';
|
||||
export 'src/flutter/utils/driver_utils.dart';
|
||||
|
||||
// Well known steps
|
||||
export 'src/flutter/steps/given_i_open_the_drawer_step.dart';
|
||||
|
|
|
@ -4,45 +4,45 @@ import 'dart:io';
|
|||
import 'package:gherkin/gherkin.dart';
|
||||
|
||||
class FlutterRunProcessHandler extends ProcessHandler {
|
||||
static const String FAIL_COLOR = "\u001b[33;31m"; // red
|
||||
static const String WARN_COLOR = "\u001b[33;10m"; // yellow
|
||||
static const String RESET_COLOR = "\u001b[33;0m";
|
||||
static const String FAIL_COLOR = '\u001b[33;31m'; // red
|
||||
static const String WARN_COLOR = '\u001b[33;10m'; // yellow
|
||||
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
|
||||
// `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(
|
||||
r"observatory (?:debugger|url) .* available .*[:]? (http[s]?:.*\/).*",
|
||||
static final RegExp _observatoryDebuggerUriRegex = RegExp(
|
||||
r'observatory (?:debugger|url) .* available .*[:]? (http[s]?:.*\/).*',
|
||||
caseSensitive: false,
|
||||
multiLine: false,
|
||||
);
|
||||
|
||||
static RegExp _noConnectedDeviceRegex = RegExp(
|
||||
r"no connected device|no supported devices connected",
|
||||
static final RegExp _noConnectedDeviceRegex = RegExp(
|
||||
r'no connected device|no supported devices connected',
|
||||
caseSensitive: false,
|
||||
multiLine: false,
|
||||
);
|
||||
|
||||
static RegExp _moreThanOneDeviceConnectedDeviceRegex = RegExp(
|
||||
r"more than one device connected",
|
||||
static final RegExp _moreThanOneDeviceConnectedDeviceRegex = RegExp(
|
||||
r'more than one device connected',
|
||||
caseSensitive: false,
|
||||
multiLine: false,
|
||||
);
|
||||
|
||||
static RegExp _errorMessageRegex = RegExp(
|
||||
r"aborted|error|failure|unexpected|failed|exception",
|
||||
static final RegExp _errorMessageRegex = RegExp(
|
||||
r'aborted|error|failure|unexpected|failed|exception',
|
||||
caseSensitive: false,
|
||||
multiLine: false,
|
||||
);
|
||||
|
||||
static RegExp _restartedApplicationSuccessRegex = RegExp(
|
||||
r"Restarted application (.*)ms.",
|
||||
static final RegExp _restartedApplicationSuccessRegex = RegExp(
|
||||
r'Restarted application (.*)ms.',
|
||||
caseSensitive: false,
|
||||
multiLine: false,
|
||||
);
|
||||
|
||||
Process _runningProcess;
|
||||
Stream<String> _processStdoutStream;
|
||||
List<StreamSubscription> _openSubscriptions = <StreamSubscription>[];
|
||||
final List<StreamSubscription> _openSubscriptions = <StreamSubscription>[];
|
||||
bool _buildApp = true;
|
||||
bool _logFlutterProcessOutput = false;
|
||||
bool _verboseFlutterLogs = false;
|
||||
|
@ -81,28 +81,28 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
_buildApp = build;
|
||||
}
|
||||
|
||||
void setVerboseFluterlogs(bool verbose) {
|
||||
void setVerboseFlutterLogs(bool verbose) {
|
||||
_verboseFlutterLogs = verbose;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> run() async {
|
||||
final arguments = ["run", "--target=$_appTarget"];
|
||||
final arguments = ['run', '--target=$_appTarget'];
|
||||
|
||||
if (_buildApp == false) {
|
||||
arguments.add("--no-build");
|
||||
arguments.add('--no-build');
|
||||
}
|
||||
|
||||
if (_buildFlavor != null && _buildFlavor.isNotEmpty) {
|
||||
arguments.add("--flavor=$_buildFlavor");
|
||||
arguments.add('--flavor=$_buildFlavor');
|
||||
}
|
||||
|
||||
if (_deviceTargetId != null && _deviceTargetId.isNotEmpty) {
|
||||
arguments.add("--device-id=$_deviceTargetId");
|
||||
arguments.add('--device-id=$_deviceTargetId');
|
||||
}
|
||||
|
||||
if (_verboseFlutterLogs) {
|
||||
arguments.add("--verbose");
|
||||
arguments.add('--verbose');
|
||||
}
|
||||
|
||||
if (_logFlutterProcessOutput) {
|
||||
|
@ -112,7 +112,7 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
}
|
||||
|
||||
_runningProcess = await Process.start(
|
||||
"flutter",
|
||||
'flutter',
|
||||
arguments,
|
||||
workingDirectory: _workingDirectory,
|
||||
runInShell: true,
|
||||
|
@ -126,21 +126,21 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
.where((event) => event.isNotEmpty)
|
||||
.listen((event) {
|
||||
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 {
|
||||
// This is most likely a depricated api usage warnings (from Gradle) and should not
|
||||
// cause the test run to fail.
|
||||
stdout.writeln("$WARN_COLOR$event$RESET_COLOR");
|
||||
stdout.writeln('$WARN_COLOR$event$RESET_COLOR');
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> terminate() async {
|
||||
int exitCode = -1;
|
||||
var exitCode = -1;
|
||||
_ensureRunningProcess();
|
||||
if (_runningProcess != null) {
|
||||
_runningProcess.stdin.write("q");
|
||||
_runningProcess.stdin.write('q');
|
||||
_openSubscriptions.forEach((s) => s.cancel());
|
||||
_openSubscriptions.clear();
|
||||
exitCode = await _runningProcess.exitCode;
|
||||
|
@ -152,10 +152,10 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
|
||||
Future<bool> restart({Duration timeout = const Duration(seconds: 90)}) async {
|
||||
_ensureRunningProcess();
|
||||
_runningProcess.stdin.write("R");
|
||||
_runningProcess.stdin.write('R');
|
||||
await _waitForStdOutMessage(
|
||||
_restartedApplicationSuccessRegex,
|
||||
"Timeout waiting for app restart",
|
||||
'Timeout waiting for app restart',
|
||||
timeout,
|
||||
);
|
||||
|
||||
|
@ -171,7 +171,7 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
]) async {
|
||||
currentObservatoryUri = await _waitForStdOutMessage(
|
||||
_observatoryDebuggerUriRegex,
|
||||
"Timeout while waiting for observatory debugger uri",
|
||||
'Timeout while waiting for observatory debugger uri',
|
||||
timeout,
|
||||
);
|
||||
|
||||
|
@ -208,12 +208,12 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
sub?.cancel();
|
||||
if (!completer.isCompleted) {
|
||||
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)) {
|
||||
sub?.cancel();
|
||||
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() {
|
||||
if (_runningProcess == null) {
|
||||
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
|
||||
/// 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)
|
||||
/// Handy if your app is seperated from your tests as flutter needs to be able to find a pubspec file
|
||||
String targetAppWorkingDirecotry;
|
||||
/// Handy if your app is separated from your tests as flutter needs to be able to find a pubspec file
|
||||
String targetAppWorkingDirectory;
|
||||
|
||||
/// The build flavor to run the tests against (optional)
|
||||
/// Defaults to empty
|
||||
String buildFlavor = "";
|
||||
String buildFlavor = '';
|
||||
|
||||
/// If the application should be built prior to running the tests
|
||||
/// Defaults to true
|
||||
|
@ -40,7 +40,7 @@ class FlutterTestConfiguration extends TestConfiguration {
|
|||
|
||||
/// The target device id to run the tests against when multiple devices detected
|
||||
/// Defaults to empty
|
||||
String targetDeviceId = "";
|
||||
String targetDeviceId = '';
|
||||
|
||||
/// Logs Flutter process output to stdout
|
||||
/// 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
|
||||
/// 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
|
||||
/// 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,
|
||||
FlutterWorld world,
|
||||
) async {
|
||||
FlutterTestConfiguration flutterConfig = config as FlutterTestConfiguration;
|
||||
var flutterConfig = config as FlutterTestConfiguration;
|
||||
world = world ?? FlutterWorld();
|
||||
|
||||
final driver = await createFlutterDriver(
|
||||
|
@ -116,6 +116,7 @@ class FlutterTestConfiguration extends TestConfiguration {
|
|||
..addAll([
|
||||
ThenExpectElementToHaveValue(),
|
||||
WhenTapWidget(),
|
||||
WhenTapWidgetWithoutScroll(),
|
||||
WhenTapBackButtonWidget(),
|
||||
GivenOpenDrawer(),
|
||||
WhenPauseStep(),
|
||||
|
@ -128,14 +129,14 @@ class FlutterTestConfiguration extends TestConfiguration {
|
|||
Future<FlutterDriver> _attemptDriverConnection(
|
||||
String dartVmServiceUrl,
|
||||
int attempt,
|
||||
int maxAttemps,
|
||||
int maxAttempts,
|
||||
) async {
|
||||
try {
|
||||
return await FlutterDriver.connect(
|
||||
dartVmServiceUrl: dartVmServiceUrl,
|
||||
);
|
||||
} catch (e) {
|
||||
if (attempt > maxAttemps) {
|
||||
if (attempt > maxAttempts) {
|
||||
rethrow;
|
||||
} else {
|
||||
print(e);
|
||||
|
@ -144,7 +145,7 @@ class FlutterTestConfiguration extends TestConfiguration {
|
|||
return _attemptDriverConnection(
|
||||
dartVmServiceUrl,
|
||||
attempt + 1,
|
||||
maxAttemps,
|
||||
maxAttempts,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,10 +69,10 @@ class FlutterAppRunnerHook extends Hook {
|
|||
} else {
|
||||
_flutterRunProcessHandler = FlutterRunProcessHandler()
|
||||
..setLogFlutterProcessOutput(config.logFlutterProcessOutput)
|
||||
..setVerboseFluterlogs(config.verboseFlutterProcessLogs)
|
||||
..setVerboseFlutterLogs(config.verboseFlutterProcessLogs)
|
||||
..setApplicationTargetFile(config.targetAppPath)
|
||||
..setDriverConnectionDelay(config.flutterDriverReconnectionDelay)
|
||||
..setWorkingDirectory(config.targetAppWorkingDirecotry)
|
||||
..setWorkingDirectory(config.targetAppWorkingDirectory)
|
||||
..setBuildRequired(haveRunFirstScenario ? false : config.build)
|
||||
..setBuildFlavor(config.buildFlavor)
|
||||
..setDeviceTargetId(config.targetDeviceId);
|
||||
|
@ -88,7 +88,7 @@ class FlutterAppRunnerHook extends Hook {
|
|||
|
||||
Future<void> _terminateApp() async {
|
||||
if (_flutterRunProcessHandler != null) {
|
||||
stdout.writeln("Terminating Flutter app under test");
|
||||
stdout.writeln('Terminating Flutter app under test');
|
||||
await _flutterRunProcessHandler.terminate();
|
||||
_flutterRunProcessHandler = null;
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ class FlutterAppRunnerHook extends Hook {
|
|||
|
||||
Future<void> _restartApp() async {
|
||||
if (_flutterRunProcessHandler != null) {
|
||||
stdout.writeln("Restarting Flutter app under test");
|
||||
stdout.writeln('Restarting Flutter app under test');
|
||||
await _flutterRunProcessHandler.restart();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import 'dart:io';
|
|||
import 'package:gherkin/gherkin.dart';
|
||||
import 'package:flutter_driver/flutter_driver.dart';
|
||||
|
||||
enum _FlutterDriverMessageLogLevel { info, warning, error }
|
||||
|
||||
/// The Flutter driver helpfully logs ALL messages to the stderr output
|
||||
/// useless something is listening to the messages.
|
||||
/// 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 logWarningMessages;
|
||||
final bool logInfoMessages;
|
||||
final List<StreamSubscription> subscriptions = List<StreamSubscription>();
|
||||
|
||||
FlutterDriverReporter({
|
||||
this.logErrorMessages = true,
|
||||
|
@ -23,38 +24,37 @@ class FlutterDriverReporter extends Reporter {
|
|||
this.logInfoMessages = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<void> onTestRunStarted() async {
|
||||
if (logInfoMessages) {
|
||||
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());
|
||||
}));
|
||||
}
|
||||
driverLog = _driverLogMessageHandler;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
subscriptions.forEach((s) => s.cancel());
|
||||
subscriptions.clear();
|
||||
driverLog = null;
|
||||
}
|
||||
|
||||
bool _isLevel(LogLevel level, Iterable<LogLevel> levels) =>
|
||||
levels.contains(level);
|
||||
void _driverLogMessageHandler(String source, String message) {
|
||||
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`
|
||||
class GivenOpenDrawer extends Given1WithWorld<String, FlutterWorld> {
|
||||
@override
|
||||
RegExp get pattern => RegExp(r"I (open|close) the drawer");
|
||||
RegExp get pattern => RegExp(r'I (open|close) the drawer');
|
||||
|
||||
@override
|
||||
Future<void> executeStep(String action) async {
|
||||
final drawerFinder = find.byType("Drawer");
|
||||
final drawerFinder = find.byType('Drawer');
|
||||
final isOpen =
|
||||
await FlutterDriverUtils.isPresent(drawerFinder, world.driver);
|
||||
// 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
|
||||
await world.driver
|
||||
.scroll(drawerFinder, -300.0, 0.0, const Duration(milliseconds: 300));
|
||||
} else if (!isOpen && action == "open") {
|
||||
} else if (!isOpen && action == 'open') {
|
||||
await FlutterDriverUtils.tap(
|
||||
world.driver,
|
||||
find.byTooltip("Open navigation menu"),
|
||||
find.byTooltip('Open navigation menu'),
|
||||
timeout: timeout,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,5 +11,5 @@ class RestartAppStep extends WhenWithWorld<FlutterWorld> {
|
|||
}
|
||||
|
||||
@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
|
||||
extends Then2WithWorld<String, String, FlutterWorld> {
|
||||
@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
|
||||
Future<void> executeStep(String key, String value) async {
|
||||
|
|
|
@ -16,7 +16,7 @@ class ThenExpectWidgetToBePresent
|
|||
extends When2WithWorld<String, int, FlutterWorld> {
|
||||
@override
|
||||
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
|
||||
Future<void> executeStep(String key, int seconds) async {
|
||||
|
|
|
@ -22,5 +22,5 @@ class WhenFillFieldStep extends When2WithWorld<String, String, FlutterWorld> {
|
|||
}
|
||||
|
||||
@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
|
||||
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"`
|
||||
class WhenTapBackButtonWidget extends WhenWithWorld<FlutterWorld> {
|
||||
@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
|
||||
Future<void> executeStep() async {
|
||||
|
|
|
@ -20,7 +20,7 @@ import 'package:gherkin/gherkin.dart';
|
|||
class WhenTapWidget extends When1WithWorld<String, FlutterWorld> {
|
||||
@override
|
||||
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
|
||||
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.
|
||||
static Future<void> waitUntil(
|
||||
FlutterDriver driver,
|
||||
Future<bool> condition(), {
|
||||
Future<bool> Function() condition, {
|
||||
Duration timeout = const Duration(seconds: 10),
|
||||
Duration pollInterval = const Duration(milliseconds: 500),
|
||||
}) async {
|
||||
return Future.microtask(() async {
|
||||
final completer = Completer<void>();
|
||||
int maxAttempts =
|
||||
var maxAttempts =
|
||||
(timeout.inMilliseconds / pollInterval.inMilliseconds).round();
|
||||
int attempts = 0;
|
||||
var attempts = 0;
|
||||
|
||||
while (attempts < maxAttempts) {
|
||||
final result = await condition();
|
||||
|
|
85
pubspec.lock
85
pubspec.lock
|
@ -7,56 +7,56 @@ packages:
|
|||
name: _fe_analyzer_shared
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
version: "3.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.39.4"
|
||||
version: "0.39.8"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.11"
|
||||
version: "2.0.13"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.2"
|
||||
version: "1.6.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
version: "2.4.1"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
version: "2.0.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
version: "1.1.3"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.14.11"
|
||||
version: "1.14.12"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -77,7 +77,7 @@ packages:
|
|||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.1.4"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -139,7 +139,7 @@ packages:
|
|||
name: http
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.0+4"
|
||||
version: "0.12.1"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -160,21 +160,21 @@ packages:
|
|||
name: image
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.12"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.16.0"
|
||||
version: "0.16.1"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.3"
|
||||
version: "0.3.4"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -237,7 +237,7 @@ packages:
|
|||
name: node_io
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1+2"
|
||||
version: "1.1.0"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -251,14 +251,7 @@ packages:
|
|||
name: package_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.9.2"
|
||||
package_resolver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_resolver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.10"
|
||||
version: "1.9.3"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -272,7 +265,7 @@ packages:
|
|||
name: pedantic
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0+1"
|
||||
version: "1.9.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -307,14 +300,14 @@ packages:
|
|||
name: pub_semver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.2"
|
||||
version: "1.4.4"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quiver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.1.3"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -328,7 +321,7 @@ packages:
|
|||
name: shelf_packages_handler
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
version: "2.0.0"
|
||||
shelf_static:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -354,7 +347,7 @@ packages:
|
|||
name: source_map_stack_trace
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.5"
|
||||
version: "2.0.0"
|
||||
source_maps:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -368,7 +361,7 @@ packages:
|
|||
name: source_span
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.5"
|
||||
version: "1.7.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -390,6 +383,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -403,21 +403,21 @@ packages:
|
|||
name: test
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.9.4"
|
||||
version: "1.14.3"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.11"
|
||||
version: "0.2.15"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.15"
|
||||
version: "0.3.4"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -438,7 +438,7 @@ packages:
|
|||
name: vm_service
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "4.0.2"
|
||||
vm_service_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -452,7 +452,7 @@ packages:
|
|||
name: watcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.7+14"
|
||||
version: "0.9.7+15"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -460,19 +460,34 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.5.0"
|
||||
version: "3.6.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.2.1"
|
||||
sdks:
|
||||
dart: ">=2.7.0 <3.0.0"
|
||||
flutter: ">=1.13.0"
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
name: flutter_gherkin
|
||||
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
|
||||
|
||||
environment:
|
||||
sdk: ">=2.1.0 <3.0.0"
|
||||
sdk: ">=2.6.0 <3.0.0"
|
||||
flutter: ">=1.13.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
|
|
@ -13,9 +13,9 @@ import 'package:test/test.dart';
|
|||
import 'mocks/step_definition_mock.dart';
|
||||
|
||||
void main() {
|
||||
group("config", () {
|
||||
group("prepare", () {
|
||||
test("flutter app runner hook added", () {
|
||||
group('config', () {
|
||||
group('prepare', () {
|
||||
test('flutter app runner hook added', () {
|
||||
final config = FlutterTestConfiguration();
|
||||
expect(config.hooks, isNull);
|
||||
config.prepare();
|
||||
|
@ -24,36 +24,38 @@ void main() {
|
|||
expect(config.hooks.elementAt(0), (x) => x is FlutterAppRunnerHook);
|
||||
});
|
||||
|
||||
test("common steps definition added", () {
|
||||
test('common steps definition added', () {
|
||||
final config = FlutterTestConfiguration();
|
||||
expect(config.stepDefinitions, isNull);
|
||||
|
||||
config.prepare();
|
||||
expect(config.stepDefinitions, isNotNull);
|
||||
expect(config.stepDefinitions.length, 8);
|
||||
expect(config.stepDefinitions.length, 9);
|
||||
expect(config.stepDefinitions.elementAt(0),
|
||||
(x) => x is ThenExpectElementToHaveValue);
|
||||
expect(config.stepDefinitions.elementAt(1), (x) => x is WhenTapWidget);
|
||||
expect(config.stepDefinitions.elementAt(2),
|
||||
(x) => x is WhenTapWidgetWithoutScroll);
|
||||
expect(config.stepDefinitions.elementAt(3),
|
||||
(x) => x is WhenTapBackButtonWidget);
|
||||
expect(
|
||||
config.stepDefinitions.elementAt(3), (x) => x is GivenOpenDrawer);
|
||||
expect(config.stepDefinitions.elementAt(4), (x) => x is WhenPauseStep);
|
||||
config.stepDefinitions.elementAt(4), (x) => x is GivenOpenDrawer);
|
||||
expect(config.stepDefinitions.elementAt(5), (x) => x is WhenPauseStep);
|
||||
expect(
|
||||
config.stepDefinitions.elementAt(5), (x) => x is WhenFillFieldStep);
|
||||
expect(config.stepDefinitions.elementAt(6),
|
||||
config.stepDefinitions.elementAt(6), (x) => x is WhenFillFieldStep);
|
||||
expect(config.stepDefinitions.elementAt(7),
|
||||
(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()
|
||||
..stepDefinitions = [MockStepDefinition()];
|
||||
expect(config.stepDefinitions.length, 1);
|
||||
|
||||
config.prepare();
|
||||
expect(config.stepDefinitions, isNotNull);
|
||||
expect(config.stepDefinitions.length, 9);
|
||||
expect(config.stepDefinitions.length, 10);
|
||||
expect(config.stepDefinitions.elementAt(0),
|
||||
(x) => x is MockStepDefinition);
|
||||
expect(config.stepDefinitions.elementAt(1),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:gherkin/gherkin.dart';
|
||||
|
||||
typedef Future<void> OnRunCode(Iterable parameters);
|
||||
typedef OnRunCode = Future<void> Function(Iterable parameters);
|
||||
|
||||
class MockStepDefinition extends StepDefinitionBase<World> {
|
||||
bool hasRun = false;
|
||||
|
|
Loading…
Reference in New Issue