feat(config): re-worked configuration so it can stay mostly immutable

- Fix #195: Adding missing export for `wait_until_key_exists_step.dart`
- 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`
This commit is contained in:
Jon 2022-06-17 16:25:39 +10:00
parent 5c15f0c2c0
commit 0961916daa
44 changed files with 743 additions and 526 deletions

View File

@ -1,2 +1,2 @@
# This is a generated file; do not edit or check into version control.
integration_test=C:\\Google\\flutter\\packages\\integration_test\\
integration_test=C:\\Development\\flutter\\packages\\integration_test\\

View File

@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"C:\\\\Google\\\\flutter\\\\packages\\\\integration_test\\\\","dependencies":[]}],"android":[{"name":"integration_test","path":"C:\\\\Google\\\\flutter\\\\packages\\\\integration_test\\\\","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]}],"date_created":"2021-11-24 07:12:59.207785","version":"2.5.3"}
{"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"}

20
.gitignore vendored
View File

@ -1,13 +1,23 @@
.DS_Store
.dart_tool/
.packages
.pub/
build/
ios/.generated/
ios/Flutter/Generated.xcconfig
ios/Runner/GeneratedPluginRegistrant.*
node_modules
package-lock.json
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
**/generated_plugin_registrant.dart
.packages
.pub-cache/
.pub/
build/
flutter_*.png
linked_*.ds
unlinked.ds
unlinked_spec.ds

View File

@ -1,3 +1,12 @@
## [3.0.0] - 17/06/2022
- Fix #195: Adding missing export for `wait_until_key_exists_step.dart`
- 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`
## [3.0.0-rc.9] - 18/11/2021
- Fix: #172: Fix for the `StdoutReporter` when running against the web

View File

@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 29
compileSdkVersion 31
sourceSets {
main.java.srcDirs += 'src/main/kotlin'

View File

@ -11,6 +11,7 @@
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"

View File

@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.6.10'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:7.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

View File

@ -3,4 +3,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
org.gradle.jvmargs=-Xmx4608m

View File

@ -1,20 +1,41 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "31.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.0"
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.2"
version: "3.1.11"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.1"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.6.1"
version: "2.8.2"
boolean_selector:
dependency: transitive
description:
@ -22,20 +43,34 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
build:
dependency: transitive
description:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.1"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.5"
clock:
dependency: transitive
description:
@ -49,7 +84,14 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
version: "1.16.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
crypto:
dependency: transitive
description:
@ -57,20 +99,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.0"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.0"
version: "6.1.2"
flutter:
dependency: "direct main"
description: flutter
@ -87,7 +136,7 @@ packages:
path: ".."
relative: true
source: path
version: "2.0.0"
version: "3.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
@ -104,59 +153,101 @@ packages:
name: gherkin
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
version: "3.0.0+1"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
integration_test:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
version: "0.12.11"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.7.0"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
version: "1.8.1"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.1.0"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.1"
version: "4.2.4"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_gen:
dependency: transitive
description:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.2"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.2"
stack_trace:
dependency: transitive
description:
@ -198,7 +289,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
version: "0.4.9"
typed_data:
dependency: transitive
description:
@ -206,20 +297,34 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
uuid:
dependency: transitive
description:
name: uuid
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.6"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.2"
vm_service:
dependency: transitive
description:
name: vm_service
url: "https://pub.dartlang.org"
source: hosted
version: "6.2.0"
version: "8.2.2"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
webdriver:
dependency: transitive
description:
@ -227,6 +332,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.1"
sdks:
dart: ">=2.12.3 <3.0.0"
dart: ">=2.17.0 <3.0.0"
flutter: ">=2.2.0"

View File

@ -6,7 +6,8 @@ publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=2.12.0 <3.0.0'
sdk: '>=2.17.0 <3.0.0'
flutter: ">=2.2.0"
dependencies:
flutter:

View File

@ -3,22 +3,26 @@ import 'package:flutter_gherkin/flutter_gherkin_with_driver.dart';
import 'package:gherkin/gherkin.dart';
Future<void> main() {
final config = FlutterDriverTestConfiguration.DEFAULT(
Iterable.empty(),
featurePath: 'features/**.feature',
final config = FlutterDriverTestConfiguration(
features: [RegExp('features/**.feature')],
targetAppPath: 'test_driver/app.dart',
)
..restartAppBetweenScenarios = true
..targetAppWorkingDirectory = '../'
..targetAppPath = 'test_driver/app.dart';
// ..buildFlavor = "staging" // uncomment when using build flavor and check android/ios flavor setup see android file android\app\build.gradle
// ..targetDeviceId = "all" // uncomment to run tests on all connected devices or set specific device target id
// ..tagExpression = '@smoke and not @ignore' // uncomment to see an example of running scenarios based on tag expressions
// ..logFlutterProcessOutput = true // uncomment to see command invoked to start the flutter test app
// ..verboseFlutterProcessLogs = true // uncomment to see the verbose output from the Flutter process
// ..flutterBuildTimeout = Duration(minutes: 3) // uncomment to change the default period that flutter is expected to build and start the app within
// ..runningAppProtocolEndpointUri =
// 'http://127.0.0.1:51540/bkegoer6eH8=/' // already running app observatory / service protocol uri (with enableFlutterDriverExtension method invoked) to test against if you use this set `restartAppBetweenScenarios` to false
targetAppWorkingDirectory: '../',
buildFlavor:
"staging", // uncomment when using build flavor and check android/ios flavor setup see android file android\app\build.gradle
targetDeviceId:
"all", // uncomment to run tests on all connected devices or set specific device target id
tagExpression:
'@smoke and not @ignore', // uncomment to see an example of running scenarios based on tag expressions
logFlutterProcessOutput:
true, // uncomment to see command invoked to start the flutter test app
verboseFlutterProcessLogs:
true, // uncomment to see the verbose output from the Flutter process
flutterBuildTimeout: Duration(
minutes:
3), // uncomment to change the default period that flutter is expected to build and start the app within
runningAppProtocolEndpointUri:
'http://127.0.0.1:51540/bkegoer6eH8=/', // already running app observatory / service protocol uri (with enableFlutterDriverExtension method invoked) to test against if you use this set `restartAppBetweenScenarios` to false
);
return GherkinRunner().execute(config);
}

View File

@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 29
compileSdkVersion 31
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
@ -39,8 +39,8 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.example_with_integration_test"
minSdkVersion 16
targetSdkVersion 29
minSdkVersion 23
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}

