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": [
|
||||
"Errored",
|
||||
"Serializable",
|
||||
"agnostically",
|
||||
"analyzer",
|
||||
"dialog",
|
||||
"Errored",
|
||||
"Flavor",
|
||||
"microtask",
|
||||
"multiline",
|
||||
"pubspec",
|
||||
"rxdart",
|
||||
"scrollable",
|
||||
"Serializable",
|
||||
"todos",
|
||||
"writeln"
|
||||
]
|
||||
],
|
||||
"cSpell.language": "en-GB"
|
||||
}
|
|
@ -4,8 +4,10 @@
|
|||
- 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`
|
||||
- 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
|
||||
|
||||
|
|
|
@ -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')],
|
||||
targetAppPath: 'test_driver/app.dart',
|
||||
targetAppWorkingDirectory: '../',
|
||||
buildFlavor:
|
||||
buildFlavour:
|
||||
"staging", // uncomment when using build flavor and check android/ios flavor setup see android file android\app\build.gradle
|
||||
targetDeviceId:
|
||||
"all", // uncomment to run tests on all connected devices or set specific device target id
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
@tag
|
||||
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
|
||||
Given I fill the "todo" field with "Buy carrots"
|
||||
When I tap the "add" button
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
@tag
|
||||
Feature: Swiping
|
||||
|
||||
@debug
|
||||
Scenario: User can swipe cards left and right
|
||||
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"`
|
||||
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';
|
||||
|
||||
FlutterTestConfiguration gherkinTestConfiguration = FlutterTestConfiguration(
|
||||
tagExpression: '@debug',
|
||||
// tagExpression: '@debug',
|
||||
stepDefinitions: [
|
||||
thenIExpectTheTodos,
|
||||
whenAnAnimationIsAwaited,
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:gherkin/gherkin.dart';
|
|||
final givenTheData = given1(
|
||||
'I have item with data',
|
||||
(jsonString, context) async {
|
||||
print(jsonString);
|
||||
// print(jsonString);
|
||||
},
|
||||
configuration: StepDefinitionConfiguration()
|
||||
..timeout = const Duration(seconds: 5),
|
||||
|
|
|
@ -8,7 +8,7 @@ part of 'gherkin_suite_test.dart';
|
|||
|
||||
class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||
_CustomGherkinIntegrationTestRunner(
|
||||
TestConfiguration configuration,
|
||||
FlutterTestConfiguration configuration,
|
||||
Future<void> Function(World) appMainFunction,
|
||||
) : super(configuration, appMainFunction);
|
||||
|
||||
|
@ -21,58 +21,78 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
|||
|
||||
void testFeature0() {
|
||||
runFeature(
|
||||
'Swiping:',
|
||||
<String>['@tag'],
|
||||
() {
|
||||
name: 'Swiping:',
|
||||
tags: <String>['@tag'],
|
||||
run: () {
|
||||
runScenario(
|
||||
name: 'User can swipe cards left and right',
|
||||
path:
|
||||
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\swiping.feature',
|
||||
tags: <String>['@tag'],
|
||||
'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\swiping.feature',
|
||||
tags: <String>['@tag', '@debug'],
|
||||
steps: [
|
||||
(TestDependencies dependencies, bool hasToSkip) async {
|
||||
(
|
||||
TestDependencies dependencies,
|
||||
bool skip,
|
||||
) async {
|
||||
return await runStep(
|
||||
'Given I swipe right by 250 pixels on the "scrollable cards"`',
|
||||
<String>[],
|
||||
null,
|
||||
dependencies,
|
||||
hasToSkip,
|
||||
name:
|
||||
'Given I swipe right by 250 pixels on the "scrollable cards"`',
|
||||
multiLineStrings: <String>[],
|
||||
table: null,
|
||||
dependencies: dependencies,
|
||||
skip: skip,
|
||||
);
|
||||
},
|
||||
(TestDependencies dependencies, bool hasToSkip) async {
|
||||
(
|
||||
TestDependencies dependencies,
|
||||
bool skip,
|
||||
) async {
|
||||
return await runStep(
|
||||
'Then Then I expect the text "Page 2" to be present',
|
||||
<String>[],
|
||||
null,
|
||||
dependencies,
|
||||
hasToSkip,
|
||||
name: 'Then I expect the text "Page 2" to be present',
|
||||
multiLineStrings: <String>[],
|
||||
table: null,
|
||||
dependencies: dependencies,
|
||||
skip: skip,
|
||||
);
|
||||
},
|
||||
(TestDependencies dependencies, bool hasToSkip) async {
|
||||
(
|
||||
TestDependencies dependencies,
|
||||
bool skip,
|
||||
) async {
|
||||
return await runStep(
|
||||
'Given I swipe left by 250 pixels on the "scrollable cards"`',
|
||||
<String>[],
|
||||
null,
|
||||
dependencies,
|
||||
hasToSkip,
|
||||
name:
|
||||
'Given I swipe left by 250 pixels on the "scrollable cards"`',
|
||||
multiLineStrings: <String>[],
|
||||
table: null,
|
||||
dependencies: dependencies,
|
||||
skip: skip,
|
||||
);
|
||||
},
|
||||
(TestDependencies dependencies, bool hasToSkip) async {
|
||||
(
|
||||
TestDependencies dependencies,
|
||||
bool skip,
|
||||
) async {
|
||||
return await runStep(
|
||||
'Then Then I expect the text "Page 1" to be present',
|
||||
<String>[],
|
||||
null,
|
||||
dependencies,
|
||||
hasToSkip,
|
||||
name: 'Then I expect the text "Page 1" to be present',
|
||||
multiLineStrings: <String>[],
|
||||
table: null,
|
||||
dependencies: dependencies,
|
||||
skip: skip,
|
||||
);
|
||||
}
|
||||
},
|
||||
],
|
||||
onBefore: () async => onBeforeRunFeature(
|
||||
'Swiping',
|
||||
<String>['@tag'],
|
||||
name: 'Swiping',
|
||||
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() {
|
||||
runFeature(
|
||||
'Creating todos:',
|
||||
<String>['@tag'],
|
||||
() {
|
||||
name: 'Checking data:',
|
||||
tags: <String>['@tag'],
|
||||
run: () {
|
||||
runScenario(
|
||||
name: 'User can create multiple new todo items',
|
||||
name: 'User can have data',
|
||||
path:
|
||||
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\create.feature',
|
||||
tags: <String>['@tag', '@debug'],
|
||||
'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\check.feature',
|
||||
tags: <String>['@tag', '@tag1'],
|
||||
steps: [
|
||||
(TestDependencies dependencies, bool hasToSkip) async {
|
||||
(
|
||||
TestDependencies dependencies,
|
||||
bool skip,
|
||||
) async {
|
||||
return await runStep(
|
||||
'Given I fill the "todo" field with "Buy carrots"',
|
||||
<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>[
|
||||
name: 'Given I have item with data',
|
||||
multiLineStrings: <String>[
|
||||
"""{
|
||||
"glossary": {
|
||||
"title": "example glossary",
|
||||
|
@ -192,18 +142,24 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
|||
}
|
||||
}"""
|
||||
],
|
||||
null,
|
||||
dependencies,
|
||||
hasToSkip,
|
||||
table: null,
|
||||
dependencies: dependencies,
|
||||
skip: skip,
|
||||
);
|
||||
}
|
||||
},
|
||||
],
|
||||
onBefore: () async => onBeforeRunFeature(
|
||||
'Creating todos',
|
||||
<String>['@tag'],
|
||||
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(
|
||||
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() {
|
||||
runFeature(
|
||||
'Checking data:',
|
||||
<String>['@tag'],
|
||||
() {
|
||||
name: 'Creating todos:',
|
||||
tags: <String>['@tag'],
|
||||
run: () {
|
||||
runScenario(
|
||||
name: 'User can have data',
|
||||
name: 'User can create single todo item',
|
||||
path:
|
||||
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\check.feature',
|
||||
tags: <String>['@tag', '@tag1'],
|
||||
'C:\\Development\\github\\flutter_gherkin\\example_with_integration_test\\.\\integration_test\\features\\create.feature',
|
||||
tags: <String>['@tag'],
|
||||
steps: [
|
||||
(TestDependencies dependencies, bool hasToSkip) async {
|
||||
(
|
||||
TestDependencies dependencies,
|
||||
bool skip,
|
||||
) async {
|
||||
return await runStep(
|
||||
'Given I have item with data',
|
||||
<String>[
|
||||
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,
|
||||
);
|
||||
},
|
||||
],
|
||||
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": {
|
||||
"title": "example glossary",
|
||||
|
@ -250,18 +393,18 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
|||
}
|
||||
}"""
|
||||
],
|
||||
null,
|
||||
dependencies,
|
||||
hasToSkip,
|
||||
table: null,
|
||||
dependencies: dependencies,
|
||||
skip: skip,
|
||||
);
|
||||
}
|
||||
},
|
||||
],
|
||||
onBefore: () async => onBeforeRunFeature(
|
||||
'Checking data',
|
||||
<String>['@tag'],
|
||||
onAfter: () async => onAfterRunFeature(
|
||||
name: 'Creating todos',
|
||||
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(
|
||||
TestConfiguration configuration,
|
||||
FlutterTestConfiguration configuration,
|
||||
Future<void> Function(World) appMainFunction,
|
||||
) {
|
||||
_CustomGherkinIntegrationTestRunner(configuration, appMainFunction).run();
|
||||
|
|
|
@ -154,9 +154,9 @@ class FlutterDriverAppDriverAdapter
|
|||
@override
|
||||
SerializableFinder findBy(
|
||||
dynamic data,
|
||||
FindType type,
|
||||
FindType findType,
|
||||
) {
|
||||
switch (type) {
|
||||
switch (findType) {
|
||||
case FindType.key:
|
||||
return find.byValueKey(data.toString());
|
||||
case FindType.text:
|
||||
|
|
|
@ -39,8 +39,9 @@ class WidgetTesterAppDriverAdapter
|
|||
Future<void> _implicitWait({
|
||||
Duration? duration = const Duration(milliseconds: 100),
|
||||
Duration? timeout = const Duration(seconds: 30),
|
||||
bool? force,
|
||||
}) async {
|
||||
if (waitImplicitlyAfterAction) {
|
||||
if (waitImplicitlyAfterAction || force == true) {
|
||||
try {
|
||||
await nativeDriver.pumpAndSettle(
|
||||
duration ?? const Duration(milliseconds: 100),
|
||||
|
@ -154,35 +155,16 @@ class WidgetTesterAppDriverAdapter
|
|||
duration: pressDuration,
|
||||
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
|
||||
Finder findBy(
|
||||
dynamic data,
|
||||
FindType type,
|
||||
FindType findType,
|
||||
) {
|
||||
switch (type) {
|
||||
switch (findType) {
|
||||
case FindType.key:
|
||||
return find.byKey(data is Key ? data : Key(data));
|
||||
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
|
||||
Future<void> scrollUntilVisible(
|
||||
Finder item, {
|
||||
|
@ -235,6 +242,12 @@ class WidgetTesterAppDriverAdapter
|
|||
dy ?? dx ?? 0,
|
||||
scrollable: scrollable,
|
||||
);
|
||||
|
||||
// must force a pump and settle to ensure the scroll is performed
|
||||
await _implicitWait(
|
||||
timeout: timeout,
|
||||
force: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -244,6 +257,12 @@ class WidgetTesterAppDriverAdapter
|
|||
}) async {
|
||||
await nativeDriver.ensureVisible(finder);
|
||||
await waitForAppToSettle();
|
||||
|
||||
// must force a pump and settle to ensure the scroll is performed
|
||||
await _implicitWait(
|
||||
timeout: timeout,
|
||||
force: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:io';
|
||||
|
||||
// ignore: implementation_imports
|
||||
import 'package:build/src/builder/build_step.dart';
|
||||
import 'package:analyzer/dart/element/element.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
|
||||
Future<void> message(String message, MessageLevel level) async {
|
||||
if (level == MessageLevel.info || level == MessageLevel.debug) {
|
||||
// ignore: avoid_print
|
||||
print(message);
|
||||
} else if (level == MessageLevel.warning) {
|
||||
// ignore: avoid_print
|
||||
print('\x1B[33m$message\x1B[0m');
|
||||
} else if (level == MessageLevel.error) {
|
||||
// ignore: avoid_print
|
||||
print('\x1B[31m$message\x1B[0m');
|
||||
}
|
||||
}
|
||||
|
@ -23,10 +27,10 @@ class NoOpReporter extends MessageReporter {
|
|||
|
||||
class GherkinSuiteTestGenerator
|
||||
extends GeneratorForAnnotation<GherkinTestSuite> {
|
||||
static const String TEMPLATE = '''
|
||||
static const String template = '''
|
||||
class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||
_CustomGherkinIntegrationTestRunner(
|
||||
TestConfiguration configuration,
|
||||
FlutterTestConfiguration configuration,
|
||||
Future<void> Function(World) appMainFunction,
|
||||
) : super(configuration, appMainFunction);
|
||||
|
||||
|
@ -39,7 +43,7 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
|||
}
|
||||
|
||||
void executeTestSuite(
|
||||
TestConfiguration configuration,
|
||||
FlutterTestConfiguration configuration,
|
||||
Future<void> Function(World) appMainFunction,
|
||||
) {
|
||||
_CustomGherkinIntegrationTestRunner(configuration, appMainFunction).run();
|
||||
|
@ -77,7 +81,7 @@ void executeTestSuite(
|
|||
|
||||
final featureExecutionFunctionsBuilder = StringBuffer();
|
||||
final generator = FeatureFileTestGenerator();
|
||||
final featuresToExecute = new StringBuffer();
|
||||
final featuresToExecute = StringBuffer();
|
||||
var id = 0;
|
||||
|
||||
for (var featureFile in featureFiles) {
|
||||
|
@ -95,7 +99,7 @@ void executeTestSuite(
|
|||
}
|
||||
}
|
||||
|
||||
return TEMPLATE
|
||||
return template
|
||||
.replaceAll('{{feature_functions}}',
|
||||
featureExecutionFunctionsBuilder.toString())
|
||||
.replaceAll(
|
||||
|
@ -126,44 +130,42 @@ class FeatureFileTestGenerator {
|
|||
}
|
||||
|
||||
class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
||||
static const String FUNCTION_TEMPLATE = '''
|
||||
static const String functionTemplate = '''
|
||||
void testFeature{{feature_number}}() {
|
||||
runFeature(
|
||||
'{{feature_name}}:',
|
||||
{{tags}},
|
||||
() {
|
||||
name: '{{feature_name}}:',
|
||||
tags: {{tags}},
|
||||
run: () {
|
||||
{{scenarios}}
|
||||
},
|
||||
);
|
||||
}
|
||||
''';
|
||||
static const String SCENARIO_TEMPLATE = '''
|
||||
static const String scenarioTemplate = '''
|
||||
runScenario(
|
||||
name: '{{scenario_name}}',
|
||||
path: '{{path}}',
|
||||
tags:{{tags}},
|
||||
steps: [
|
||||
{{steps}}
|
||||
],
|
||||
steps: [{{steps}},],
|
||||
{{onBefore}}
|
||||
{{onAfter}}
|
||||
);
|
||||
''';
|
||||
static const String STEP_TEMPLATE = '''
|
||||
(TestDependencies dependencies, bool hasToSkip) async {
|
||||
static const String stepTemplate = '''
|
||||
(TestDependencies dependencies, bool skip,) async {
|
||||
return await runStep(
|
||||
'{{step_name}}',
|
||||
{{step_multi_line_strings}},
|
||||
{{step_table}},
|
||||
dependencies,
|
||||
hasToSkip,
|
||||
name: '{{step_name}}',
|
||||
multiLineStrings: {{step_multi_line_strings}},
|
||||
table: {{step_table}},
|
||||
dependencies: dependencies,
|
||||
skip: skip,
|
||||
);}
|
||||
''';
|
||||
static const String ON_BEFORE_SCENARIO_RUN = '''
|
||||
onBefore: () async => onBeforeRunFeature('{{feature_name}}', {{feature_tags}},),
|
||||
static const String onBeforeScenarioRun = '''
|
||||
onBefore: () async => onBeforeRunFeature(name:'{{feature_name}}', path:r'{{path}}', tags:{{feature_tags}},),
|
||||
''';
|
||||
static const String ON_AFTER_SCENARIO_RUN = '''
|
||||
onAfter: () async => onAfterRunFeature('{{feature_name}}', '{{path}}'),
|
||||
static const String onAfterScenarioRun = '''
|
||||
onAfter: () async => onAfterRunFeature(name:'{{feature_name}}', path:r'{{path}}', tags:{{feature_tags}},),
|
||||
''';
|
||||
|
||||
final StringBuffer _buffer = StringBuffer();
|
||||
|
@ -204,7 +206,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
|||
) async {
|
||||
if (childScenarioCount > 0) {
|
||||
_currentFeatureCode = _replaceVariable(
|
||||
FUNCTION_TEMPLATE,
|
||||
functionTemplate,
|
||||
'feature_number',
|
||||
_id.toString(),
|
||||
);
|
||||
|
@ -227,14 +229,14 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
|||
{required bool isFirst, required bool isLast}) async {
|
||||
_flushScenario();
|
||||
_currentScenarioCode = _replaceVariable(
|
||||
SCENARIO_TEMPLATE,
|
||||
scenarioTemplate,
|
||||
'onBefore',
|
||||
isFirst ? ON_BEFORE_SCENARIO_RUN : '',
|
||||
isFirst ? onBeforeScenarioRun : '',
|
||||
);
|
||||
_currentScenarioCode = _replaceVariable(
|
||||
_currentScenarioCode!,
|
||||
'onAfter',
|
||||
isLast ? ON_AFTER_SCENARIO_RUN : '',
|
||||
isLast ? onAfterScenarioRun : '',
|
||||
);
|
||||
_currentScenarioCode = _replaceVariable(
|
||||
_currentScenarioCode!,
|
||||
|
@ -270,7 +272,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
|||
GherkinTable? table,
|
||||
) async {
|
||||
var code = _replaceVariable(
|
||||
STEP_TEMPLATE,
|
||||
stepTemplate,
|
||||
'step_name',
|
||||
_escapeText(name),
|
||||
);
|
||||
|
@ -326,5 +328,6 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
|||
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,
|
||||
this.targetAppPath = 'test_driver/app.dart',
|
||||
this.targetAppWorkingDirectory,
|
||||
this.buildFlavor,
|
||||
this.buildFlavour,
|
||||
this.targetDeviceId,
|
||||
this.runningAppProtocolEndpointUri,
|
||||
this.onBeforeFlutterDriverConnect,
|
||||
|
@ -36,18 +36,19 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
|
|||
this.keepAppRunningAfterTests = false,
|
||||
this.verboseFlutterProcessLogs = false,
|
||||
this.build = true,
|
||||
this.buildMode = BuildMode.Debug,
|
||||
this.buildMode = BuildMode.debug,
|
||||
this.flutterBuildTimeout = const Duration(seconds: 90),
|
||||
this.flutterDriverReconnectionDelay = const Duration(seconds: 2),
|
||||
this.flutterDriverMaxConnectionAttempts = 3,
|
||||
}) :
|
||||
// assert(featurePath != null && features != null),
|
||||
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
|
||||
/// Additional setting on the configuration object can be set on the returned instance.
|
||||
// ignore: non_constant_identifier_names
|
||||
static FlutterDriverTestConfiguration DEFAULT(
|
||||
Iterable<StepDefinitionGeneric<World>> steps, {
|
||||
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
|
||||
final String? targetAppWorkingDirectory;
|
||||
|
||||
/// The build flavor to run the tests against (optional)
|
||||
/// The build flavour to run the tests against (optional)
|
||||
/// Defaults to null
|
||||
final String? buildFlavor;
|
||||
final String? buildFlavour;
|
||||
|
||||
/// The default build mode used for running tests is --debug.
|
||||
/// We are exposing the option to run the tests also in --profile mode
|
||||
|
@ -201,25 +202,25 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
|
|||
final providedCreateWorld = createWorld;
|
||||
|
||||
return FlutterDriverTestConfiguration(
|
||||
buildFlavor: this.buildFlavor,
|
||||
customStepParameterDefinitions: this.customStepParameterDefinitions,
|
||||
defaultTimeout: this.defaultTimeout,
|
||||
featureDefaultLanguage: this.featureDefaultLanguage,
|
||||
featureFileMatcher: this.featureFileMatcher,
|
||||
featureFileReader: this.featureFileReader,
|
||||
features: this.features,
|
||||
onAfterFlutterDriverConnect: this.onAfterFlutterDriverConnect,
|
||||
onBeforeFlutterDriverConnect: this.onBeforeFlutterDriverConnect,
|
||||
order: this.order,
|
||||
reporters: this.reporters,
|
||||
restartAppBetweenScenarios: this.restartAppBetweenScenarios,
|
||||
runningAppProtocolEndpointUri: this.runningAppProtocolEndpointUri,
|
||||
stepDefinitions: this.stepDefinitions,
|
||||
stopAfterTestFailed: this.stopAfterTestFailed,
|
||||
tagExpression: this.tagExpression,
|
||||
targetAppPath: this.targetAppPath,
|
||||
targetAppWorkingDirectory: this.targetAppWorkingDirectory,
|
||||
targetDeviceId: this.targetDeviceId,
|
||||
buildFlavour: buildFlavour,
|
||||
customStepParameterDefinitions: customStepParameterDefinitions,
|
||||
defaultTimeout: defaultTimeout,
|
||||
featureDefaultLanguage: featureDefaultLanguage,
|
||||
featureFileMatcher: featureFileMatcher,
|
||||
featureFileReader: featureFileReader,
|
||||
features: features,
|
||||
onAfterFlutterDriverConnect: onAfterFlutterDriverConnect,
|
||||
onBeforeFlutterDriverConnect: onBeforeFlutterDriverConnect,
|
||||
order: order,
|
||||
reporters: reporters,
|
||||
restartAppBetweenScenarios: restartAppBetweenScenarios,
|
||||
runningAppProtocolEndpointUri: runningAppProtocolEndpointUri,
|
||||
stepDefinitions: stepDefinitions,
|
||||
stopAfterTestFailed: stopAfterTestFailed,
|
||||
tagExpression: tagExpression,
|
||||
targetAppPath: targetAppPath,
|
||||
targetAppWorkingDirectory: targetAppWorkingDirectory,
|
||||
targetDeviceId: targetDeviceId,
|
||||
createWorld: (config) async {
|
||||
FlutterWorld? world;
|
||||
if (providedCreateWorld != null) {
|
||||
|
@ -228,7 +229,10 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
|
|||
|
||||
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) {
|
||||
throw e;
|
||||
} else {
|
||||
// ignore: avoid_print
|
||||
print(
|
||||
'Fluter driver error connecting to application at `$dartVmServiceUrl`,'
|
||||
'Flutter driver error connecting to application at `$dartVmServiceUrl`,'
|
||||
'retrying after delay of $flutterDriverReconnectionDelay',
|
||||
);
|
||||
await Future<void>.delayed(flutterDriverReconnectionDelay);
|
||||
|
|
|
@ -12,29 +12,29 @@ class FlutterTestConfiguration extends TestConfiguration {
|
|||
SwipeDirectionParameter(),
|
||||
];
|
||||
static final _wellKnownStepDefinitions = [
|
||||
ThenExpectElementToHaveValue(),
|
||||
WhenTapBackButtonWidget(),
|
||||
WhenTapWidget(),
|
||||
WhenTapWidgetWithoutScroll(),
|
||||
WhenLongPressWidget(),
|
||||
WhenLongPressWidgetWithoutScroll(),
|
||||
WhenLongPressWidgetForDuration(),
|
||||
GivenOpenDrawer(),
|
||||
WhenPauseStep(),
|
||||
WhenFillFieldStep(),
|
||||
ThenExpectWidgetToBePresent(),
|
||||
RestartAppStep(),
|
||||
SiblingContainsTextStep(),
|
||||
SwipeOnKeyStep(),
|
||||
SwipeOnTextStep(),
|
||||
TapTextWithinWidgetStep(),
|
||||
TapWidgetOfTypeStep(),
|
||||
TapWidgetOfTypeWithinStep(),
|
||||
TapWidgetWithTextStep(),
|
||||
TextExistsStep(),
|
||||
TextExistsWithinStep(),
|
||||
WaitUntilKeyExistsStep(),
|
||||
WaitUntilTypeExistsStep(),
|
||||
thenExpectElementToHaveValue(),
|
||||
whenTapBackButtonWidget(),
|
||||
whenTapWidget(),
|
||||
whenTapWidgetWithoutScroll(),
|
||||
whenLongPressWidget(),
|
||||
whenLongPressWidgetWithoutScroll(),
|
||||
whenLongPressWidgetForDuration(),
|
||||
givenOpenDrawer(),
|
||||
whenPauseStep(),
|
||||
whenFillFieldStep(),
|
||||
thenExpectWidgetToBePresent(),
|
||||
restartAppStep(),
|
||||
siblingContainsTextStep(),
|
||||
tapTextWithinWidgetStep(),
|
||||
tapWidgetOfTypeStep(),
|
||||
tapWidgetOfTypeWithinStep(),
|
||||
tapWidgetWithTextStep(),
|
||||
textExistsStep(),
|
||||
textExistsWithinStep(),
|
||||
waitUntilKeyExistsStep(),
|
||||
waitUntilTypeExistsStep(),
|
||||
];
|
||||
|
||||
/// 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
|
||||
/// Additional setting on the configuration object can be set on the returned instance.
|
||||
static FlutterTestConfiguration DEFAULT(
|
||||
static FlutterTestConfiguration standard(
|
||||
Iterable<StepDefinitionGeneric<World>> steps, {
|
||||
String featurePath = 'integration_test/features/*.*.feature',
|
||||
String targetAppPath = 'test_driver/integration_test_driver.dart',
|
||||
|
@ -57,7 +57,6 @@ class FlutterTestConfiguration extends TestConfiguration {
|
|||
StdoutReporter(MessageLevel.error),
|
||||
ProgressReporter(),
|
||||
TestRunSummaryReporter(),
|
||||
// JsonReporter(path: './report.json'),
|
||||
],
|
||||
stepDefinitions: steps,
|
||||
);
|
||||
|
@ -80,10 +79,11 @@ class FlutterTestConfiguration extends TestConfiguration {
|
|||
Iterable<CustomParameter<dynamic>>? customStepParameterDefinitions,
|
||||
Iterable<StepDefinitionGeneric<World>>? stepDefinitions,
|
||||
}) : super(
|
||||
customStepParameterDefinitions:
|
||||
List.from(customStepParameterDefinitions ?? Iterable.empty())
|
||||
..addAll(_wellKnownParameters),
|
||||
stepDefinitions: List.from(stepDefinitions ?? Iterable.empty())
|
||||
..addAll(_wellKnownStepDefinitions),
|
||||
customStepParameterDefinitions: List.from(
|
||||
customStepParameterDefinitions ?? const Iterable.empty(),
|
||||
)..addAll(_wellKnownParameters),
|
||||
stepDefinitions: List.from(
|
||||
stepDefinitions ?? const Iterable.empty(),
|
||||
)..addAll(_wellKnownStepDefinitions),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ class FlutterAppRunnerHook extends Hook {
|
|||
..setWorkingDirectory(config.targetAppWorkingDirectory)
|
||||
..setBuildRequired(haveRunFirstScenario ? false : config.build)
|
||||
..setKeepAppRunning(config.keepAppRunningAfterTests)
|
||||
..setBuildFlavor(config.buildFlavor)
|
||||
..setBuildFlavour(config.buildFlavour)
|
||||
..setBuildMode(config.buildMode)
|
||||
..setDeviceTargetId(config.targetDeviceId);
|
||||
|
||||
|
|
|
@ -5,10 +5,6 @@ import 'package:flutter_gherkin/src/flutter/configuration/build_mode.dart';
|
|||
import 'package:gherkin/gherkin.dart';
|
||||
|
||||
class FlutterRunProcessHandler extends ProcessHandler {
|
||||
static const String FAIL_COLOR = '\u001b[33;31m'; // red
|
||||
static const String WARN_COLOR = '\u001b[33;10m'; // yellow
|
||||
static const String RESET_COLOR = '\u001b[33;0m';
|
||||
|
||||
// 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=/`
|
||||
// `Observatory URL on device: http://127.0.0.1:37849/t2xp9hvaxNs=/`
|
||||
|
@ -49,10 +45,10 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
bool _logFlutterProcessOutput = false;
|
||||
bool _verboseFlutterLogs = false;
|
||||
bool _keepAppRunning = false;
|
||||
BuildMode _buildMode = BuildMode.Debug;
|
||||
BuildMode _buildMode = BuildMode.debug;
|
||||
String? _workingDirectory;
|
||||
String? _appTarget;
|
||||
String? _buildFlavor;
|
||||
String? _buildFlavour;
|
||||
String? _deviceTargetId;
|
||||
Duration _driverConnectionDelay = const Duration(seconds: 2);
|
||||
String? currentObservatoryUri;
|
||||
|
@ -73,8 +69,8 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
_workingDirectory = workingDirectory;
|
||||
}
|
||||
|
||||
void setBuildFlavor(String? buildFlavor) {
|
||||
_buildFlavor = buildFlavor;
|
||||
void setBuildFlavour(String? buildFlavour) {
|
||||
_buildFlavour = buildFlavour;
|
||||
}
|
||||
|
||||
void setBuildMode(BuildMode buildMode) {
|
||||
|
@ -101,9 +97,9 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
Future<void> run() async {
|
||||
final arguments = ['run', '--target=$_appTarget'];
|
||||
|
||||
if (_buildMode == BuildMode.Debug) {
|
||||
if (_buildMode == BuildMode.debug) {
|
||||
arguments.add('--debug');
|
||||
} else if (_buildMode == BuildMode.Profile) {
|
||||
} else if (_buildMode == BuildMode.profile) {
|
||||
arguments.add('--profile');
|
||||
}
|
||||
|
||||
|
@ -111,8 +107,8 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
arguments.add('--no-build');
|
||||
}
|
||||
|
||||
if (_buildFlavor != null && _buildFlavor!.isNotEmpty) {
|
||||
arguments.add('--flavor=$_buildFlavor');
|
||||
if (_buildFlavour != null && _buildFlavour!.isNotEmpty) {
|
||||
arguments.add('--flavor=$_buildFlavour');
|
||||
}
|
||||
|
||||
if (_deviceTargetId != null && _deviceTargetId!.isNotEmpty) {
|
||||
|
@ -148,11 +144,15 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
.where((event) => event.isNotEmpty)
|
||||
.listen((event) {
|
||||
if (event.contains(_errorMessageRegex)) {
|
||||
stderr.writeln('${FAIL_COLOR}Flutter build error: $event$RESET_COLOR');
|
||||
stderr.writeln(
|
||||
'${StdoutReporter.kFailColor}Flutter build error: $event${StdoutReporter.kResetColor}',
|
||||
);
|
||||
} else {
|
||||
// This is most likely a deprecated api usage warnings (from Gradle) and should not
|
||||
// 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();
|
||||
if (_runningProcess != null) {
|
||||
_runningProcess!.stdin.write('q');
|
||||
_openSubscriptions.forEach((s) => s.cancel());
|
||||
for (var s in _openSubscriptions) {
|
||||
s.cancel();
|
||||
}
|
||||
_openSubscriptions.clear();
|
||||
exitCode = await _runningProcess!.exitCode;
|
||||
_runningProcess = null;
|
||||
|
@ -232,12 +234,17 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
|||
sub?.cancel();
|
||||
if (!completer.isCompleted) {
|
||||
stderr.writeln(
|
||||
'${FAIL_COLOR}No connected devices found to run app on and tests against$RESET_COLOR');
|
||||
'${StdoutReporter.kFailColor}'
|
||||
'No connected devices found to run app on and tests against'
|
||||
'${StdoutReporter.kResetColor}',
|
||||
);
|
||||
}
|
||||
} else if (_moreThanOneDeviceConnectedDeviceRegex.hasMatch(logLine)) {
|
||||
sub?.cancel();
|
||||
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:collection/collection.dart';
|
||||
|
||||
typedef StepFn = Future<StepResult> Function(
|
||||
TestDependencies dependencies,
|
||||
bool skip,
|
||||
);
|
||||
|
||||
typedef StartAppFn = Future<void> Function(World world);
|
||||
|
||||
class TestDependencies {
|
||||
final World world;
|
||||
final AttachmentManager attachmentManager;
|
||||
|
@ -18,25 +25,25 @@ class TestDependencies {
|
|||
abstract class GherkinIntegrationTestRunner {
|
||||
final TagExpressionEvaluator _tagExpressionEvaluator =
|
||||
TagExpressionEvaluator();
|
||||
final TestConfiguration configuration;
|
||||
final Future<void> Function(World world) appMainFunction;
|
||||
AggregatedReporter _reporter = new AggregatedReporter();
|
||||
final FlutterTestConfiguration configuration;
|
||||
final StartAppFn appMainFunction;
|
||||
final Timeout scenarioExecutionTimeout;
|
||||
final AggregatedReporter _reporter = AggregatedReporter();
|
||||
Hook? _hook;
|
||||
Iterable<ExecutableStep>? _executableSteps;
|
||||
Iterable<CustomParameter>? _customParameters;
|
||||
|
||||
IntegrationTestWidgetsFlutterBinding? _binding;
|
||||
late final IntegrationTestWidgetsFlutterBinding _binding;
|
||||
|
||||
AggregatedReporter get reporter => _reporter;
|
||||
Hook get hook => _hook!;
|
||||
LiveTestWidgetsFlutterBindingFramePolicy? get framePolicy => null;
|
||||
|
||||
Timeout scenarioExecutionTimeout = const Timeout(Duration(minutes: 10));
|
||||
|
||||
GherkinIntegrationTestRunner(
|
||||
this.configuration,
|
||||
this.appMainFunction,
|
||||
) {
|
||||
this.appMainFunction, {
|
||||
this.scenarioExecutionTimeout = const Timeout(Duration(minutes: 10)),
|
||||
}) {
|
||||
configuration.prepare();
|
||||
_registerReporters(configuration.reporters);
|
||||
_hook = _registerHooks(configuration.hooks);
|
||||
|
@ -51,7 +58,7 @@ abstract class GherkinIntegrationTestRunner {
|
|||
Future<void> run() async {
|
||||
_binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
_binding!.framePolicy =
|
||||
_binding.framePolicy =
|
||||
framePolicy ?? LiveTestWidgetsFlutterBindingFramePolicy.benchmarkLive;
|
||||
|
||||
tearDownAll(
|
||||
|
@ -71,7 +78,7 @@ abstract class GherkinIntegrationTestRunner {
|
|||
void onRunComplete() {
|
||||
_safeInvokeFuture(() async => await reporter.test.onFinished.maybeCall());
|
||||
_safeInvokeFuture(() async => await hook.onAfterRun(configuration));
|
||||
setTestResultData(_binding!);
|
||||
setTestResultData(_binding);
|
||||
_safeInvokeFuture(() async => await reporter.dispose());
|
||||
}
|
||||
|
||||
|
@ -81,27 +88,28 @@ abstract class GherkinIntegrationTestRunner {
|
|||
}
|
||||
|
||||
@protected
|
||||
void runFeature(
|
||||
String name,
|
||||
void runFeature({
|
||||
required String name,
|
||||
required void Function() run,
|
||||
Iterable<String>? tags,
|
||||
void Function() runFeature,
|
||||
) {
|
||||
}) {
|
||||
group(
|
||||
name,
|
||||
() {
|
||||
runFeature();
|
||||
run();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@protected
|
||||
Future<void> onBeforeRunFeature(
|
||||
String name,
|
||||
Future<void> onBeforeRunFeature({
|
||||
required String name,
|
||||
required String path,
|
||||
Iterable<String>? tags,
|
||||
) async {
|
||||
final debugInformation = RunnableDebugInformation('', 0, name);
|
||||
}) async {
|
||||
final debugInformation = RunnableDebugInformation(path, 0, name);
|
||||
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(
|
||||
FeatureMessage(
|
||||
name: name,
|
||||
|
@ -112,13 +120,21 @@ abstract class GherkinIntegrationTestRunner {
|
|||
}
|
||||
|
||||
@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);
|
||||
await reporter.test.onFinished.maybeCall(
|
||||
TestMessage(
|
||||
target: Target.feature,
|
||||
await reporter.feature.onFinished.maybeCall(
|
||||
FeatureMessage(
|
||||
name: name,
|
||||
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({
|
||||
required String name,
|
||||
required Iterable<String>? tags,
|
||||
required List<
|
||||
Future<StepResult> Function(
|
||||
TestDependencies dependencies,
|
||||
bool skip,
|
||||
)>
|
||||
steps,
|
||||
required List<StepFn> steps,
|
||||
required String path,
|
||||
Future<void> Function()? onBefore,
|
||||
Future<void> Function()? onAfter,
|
||||
|
@ -144,11 +155,12 @@ abstract class GherkinIntegrationTestRunner {
|
|||
if (onBefore != null) {
|
||||
await onBefore();
|
||||
}
|
||||
var failed = false;
|
||||
bool failed = false;
|
||||
|
||||
final debugInformation = RunnableDebugInformation(path, 0, name);
|
||||
final scenarioTags =
|
||||
(tags ?? Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0));
|
||||
final scenarioTags = (tags ?? const Iterable<Tag>.empty()).map(
|
||||
(t) => Tag(t.toString(), 0),
|
||||
);
|
||||
final dependencies = await createTestDependencies(
|
||||
configuration,
|
||||
tester,
|
||||
|
@ -190,7 +202,6 @@ abstract class GherkinIntegrationTestRunner {
|
|||
} catch (e) {
|
||||
failed = true;
|
||||
hasToSkip = true;
|
||||
// rethrow;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
@ -213,13 +224,11 @@ abstract class GherkinIntegrationTestRunner {
|
|||
await onAfter();
|
||||
}
|
||||
|
||||
cleanupScenarioRun(dependencies);
|
||||
cleanUpScenarioRun(dependencies);
|
||||
}
|
||||
},
|
||||
timeout: scenarioExecutionTimeout,
|
||||
semanticsEnabled: configuration is FlutterTestConfiguration
|
||||
? (configuration as FlutterTestConfiguration).semanticsEnabled
|
||||
: true,
|
||||
semanticsEnabled: configuration.semanticsEnabled,
|
||||
);
|
||||
} else {
|
||||
_safeInvokeFuture(
|
||||
|
@ -237,7 +246,13 @@ abstract class GherkinIntegrationTestRunner {
|
|||
World world,
|
||||
) async {
|
||||
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
|
||||
|
@ -259,7 +274,7 @@ abstract class GherkinIntegrationTestRunner {
|
|||
(world as FlutterWorld).setAppAdapter(
|
||||
WidgetTesterAppDriverAdapter(
|
||||
rawAdapter: tester,
|
||||
binding: _binding!,
|
||||
binding: _binding,
|
||||
waitImplicitlyAfterAction: configuration is FlutterTestConfiguration
|
||||
? (configuration).waitImplicitlyAfterAction
|
||||
: true,
|
||||
|
@ -273,43 +288,46 @@ abstract class GherkinIntegrationTestRunner {
|
|||
}
|
||||
|
||||
@protected
|
||||
Future<StepResult> runStep(
|
||||
String step,
|
||||
Iterable<String> multiLineStrings,
|
||||
dynamic table,
|
||||
TestDependencies dependencies,
|
||||
bool hasToSkip,
|
||||
) async {
|
||||
Future<StepResult> runStep({
|
||||
required String name,
|
||||
required Iterable<String> multiLineStrings,
|
||||
required dynamic table,
|
||||
required TestDependencies dependencies,
|
||||
required bool skip,
|
||||
}) async {
|
||||
final executable = _executableSteps!.firstWhereOrNull(
|
||||
(s) => s.expression.isMatch(step),
|
||||
(s) => s.expression.isMatch(name),
|
||||
);
|
||||
|
||||
if (executable == null) {
|
||||
final message = 'Step definition not found for text: `$step`';
|
||||
final message = 'Step definition not found for text: `$name`';
|
||||
throw GherkinStepNotDefinedException(message);
|
||||
}
|
||||
|
||||
var parameters = _getStepParameters(
|
||||
step,
|
||||
multiLineStrings,
|
||||
table,
|
||||
executable,
|
||||
step: name,
|
||||
multiLineStrings: multiLineStrings,
|
||||
table: table,
|
||||
code: executable,
|
||||
);
|
||||
|
||||
await _onBeforeStepRun(
|
||||
dependencies.world,
|
||||
step,
|
||||
table,
|
||||
multiLineStrings,
|
||||
world: dependencies.world,
|
||||
step: name,
|
||||
table: table,
|
||||
multiLineStrings: multiLineStrings,
|
||||
);
|
||||
|
||||
StepResult? result;
|
||||
|
||||
if (hasToSkip) {
|
||||
result = new StepResult(0, StepExecutionResult.skipped,
|
||||
resultReason: "Previous step(s) failed.");
|
||||
if (skip) {
|
||||
result = StepResult(
|
||||
0,
|
||||
StepExecutionResult.skipped,
|
||||
resultReason: 'Previous step(s) failed',
|
||||
);
|
||||
} 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(
|
||||
dependencies.world,
|
||||
reporter,
|
||||
|
@ -319,20 +337,16 @@ abstract class GherkinIntegrationTestRunner {
|
|||
if (!_isNegativeResult(result.result)) {
|
||||
break;
|
||||
} else {
|
||||
await Future.delayed(this.configuration.retryDelay);
|
||||
await Future.delayed(configuration.retryDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
await _onAfterStepRun(
|
||||
step,
|
||||
name,
|
||||
result!,
|
||||
dependencies,
|
||||
);
|
||||
|
||||
if (result is ErroredStepResult) {
|
||||
// result.resultReason = result.exception.toString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -343,7 +357,7 @@ abstract class GherkinIntegrationTestRunner {
|
|||
}
|
||||
|
||||
@protected
|
||||
void cleanupScenarioRun(TestDependencies dependencies) {
|
||||
void cleanUpScenarioRun(TestDependencies dependencies) {
|
||||
_safeInvokeFuture(
|
||||
() async => dependencies.attachmentManager.dispose(),
|
||||
);
|
||||
|
@ -354,7 +368,9 @@ abstract class GherkinIntegrationTestRunner {
|
|||
|
||||
void _registerReporters(Iterable<Reporter>? reporters) {
|
||||
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);
|
||||
}
|
||||
|
||||
Iterable<dynamic> _getStepParameters(
|
||||
String step,
|
||||
Iterable<String> multiLineStrings,
|
||||
dynamic table,
|
||||
ExecutableStep code,
|
||||
) {
|
||||
Iterable<dynamic> _getStepParameters({
|
||||
required String step,
|
||||
required Iterable<String> multiLineStrings,
|
||||
required ExecutableStep code,
|
||||
GherkinTable? table,
|
||||
}) {
|
||||
var parameters = code.expression.getParameters(step);
|
||||
if (multiLineStrings.isNotEmpty) {
|
||||
parameters = parameters.toList()..addAll(multiLineStrings);
|
||||
|
@ -433,7 +449,7 @@ abstract class GherkinIntegrationTestRunner {
|
|||
result,
|
||||
);
|
||||
|
||||
await reporter.step.onStarted.maybeCall(
|
||||
await reporter.step.onFinished.maybeCall(
|
||||
StepMessage(
|
||||
name: step,
|
||||
context: RunnableDebugInformation('', 0, step),
|
||||
|
@ -445,12 +461,12 @@ abstract class GherkinIntegrationTestRunner {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> _onBeforeStepRun(
|
||||
World world,
|
||||
String step,
|
||||
table,
|
||||
Iterable<String> multiLineStrings,
|
||||
) async {
|
||||
Future<void> _onBeforeStepRun({
|
||||
required World world,
|
||||
required String step,
|
||||
required Iterable<String> multiLineStrings,
|
||||
GherkinTable? table,
|
||||
}) async {
|
||||
await hook.onBeforeStep(world, step);
|
||||
await reporter.step.onStarted.maybeCall(
|
||||
StepMessage(
|
||||
|
@ -475,6 +491,9 @@ abstract class GherkinIntegrationTestRunner {
|
|||
) {
|
||||
return tagExpression == null || tagExpression.isEmpty
|
||||
? true
|
||||
: _tagExpressionEvaluator.evaluate(tagExpression, tags!.toList());
|
||||
: _tagExpressionEvaluator.evaluate(
|
||||
tagExpression,
|
||||
tags!.toList(growable: false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:gherkin/gherkin.dart';
|
|||
/// Examples:
|
||||
///
|
||||
/// `Given I open the drawer`
|
||||
StepDefinitionGeneric GivenOpenDrawer() {
|
||||
StepDefinitionGeneric givenOpenDrawer() {
|
||||
return given1<String, FlutterWorld>(
|
||||
RegExp(r'I (open|close) the drawer'),
|
||||
(action, context) async {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter_gherkin/flutter_gherkin.dart';
|
||||
import 'package:gherkin/gherkin.dart';
|
||||
|
||||
StepDefinitionGeneric RestartAppStep() {
|
||||
StepDefinitionGeneric restartAppStep() {
|
||||
return given<FlutterWorld>(
|
||||
'I restart the app',
|
||||
(context) async {
|
||||
|
|
|
@ -11,7 +11,7 @@ import 'package:gherkin/gherkin.dart';
|
|||
/// Examples:
|
||||
///
|
||||
/// `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>(
|
||||
'I expect a {string} that contains the text {string} to also contain the text {string}',
|
||||
(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:gherkin/gherkin.dart';
|
||||
|
||||
|
@ -17,7 +19,7 @@ mixin _SwipeHelper
|
|||
await world.appDriver.scroll(
|
||||
finder,
|
||||
dx: offset.toDouble(),
|
||||
duration: Duration(milliseconds: 500),
|
||||
duration: const Duration(milliseconds: 500),
|
||||
timeout: timeout,
|
||||
);
|
||||
} else {
|
||||
|
@ -27,7 +29,7 @@ mixin _SwipeHelper
|
|||
await world.appDriver.scroll(
|
||||
finder,
|
||||
dy: offset.toDouble(),
|
||||
duration: Duration(milliseconds: 500),
|
||||
duration: const Duration(milliseconds: 500),
|
||||
timeout: timeout,
|
||||
);
|
||||
}
|
||||
|
@ -49,7 +51,7 @@ class SwipeOnKeyStep
|
|||
int swipeAmount,
|
||||
String key,
|
||||
) async {
|
||||
final finder = this.world.appDriver.findBy(key, FindType.key);
|
||||
final finder = world.appDriver.findBy(key, FindType.key);
|
||||
await swipeOnFinder(finder, direction, swipeAmount);
|
||||
}
|
||||
|
||||
|
@ -72,7 +74,7 @@ class SwipeOnTextStep
|
|||
int swipeAmount,
|
||||
String text,
|
||||
) async {
|
||||
final finder = this.world.appDriver.findBy(text, FindType.text);
|
||||
final finder = world.appDriver.findBy(text, FindType.text);
|
||||
await swipeOnFinder(finder, direction, swipeAmount);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:gherkin/gherkin.dart';
|
|||
/// Examples:
|
||||
///
|
||||
/// `Then I tap the label that contains the text "Logout" within the "user_settings_list"`
|
||||
StepDefinitionGeneric TapTextWithinWidgetStep() {
|
||||
StepDefinitionGeneric tapTextWithinWidgetStep() {
|
||||
return given2<String, String, FlutterWorld>(
|
||||
RegExp(
|
||||
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 label of type "ListTile"`
|
||||
/// `Then I tap the field of type "TextField"`
|
||||
StepDefinitionGeneric TapWidgetOfTypeStep() {
|
||||
StepDefinitionGeneric tapWidgetOfTypeStep() {
|
||||
return given1<String, FlutterWorld>(
|
||||
RegExp(
|
||||
r'I tap the (?:button|element|label|icon|field|text|widget) of type {string}$'),
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:gherkin/gherkin.dart';
|
|||
/// Examples:
|
||||
///
|
||||
/// `Then I tap the element of type "MaterialButton" within the "user_settings_list"`
|
||||
StepDefinitionGeneric TapWidgetOfTypeWithinStep() {
|
||||
StepDefinitionGeneric tapWidgetOfTypeWithinStep() {
|
||||
return when2<String, String, FlutterWorld>(
|
||||
RegExp(
|
||||
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 button that contains the text "Sign up"`
|
||||
/// `Then I tap the widget that contains the text "My User Profile"`
|
||||
StepDefinitionGeneric TapWidgetWithTextStep() {
|
||||
StepDefinitionGeneric tapWidgetWithTextStep() {
|
||||
return then1<String, FlutterWorld>(
|
||||
RegExp(
|
||||
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:
|
||||
///
|
||||
/// `Then I expect the text "Logout" to be present`
|
||||
/// `But I expect the text "Signup" to be absent`
|
||||
StepDefinitionGeneric TextExistsStep() {
|
||||
/// `But I expect the text "Sign up" to be absent`
|
||||
StepDefinitionGeneric textExistsStep() {
|
||||
return then2<String, Existence, FlutterWorld>(
|
||||
RegExp(r'I expect the text {string} to be {existence}$'),
|
||||
(text, exists, context) async {
|
||||
|
|
|
@ -8,8 +8,8 @@ import '../parameters/existence_parameter.dart';
|
|||
/// Examples:
|
||||
///
|
||||
/// `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"`
|
||||
StepDefinitionGeneric TextExistsWithinStep() {
|
||||
/// `But I expect the text "Sign up" to be absent within the "login_screen"`
|
||||
StepDefinitionGeneric textExistsWithinStep() {
|
||||
return then3<String, Existence, String, FlutterWorld>(
|
||||
RegExp(
|
||||
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"`
|
||||
/// `And I expect the "controlKey" to be "Hello World"`
|
||||
StepDefinitionGeneric ThenExpectElementToHaveValue() {
|
||||
StepDefinitionGeneric thenExpectElementToHaveValue() {
|
||||
return given2<String, String, FlutterWorld>(
|
||||
RegExp(r'I expect the {string} to be {string}$'),
|
||||
(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 button 'save' to be present within 1 second`
|
||||
StepDefinitionGeneric ThenExpectWidgetToBePresent() {
|
||||
StepDefinitionGeneric thenExpectWidgetToBePresent() {
|
||||
return given2<String, int, FlutterWorld>(
|
||||
RegExp(
|
||||
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`
|
||||
/// `And I wait until the "login_screen" is present`
|
||||
StepDefinitionGeneric WaitUntilKeyExistsStep() {
|
||||
StepDefinitionGeneric waitUntilKeyExistsStep() {
|
||||
return then2<String, Existence, FlutterWorld>(
|
||||
'I wait until the {string} is {existence}',
|
||||
(keyString, existence, context) async {
|
||||
|
|
|
@ -9,7 +9,7 @@ import '../parameters/existence_parameter.dart';
|
|||
///
|
||||
/// `Then I wait until the element of type "ProgressIndicator" is absent`
|
||||
/// `And I wait until the button of type the "MaterialButton" is present`
|
||||
StepDefinitionGeneric WaitUntilTypeExistsStep() {
|
||||
StepDefinitionGeneric waitUntilTypeExistsStep() {
|
||||
return then2<String, Existence, FlutterWorld>(
|
||||
'I wait until the (?:button|element|label|icon|field|text|widget) of type {string} is {existence}',
|
||||
(ofType, existence, context) async {
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:gherkin/gherkin.dart';
|
|||
/// Examples:
|
||||
/// Then I fill the "email" field with "bob@gmail.com"
|
||||
/// Then I fill the "name" field with "Woody Johnson"
|
||||
StepDefinitionGeneric WhenFillFieldStep() {
|
||||
StepDefinitionGeneric whenFillFieldStep() {
|
||||
return given2<String, String, FlutterWorld>(
|
||||
'I fill the {string} field with {string}',
|
||||
(key, value, context) async {
|
||||
|
|
|
@ -16,7 +16,7 @@ import 'package:gherkin/gherkin.dart';
|
|||
/// `When I long press "controlKey" field`
|
||||
/// `When I long press "controlKey" text`
|
||||
/// `When I long press "controlKey" widget`
|
||||
StepDefinitionGeneric WhenLongPressWidget() {
|
||||
StepDefinitionGeneric whenLongPressWidget() {
|
||||
return when1<String, FlutterWorld>(
|
||||
RegExp(
|
||||
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
|
||||
StepDefinitionGeneric WhenLongPressWidgetWithoutScroll() {
|
||||
StepDefinitionGeneric whenLongPressWidgetWithoutScroll() {
|
||||
return when1<String, FlutterWorld>(
|
||||
RegExp(
|
||||
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
|
||||
StepDefinitionGeneric WhenLongPressWidgetForDuration() {
|
||||
StepDefinitionGeneric whenLongPressWidgetForDuration() {
|
||||
return when2<String, int, FlutterWorld>(
|
||||
RegExp(
|
||||
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:
|
||||
/// When I pause for 10 seconds
|
||||
/// When I pause for 120 seconds
|
||||
StepDefinitionGeneric WhenPauseStep() {
|
||||
StepDefinitionGeneric whenPauseStep() {
|
||||
return when1<int, FlutterWorld>(
|
||||
'I (?:pause|wait) for {int} second(?:s)?',
|
||||
(wait, context) async {
|
||||
|
|
|
@ -8,7 +8,7 @@ import 'package:gherkin/gherkin.dart';
|
|||
/// `When I tap the back button"`
|
||||
/// `When I tap the back element"`
|
||||
/// `When I tap the back widget"`
|
||||
StepDefinitionGeneric WhenTapBackButtonWidget() {
|
||||
StepDefinitionGeneric whenTapBackButtonWidget() {
|
||||
return when<FlutterWorld>(
|
||||
RegExp(r'I tap the back (?:button|element|widget|icon|text)$'),
|
||||
(context) async {
|
||||
|
|
|
@ -16,7 +16,7 @@ import 'package:gherkin/gherkin.dart';
|
|||
/// `When I tap "controlKey" field"`
|
||||
/// `When I tap "controlKey" text"`
|
||||
/// `When I tap "controlKey" widget"`
|
||||
StepDefinitionGeneric WhenTapWidget() {
|
||||
StepDefinitionGeneric whenTapWidget() {
|
||||
return when1<String, FlutterWorld>(
|
||||
RegExp(
|
||||
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>(
|
||||
RegExp(
|
||||
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({
|
||||
Duration? timeout = const Duration(seconds: 60),
|
||||
}) async {
|
||||
// ignore: unnecessary_null_comparison
|
||||
if (rawAppDriver != null) {
|
||||
await rawAppDriver.close().catchError(
|
||||
(e, st) {
|
||||
// Avoid an unhandled error
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
await rawAppDriver.close().catchError(
|
||||
(e, st) {
|
||||
// Avoid an unhandled error
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
CALL flutter analyze
|
||||
CALL "C:\Google\flutter\bin\cache\dart-sdk\bin\dartfmt" . -w
|
||||
CALL flutter packages upgrade
|
||||
CALL flutter test --no-sound-null-safety
|
||||
CALL dart format . --fix
|
||||
CALL flutter test
|
67
pubspec.lock
67
pubspec.lock
|
@ -7,14 +7,28 @@ packages:
|
|||
name: _fe_analyzer_shared
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "40.0.0"
|
||||
version: "39.0.0"
|
||||
analyzer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: analyzer
|
||||
url: "https://pub.dartlang.org"
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -106,6 +120,20 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -137,6 +165,13 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -161,6 +196,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.15.0"
|
||||
integration_test:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -173,6 +215,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.5.0"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -222,6 +271,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -353,6 +409,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -27,5 +27,7 @@ dev_dependencies:
|
|||
meta: '>=1.7.0 < 2.0.0'
|
||||
pedantic: ^1.11.1
|
||||
build_config: ^1.0.0
|
||||
flutter_lints: ^2.0.1
|
||||
dart_code_metrics: ^4.15.2
|
||||
|
||||
flutter:
|
||||
|
|
Loading…
Reference in New Issue