chore(lints): fixed linting and improved code quality
fix(reporters): fixed summary reporter being called after each feature
This commit is contained in:
parent
0961916daa
commit
551a4fc34c
|
@ -1 +1 @@
|
||||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"C:\\\\Development\\\\flutter\\\\packages\\\\integration_test\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"C:\\\\Development\\\\flutter\\\\packages\\\\integration_test\\\\","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]}],"date_created":"2022-06-17 15:37:47.695519","version":"3.0.2"}
|
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"C:\\\\Development\\\\flutter\\\\packages\\\\integration_test\\\\","native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"C:\\\\Development\\\\flutter\\\\packages\\\\integration_test\\\\","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]}],"date_created":"2022-06-20 11:46:06.283670","version":"3.0.2"}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"dart-code.dart-code",
|
||||||
|
"dart-code.flutter",
|
||||||
|
"alexkrechik.cucumberautocomplete"
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,13 +1,18 @@
|
||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"Errored",
|
|
||||||
"Serializable",
|
|
||||||
"agnostically",
|
"agnostically",
|
||||||
|
"analyzer",
|
||||||
|
"dialog",
|
||||||
|
"Errored",
|
||||||
|
"Flavor",
|
||||||
"microtask",
|
"microtask",
|
||||||
"multiline",
|
"multiline",
|
||||||
"pubspec",
|
"pubspec",
|
||||||
|
"rxdart",
|
||||||
"scrollable",
|
"scrollable",
|
||||||
|
"Serializable",
|
||||||
"todos",
|
"todos",
|
||||||
"writeln"
|
"writeln"
|
||||||
]
|
],
|
||||||
|
"cSpell.language": "en-GB"
|
||||||
}
|
}
|
|
@ -4,8 +4,10 @@
|
||||||
- Fix #226: Allow compatibility with dev and master flutter branches
|
- Fix #226: Allow compatibility with dev and master flutter branches
|
||||||
- Feat #218: Allow retry steps in case of intermittent failure by setting the configuration properties `stepMaxRetries` & `retryDelay`
|
- Feat #218: Allow retry steps in case of intermittent failure by setting the configuration properties `stepMaxRetries` & `retryDelay`
|
||||||
- Fix #210 & #191: Ability to take screenshots on web
|
- Fix #210 & #191: Ability to take screenshots on web
|
||||||
- Fix #198: Allow the use of implicit pumpAndSettle methods in the app driver to be turned off using the configuration property `waitImplicitlyAfterAction`
|
- Fix #198: Allow the use of implicit pumpAndSettle methods in the app driver to be turned off using the configuration property `waitImplicitlyAfterAction`. Off by default
|
||||||
|
|
||||||
|
* BREAKING CHANGE:
|
||||||
|
- `NEW API FOR REPORTERS`: All reporters implement (do not extend) separated interfaces see https://github.com/jonsamwell/dart_gherkin/blob/master/CHANGELOG.md#300---16052022
|
||||||
|
|
||||||
## [3.0.0-rc.9] - 18/11/2021
|
## [3.0.0-rc.9] - 18/11/2021
|
||||||
|
|
||||||
|
|
|
@ -1 +1,29 @@
|
||||||
# include: package:pedantic/analysis_options.yaml
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at
|
||||||
|
# https://dart-lang.github.io/linter/lints/index.html.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
|
@ -7,7 +7,7 @@ Future<void> main() {
|
||||||
features: [RegExp('features/**.feature')],
|
features: [RegExp('features/**.feature')],
|
||||||
targetAppPath: 'test_driver/app.dart',
|
targetAppPath: 'test_driver/app.dart',
|
||||||
targetAppWorkingDirectory: '../',
|
targetAppWorkingDirectory: '../',
|
||||||
buildFlavor:
|
buildFlavour:
|
||||||
"staging", // uncomment when using build flavor and check android/ios flavor setup see android file android\app\build.gradle
|
"staging", // uncomment when using build flavor and check android/ios flavor setup see android file android\app\build.gradle
|
||||||
targetDeviceId:
|
targetDeviceId:
|
||||||
"all", // uncomment to run tests on all connected devices or set specific device target id
|
"all", // uncomment to run tests on all connected devices or set specific device target id
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
@tag
|
@tag
|
||||||
Feature: Creating todos
|
Feature: Creating todos
|
||||||
|
|
||||||
@debug
|
Scenario: User can create single todo item
|
||||||
|
Given I fill the "todo" field with "Buy spinach"
|
||||||
|
When I tap the "add" button
|
||||||
|
Then I expect the todo list
|
||||||
|
| Todo |
|
||||||
|
| Buy spinach |
|
||||||
|
|
||||||
Scenario: User can create multiple new todo items
|
Scenario: User can create multiple new todo items
|
||||||
Given I fill the "todo" field with "Buy carrots"
|
Given I fill the "todo" field with "Buy carrots"
|
||||||
When I tap the "add" button
|
When I tap the "add" button
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
@tag
|
@tag
|
||||||
Feature: Swiping
|
Feature: Swiping
|
||||||
|
|
||||||
|
@debug
|
||||||
Scenario: User can swipe cards left and right
|
Scenario: User can swipe cards left and right
|
||||||
Given I swipe right by 250 pixels on the "scrollable cards"`
|
Given I swipe right by 250 pixels on the "scrollable cards"`
|
||||||
Then Then I expect the text "Page 2" to be present
|
Then I expect the text "Page 2" to be present
|
||||||
|
|
||||||
Given I swipe left by 250 pixels on the "scrollable cards"`
|
Given I swipe left by 250 pixels on the "scrollable cards"`
|
||||||
Then Then I expect the text "Page 1" to be present
|
Then I expect the text "Page 1" to be present
|
|
@ -13,7 +13,7 @@ import 'steps/when_step_has_timeout.dart';
|
||||||
import 'world/custom_world.dart';
|
import 'world/custom_world.dart';
|
||||||
|
|
||||||
FlutterTestConfiguration gherkinTestConfiguration = FlutterTestConfiguration(
|
FlutterTestConfiguration gherkinTestConfiguration = FlutterTestConfiguration(
|
||||||
tagExpression: '@debug',
|
// tagExpression: '@debug',
|
||||||
stepDefinitions: [
|
stepDefinitions: [
|
||||||
thenIExpectTheTodos,
|
thenIExpectTheTodos,
|
||||||
whenAnAnimationIsAwaited,
|
whenAnAnimationIsAwaited,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
final givenTheData = given1(
|
final givenTheData = given1(
|
||||||
'I have item with data',
|
'I have item with data',
|
||||||
(jsonString, context) async {
|
(jsonString, context) async {
|
||||||
print(jsonString);
|
// print(jsonString);
|
||||||
},
|
},
|
||||||
configuration: StepDefinitionConfiguration()
|
configuration: StepDefinitionConfiguration()
|
||||||
..timeout = const Duration(seconds: 5),
|
..timeout = const Duration(seconds: 5),
|
||||||
|
|
|
@ -8,7 +8,7 @@ part of 'gherkin_suite_test.dart';
|
||||||
|
|
||||||
class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||||
_CustomGherkinIntegrationTestRunner(
|
_CustomGherkinIntegrationTestRunner(
|
||||||
TestConfiguration configuration,
|
FlutterTestConfiguration configuration,
|
||||||
Future<void> Function(World) appMainFunction,
|
Future<void> Function(World) appMainFunction,
|
||||||
) : super(configuration, appMainFunction);
|
) : super(configuration, appMainFunction);
|
||||||
|
|
||||||
|
@ -21,58 +21,78 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||||
|
|
||||||
void testFeature0() {
|
void testFeature0() {
|
||||||
runFeature(
|
runFeature(
|
||||||
'Swiping:',
|
name: 'Swiping:',
|
||||||
<String>['@tag'],
|
tags: <String>['@tag'],
|
||||||
() {
|
run: () {
|
||||||
runScenario(
|
runScenario(
|
||||||
name: 'User can swipe cards left and right',
|
name: 'User can swipe cards left and right',
|
||||||
path:
|
path:
|
||||||
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\swiping.feature',
|
'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\swiping.feature',
|
||||||
tags: <String>['@tag'],
|
tags: <String>['@tag', '@debug'],
|
||||||
steps: [
|
steps: [
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
return await runStep(
|
return await runStep(
|
||||||
'Given I swipe right by 250 pixels on the "scrollable cards"`',
|
name:
|
||||||
<String>[],
|
'Given I swipe right by 250 pixels on the "scrollable cards"`',
|
||||||
null,
|
multiLineStrings: <String>[],
|
||||||
dependencies,
|
table: null,
|
||||||
hasToSkip,
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
return await runStep(
|
return await runStep(
|
||||||
'Then Then I expect the text "Page 2" to be present',
|
name: 'Then I expect the text "Page 2" to be present',
|
||||||
<String>[],
|
multiLineStrings: <String>[],
|
||||||
null,
|
table: null,
|
||||||
dependencies,
|
dependencies: dependencies,
|
||||||
hasToSkip,
|
skip: skip,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
return await runStep(
|
return await runStep(
|
||||||
'Given I swipe left by 250 pixels on the "scrollable cards"`',
|
name:
|
||||||
<String>[],
|
'Given I swipe left by 250 pixels on the "scrollable cards"`',
|
||||||
null,
|
multiLineStrings: <String>[],
|
||||||
dependencies,
|
table: null,
|
||||||
hasToSkip,
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
return await runStep(
|
return await runStep(
|
||||||
'Then Then I expect the text "Page 1" to be present',
|
name: 'Then I expect the text "Page 1" to be present',
|
||||||
<String>[],
|
multiLineStrings: <String>[],
|
||||||
null,
|
table: null,
|
||||||
dependencies,
|
dependencies: dependencies,
|
||||||
hasToSkip,
|
skip: skip,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
onBefore: () async => onBeforeRunFeature(
|
onBefore: () async => onBeforeRunFeature(
|
||||||
'Swiping',
|
name: 'Swiping',
|
||||||
<String>['@tag'],
|
path:
|
||||||
|
r'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\swiping.feature',
|
||||||
|
tags: <String>['@tag'],
|
||||||
|
),
|
||||||
|
onAfter: () async => onAfterRunFeature(
|
||||||
|
name: 'Swiping',
|
||||||
|
path:
|
||||||
|
r'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\swiping.feature',
|
||||||
|
tags: <String>['@tag'],
|
||||||
),
|
),
|
||||||
onAfter: () async => onAfterRunFeature('Swiping',
|
|
||||||
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\swiping.feature'),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -80,92 +100,22 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||||
|
|
||||||
void testFeature1() {
|
void testFeature1() {
|
||||||
runFeature(
|
runFeature(
|
||||||
'Creating todos:',
|
name: 'Checking data:',
|
||||||
<String>['@tag'],
|
tags: <String>['@tag'],
|
||||||
() {
|
run: () {
|
||||||
runScenario(
|
runScenario(
|
||||||
name: 'User can create multiple new todo items',
|
name: 'User can have data',
|
||||||
path:
|
path:
|
||||||
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\create.feature',
|
'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\check.feature',
|
||||||
tags: <String>['@tag', '@debug'],
|
tags: <String>['@tag', '@tag1'],
|
||||||
steps: [
|
steps: [
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
return await runStep(
|
return await runStep(
|
||||||
'Given I fill the "todo" field with "Buy carrots"',
|
name: 'Given I have item with data',
|
||||||
<String>[],
|
multiLineStrings: <String>[
|
||||||
null,
|
|
||||||
dependencies,
|
|
||||||
hasToSkip,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
|
||||||
return await runStep(
|
|
||||||
'When I tap the "add" button',
|
|
||||||
<String>[],
|
|
||||||
null,
|
|
||||||
dependencies,
|
|
||||||
hasToSkip,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
|
||||||
return await runStep(
|
|
||||||
'And I fill the "todo" field with "Buy apples"',
|
|
||||||
<String>[],
|
|
||||||
null,
|
|
||||||
dependencies,
|
|
||||||
hasToSkip,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
|
||||||
return await runStep(
|
|
||||||
'When I tap the "add" button',
|
|
||||||
<String>[],
|
|
||||||
null,
|
|
||||||
dependencies,
|
|
||||||
hasToSkip,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
|
||||||
return await runStep(
|
|
||||||
'And I fill the "todo" field with "Buy blueberries"',
|
|
||||||
<String>[],
|
|
||||||
null,
|
|
||||||
dependencies,
|
|
||||||
hasToSkip,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
|
||||||
return await runStep(
|
|
||||||
'When I tap the "add" button',
|
|
||||||
<String>[],
|
|
||||||
null,
|
|
||||||
dependencies,
|
|
||||||
hasToSkip,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
|
||||||
return await runStep(
|
|
||||||
'Then I expect the todo list',
|
|
||||||
<String>[],
|
|
||||||
GherkinTable.fromJson(
|
|
||||||
'[{"Todo":"Buy blueberries"},{"Todo":"Buy apples"},{"Todo":"Buy carrots"}]'),
|
|
||||||
dependencies,
|
|
||||||
hasToSkip,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
|
||||||
return await runStep(
|
|
||||||
'Given I wait 5 seconds for the animation to complete',
|
|
||||||
<String>[],
|
|
||||||
null,
|
|
||||||
dependencies,
|
|
||||||
hasToSkip,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
|
||||||
return await runStep(
|
|
||||||
'Given I have item with data',
|
|
||||||
<String>[
|
|
||||||
"""{
|
"""{
|
||||||
"glossary": {
|
"glossary": {
|
||||||
"title": "example glossary",
|
"title": "example glossary",
|
||||||
|
@ -192,18 +142,24 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
],
|
],
|
||||||
null,
|
table: null,
|
||||||
dependencies,
|
dependencies: dependencies,
|
||||||
hasToSkip,
|
skip: skip,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
onBefore: () async => onBeforeRunFeature(
|
onBefore: () async => onBeforeRunFeature(
|
||||||
'Creating todos',
|
name: 'Checking data',
|
||||||
<String>['@tag'],
|
path:
|
||||||
|
r'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\check.feature',
|
||||||
|
tags: <String>['@tag'],
|
||||||
|
),
|
||||||
|
onAfter: () async => onAfterRunFeature(
|
||||||
|
name: 'Checking data',
|
||||||
|
path:
|
||||||
|
r'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\check.feature',
|
||||||
|
tags: <String>['@tag'],
|
||||||
),
|
),
|
||||||
onAfter: () async => onAfterRunFeature('Creating todos',
|
|
||||||
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\create.feature'),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -211,19 +167,206 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||||
|
|
||||||
void testFeature2() {
|
void testFeature2() {
|
||||||
runFeature(
|
runFeature(
|
||||||
'Checking data:',
|
name: 'Creating todos:',
|
||||||
<String>['@tag'],
|
tags: <String>['@tag'],
|
||||||
() {
|
run: () {
|
||||||
runScenario(
|
runScenario(
|
||||||
name: 'User can have data',
|
name: 'User can create single todo item',
|
||||||
path:
|
path:
|
||||||
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\check.feature',
|
'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\create.feature',
|
||||||
tags: <String>['@tag', '@tag1'],
|
tags: <String>['@tag'],
|
||||||
steps: [
|
steps: [
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
return await runStep(
|
return await runStep(
|
||||||
'Given I have item with data',
|
name: 'Given I fill the "todo" field with "Buy spinach"',
|
||||||
<String>[
|
multiLineStrings: <String>[],
|
||||||
|
table: null,
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'When I tap the "add" button',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: null,
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'Then I expect the todo list',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: GherkinTable.fromJson('[{"Todo":"Buy spinach"}]'),
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onBefore: () async => onBeforeRunFeature(
|
||||||
|
name: 'Creating todos',
|
||||||
|
path:
|
||||||
|
r'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\create.feature',
|
||||||
|
tags: <String>['@tag'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
runScenario(
|
||||||
|
name: 'User can create multiple new todo items',
|
||||||
|
path:
|
||||||
|
'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\create.feature',
|
||||||
|
tags: <String>['@tag'],
|
||||||
|
steps: [
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'Given I fill the "todo" field with "Buy spinach"',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: null,
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'When I tap the "add" button',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: null,
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'Then I expect the todo list',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: GherkinTable.fromJson('[{"Todo":"Buy spinach"}]'),
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'Given I fill the "todo" field with "Buy carrots"',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: null,
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'When I tap the "add" button',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: null,
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'And I fill the "todo" field with "Buy apples"',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: null,
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'When I tap the "add" button',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: null,
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'And I fill the "todo" field with "Buy blueberries"',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: null,
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'When I tap the "add" button',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: null,
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'Then I expect the todo list',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: GherkinTable.fromJson(
|
||||||
|
'[{"Todo":"Buy blueberries"},{"Todo":"Buy apples"},{"Todo":"Buy carrots"}]'),
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'Given I wait 5 seconds for the animation to complete',
|
||||||
|
multiLineStrings: <String>[],
|
||||||
|
table: null,
|
||||||
|
dependencies: dependencies,
|
||||||
|
skip: skip,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
) async {
|
||||||
|
return await runStep(
|
||||||
|
name: 'Given I have item with data',
|
||||||
|
multiLineStrings: <String>[
|
||||||
"""{
|
"""{
|
||||||
"glossary": {
|
"glossary": {
|
||||||
"title": "example glossary",
|
"title": "example glossary",
|
||||||
|
@ -250,18 +393,18 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
],
|
],
|
||||||
null,
|
table: null,
|
||||||
dependencies,
|
dependencies: dependencies,
|
||||||
hasToSkip,
|
skip: skip,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
onBefore: () async => onBeforeRunFeature(
|
onAfter: () async => onAfterRunFeature(
|
||||||
'Checking data',
|
name: 'Creating todos',
|
||||||
<String>['@tag'],
|
path:
|
||||||
|
r'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\create.feature',
|
||||||
|
tags: <String>['@tag'],
|
||||||
),
|
),
|
||||||
onAfter: () async => onAfterRunFeature('Checking data',
|
|
||||||
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\check.feature'),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -269,7 +412,7 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
void executeTestSuite(
|
void executeTestSuite(
|
||||||
TestConfiguration configuration,
|
FlutterTestConfiguration configuration,
|
||||||
Future<void> Function(World) appMainFunction,
|
Future<void> Function(World) appMainFunction,
|
||||||
) {
|
) {
|
||||||
_CustomGherkinIntegrationTestRunner(configuration, appMainFunction).run();
|
_CustomGherkinIntegrationTestRunner(configuration, appMainFunction).run();
|
||||||
|
|
|
@ -154,9 +154,9 @@ class FlutterDriverAppDriverAdapter
|
||||||
@override
|
@override
|
||||||
SerializableFinder findBy(
|
SerializableFinder findBy(
|
||||||
dynamic data,
|
dynamic data,
|
||||||
FindType type,
|
FindType findType,
|
||||||
) {
|
) {
|
||||||
switch (type) {
|
switch (findType) {
|
||||||
case FindType.key:
|
case FindType.key:
|
||||||
return find.byValueKey(data.toString());
|
return find.byValueKey(data.toString());
|
||||||
case FindType.text:
|
case FindType.text:
|
||||||
|
|
|
@ -39,8 +39,9 @@ class WidgetTesterAppDriverAdapter
|
||||||
Future<void> _implicitWait({
|
Future<void> _implicitWait({
|
||||||
Duration? duration = const Duration(milliseconds: 100),
|
Duration? duration = const Duration(milliseconds: 100),
|
||||||
Duration? timeout = const Duration(seconds: 30),
|
Duration? timeout = const Duration(seconds: 30),
|
||||||
|
bool? force,
|
||||||
}) async {
|
}) async {
|
||||||
if (waitImplicitlyAfterAction) {
|
if (waitImplicitlyAfterAction || force == true) {
|
||||||
try {
|
try {
|
||||||
await nativeDriver.pumpAndSettle(
|
await nativeDriver.pumpAndSettle(
|
||||||
duration ?? const Duration(milliseconds: 100),
|
duration ?? const Duration(milliseconds: 100),
|
||||||
|
@ -154,35 +155,16 @@ class WidgetTesterAppDriverAdapter
|
||||||
duration: pressDuration,
|
duration: pressDuration,
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
);
|
);
|
||||||
|
|
||||||
await _implicitWait(timeout: timeout);
|
await _implicitWait(timeout: timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> scroll(
|
|
||||||
Finder finder, {
|
|
||||||
double? dx,
|
|
||||||
double? dy,
|
|
||||||
Duration? duration = const Duration(milliseconds: 200),
|
|
||||||
Duration? timeout = const Duration(seconds: 30),
|
|
||||||
}) async {
|
|
||||||
final scrollableFinder = findByDescendant(
|
|
||||||
finder,
|
|
||||||
find.byType(Scrollable),
|
|
||||||
matchRoot: true,
|
|
||||||
);
|
|
||||||
final state = nativeDriver.state(scrollableFinder) as ScrollableState;
|
|
||||||
final position = state.position;
|
|
||||||
position.jumpTo(dy ?? dx ?? 0);
|
|
||||||
|
|
||||||
await nativeDriver.pump();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Finder findBy(
|
Finder findBy(
|
||||||
dynamic data,
|
dynamic data,
|
||||||
FindType type,
|
FindType findType,
|
||||||
) {
|
) {
|
||||||
switch (type) {
|
switch (findType) {
|
||||||
case FindType.key:
|
case FindType.key:
|
||||||
return find.byKey(data is Key ? data : Key(data));
|
return find.byKey(data is Key ? data : Key(data));
|
||||||
case FindType.text:
|
case FindType.text:
|
||||||
|
@ -222,6 +204,31 @@ class WidgetTesterAppDriverAdapter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> scroll(
|
||||||
|
Finder finder, {
|
||||||
|
double? dx,
|
||||||
|
double? dy,
|
||||||
|
Duration? duration = const Duration(milliseconds: 200),
|
||||||
|
Duration? timeout = const Duration(seconds: 30),
|
||||||
|
}) async {
|
||||||
|
final scrollableFinder = findByDescendant(
|
||||||
|
finder,
|
||||||
|
find.byType(Scrollable),
|
||||||
|
matchRoot: true,
|
||||||
|
);
|
||||||
|
final state = nativeDriver.state(scrollableFinder) as ScrollableState;
|
||||||
|
final position = state.position;
|
||||||
|
position.jumpTo(dy ?? dx ?? 0);
|
||||||
|
|
||||||
|
// must force a pump and settle to ensure the scroll is performed
|
||||||
|
await _implicitWait(
|
||||||
|
duration: duration,
|
||||||
|
timeout: timeout,
|
||||||
|
force: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> scrollUntilVisible(
|
Future<void> scrollUntilVisible(
|
||||||
Finder item, {
|
Finder item, {
|
||||||
|
@ -235,6 +242,12 @@ class WidgetTesterAppDriverAdapter
|
||||||
dy ?? dx ?? 0,
|
dy ?? dx ?? 0,
|
||||||
scrollable: scrollable,
|
scrollable: scrollable,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// must force a pump and settle to ensure the scroll is performed
|
||||||
|
await _implicitWait(
|
||||||
|
timeout: timeout,
|
||||||
|
force: true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -244,6 +257,12 @@ class WidgetTesterAppDriverAdapter
|
||||||
}) async {
|
}) async {
|
||||||
await nativeDriver.ensureVisible(finder);
|
await nativeDriver.ensureVisible(finder);
|
||||||
await waitForAppToSettle();
|
await waitForAppToSettle();
|
||||||
|
|
||||||
|
// must force a pump and settle to ensure the scroll is performed
|
||||||
|
await _implicitWait(
|
||||||
|
timeout: timeout,
|
||||||
|
force: true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
// ignore: implementation_imports
|
||||||
import 'package:build/src/builder/build_step.dart';
|
import 'package:build/src/builder/build_step.dart';
|
||||||
import 'package:analyzer/dart/element/element.dart';
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
import 'package:flutter_gherkin/src/flutter/code_generation/annotations/gherkin_full_test_suite_annotation.dart';
|
import 'package:flutter_gherkin/src/flutter/code_generation/annotations/gherkin_full_test_suite_annotation.dart';
|
||||||
|
@ -12,10 +13,13 @@ class NoOpReporter extends MessageReporter {
|
||||||
@override
|
@override
|
||||||
Future<void> message(String message, MessageLevel level) async {
|
Future<void> message(String message, MessageLevel level) async {
|
||||||
if (level == MessageLevel.info || level == MessageLevel.debug) {
|
if (level == MessageLevel.info || level == MessageLevel.debug) {
|
||||||
|
// ignore: avoid_print
|
||||||
print(message);
|
print(message);
|
||||||
} else if (level == MessageLevel.warning) {
|
} else if (level == MessageLevel.warning) {
|
||||||
|
// ignore: avoid_print
|
||||||
print('\x1B[33m$message\x1B[0m');
|
print('\x1B[33m$message\x1B[0m');
|
||||||
} else if (level == MessageLevel.error) {
|
} else if (level == MessageLevel.error) {
|
||||||
|
// ignore: avoid_print
|
||||||
print('\x1B[31m$message\x1B[0m');
|
print('\x1B[31m$message\x1B[0m');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,10 +27,10 @@ class NoOpReporter extends MessageReporter {
|
||||||
|
|
||||||
class GherkinSuiteTestGenerator
|
class GherkinSuiteTestGenerator
|
||||||
extends GeneratorForAnnotation<GherkinTestSuite> {
|
extends GeneratorForAnnotation<GherkinTestSuite> {
|
||||||
static const String TEMPLATE = '''
|
static const String template = '''
|
||||||
class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||||
_CustomGherkinIntegrationTestRunner(
|
_CustomGherkinIntegrationTestRunner(
|
||||||
TestConfiguration configuration,
|
FlutterTestConfiguration configuration,
|
||||||
Future<void> Function(World) appMainFunction,
|
Future<void> Function(World) appMainFunction,
|
||||||
) : super(configuration, appMainFunction);
|
) : super(configuration, appMainFunction);
|
||||||
|
|
||||||
|
@ -39,7 +43,7 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
void executeTestSuite(
|
void executeTestSuite(
|
||||||
TestConfiguration configuration,
|
FlutterTestConfiguration configuration,
|
||||||
Future<void> Function(World) appMainFunction,
|
Future<void> Function(World) appMainFunction,
|
||||||
) {
|
) {
|
||||||
_CustomGherkinIntegrationTestRunner(configuration, appMainFunction).run();
|
_CustomGherkinIntegrationTestRunner(configuration, appMainFunction).run();
|
||||||
|
@ -77,7 +81,7 @@ void executeTestSuite(
|
||||||
|
|
||||||
final featureExecutionFunctionsBuilder = StringBuffer();
|
final featureExecutionFunctionsBuilder = StringBuffer();
|
||||||
final generator = FeatureFileTestGenerator();
|
final generator = FeatureFileTestGenerator();
|
||||||
final featuresToExecute = new StringBuffer();
|
final featuresToExecute = StringBuffer();
|
||||||
var id = 0;
|
var id = 0;
|
||||||
|
|
||||||
for (var featureFile in featureFiles) {
|
for (var featureFile in featureFiles) {
|
||||||
|
@ -95,7 +99,7 @@ void executeTestSuite(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TEMPLATE
|
return template
|
||||||
.replaceAll('{{feature_functions}}',
|
.replaceAll('{{feature_functions}}',
|
||||||
featureExecutionFunctionsBuilder.toString())
|
featureExecutionFunctionsBuilder.toString())
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
|
@ -126,44 +130,42 @@ class FeatureFileTestGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
||||||
static const String FUNCTION_TEMPLATE = '''
|
static const String functionTemplate = '''
|
||||||
void testFeature{{feature_number}}() {
|
void testFeature{{feature_number}}() {
|
||||||
runFeature(
|
runFeature(
|
||||||
'{{feature_name}}:',
|
name: '{{feature_name}}:',
|
||||||
{{tags}},
|
tags: {{tags}},
|
||||||
() {
|
run: () {
|
||||||
{{scenarios}}
|
{{scenarios}}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
static const String SCENARIO_TEMPLATE = '''
|
static const String scenarioTemplate = '''
|
||||||
runScenario(
|
runScenario(
|
||||||
name: '{{scenario_name}}',
|
name: '{{scenario_name}}',
|
||||||
path: '{{path}}',
|
path: '{{path}}',
|
||||||
tags:{{tags}},
|
tags:{{tags}},
|
||||||
steps: [
|
steps: [{{steps}},],
|
||||||
{{steps}}
|
|
||||||
],
|
|
||||||
{{onBefore}}
|
{{onBefore}}
|
||||||
{{onAfter}}
|
{{onAfter}}
|
||||||
);
|
);
|
||||||
''';
|
''';
|
||||||
static const String STEP_TEMPLATE = '''
|
static const String stepTemplate = '''
|
||||||
(TestDependencies dependencies, bool hasToSkip) async {
|
(TestDependencies dependencies, bool skip,) async {
|
||||||
return await runStep(
|
return await runStep(
|
||||||
'{{step_name}}',
|
name: '{{step_name}}',
|
||||||
{{step_multi_line_strings}},
|
multiLineStrings: {{step_multi_line_strings}},
|
||||||
{{step_table}},
|
table: {{step_table}},
|
||||||
dependencies,
|
dependencies: dependencies,
|
||||||
hasToSkip,
|
skip: skip,
|
||||||
);}
|
);}
|
||||||
''';
|
''';
|
||||||
static const String ON_BEFORE_SCENARIO_RUN = '''
|
static const String onBeforeScenarioRun = '''
|
||||||
onBefore: () async => onBeforeRunFeature('{{feature_name}}', {{feature_tags}},),
|
onBefore: () async => onBeforeRunFeature(name:'{{feature_name}}', path:r'{{path}}', tags:{{feature_tags}},),
|
||||||
''';
|
''';
|
||||||
static const String ON_AFTER_SCENARIO_RUN = '''
|
static const String onAfterScenarioRun = '''
|
||||||
onAfter: () async => onAfterRunFeature('{{feature_name}}', '{{path}}'),
|
onAfter: () async => onAfterRunFeature(name:'{{feature_name}}', path:r'{{path}}', tags:{{feature_tags}},),
|
||||||
''';
|
''';
|
||||||
|
|
||||||
final StringBuffer _buffer = StringBuffer();
|
final StringBuffer _buffer = StringBuffer();
|
||||||
|
@ -204,7 +206,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
||||||
) async {
|
) async {
|
||||||
if (childScenarioCount > 0) {
|
if (childScenarioCount > 0) {
|
||||||
_currentFeatureCode = _replaceVariable(
|
_currentFeatureCode = _replaceVariable(
|
||||||
FUNCTION_TEMPLATE,
|
functionTemplate,
|
||||||
'feature_number',
|
'feature_number',
|
||||||
_id.toString(),
|
_id.toString(),
|
||||||
);
|
);
|
||||||
|
@ -227,14 +229,14 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
||||||
{required bool isFirst, required bool isLast}) async {
|
{required bool isFirst, required bool isLast}) async {
|
||||||
_flushScenario();
|
_flushScenario();
|
||||||
_currentScenarioCode = _replaceVariable(
|
_currentScenarioCode = _replaceVariable(
|
||||||
SCENARIO_TEMPLATE,
|
scenarioTemplate,
|
||||||
'onBefore',
|
'onBefore',
|
||||||
isFirst ? ON_BEFORE_SCENARIO_RUN : '',
|
isFirst ? onBeforeScenarioRun : '',
|
||||||
);
|
);
|
||||||
_currentScenarioCode = _replaceVariable(
|
_currentScenarioCode = _replaceVariable(
|
||||||
_currentScenarioCode!,
|
_currentScenarioCode!,
|
||||||
'onAfter',
|
'onAfter',
|
||||||
isLast ? ON_AFTER_SCENARIO_RUN : '',
|
isLast ? onAfterScenarioRun : '',
|
||||||
);
|
);
|
||||||
_currentScenarioCode = _replaceVariable(
|
_currentScenarioCode = _replaceVariable(
|
||||||
_currentScenarioCode!,
|
_currentScenarioCode!,
|
||||||
|
@ -270,7 +272,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
||||||
GherkinTable? table,
|
GherkinTable? table,
|
||||||
) async {
|
) async {
|
||||||
var code = _replaceVariable(
|
var code = _replaceVariable(
|
||||||
STEP_TEMPLATE,
|
stepTemplate,
|
||||||
'step_name',
|
'step_name',
|
||||||
_escapeText(name),
|
_escapeText(name),
|
||||||
);
|
);
|
||||||
|
@ -326,5 +328,6 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
||||||
return content.replaceAll('{{$property}}', value);
|
return content.replaceAll('{{$property}}', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _escapeText(String text) => text.replaceAll("'", "\\'");
|
String _escapeText(String text) =>
|
||||||
|
text.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
enum BuildMode { Debug, Profile }
|
enum BuildMode { debug, profile }
|
||||||
|
|
|
@ -26,7 +26,7 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
|
||||||
super.stepDefinitions,
|
super.stepDefinitions,
|
||||||
this.targetAppPath = 'test_driver/app.dart',
|
this.targetAppPath = 'test_driver/app.dart',
|
||||||
this.targetAppWorkingDirectory,
|
this.targetAppWorkingDirectory,
|
||||||
this.buildFlavor,
|
this.buildFlavour,
|
||||||
this.targetDeviceId,
|
this.targetDeviceId,
|
||||||
this.runningAppProtocolEndpointUri,
|
this.runningAppProtocolEndpointUri,
|
||||||
this.onBeforeFlutterDriverConnect,
|
this.onBeforeFlutterDriverConnect,
|
||||||
|
@ -36,18 +36,19 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
|
||||||
this.keepAppRunningAfterTests = false,
|
this.keepAppRunningAfterTests = false,
|
||||||
this.verboseFlutterProcessLogs = false,
|
this.verboseFlutterProcessLogs = false,
|
||||||
this.build = true,
|
this.build = true,
|
||||||
this.buildMode = BuildMode.Debug,
|
this.buildMode = BuildMode.debug,
|
||||||
this.flutterBuildTimeout = const Duration(seconds: 90),
|
this.flutterBuildTimeout = const Duration(seconds: 90),
|
||||||
this.flutterDriverReconnectionDelay = const Duration(seconds: 2),
|
this.flutterDriverReconnectionDelay = const Duration(seconds: 2),
|
||||||
this.flutterDriverMaxConnectionAttempts = 3,
|
this.flutterDriverMaxConnectionAttempts = 3,
|
||||||
}) :
|
}) :
|
||||||
// assert(featurePath != null && features != null),
|
// assert(featurePath != null && features != null),
|
||||||
super(
|
super(
|
||||||
features: features != null ? features : [RegExp(featurePath!)],
|
features: features ?? [RegExp(featurePath!)],
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Provide a configuration object with default settings such as the reports and feature file location
|
/// Provide a configuration object with default settings such as the reports and feature file location
|
||||||
/// Additional setting on the configuration object can be set on the returned instance.
|
/// Additional setting on the configuration object can be set on the returned instance.
|
||||||
|
// ignore: non_constant_identifier_names
|
||||||
static FlutterDriverTestConfiguration DEFAULT(
|
static FlutterDriverTestConfiguration DEFAULT(
|
||||||
Iterable<StepDefinitionGeneric<World>> steps, {
|
Iterable<StepDefinitionGeneric<World>> steps, {
|
||||||
String featurePath = 'features/*.*.feature',
|
String featurePath = 'features/*.*.feature',
|
||||||
|
@ -88,9 +89,9 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
|
||||||
/// Handy if your app is separated 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
|
||||||
final String? targetAppWorkingDirectory;
|
final String? targetAppWorkingDirectory;
|
||||||
|
|
||||||
/// The build flavor to run the tests against (optional)
|
/// The build flavour to run the tests against (optional)
|
||||||
/// Defaults to null
|
/// Defaults to null
|
||||||
final String? buildFlavor;
|
final String? buildFlavour;
|
||||||
|
|
||||||
/// The default build mode used for running tests is --debug.
|
/// The default build mode used for running tests is --debug.
|
||||||
/// We are exposing the option to run the tests also in --profile mode
|
/// We are exposing the option to run the tests also in --profile mode
|
||||||
|
@ -201,25 +202,25 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
|
||||||
final providedCreateWorld = createWorld;
|
final providedCreateWorld = createWorld;
|
||||||
|
|
||||||
return FlutterDriverTestConfiguration(
|
return FlutterDriverTestConfiguration(
|
||||||
buildFlavor: this.buildFlavor,
|
buildFlavour: buildFlavour,
|
||||||
customStepParameterDefinitions: this.customStepParameterDefinitions,
|
customStepParameterDefinitions: customStepParameterDefinitions,
|
||||||
defaultTimeout: this.defaultTimeout,
|
defaultTimeout: defaultTimeout,
|
||||||
featureDefaultLanguage: this.featureDefaultLanguage,
|
featureDefaultLanguage: featureDefaultLanguage,
|
||||||
featureFileMatcher: this.featureFileMatcher,
|
featureFileMatcher: featureFileMatcher,
|
||||||
featureFileReader: this.featureFileReader,
|
featureFileReader: featureFileReader,
|
||||||
features: this.features,
|
features: features,
|
||||||
onAfterFlutterDriverConnect: this.onAfterFlutterDriverConnect,
|
onAfterFlutterDriverConnect: onAfterFlutterDriverConnect,
|
||||||
onBeforeFlutterDriverConnect: this.onBeforeFlutterDriverConnect,
|
onBeforeFlutterDriverConnect: onBeforeFlutterDriverConnect,
|
||||||
order: this.order,
|
order: order,
|
||||||
reporters: this.reporters,
|
reporters: reporters,
|
||||||
restartAppBetweenScenarios: this.restartAppBetweenScenarios,
|
restartAppBetweenScenarios: restartAppBetweenScenarios,
|
||||||
runningAppProtocolEndpointUri: this.runningAppProtocolEndpointUri,
|
runningAppProtocolEndpointUri: runningAppProtocolEndpointUri,
|
||||||
stepDefinitions: this.stepDefinitions,
|
stepDefinitions: stepDefinitions,
|
||||||
stopAfterTestFailed: this.stopAfterTestFailed,
|
stopAfterTestFailed: stopAfterTestFailed,
|
||||||
tagExpression: this.tagExpression,
|
tagExpression: tagExpression,
|
||||||
targetAppPath: this.targetAppPath,
|
targetAppPath: targetAppPath,
|
||||||
targetAppWorkingDirectory: this.targetAppWorkingDirectory,
|
targetAppWorkingDirectory: targetAppWorkingDirectory,
|
||||||
targetDeviceId: this.targetDeviceId,
|
targetDeviceId: targetDeviceId,
|
||||||
createWorld: (config) async {
|
createWorld: (config) async {
|
||||||
FlutterWorld? world;
|
FlutterWorld? world;
|
||||||
if (providedCreateWorld != null) {
|
if (providedCreateWorld != null) {
|
||||||
|
@ -228,7 +229,10 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
|
||||||
|
|
||||||
return await createFlutterWorld(config, world);
|
return await createFlutterWorld(config, world);
|
||||||
},
|
},
|
||||||
hooks: List.from(hooks ?? Iterable.empty())..add(FlutterAppRunnerHook()),
|
hooks: List.from(hooks ?? const Iterable.empty())
|
||||||
|
..add(
|
||||||
|
FlutterAppRunnerHook(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,8 +248,9 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
|
||||||
if (attempt > maxAttempts) {
|
if (attempt > maxAttempts) {
|
||||||
throw e;
|
throw e;
|
||||||
} else {
|
} else {
|
||||||
|
// ignore: avoid_print
|
||||||
print(
|
print(
|
||||||
'Fluter driver error connecting to application at `$dartVmServiceUrl`,'
|
'Flutter driver error connecting to application at `$dartVmServiceUrl`,'
|
||||||
'retrying after delay of $flutterDriverReconnectionDelay',
|
'retrying after delay of $flutterDriverReconnectionDelay',
|
||||||
);
|
);
|
||||||
await Future<void>.delayed(flutterDriverReconnectionDelay);
|
await Future<void>.delayed(flutterDriverReconnectionDelay);
|
||||||
|
|
|
@ -12,29 +12,29 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
SwipeDirectionParameter(),
|
SwipeDirectionParameter(),
|
||||||
];
|
];
|
||||||
static final _wellKnownStepDefinitions = [
|
static final _wellKnownStepDefinitions = [
|
||||||
ThenExpectElementToHaveValue(),
|
|
||||||
WhenTapBackButtonWidget(),
|
|
||||||
WhenTapWidget(),
|
|
||||||
WhenTapWidgetWithoutScroll(),
|
|
||||||
WhenLongPressWidget(),
|
|
||||||
WhenLongPressWidgetWithoutScroll(),
|
|
||||||
WhenLongPressWidgetForDuration(),
|
|
||||||
GivenOpenDrawer(),
|
|
||||||
WhenPauseStep(),
|
|
||||||
WhenFillFieldStep(),
|
|
||||||
ThenExpectWidgetToBePresent(),
|
|
||||||
RestartAppStep(),
|
|
||||||
SiblingContainsTextStep(),
|
|
||||||
SwipeOnKeyStep(),
|
SwipeOnKeyStep(),
|
||||||
SwipeOnTextStep(),
|
SwipeOnTextStep(),
|
||||||
TapTextWithinWidgetStep(),
|
thenExpectElementToHaveValue(),
|
||||||
TapWidgetOfTypeStep(),
|
whenTapBackButtonWidget(),
|
||||||
TapWidgetOfTypeWithinStep(),
|
whenTapWidget(),
|
||||||
TapWidgetWithTextStep(),
|
whenTapWidgetWithoutScroll(),
|
||||||
TextExistsStep(),
|
whenLongPressWidget(),
|
||||||
TextExistsWithinStep(),
|
whenLongPressWidgetWithoutScroll(),
|
||||||
WaitUntilKeyExistsStep(),
|
whenLongPressWidgetForDuration(),
|
||||||
WaitUntilTypeExistsStep(),
|
givenOpenDrawer(),
|
||||||
|
whenPauseStep(),
|
||||||
|
whenFillFieldStep(),
|
||||||
|
thenExpectWidgetToBePresent(),
|
||||||
|
restartAppStep(),
|
||||||
|
siblingContainsTextStep(),
|
||||||
|
tapTextWithinWidgetStep(),
|
||||||
|
tapWidgetOfTypeStep(),
|
||||||
|
tapWidgetOfTypeWithinStep(),
|
||||||
|
tapWidgetWithTextStep(),
|
||||||
|
textExistsStep(),
|
||||||
|
textExistsWithinStep(),
|
||||||
|
waitUntilKeyExistsStep(),
|
||||||
|
waitUntilTypeExistsStep(),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Enable semantics in a test by creating a [SemanticsHandle].
|
/// Enable semantics in a test by creating a [SemanticsHandle].
|
||||||
|
@ -47,7 +47,7 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
|
|
||||||
/// Provide a configuration object with default settings such as the reports and feature file location
|
/// Provide a configuration object with default settings such as the reports and feature file location
|
||||||
/// Additional setting on the configuration object can be set on the returned instance.
|
/// Additional setting on the configuration object can be set on the returned instance.
|
||||||
static FlutterTestConfiguration DEFAULT(
|
static FlutterTestConfiguration standard(
|
||||||
Iterable<StepDefinitionGeneric<World>> steps, {
|
Iterable<StepDefinitionGeneric<World>> steps, {
|
||||||
String featurePath = 'integration_test/features/*.*.feature',
|
String featurePath = 'integration_test/features/*.*.feature',
|
||||||
String targetAppPath = 'test_driver/integration_test_driver.dart',
|
String targetAppPath = 'test_driver/integration_test_driver.dart',
|
||||||
|
@ -57,7 +57,6 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
StdoutReporter(MessageLevel.error),
|
StdoutReporter(MessageLevel.error),
|
||||||
ProgressReporter(),
|
ProgressReporter(),
|
||||||
TestRunSummaryReporter(),
|
TestRunSummaryReporter(),
|
||||||
// JsonReporter(path: './report.json'),
|
|
||||||
],
|
],
|
||||||
stepDefinitions: steps,
|
stepDefinitions: steps,
|
||||||
);
|
);
|
||||||
|
@ -80,10 +79,11 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
Iterable<CustomParameter<dynamic>>? customStepParameterDefinitions,
|
Iterable<CustomParameter<dynamic>>? customStepParameterDefinitions,
|
||||||
Iterable<StepDefinitionGeneric<World>>? stepDefinitions,
|
Iterable<StepDefinitionGeneric<World>>? stepDefinitions,
|
||||||
}) : super(
|
}) : super(
|
||||||
customStepParameterDefinitions:
|
customStepParameterDefinitions: List.from(
|
||||||
List.from(customStepParameterDefinitions ?? Iterable.empty())
|
customStepParameterDefinitions ?? const Iterable.empty(),
|
||||||
..addAll(_wellKnownParameters),
|
)..addAll(_wellKnownParameters),
|
||||||
stepDefinitions: List.from(stepDefinitions ?? Iterable.empty())
|
stepDefinitions: List.from(
|
||||||
..addAll(_wellKnownStepDefinitions),
|
stepDefinitions ?? const Iterable.empty(),
|
||||||
|
)..addAll(_wellKnownStepDefinitions),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ class FlutterAppRunnerHook extends Hook {
|
||||||
..setWorkingDirectory(config.targetAppWorkingDirectory)
|
..setWorkingDirectory(config.targetAppWorkingDirectory)
|
||||||
..setBuildRequired(haveRunFirstScenario ? false : config.build)
|
..setBuildRequired(haveRunFirstScenario ? false : config.build)
|
||||||
..setKeepAppRunning(config.keepAppRunningAfterTests)
|
..setKeepAppRunning(config.keepAppRunningAfterTests)
|
||||||
..setBuildFlavor(config.buildFlavor)
|
..setBuildFlavour(config.buildFlavour)
|
||||||
..setBuildMode(config.buildMode)
|
..setBuildMode(config.buildMode)
|
||||||
..setDeviceTargetId(config.targetDeviceId);
|
..setDeviceTargetId(config.targetDeviceId);
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,6 @@ import 'package:flutter_gherkin/src/flutter/configuration/build_mode.dart';
|
||||||
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 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
|
// 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=/`
|
||||||
// `Observatory URL on device: http://127.0.0.1:37849/t2xp9hvaxNs=/`
|
// `Observatory URL on device: http://127.0.0.1:37849/t2xp9hvaxNs=/`
|
||||||
|
@ -49,10 +45,10 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
bool _logFlutterProcessOutput = false;
|
bool _logFlutterProcessOutput = false;
|
||||||
bool _verboseFlutterLogs = false;
|
bool _verboseFlutterLogs = false;
|
||||||
bool _keepAppRunning = false;
|
bool _keepAppRunning = false;
|
||||||
BuildMode _buildMode = BuildMode.Debug;
|
BuildMode _buildMode = BuildMode.debug;
|
||||||
String? _workingDirectory;
|
String? _workingDirectory;
|
||||||
String? _appTarget;
|
String? _appTarget;
|
||||||
String? _buildFlavor;
|
String? _buildFlavour;
|
||||||
String? _deviceTargetId;
|
String? _deviceTargetId;
|
||||||
Duration _driverConnectionDelay = const Duration(seconds: 2);
|
Duration _driverConnectionDelay = const Duration(seconds: 2);
|
||||||
String? currentObservatoryUri;
|
String? currentObservatoryUri;
|
||||||
|
@ -73,8 +69,8 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
_workingDirectory = workingDirectory;
|
_workingDirectory = workingDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBuildFlavor(String? buildFlavor) {
|
void setBuildFlavour(String? buildFlavour) {
|
||||||
_buildFlavor = buildFlavor;
|
_buildFlavour = buildFlavour;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBuildMode(BuildMode buildMode) {
|
void setBuildMode(BuildMode buildMode) {
|
||||||
|
@ -101,9 +97,9 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
Future<void> run() async {
|
Future<void> run() async {
|
||||||
final arguments = ['run', '--target=$_appTarget'];
|
final arguments = ['run', '--target=$_appTarget'];
|
||||||
|
|
||||||
if (_buildMode == BuildMode.Debug) {
|
if (_buildMode == BuildMode.debug) {
|
||||||
arguments.add('--debug');
|
arguments.add('--debug');
|
||||||
} else if (_buildMode == BuildMode.Profile) {
|
} else if (_buildMode == BuildMode.profile) {
|
||||||
arguments.add('--profile');
|
arguments.add('--profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,8 +107,8 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
arguments.add('--no-build');
|
arguments.add('--no-build');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_buildFlavor != null && _buildFlavor!.isNotEmpty) {
|
if (_buildFlavour != null && _buildFlavour!.isNotEmpty) {
|
||||||
arguments.add('--flavor=$_buildFlavor');
|
arguments.add('--flavor=$_buildFlavour');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_deviceTargetId != null && _deviceTargetId!.isNotEmpty) {
|
if (_deviceTargetId != null && _deviceTargetId!.isNotEmpty) {
|
||||||
|
@ -148,11 +144,15 @@ 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(
|
||||||
|
'${StdoutReporter.kFailColor}Flutter build error: $event${StdoutReporter.kResetColor}',
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// This is most likely a deprecated api usage warnings (from Gradle) and should not
|
// This is most likely a deprecated 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(
|
||||||
|
'${StdoutReporter.kWarnColor}$event${StdoutReporter.kResetColor}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,9 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
_ensureRunningProcess();
|
_ensureRunningProcess();
|
||||||
if (_runningProcess != null) {
|
if (_runningProcess != null) {
|
||||||
_runningProcess!.stdin.write('q');
|
_runningProcess!.stdin.write('q');
|
||||||
_openSubscriptions.forEach((s) => s.cancel());
|
for (var s in _openSubscriptions) {
|
||||||
|
s.cancel();
|
||||||
|
}
|
||||||
_openSubscriptions.clear();
|
_openSubscriptions.clear();
|
||||||
exitCode = await _runningProcess!.exitCode;
|
exitCode = await _runningProcess!.exitCode;
|
||||||
_runningProcess = null;
|
_runningProcess = null;
|
||||||
|
@ -232,12 +234,17 @@ 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');
|
'${StdoutReporter.kFailColor}'
|
||||||
|
'No connected devices found to run app on and tests against'
|
||||||
|
'${StdoutReporter.kResetColor}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} 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(
|
||||||
|
'${StdoutReporter.kFailColor}$logLine${StdoutReporter.kResetColor}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,6 +5,13 @@ import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
import 'package:integration_test/integration_test.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
|
typedef StepFn = Future<StepResult> Function(
|
||||||
|
TestDependencies dependencies,
|
||||||
|
bool skip,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef StartAppFn = Future<void> Function(World world);
|
||||||
|
|
||||||
class TestDependencies {
|
class TestDependencies {
|
||||||
final World world;
|
final World world;
|
||||||
final AttachmentManager attachmentManager;
|
final AttachmentManager attachmentManager;
|
||||||
|
@ -18,25 +25,25 @@ class TestDependencies {
|
||||||
abstract class GherkinIntegrationTestRunner {
|
abstract class GherkinIntegrationTestRunner {
|
||||||
final TagExpressionEvaluator _tagExpressionEvaluator =
|
final TagExpressionEvaluator _tagExpressionEvaluator =
|
||||||
TagExpressionEvaluator();
|
TagExpressionEvaluator();
|
||||||
final TestConfiguration configuration;
|
final FlutterTestConfiguration configuration;
|
||||||
final Future<void> Function(World world) appMainFunction;
|
final StartAppFn appMainFunction;
|
||||||
AggregatedReporter _reporter = new AggregatedReporter();
|
final Timeout scenarioExecutionTimeout;
|
||||||
|
final AggregatedReporter _reporter = AggregatedReporter();
|
||||||
Hook? _hook;
|
Hook? _hook;
|
||||||
Iterable<ExecutableStep>? _executableSteps;
|
Iterable<ExecutableStep>? _executableSteps;
|
||||||
Iterable<CustomParameter>? _customParameters;
|
Iterable<CustomParameter>? _customParameters;
|
||||||
|
|
||||||
IntegrationTestWidgetsFlutterBinding? _binding;
|
late final IntegrationTestWidgetsFlutterBinding _binding;
|
||||||
|
|
||||||
AggregatedReporter get reporter => _reporter;
|
AggregatedReporter get reporter => _reporter;
|
||||||
Hook get hook => _hook!;
|
Hook get hook => _hook!;
|
||||||
LiveTestWidgetsFlutterBindingFramePolicy? get framePolicy => null;
|
LiveTestWidgetsFlutterBindingFramePolicy? get framePolicy => null;
|
||||||
|
|
||||||
Timeout scenarioExecutionTimeout = const Timeout(Duration(minutes: 10));
|
|
||||||
|
|
||||||
GherkinIntegrationTestRunner(
|
GherkinIntegrationTestRunner(
|
||||||
this.configuration,
|
this.configuration,
|
||||||
this.appMainFunction,
|
this.appMainFunction, {
|
||||||
) {
|
this.scenarioExecutionTimeout = const Timeout(Duration(minutes: 10)),
|
||||||
|
}) {
|
||||||
configuration.prepare();
|
configuration.prepare();
|
||||||
_registerReporters(configuration.reporters);
|
_registerReporters(configuration.reporters);
|
||||||
_hook = _registerHooks(configuration.hooks);
|
_hook = _registerHooks(configuration.hooks);
|
||||||
|
@ -51,7 +58,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
Future<void> run() async {
|
Future<void> run() async {
|
||||||
_binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
_binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
_binding!.framePolicy =
|
_binding.framePolicy =
|
||||||
framePolicy ?? LiveTestWidgetsFlutterBindingFramePolicy.benchmarkLive;
|
framePolicy ?? LiveTestWidgetsFlutterBindingFramePolicy.benchmarkLive;
|
||||||
|
|
||||||
tearDownAll(
|
tearDownAll(
|
||||||
|
@ -71,7 +78,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
void onRunComplete() {
|
void onRunComplete() {
|
||||||
_safeInvokeFuture(() async => await reporter.test.onFinished.maybeCall());
|
_safeInvokeFuture(() async => await reporter.test.onFinished.maybeCall());
|
||||||
_safeInvokeFuture(() async => await hook.onAfterRun(configuration));
|
_safeInvokeFuture(() async => await hook.onAfterRun(configuration));
|
||||||
setTestResultData(_binding!);
|
setTestResultData(_binding);
|
||||||
_safeInvokeFuture(() async => await reporter.dispose());
|
_safeInvokeFuture(() async => await reporter.dispose());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,27 +88,28 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void runFeature(
|
void runFeature({
|
||||||
String name,
|
required String name,
|
||||||
|
required void Function() run,
|
||||||
Iterable<String>? tags,
|
Iterable<String>? tags,
|
||||||
void Function() runFeature,
|
}) {
|
||||||
) {
|
|
||||||
group(
|
group(
|
||||||
name,
|
name,
|
||||||
() {
|
() {
|
||||||
runFeature();
|
run();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
Future<void> onBeforeRunFeature(
|
Future<void> onBeforeRunFeature({
|
||||||
String name,
|
required String name,
|
||||||
|
required String path,
|
||||||
Iterable<String>? tags,
|
Iterable<String>? tags,
|
||||||
) async {
|
}) async {
|
||||||
final debugInformation = RunnableDebugInformation('', 0, name);
|
final debugInformation = RunnableDebugInformation(path, 0, name);
|
||||||
final featureTags =
|
final featureTags =
|
||||||
(tags ?? Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0));
|
(tags ?? const Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0));
|
||||||
await reporter.feature.onStarted.maybeCall(
|
await reporter.feature.onStarted.maybeCall(
|
||||||
FeatureMessage(
|
FeatureMessage(
|
||||||
name: name,
|
name: name,
|
||||||
|
@ -112,13 +120,21 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
Future<void> onAfterRunFeature(String name, String path) async {
|
Future<void> onAfterRunFeature({
|
||||||
|
required String name,
|
||||||
|
required String path,
|
||||||
|
required List<String>? tags,
|
||||||
|
}) async {
|
||||||
final debugInformation = RunnableDebugInformation(path, 0, name);
|
final debugInformation = RunnableDebugInformation(path, 0, name);
|
||||||
await reporter.test.onFinished.maybeCall(
|
await reporter.feature.onFinished.maybeCall(
|
||||||
TestMessage(
|
FeatureMessage(
|
||||||
target: Target.feature,
|
|
||||||
name: name,
|
name: name,
|
||||||
context: debugInformation,
|
context: debugInformation,
|
||||||
|
tags: (tags ?? const Iterable<String>.empty())
|
||||||
|
.map(
|
||||||
|
(t) => Tag(t.toString(), 0),
|
||||||
|
)
|
||||||
|
.toList(growable: false),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -127,12 +143,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
void runScenario({
|
void runScenario({
|
||||||
required String name,
|
required String name,
|
||||||
required Iterable<String>? tags,
|
required Iterable<String>? tags,
|
||||||
required List<
|
required List<StepFn> steps,
|
||||||
Future<StepResult> Function(
|
|
||||||
TestDependencies dependencies,
|
|
||||||
bool skip,
|
|
||||||
)>
|
|
||||||
steps,
|
|
||||||
required String path,
|
required String path,
|
||||||
Future<void> Function()? onBefore,
|
Future<void> Function()? onBefore,
|
||||||
Future<void> Function()? onAfter,
|
Future<void> Function()? onAfter,
|
||||||
|
@ -144,11 +155,12 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
if (onBefore != null) {
|
if (onBefore != null) {
|
||||||
await onBefore();
|
await onBefore();
|
||||||
}
|
}
|
||||||
var failed = false;
|
bool failed = false;
|
||||||
|
|
||||||
final debugInformation = RunnableDebugInformation(path, 0, name);
|
final debugInformation = RunnableDebugInformation(path, 0, name);
|
||||||
final scenarioTags =
|
final scenarioTags = (tags ?? const Iterable<Tag>.empty()).map(
|
||||||
(tags ?? Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0));
|
(t) => Tag(t.toString(), 0),
|
||||||
|
);
|
||||||
final dependencies = await createTestDependencies(
|
final dependencies = await createTestDependencies(
|
||||||
configuration,
|
configuration,
|
||||||
tester,
|
tester,
|
||||||
|
@ -190,7 +202,6 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
failed = true;
|
failed = true;
|
||||||
hasToSkip = true;
|
hasToSkip = true;
|
||||||
// rethrow;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -213,13 +224,11 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
await onAfter();
|
await onAfter();
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanupScenarioRun(dependencies);
|
cleanUpScenarioRun(dependencies);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
timeout: scenarioExecutionTimeout,
|
timeout: scenarioExecutionTimeout,
|
||||||
semanticsEnabled: configuration is FlutterTestConfiguration
|
semanticsEnabled: configuration.semanticsEnabled,
|
||||||
? (configuration as FlutterTestConfiguration).semanticsEnabled
|
|
||||||
: true,
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
_safeInvokeFuture(
|
_safeInvokeFuture(
|
||||||
|
@ -237,7 +246,13 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
World world,
|
World world,
|
||||||
) async {
|
) async {
|
||||||
await appMainFunction(world);
|
await appMainFunction(world);
|
||||||
await tester.pumpAndSettle();
|
|
||||||
|
// need to pump so app is initialised
|
||||||
|
await tester.pumpAndSettle(
|
||||||
|
const Duration(milliseconds: 200),
|
||||||
|
EnginePhase.sendSemanticsUpdate,
|
||||||
|
const Duration(milliseconds: 2000),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
|
@ -259,7 +274,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
(world as FlutterWorld).setAppAdapter(
|
(world as FlutterWorld).setAppAdapter(
|
||||||
WidgetTesterAppDriverAdapter(
|
WidgetTesterAppDriverAdapter(
|
||||||
rawAdapter: tester,
|
rawAdapter: tester,
|
||||||
binding: _binding!,
|
binding: _binding,
|
||||||
waitImplicitlyAfterAction: configuration is FlutterTestConfiguration
|
waitImplicitlyAfterAction: configuration is FlutterTestConfiguration
|
||||||
? (configuration).waitImplicitlyAfterAction
|
? (configuration).waitImplicitlyAfterAction
|
||||||
: true,
|
: true,
|
||||||
|
@ -273,43 +288,46 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
Future<StepResult> runStep(
|
Future<StepResult> runStep({
|
||||||
String step,
|
required String name,
|
||||||
Iterable<String> multiLineStrings,
|
required Iterable<String> multiLineStrings,
|
||||||
dynamic table,
|
required dynamic table,
|
||||||
TestDependencies dependencies,
|
required TestDependencies dependencies,
|
||||||
bool hasToSkip,
|
required bool skip,
|
||||||
) async {
|
}) async {
|
||||||
final executable = _executableSteps!.firstWhereOrNull(
|
final executable = _executableSteps!.firstWhereOrNull(
|
||||||
(s) => s.expression.isMatch(step),
|
(s) => s.expression.isMatch(name),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (executable == null) {
|
if (executable == null) {
|
||||||
final message = 'Step definition not found for text: `$step`';
|
final message = 'Step definition not found for text: `$name`';
|
||||||
throw GherkinStepNotDefinedException(message);
|
throw GherkinStepNotDefinedException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
var parameters = _getStepParameters(
|
var parameters = _getStepParameters(
|
||||||
step,
|
step: name,
|
||||||
multiLineStrings,
|
multiLineStrings: multiLineStrings,
|
||||||
table,
|
table: table,
|
||||||
executable,
|
code: executable,
|
||||||
);
|
);
|
||||||
|
|
||||||
await _onBeforeStepRun(
|
await _onBeforeStepRun(
|
||||||
dependencies.world,
|
world: dependencies.world,
|
||||||
step,
|
step: name,
|
||||||
table,
|
table: table,
|
||||||
multiLineStrings,
|
multiLineStrings: multiLineStrings,
|
||||||
);
|
);
|
||||||
|
|
||||||
StepResult? result;
|
StepResult? result;
|
||||||
|
|
||||||
if (hasToSkip) {
|
if (skip) {
|
||||||
result = new StepResult(0, StepExecutionResult.skipped,
|
result = StepResult(
|
||||||
resultReason: "Previous step(s) failed.");
|
0,
|
||||||
|
StepExecutionResult.skipped,
|
||||||
|
resultReason: 'Previous step(s) failed',
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < this.configuration.stepMaxRetries + 1; i++) {
|
for (int i = 0; i < configuration.stepMaxRetries + 1; i++) {
|
||||||
result = await executable.step.run(
|
result = await executable.step.run(
|
||||||
dependencies.world,
|
dependencies.world,
|
||||||
reporter,
|
reporter,
|
||||||
|
@ -319,20 +337,16 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
if (!_isNegativeResult(result.result)) {
|
if (!_isNegativeResult(result.result)) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
await Future.delayed(this.configuration.retryDelay);
|
await Future.delayed(configuration.retryDelay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await _onAfterStepRun(
|
await _onAfterStepRun(
|
||||||
step,
|
name,
|
||||||
result!,
|
result!,
|
||||||
dependencies,
|
dependencies,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result is ErroredStepResult) {
|
|
||||||
// result.resultReason = result.exception.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,7 +357,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void cleanupScenarioRun(TestDependencies dependencies) {
|
void cleanUpScenarioRun(TestDependencies dependencies) {
|
||||||
_safeInvokeFuture(
|
_safeInvokeFuture(
|
||||||
() async => dependencies.attachmentManager.dispose(),
|
() async => dependencies.attachmentManager.dispose(),
|
||||||
);
|
);
|
||||||
|
@ -354,7 +368,9 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
|
|
||||||
void _registerReporters(Iterable<Reporter>? reporters) {
|
void _registerReporters(Iterable<Reporter>? reporters) {
|
||||||
if (reporters != null) {
|
if (reporters != null) {
|
||||||
reporters.forEach((r) => _reporter.addReporter(r));
|
for (var r in reporters) {
|
||||||
|
_reporter.addReporter(r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,12 +420,12 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
.toList(growable: false);
|
.toList(growable: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<dynamic> _getStepParameters(
|
Iterable<dynamic> _getStepParameters({
|
||||||
String step,
|
required String step,
|
||||||
Iterable<String> multiLineStrings,
|
required Iterable<String> multiLineStrings,
|
||||||
dynamic table,
|
required ExecutableStep code,
|
||||||
ExecutableStep code,
|
GherkinTable? table,
|
||||||
) {
|
}) {
|
||||||
var parameters = code.expression.getParameters(step);
|
var parameters = code.expression.getParameters(step);
|
||||||
if (multiLineStrings.isNotEmpty) {
|
if (multiLineStrings.isNotEmpty) {
|
||||||
parameters = parameters.toList()..addAll(multiLineStrings);
|
parameters = parameters.toList()..addAll(multiLineStrings);
|
||||||
|
@ -433,7 +449,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
result,
|
result,
|
||||||
);
|
);
|
||||||
|
|
||||||
await reporter.step.onStarted.maybeCall(
|
await reporter.step.onFinished.maybeCall(
|
||||||
StepMessage(
|
StepMessage(
|
||||||
name: step,
|
name: step,
|
||||||
context: RunnableDebugInformation('', 0, step),
|
context: RunnableDebugInformation('', 0, step),
|
||||||
|
@ -445,12 +461,12 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onBeforeStepRun(
|
Future<void> _onBeforeStepRun({
|
||||||
World world,
|
required World world,
|
||||||
String step,
|
required String step,
|
||||||
table,
|
required Iterable<String> multiLineStrings,
|
||||||
Iterable<String> multiLineStrings,
|
GherkinTable? table,
|
||||||
) async {
|
}) async {
|
||||||
await hook.onBeforeStep(world, step);
|
await hook.onBeforeStep(world, step);
|
||||||
await reporter.step.onStarted.maybeCall(
|
await reporter.step.onStarted.maybeCall(
|
||||||
StepMessage(
|
StepMessage(
|
||||||
|
@ -475,6 +491,9 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
) {
|
) {
|
||||||
return tagExpression == null || tagExpression.isEmpty
|
return tagExpression == null || tagExpression.isEmpty
|
||||||
? true
|
? true
|
||||||
: _tagExpressionEvaluator.evaluate(tagExpression, tags!.toList());
|
: _tagExpressionEvaluator.evaluate(
|
||||||
|
tagExpression,
|
||||||
|
tags!.toList(growable: false),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// Examples:
|
/// Examples:
|
||||||
///
|
///
|
||||||
/// `Given I open the drawer`
|
/// `Given I open the drawer`
|
||||||
StepDefinitionGeneric GivenOpenDrawer() {
|
StepDefinitionGeneric givenOpenDrawer() {
|
||||||
return given1<String, FlutterWorld>(
|
return given1<String, FlutterWorld>(
|
||||||
RegExp(r'I (open|close) the drawer'),
|
RegExp(r'I (open|close) the drawer'),
|
||||||
(action, context) async {
|
(action, context) async {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:flutter_gherkin/flutter_gherkin.dart';
|
import 'package:flutter_gherkin/flutter_gherkin.dart';
|
||||||
import 'package:gherkin/gherkin.dart';
|
import 'package:gherkin/gherkin.dart';
|
||||||
|
|
||||||
StepDefinitionGeneric RestartAppStep() {
|
StepDefinitionGeneric restartAppStep() {
|
||||||
return given<FlutterWorld>(
|
return given<FlutterWorld>(
|
||||||
'I restart the app',
|
'I restart the app',
|
||||||
(context) async {
|
(context) async {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// Examples:
|
/// Examples:
|
||||||
///
|
///
|
||||||
/// `Then I expect a "Row" that contains the text "X" to also contain the text "Y"`
|
/// `Then I expect a "Row" that contains the text "X" to also contain the text "Y"`
|
||||||
StepDefinitionGeneric SiblingContainsTextStep() {
|
StepDefinitionGeneric siblingContainsTextStep() {
|
||||||
return given3<String, String, String, FlutterWorld>(
|
return given3<String, String, String, FlutterWorld>(
|
||||||
'I expect a {string} that contains the text {string} to also contain the text {string}',
|
'I expect a {string} that contains the text {string} to also contain the text {string}',
|
||||||
(ancestorType, leadingText, valueText, context) async {
|
(ancestorType, leadingText, valueText, context) async {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: avoid_renaming_method_parameters
|
||||||
|
|
||||||
import 'package:flutter_gherkin/flutter_gherkin.dart';
|
import 'package:flutter_gherkin/flutter_gherkin.dart';
|
||||||
import 'package:gherkin/gherkin.dart';
|
import 'package:gherkin/gherkin.dart';
|
||||||
|
|
||||||
|
@ -17,7 +19,7 @@ mixin _SwipeHelper
|
||||||
await world.appDriver.scroll(
|
await world.appDriver.scroll(
|
||||||
finder,
|
finder,
|
||||||
dx: offset.toDouble(),
|
dx: offset.toDouble(),
|
||||||
duration: Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -27,7 +29,7 @@ mixin _SwipeHelper
|
||||||
await world.appDriver.scroll(
|
await world.appDriver.scroll(
|
||||||
finder,
|
finder,
|
||||||
dy: offset.toDouble(),
|
dy: offset.toDouble(),
|
||||||
duration: Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -49,7 +51,7 @@ class SwipeOnKeyStep
|
||||||
int swipeAmount,
|
int swipeAmount,
|
||||||
String key,
|
String key,
|
||||||
) async {
|
) async {
|
||||||
final finder = this.world.appDriver.findBy(key, FindType.key);
|
final finder = world.appDriver.findBy(key, FindType.key);
|
||||||
await swipeOnFinder(finder, direction, swipeAmount);
|
await swipeOnFinder(finder, direction, swipeAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +74,7 @@ class SwipeOnTextStep
|
||||||
int swipeAmount,
|
int swipeAmount,
|
||||||
String text,
|
String text,
|
||||||
) async {
|
) async {
|
||||||
final finder = this.world.appDriver.findBy(text, FindType.text);
|
final finder = world.appDriver.findBy(text, FindType.text);
|
||||||
await swipeOnFinder(finder, direction, swipeAmount);
|
await swipeOnFinder(finder, direction, swipeAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// Examples:
|
/// Examples:
|
||||||
///
|
///
|
||||||
/// `Then I tap the label that contains the text "Logout" within the "user_settings_list"`
|
/// `Then I tap the label that contains the text "Logout" within the "user_settings_list"`
|
||||||
StepDefinitionGeneric TapTextWithinWidgetStep() {
|
StepDefinitionGeneric tapTextWithinWidgetStep() {
|
||||||
return given2<String, String, FlutterWorld>(
|
return given2<String, String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(
|
||||||
r'I tap the (?:button|element|label|field|text|widget) that contains the text {string} within the {string}'),
|
r'I tap the (?:button|element|label|field|text|widget) that contains the text {string} within the {string}'),
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `Then I tap the element of type "MaterialButton"`
|
/// `Then I tap the element of type "MaterialButton"`
|
||||||
/// `Then I tap the label of type "ListTile"`
|
/// `Then I tap the label of type "ListTile"`
|
||||||
/// `Then I tap the field of type "TextField"`
|
/// `Then I tap the field of type "TextField"`
|
||||||
StepDefinitionGeneric TapWidgetOfTypeStep() {
|
StepDefinitionGeneric tapWidgetOfTypeStep() {
|
||||||
return given1<String, FlutterWorld>(
|
return given1<String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(
|
||||||
r'I tap the (?:button|element|label|icon|field|text|widget) of type {string}$'),
|
r'I tap the (?:button|element|label|icon|field|text|widget) of type {string}$'),
|
||||||
|
|
|
@ -6,7 +6,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// Examples:
|
/// Examples:
|
||||||
///
|
///
|
||||||
/// `Then I tap the element of type "MaterialButton" within the "user_settings_list"`
|
/// `Then I tap the element of type "MaterialButton" within the "user_settings_list"`
|
||||||
StepDefinitionGeneric TapWidgetOfTypeWithinStep() {
|
StepDefinitionGeneric tapWidgetOfTypeWithinStep() {
|
||||||
return when2<String, String, FlutterWorld>(
|
return when2<String, String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(
|
||||||
r'I tap the (?:button|element|label|icon|field|text|widget) of type {string} within the {string}$'),
|
r'I tap the (?:button|element|label|icon|field|text|widget) of type {string} within the {string}$'),
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `Then I tap the label that contains the text "Logout"`
|
/// `Then I tap the label that contains the text "Logout"`
|
||||||
/// `Then I tap the button that contains the text "Sign up"`
|
/// `Then I tap the button that contains the text "Sign up"`
|
||||||
/// `Then I tap the widget that contains the text "My User Profile"`
|
/// `Then I tap the widget that contains the text "My User Profile"`
|
||||||
StepDefinitionGeneric TapWidgetWithTextStep() {
|
StepDefinitionGeneric tapWidgetWithTextStep() {
|
||||||
return then1<String, FlutterWorld>(
|
return then1<String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(
|
||||||
r'I tap the (?:button|element|label|field|text|widget) that contains the text {string}$'),
|
r'I tap the (?:button|element|label|field|text|widget) that contains the text {string}$'),
|
||||||
|
|
|
@ -8,8 +8,8 @@ import '../parameters/existence_parameter.dart';
|
||||||
/// Examples:
|
/// Examples:
|
||||||
///
|
///
|
||||||
/// `Then I expect the text "Logout" to be present`
|
/// `Then I expect the text "Logout" to be present`
|
||||||
/// `But I expect the text "Signup" to be absent`
|
/// `But I expect the text "Sign up" to be absent`
|
||||||
StepDefinitionGeneric TextExistsStep() {
|
StepDefinitionGeneric textExistsStep() {
|
||||||
return then2<String, Existence, FlutterWorld>(
|
return then2<String, Existence, FlutterWorld>(
|
||||||
RegExp(r'I expect the text {string} to be {existence}$'),
|
RegExp(r'I expect the text {string} to be {existence}$'),
|
||||||
(text, exists, context) async {
|
(text, exists, context) async {
|
||||||
|
|
|
@ -8,8 +8,8 @@ import '../parameters/existence_parameter.dart';
|
||||||
/// Examples:
|
/// Examples:
|
||||||
///
|
///
|
||||||
/// `Then I expect the text "Logout" to be present within the "user_settings_list"`
|
/// `Then I expect the text "Logout" to be present within the "user_settings_list"`
|
||||||
/// `But I expect the text "Signup" to be absent within the "login_screen"`
|
/// `But I expect the text "Sign up" to be absent within the "login_screen"`
|
||||||
StepDefinitionGeneric TextExistsWithinStep() {
|
StepDefinitionGeneric textExistsWithinStep() {
|
||||||
return then3<String, Existence, String, FlutterWorld>(
|
return then3<String, Existence, String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(
|
||||||
r'I expect the text {string} to be {existence} within the {string}$'),
|
r'I expect the text {string} to be {existence} within the {string}$'),
|
||||||
|
|
|
@ -12,7 +12,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
///
|
///
|
||||||
/// `Then I expect the "controlKey" to be "Hello World"`
|
/// `Then I expect the "controlKey" to be "Hello World"`
|
||||||
/// `And I expect the "controlKey" to be "Hello World"`
|
/// `And I expect the "controlKey" to be "Hello World"`
|
||||||
StepDefinitionGeneric ThenExpectElementToHaveValue() {
|
StepDefinitionGeneric thenExpectElementToHaveValue() {
|
||||||
return given2<String, String, FlutterWorld>(
|
return given2<String, String, FlutterWorld>(
|
||||||
RegExp(r'I expect the {string} to be {string}$'),
|
RegExp(r'I expect the {string} to be {string}$'),
|
||||||
(key, value, context) async {
|
(key, value, context) async {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
///
|
///
|
||||||
/// `Then I expect the widget 'notification' to be present within 10 seconds`
|
/// `Then I expect the widget 'notification' to be present within 10 seconds`
|
||||||
/// `Then I expect the button 'save' to be present within 1 second`
|
/// `Then I expect the button 'save' to be present within 1 second`
|
||||||
StepDefinitionGeneric ThenExpectWidgetToBePresent() {
|
StepDefinitionGeneric thenExpectWidgetToBePresent() {
|
||||||
return given2<String, int, FlutterWorld>(
|
return given2<String, int, FlutterWorld>(
|
||||||
RegExp(
|
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)$'),
|
||||||
|
|
|
@ -9,7 +9,7 @@ import '../parameters/existence_parameter.dart';
|
||||||
///
|
///
|
||||||
/// `Then I wait until the "login_loading_indicator" is absent`
|
/// `Then I wait until the "login_loading_indicator" is absent`
|
||||||
/// `And I wait until the "login_screen" is present`
|
/// `And I wait until the "login_screen" is present`
|
||||||
StepDefinitionGeneric WaitUntilKeyExistsStep() {
|
StepDefinitionGeneric waitUntilKeyExistsStep() {
|
||||||
return then2<String, Existence, FlutterWorld>(
|
return then2<String, Existence, FlutterWorld>(
|
||||||
'I wait until the {string} is {existence}',
|
'I wait until the {string} is {existence}',
|
||||||
(keyString, existence, context) async {
|
(keyString, existence, context) async {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import '../parameters/existence_parameter.dart';
|
||||||
///
|
///
|
||||||
/// `Then I wait until the element of type "ProgressIndicator" is absent`
|
/// `Then I wait until the element of type "ProgressIndicator" is absent`
|
||||||
/// `And I wait until the button of type the "MaterialButton" is present`
|
/// `And I wait until the button of type the "MaterialButton" is present`
|
||||||
StepDefinitionGeneric WaitUntilTypeExistsStep() {
|
StepDefinitionGeneric waitUntilTypeExistsStep() {
|
||||||
return then2<String, Existence, FlutterWorld>(
|
return then2<String, Existence, FlutterWorld>(
|
||||||
'I wait until the (?:button|element|label|icon|field|text|widget) of type {string} is {existence}',
|
'I wait until the (?:button|element|label|icon|field|text|widget) of type {string} is {existence}',
|
||||||
(ofType, existence, context) async {
|
(ofType, existence, context) async {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// Examples:
|
/// Examples:
|
||||||
/// Then I fill the "email" field with "bob@gmail.com"
|
/// Then I fill the "email" field with "bob@gmail.com"
|
||||||
/// Then I fill the "name" field with "Woody Johnson"
|
/// Then I fill the "name" field with "Woody Johnson"
|
||||||
StepDefinitionGeneric WhenFillFieldStep() {
|
StepDefinitionGeneric whenFillFieldStep() {
|
||||||
return given2<String, String, FlutterWorld>(
|
return given2<String, String, FlutterWorld>(
|
||||||
'I fill the {string} field with {string}',
|
'I fill the {string} field with {string}',
|
||||||
(key, value, context) async {
|
(key, value, context) async {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `When I long press "controlKey" field`
|
/// `When I long press "controlKey" field`
|
||||||
/// `When I long press "controlKey" text`
|
/// `When I long press "controlKey" text`
|
||||||
/// `When I long press "controlKey" widget`
|
/// `When I long press "controlKey" widget`
|
||||||
StepDefinitionGeneric WhenLongPressWidget() {
|
StepDefinitionGeneric whenLongPressWidget() {
|
||||||
return when1<String, FlutterWorld>(
|
return when1<String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(
|
||||||
r'I long press the {string} (?:button|element|label|icon|field|text|widget)$'),
|
r'I long press the {string} (?:button|element|label|icon|field|text|widget)$'),
|
||||||
|
@ -31,7 +31,7 @@ StepDefinitionGeneric WhenLongPressWidget() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Long presses the widget found with the given control key, without scrolling into view
|
/// Long presses the widget found with the given control key, without scrolling into view
|
||||||
StepDefinitionGeneric WhenLongPressWidgetWithoutScroll() {
|
StepDefinitionGeneric whenLongPressWidgetWithoutScroll() {
|
||||||
return when1<String, FlutterWorld>(
|
return when1<String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(
|
||||||
r'I long press the {string} (?:button|element|label|icon|field|text|widget) without scrolling it into view$'),
|
r'I long press the {string} (?:button|element|label|icon|field|text|widget) without scrolling it into view$'),
|
||||||
|
@ -46,7 +46,7 @@ StepDefinitionGeneric WhenLongPressWidgetWithoutScroll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Long presses the widget found with the given control key, for the given duration
|
/// Long presses the widget found with the given control key, for the given duration
|
||||||
StepDefinitionGeneric WhenLongPressWidgetForDuration() {
|
StepDefinitionGeneric whenLongPressWidgetForDuration() {
|
||||||
return when2<String, int, FlutterWorld>(
|
return when2<String, int, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(
|
||||||
r'I long press the {string} (?:button|element|label|icon|field|text|widget) for {int} milliseconds$'),
|
r'I long press the {string} (?:button|element|label|icon|field|text|widget) for {int} milliseconds$'),
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// Examples:
|
/// Examples:
|
||||||
/// When I pause for 10 seconds
|
/// When I pause for 10 seconds
|
||||||
/// When I pause for 120 seconds
|
/// When I pause for 120 seconds
|
||||||
StepDefinitionGeneric WhenPauseStep() {
|
StepDefinitionGeneric whenPauseStep() {
|
||||||
return when1<int, FlutterWorld>(
|
return when1<int, FlutterWorld>(
|
||||||
'I (?:pause|wait) for {int} second(?:s)?',
|
'I (?:pause|wait) for {int} second(?:s)?',
|
||||||
(wait, context) async {
|
(wait, context) async {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `When I tap the back button"`
|
/// `When I tap the back button"`
|
||||||
/// `When I tap the back element"`
|
/// `When I tap the back element"`
|
||||||
/// `When I tap the back widget"`
|
/// `When I tap the back widget"`
|
||||||
StepDefinitionGeneric WhenTapBackButtonWidget() {
|
StepDefinitionGeneric whenTapBackButtonWidget() {
|
||||||
return when<FlutterWorld>(
|
return when<FlutterWorld>(
|
||||||
RegExp(r'I tap the back (?:button|element|widget|icon|text)$'),
|
RegExp(r'I tap the back (?:button|element|widget|icon|text)$'),
|
||||||
(context) async {
|
(context) async {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `When I tap "controlKey" field"`
|
/// `When I tap "controlKey" field"`
|
||||||
/// `When I tap "controlKey" text"`
|
/// `When I tap "controlKey" text"`
|
||||||
/// `When I tap "controlKey" widget"`
|
/// `When I tap "controlKey" widget"`
|
||||||
StepDefinitionGeneric WhenTapWidget() {
|
StepDefinitionGeneric whenTapWidget() {
|
||||||
return when1<String, FlutterWorld>(
|
return when1<String, FlutterWorld>(
|
||||||
RegExp(
|
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)$'),
|
||||||
|
@ -37,7 +37,7 @@ StepDefinitionGeneric WhenTapWidget() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
StepDefinitionGeneric WhenTapWidgetWithoutScroll() {
|
StepDefinitionGeneric whenTapWidgetWithoutScroll() {
|
||||||
return when1<String, FlutterWorld>(
|
return when1<String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(
|
||||||
r'I tap the {string} (?:button|element|label|icon|field|text|widget) without scrolling it into view$'),
|
r'I tap the {string} (?:button|element|label|icon|field|text|widget) without scrolling it into view$'),
|
||||||
|
|
|
@ -47,14 +47,11 @@ class FlutterDriverWorld extends FlutterTypedAdapterWorld<FlutterDriver,
|
||||||
Future<void> _closeDriver({
|
Future<void> _closeDriver({
|
||||||
Duration? timeout = const Duration(seconds: 60),
|
Duration? timeout = const Duration(seconds: 60),
|
||||||
}) async {
|
}) async {
|
||||||
// ignore: unnecessary_null_comparison
|
await rawAppDriver.close().catchError(
|
||||||
if (rawAppDriver != null) {
|
(e, st) {
|
||||||
await rawAppDriver.close().catchError(
|
// Avoid an unhandled error
|
||||||
(e, st) {
|
return null;
|
||||||
// Avoid an unhandled error
|
},
|
||||||
return null;
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
CALL flutter analyze
|
CALL flutter analyze
|
||||||
CALL "C:\Google\flutter\bin\cache\dart-sdk\bin\dartfmt" . -w
|
CALL dart format . --fix
|
||||||
CALL flutter packages upgrade
|
CALL flutter test
|
||||||
CALL flutter test --no-sound-null-safety
|
|
67
pubspec.lock
67
pubspec.lock
|
@ -7,14 +7,28 @@ packages:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "40.0.0"
|
version: "39.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.0"
|
version: "4.0.0"
|
||||||
|
analyzer_plugin:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: analyzer_plugin
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.10.0"
|
||||||
|
ansicolor:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ansicolor
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -106,6 +120,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.2"
|
||||||
|
dart_code_metrics:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: dart_code_metrics
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.15.2"
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -137,6 +165,13 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_lints:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_lints
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -161,6 +196,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.0"
|
||||||
integration_test:
|
integration_test:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -173,6 +215,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.0"
|
version: "4.5.0"
|
||||||
|
lints:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lints
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -222,6 +271,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.1"
|
version: "1.11.1"
|
||||||
|
petitparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: petitparser
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -353,6 +409,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
|
xml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xml
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.0"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -27,5 +27,7 @@ dev_dependencies:
|
||||||
meta: '>=1.7.0 < 2.0.0'
|
meta: '>=1.7.0 < 2.0.0'
|
||||||
pedantic: ^1.11.1
|
pedantic: ^1.11.1
|
||||||
build_config: ^1.0.0
|
build_config: ^1.0.0
|
||||||
|
flutter_lints: ^2.0.1
|
||||||
|
dart_code_metrics: ^4.15.2
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
Loading…
Reference in New Issue