View File

@ -6,11 +6,12 @@
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:name="${applicationName}"
android:label="example_with_integration_test"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"

View File

@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.6.10'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:7.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

View File

@ -3,4 +3,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
org.gradle.jvmargs=-Xmx4608m

View File

@ -1,14 +1,6 @@
@tag
Feature: Creating todos
@tag1 @tag_two
Scenario: User can create a new todo item
Given I fill the "todo" field with "Buy carrots"
When I tap the 'add' button
Then I expect the todo list
| Todo |
| Buy carrots |
@debug
Scenario: User can create multiple new todo items
Given I fill the "todo" field with "Buy carrots"

View File

@ -1,7 +1,6 @@
@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

View File

@ -12,34 +12,30 @@ import 'steps/when_await_animation.dart';
import 'steps/when_step_has_timeout.dart';
import 'world/custom_world.dart';
FlutterTestConfiguration gherkinTestConfiguration =
FlutterTestConfiguration.DEFAULT(
[
FlutterTestConfiguration gherkinTestConfiguration = FlutterTestConfiguration(
tagExpression: '@debug',
stepDefinitions: [
thenIExpectTheTodos,
whenAnAnimationIsAwaited,
whenStepHasTimeout,
givenTheData
],
)
// ..tagExpression = '@debug'
..hooks = [
ResetAppHook(),
]
..reporters = [
StdoutReporter(MessageLevel.error)
..setWriteLineFn(print)
..setWriteFn(print),
ProgressReporter()
..setWriteLineFn(print)
..setWriteFn(print),
TestRunSummaryReporter()
..setWriteLineFn(print)
..setWriteFn(print),
JsonReporter(
writeReport: (_, __) => Future<void>.value(),
),
]
..createWorld = (config) => Future.value(CustomWorld());
hooks: [
ResetAppHook(),
],
reporters: [
StdoutReporter(MessageLevel.error)
..setWriteLineFn(print)
..setWriteFn(print),
ProgressReporter()
..setWriteLineFn(print)
..setWriteFn(print),
TestRunSummaryReporter()
..setWriteLineFn(print)
..setWriteFn(print),
],
createWorld: (config) => Future.value(CustomWorld()),
);
Future<void> Function(World) appInitializationFn = (World world) async {
// ensure a new injector instance is created each time

View File

@ -21,209 +21,152 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
void testFeature0() {
runFeature(
'Checking data:',
'Swiping:',
<String>['@tag'],
() {
runScenario(
'User can have data',
<String>['@tag', '@tag1'],
(TestDependencies dependencies) async {
await runStep(
'Given I have item with data',
<String>[
"""{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": [
"GML",
"XML"
]
},
"GlossSee": "markup"
}
}
}
}
}"""
],
null,
dependencies,
);
},
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'],
steps: [
(TestDependencies dependencies, bool hasToSkip) async {
return await runStep(
'Given I swipe right by 250 pixels on the "scrollable cards"`',
<String>[],
null,
dependencies,
hasToSkip,
);
},
(TestDependencies dependencies, bool hasToSkip) async {
return await runStep(
'Then Then I expect the text "Page 2" to be present',
<String>[],
null,
dependencies,
hasToSkip,
);
},
(TestDependencies dependencies, bool hasToSkip) async {
return await runStep(
'Given I swipe left by 250 pixels on the "scrollable cards"`',
<String>[],
null,
dependencies,
hasToSkip,
);
},
(TestDependencies dependencies, bool hasToSkip) async {
return await runStep(
'Then Then I expect the text "Page 1" to be present',
<String>[],
null,
dependencies,
hasToSkip,
);
}
],
onBefore: () async => onBeforeRunFeature(
'Checking data',
'Swiping',
<String>['@tag'],
),
onAfter: () async => onAfterRunFeature(
'Checking data',
),
onAfter: () async => onAfterRunFeature('Swiping',
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\swiping.feature'),
);
},
);
}
void testFeature1() {
runFeature(
'Swiping:',
<String>['@tag'],
() {
runScenario(
'User can swipe cards left and right',
<String>['@tag', '@debug'],
(TestDependencies dependencies) async {
await runStep(
'Given I swipe right by 250 pixels on the "scrollable cards"`',
<String>[],
null,
dependencies,
);
await runStep(
'Then Then I expect the text "Page 2" to be present',
<String>[],
null,
dependencies,
);
await runStep(
'Given I swipe left by 250 pixels on the "scrollable cards"`',
<String>[],
null,
dependencies,
);
await runStep(
'Then Then I expect the text "Page 1" to be present',
<String>[],
null,
dependencies,
);
},
onBefore: () async => onBeforeRunFeature(
'Swiping',
<String>['@tag'],
),
onAfter: () async => onAfterRunFeature(
'Swiping',
),
);
},
);
}
void testFeature2() {
runFeature(
'Creating todos:',
<String>['@tag'],
() {
runScenario(
'User can create a new todo item',
<String>['@tag', '@tag1', '@tag_two'],
(TestDependencies dependencies) async {
await runStep(
'Given I fill the "todo" field with "Buy carrots"',
<String>[],
null,
dependencies,
);
await runStep(
'When I tap the \'add\' button',
<String>[],
null,
dependencies,
);
await runStep(
'Then I expect the todo list',
<String>[],
GherkinTable.fromJson('[{"Todo":"Buy carrots"}]'),
dependencies,
);
},
onBefore: () async => onBeforeRunFeature(
'Creating todos',
<String>['@tag'],
),
onAfter: null,
);
runScenario(
'User can create multiple new todo items',
<String>['@tag', '@debug'],
(TestDependencies dependencies) async {
await runStep(
'Given I fill the "todo" field with "Buy carrots"',
<String>[],
null,
dependencies,
);
await runStep(
'When I tap the "add" button',
<String>[],
null,
dependencies,
);
await runStep(
'And I fill the "todo" field with "Buy apples"',
<String>[],
null,
dependencies,
);
await runStep(
'When I tap the "add" button',
<String>[],
null,
dependencies,
);
await runStep(
'And I fill the "todo" field with "Buy blueberries"',
<String>[],
null,
dependencies,
);
await runStep(
'When I tap the "add" button',
<String>[],
null,
dependencies,
);
await runStep(
'Then I expect the todo list',
<String>[],
GherkinTable.fromJson(
'[{"Todo":"Buy blueberries"},{"Todo":"Buy apples"},{"Todo":"Buy carrots"}]'),
dependencies,
);
await runStep(
'Given I wait 5 seconds for the animation to complete',
<String>[],
null,
dependencies,
);
await runStep(
'Given I have item with data',
<String>[
"""{
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', '@debug'],
steps: [
(TestDependencies dependencies, bool hasToSkip) 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>[
"""{
"glossary": {
"title": "example glossary",
"GlossDiv": {
@ -248,15 +191,77 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
}
}
}"""
],
null,
dependencies,
);
},
onBefore: null,
onAfter: () async => onAfterRunFeature(
],
null,
dependencies,
hasToSkip,
);
}
],
onBefore: () async => onBeforeRunFeature(
'Creating todos',
<String>['@tag'],
),
onAfter: () async => onAfterRunFeature('Creating todos',
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\create.feature'),
);
},
);
}
void testFeature2() {
runFeature(
'Checking data:',
<String>['@tag'],
() {
runScenario(
name: 'User can have data',
path:
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\check.feature',
tags: <String>['@tag', '@tag1'],
steps: [
(TestDependencies dependencies, bool hasToSkip) async {
return await runStep(
'Given I have item with data',
<String>[
"""{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": [
"GML",
"XML"
]
},
"GlossSee": "markup"
}
}
}
}
}"""
],
null,
dependencies,
hasToSkip,
);
}
],
onBefore: () async => onBeforeRunFeature(
'Checking data',
<String>['@tag'],
),
onAfter: () async => onAfterRunFeature('Checking data',
'C:\Development\github\flutter_gherkin\example_with_integration_test\.\integration_test\features\check.feature'),
);
},
);

View File

@ -1,7 +1,6 @@
import 'package:example_with_integration_test/models/todo_model.dart';
import 'package:example_with_integration_test/widgets/view_utils_mixin.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:rxdart/rxdart.dart';
class AddTodoComponent extends StatefulWidget {

View File

@ -3,7 +3,6 @@ import 'package:example_with_integration_test/models/todo_model.dart';
import 'package:example_with_integration_test/models/todo_status_enum.dart';
import 'package:example_with_integration_test/widgets/components/add_todo_component.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import '../view_utils_mixin.dart';

View File

@ -21,7 +21,7 @@ packages:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.2"
version: "3.1.11"
args:
dependency: transitive
description:
@ -35,7 +35,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.1"
version: "2.8.2"
boolean_selector:
dependency: transitive
description:
@ -105,7 +105,7 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.0"
charcode:
dependency: transitive
description:
@ -147,7 +147,7 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
version: "1.16.0"
convert:
dependency: transitive
description:
@ -175,7 +175,7 @@ packages:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.0"
ffi:
dependency: transitive
description:
@ -213,7 +213,7 @@ packages:
path: ".."
relative: true
source: path
version: "3.0.0-rc.9"
version: "3.0.0"
flutter_simple_dependency_injection:
dependency: "direct main"
description:
@ -249,7 +249,7 @@ packages:
name: gherkin
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
version: "3.0.0+1"
glob:
dependency: transitive
description:
@ -296,7 +296,7 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
version: "0.6.4"
json_annotation:
dependency: "direct main"
description:
@ -324,7 +324,14 @@ packages:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
version: "0.12.11"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
meta:
dependency: transitive
description:
@ -352,7 +359,7 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
version: "1.8.1"
path_provider_linux:
dependency: transitive
description:
@ -380,7 +387,7 @@ packages:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.1.0"
plugin_platform_interface:
dependency: transitive
description:
@ -401,7 +408,7 @@ packages:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.3"
version: "4.2.4"
pub_semver:
dependency: transitive
description:
@ -504,7 +511,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.2"
stack_trace:
dependency: transitive
description:
@ -553,7 +560,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
version: "0.4.9"
timing:
dependency: transitive
description:
@ -574,21 +581,21 @@ packages:
name: uuid
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.5"
version: "3.0.6"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.2"
vm_service:
dependency: transitive
description:
name: vm_service
url: "https://pub.dartlang.org"
source: hosted
version: "7.1.1"
version: "8.2.2"
watcher:
dependency: transitive
description:
@ -632,5 +639,5 @@ packages:
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.14.0 <3.0.0"
dart: ">=2.17.0 <3.0.0"
flutter: ">=2.5.0"

View File

@ -6,8 +6,8 @@ publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=2.14.0 <3.0.0'
flutter: ">=2.5.0"
sdk: '>=2.17.0 <3.0.0'
flutter: ">=2.2.0"
dependencies:
flutter:

View File

@ -29,6 +29,7 @@ export 'src/flutter/steps/text_exists_within_step.dart';
export 'src/flutter/steps/wait_until_key_exists_step.dart';
export 'src/flutter/steps/when_tap_the_back_button_step.dart';
export 'src/flutter/steps/wait_until_type_exists_step.dart';
export 'src/flutter/steps/wait_until_key_exists_step.dart';
// Hooks
export 'src/flutter/hooks/attach_screenshot_on_failed_step_hook.dart';

View File

@ -1,16 +1,22 @@
import 'dart:async';
import 'dart:ui' as ui show ImageByteFormat;
import 'dart:io' if (dart.library.html) 'dart:html';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'app_driver_adapter.dart';
class WidgetTesterAppDriverAdapter
extends AppDriverAdapter<WidgetTester, Finder, Widget> {
WidgetTesterAppDriverAdapter(WidgetTester rawAdapter) : super(rawAdapter);
IntegrationTestWidgetsFlutterBinding binding;
bool waitImplicitlyAfterAction;
WidgetTesterAppDriverAdapter({
required WidgetTester rawAdapter,
required this.binding,
required this.waitImplicitlyAfterAction,
}) : super(rawAdapter);
@override
Future<int> waitForAppToSettle({
@ -30,6 +36,21 @@ class WidgetTesterAppDriverAdapter
}
}
Future<void> _implicitWait({
Duration? duration = const Duration(milliseconds: 100),
Duration? timeout = const Duration(seconds: 30),
}) async {
if (waitImplicitlyAfterAction) {
try {
await nativeDriver.pumpAndSettle(
duration ?? const Duration(milliseconds: 100),
EnginePhase.sendSemanticsUpdate,
timeout ?? const Duration(seconds: 30),
);
} catch (_) {}
}
}
@override
Future<T> widget<T extends Widget>(
Finder finder, [
@ -47,20 +68,15 @@ class WidgetTesterAppDriverAdapter
}
@override
Future<List<int>> screenshot() {
var renderObject = nativeDriver.binding.renderViewElement?.renderObject;
while (renderObject != null && !renderObject.isRepaintBoundary) {
renderObject = renderObject.parent as RenderObject;
Future<List<int>> screenshot() async {
if (!kIsWeb && Platform.isAndroid) {
await binding.convertFlutterSurfaceToImage();
await binding.pump();
}
assert(renderObject != null && !renderObject.debugNeedsPaint);
final layer = renderObject!.debugLayer as OffsetLayer;
return layer
.toImage(renderObject.semanticBounds)
.then((value) => value.toByteData(format: ui.ImageByteFormat.png))
.then((value) => value?.buffer.asUint8List() ?? List<int>.empty());
return binding.takeScreenshot(
'screenshot_${DateTime.now().millisecondsSinceEpoch}',
);
}
@override
@ -84,7 +100,7 @@ class WidgetTesterAppDriverAdapter
Finder finder, {
Duration? timeout = const Duration(seconds: 30),
}) async {
await waitForAppToSettle(timeout: timeout);
await _implicitWait(timeout: timeout);
final instance = await widget(finder);
if (instance is Text) {
@ -109,7 +125,7 @@ class WidgetTesterAppDriverAdapter
finder,
text,
);
await waitForAppToSettle(
await _implicitWait(
timeout: timeout,
);
}
@ -120,7 +136,7 @@ class WidgetTesterAppDriverAdapter
Duration? timeout = const Duration(seconds: 30),
}) async {
await nativeDriver.tap(finder);
await waitForAppToSettle(
await _implicitWait(
timeout: timeout,
);
}
@ -138,7 +154,7 @@ class WidgetTesterAppDriverAdapter
duration: pressDuration,
timeout: timeout,
);
await waitForAppToSettle(timeout: timeout);
await _implicitWait(timeout: timeout);
}
@override
@ -233,6 +249,6 @@ class WidgetTesterAppDriverAdapter
@override
Future<void> pageBack() async {
await nativeDriver.pageBack();
await waitForAppToSettle();
await _implicitWait();
}
}

View File

@ -11,11 +11,11 @@ import 'package:source_gen/source_gen.dart';
class NoOpReporter extends MessageReporter {
@override
Future<void> message(String message, MessageLevel level) async {
if(level == MessageLevel.info || level == MessageLevel.debug) {
if (level == MessageLevel.info || level == MessageLevel.debug) {
print(message);
}else if(level == MessageLevel.warning) {
} else if (level == MessageLevel.warning) {
print('\x1B[33m$message\x1B[0m');
}else if(level == MessageLevel.error) {
} else if (level == MessageLevel.error) {
print('\x1B[31m$message\x1B[0m');
}
}
@ -63,7 +63,7 @@ void executeTestSuite(
.getField('index')!
.toIntValue()!;
final executionOrder = ExecutionOrder.values[idx];
final featureFiles = annotation
final featureFiles = annotation
.read('featurePaths')
.listValue
.map((path) => Glob(path.toStringValue()!))