feat(drivers): added typed driver flutter world

This commit is contained in:
Jon Samwell 2021-01-14 08:07:18 +11:00
parent c372525e42
commit 2727535321
15 changed files with 104 additions and 43 deletions

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\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\integration_test-1.0.1\\\\","dependencies":[]}],"android":[{"name":"integration_test","path":"C:\\\\Google\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\integration_test-1.0.1\\\\","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]}],"date_created":"2021-01-13 12:57:44.197577","version":"1.22.5"}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"C:\\\\Google\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\integration_test-1.0.1\\\\","dependencies":[]}],"android":[{"name":"integration_test","path":"C:\\\\Google\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\integration_test-1.0.1\\\\","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]}],"date_created":"2021-01-14 08:07:08.545183","version":"1.22.5"}

View File

@ -2,6 +2,7 @@
"cSpell.words": [
"Errored",
"Serializable",
"agnostically",
"microtask",
"multiline",
"pubspec",

View File

@ -107,7 +107,7 @@ flutter drive --driver=test_driver/integration_test_driver.dart --target=integra
}
```
11. Custom world need to extend `FlutterWorld` note `FlutterDriverWorld`.
12. If you change any of the feature files you will need to regenerate the tests using the below command
12. If you change any of the feature files you will need to re-generate the tests using the below command
```
flutter pub run build_runner build
```

View File

@ -12,12 +12,13 @@ Feature: Creating todos
Scenario: User can create multiple new todo items
Given I fill the "todo" field with "Buy carrots"
When I tap the "add" button
Given I fill the "todo" field with "Buy apples"
And I fill the "todo" field with "Buy apples"
When I tap the "add" button
Given I fill the "todo" field with "Buy blueberries"
And I fill the "todo" field with "Buy blueberries"
When I tap the "add" button
Then I expect the todo list
| Todo |
| Buy blueberries |
| Buy apples |
| Buy carrots |
| Buy carrots |
Given I wait 5 seconds for the animation to complete

View File

@ -7,12 +7,16 @@ import 'package:gherkin/gherkin.dart';
import 'hooks/reset_app_hook.dart';
import 'steps/expect_todos_step.dart';
import 'steps/when_await_animation.dart';
import 'world/custom_world.dart';
FlutterTestConfiguration gherkinTestConfiguration =
FlutterTestConfiguration.DEFAULT([
thenIExpectTheTodos(),
])
FlutterTestConfiguration.DEFAULT(
[
thenIExpectTheTodos,
whenAnAnimationIsAwaited,
],
)
..hooks = [
ResetAppHook(),
]

View File

@ -1,32 +1,36 @@
import 'package:flutter/material.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:gherkin/gherkin.dart';
StepDefinitionGeneric thenIExpectTheTodos() {
return then1<GherkinTable, FlutterWorld>(
'I expect the todo list',
(table, context) async {
// get the parent list
final listTileFinder = context.world.appDriver.findBy(
ListTile,
FindType.type,
final thenIExpectTheTodos = then1<GherkinTable, FlutterWorld>(
'I expect the todo list',
(table, context) async {
expect(context.configuration.timeout, isNotNull);
expect(context.configuration.timeout.inSeconds, 5);
// get the parent list
final listTileFinder = context.world.appDriver.findBy(
ListTile,
FindType.type,
);
for (final row in table.rows) {
final todoText = row.columns.elementAt(0);
final listTileTextFinder = context.world.appDriver.findBy(
todoText,
FindType.text,
);
// find the todo by the expected text
final finder = await context.world.appDriver
.findByDescendant(listTileFinder, listTileTextFinder);
for (final row in table.rows) {
final todoText = row.columns.elementAt(0);
final listTileTextFinder = context.world.appDriver.findBy(
todoText,
FindType.text,
);
// find the todo by the expected text
final finder = await context.world.appDriver
.findByDescendant(listTileFinder, listTileTextFinder);
final text = await context.world.appDriver.getText(finder);
final text = await context.world.appDriver.getText(finder);
context.expect(todoText, text);
}
},
);
}
context.expect(todoText, text);
}
},
configuration: StepDefinitionConfiguration()
..timeout = const Duration(seconds: 5),
);

View File

@ -0,0 +1,24 @@
import 'package:flutter/widgets.dart';
import 'package:gherkin/gherkin.dart';
import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart';
import 'package:flutter_test/flutter_test.dart';
final whenAnAnimationIsAwaited = when1<int, FlutterWidgetTesterWorld>(
'I wait {int} seconds for the animation to complete',
(duration, context) async {
final tester = context.world.appDriver.rawDriver;
try {
await tester.pumpAndSettle(
const Duration(milliseconds: 100),
EnginePhase.sendSemanticsUpdate,
Duration(seconds: duration),
);
// ignore: avoid_catching_errors
} on FlutterError {
// pump for 2 seconds and stop
}
},
configuration: StepDefinitionConfiguration()
..timeout = const Duration(minutes: 5),
);

View File

@ -1,7 +1,7 @@
import 'package:example_with_integration_test/services/external_application_manager.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart';
class CustomWorld extends FlutterWorld {
class CustomWorld extends FlutterWidgetTesterWorld {
ExternalApplicationManager _externalApplicationManager;
ExternalApplicationManager get externalApplicationManager =>

View File

@ -68,7 +68,7 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
);
await runStep(
'Given I fill the "todo" field with "Buy apples"',
'And I fill the "todo" field with "Buy apples"',
<String>[],
null,
dependencies,
@ -82,7 +82,7 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
);
await runStep(
'Given I fill the "todo" field with "Buy blueberries"',
'And I fill the "todo" field with "Buy blueberries"',
<String>[],
null,
dependencies,
@ -102,6 +102,13 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
'[{"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,
);
},
);
},

View File

@ -32,3 +32,4 @@ export 'src/flutter/hooks/attach_screenshot_on_failed_step_hook.dart';
export 'src/flutter/configuration/flutter_driver_test_configuration.dart';
export 'src/flutter/adapters/flutter_driver_app_driver_adapter.dart';
export 'src/flutter/reporters/flutter_driver_reporter.dart';
export 'src/flutter/world/flutter_driver_world.dart';

View File

@ -37,3 +37,4 @@ export 'src/flutter/hooks/attach_screenshot_on_failed_step_hook.dart';
export 'src/flutter/adapters/widget_tester_app_driver_adapter.dart';
export 'src/flutter/code_generation/annotations/gherkin_full_test_suite_annotation.dart';
export 'src/flutter/runners/gherkin_integration_test_runner.dart';
export 'src/flutter/world/flutter_widget_tester_world.dart';

View File

@ -3,11 +3,9 @@ import 'package:flutter_gherkin/flutter_gherkin.dart';
import '../runners/flutter_run_process_handler.dart';
class FlutterDriverWorld extends FlutterWorld {
class FlutterDriverWorld extends FlutterWorld<FlutterDriver> {
FlutterRunProcessHandler _flutterRunProcessHandler;
FlutterDriver get driver => appDriver.rawDriver as FlutterDriver;
void setFlutterDriver(FlutterDriver flutterDriver) {
setAppAdapter(FlutterDriverAppDriverAdapter(flutterDriver));
}
@ -47,8 +45,8 @@ class FlutterDriverWorld extends FlutterWorld {
Future<void> _closeDriver({
Duration timeout = const Duration(seconds: 60),
}) async {
if (driver != null) {
await driver.close().catchError(
if (rawAppDriver != null) {
await rawAppDriver.close().catchError(
(e, st) {
// Avoid an unhandled error
return null;

View File

@ -0,0 +1,8 @@
import 'package:flutter_gherkin/flutter_gherkin.dart';
/// The world object that can be used to store state during a single test.
/// It also allows interaction with the app under test through the `appDriver`
/// which exposes an instance of `AppDriverAdapter` and an
/// instance of `WidgetTester` via the property `rawAppDriver`
class FlutterWidgetTesterWorld<WidgetTester>
extends FlutterWorld<WidgetTester> {}

View File

@ -3,15 +3,27 @@ import 'package:gherkin/gherkin.dart';
import '../adapters/app_driver_adapter.dart';
class FlutterWorld extends World {
/// The world object that can be used to store state during a single test.
/// It also allows interaction with the app under test through the `appDriver`
/// which exposes an instance of `AppDriverAdapter`
class FlutterWorld<TDriver> extends World {
AppDriverAdapter _adapter;
/// The adapter that is used to agnostically drive the app under test
AppDriverAdapter get appDriver => _adapter;
/// The underlying driver that is able to instrument the app under test
/// It is suggested you use `appDriver` for all interactions with the app under tests
/// however if you need a specific api not available on `appDriver` this property
/// exposes the actual class that can interact with the app under test
TDriver get rawAppDriver => _adapter.rawDriver as TDriver;
/// Sets the app driver that is used to control the app under test
void setAppAdapter(AppDriverAdapter appAdapter) {
_adapter = appAdapter;
}
/// Restart the app under test
Future<bool> restartApp({
Duration timeout = const Duration(seconds: 60),
}) {