feat(null-safety): first pass of null safety

This commit is contained in:
Jon Samwell 2021-05-01 13:56:17 +10:00
parent 172dde7a56
commit 31bf38cef1
49 changed files with 585 additions and 752 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\\.pub-cache\\hosted\\pub.dartlang.org\\integration_test-1.0.2+2\\
integration_test=C:\\Google\\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\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\integration_test-1.0.2+2\\\\","dependencies":[]}],"android":[{"name":"integration_test","path":"C:\\\\Google\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\integration_test-1.0.2+2\\\\","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]}],"date_created":"2021-02-10 11:35:15.103743","version":"1.22.6"}
{"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-05-01 13:05:07.936285","version":"2.0.5"}

View File

@ -27,7 +27,7 @@ targets:
# Allows the code generator to target files outside of the lib folder
- integration_test/**.dart
```
3. Add the following file (and folder) `example_with_integration_test\test_driver\integration_test_driver.dart`. This file is the entry point to run your tests. See `https://flutter.dev/docs/testing/integration-tests` for more information.
3. Add the following file (and folder) `\test_driver\integration_test_driver.dart`. This file is the entry point to run your tests. See `https://flutter.dev/docs/testing/integration-tests` for more information.
```dart
import 'package:integration_test/integration_test_driver.dart' as integration_test_driver;

View File

@ -13,13 +13,13 @@ class MyApp extends StatelessWidget {
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
home: MyHomePage('Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
MyHomePage(this.title) : super();
final String title;
@ -60,8 +60,8 @@ class _MyHomePageState extends State<MyHomePage> {
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
key: const Key('increment'),
child: Icon(Icons.add),
),
);
}

View File

@ -35,49 +35,28 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0-nullsafety.1"
version: "2.5.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.1"
build:
dependency: transitive
description:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.0"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.5"
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.3"
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.1"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "1.2.0"
cli_util:
dependency: transitive
description:
@ -91,14 +70,14 @@ packages:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.1"
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0-nullsafety.3"
version: "1.15.0"
convert:
dependency: transitive
description:
@ -112,7 +91,7 @@ packages:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.2"
version: "0.15.2"
crypto:
dependency: transitive
description:
@ -120,34 +99,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.5"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.10"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.1"
version: "1.2.0"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.0-nullsafety.2"
version: "6.0.0"
flutter:
dependency: "direct main"
description: flutter
@ -178,12 +143,10 @@ packages:
gherkin:
dependency: transitive
description:
path: "."
ref: "2eb30d84c3f7d7f6fa2ad5f93c8d9bfb160a3ced"
resolved-ref: "2eb30d84c3f7d7f6fa2ad5f93c8d9bfb160a3ced"
url: "https://github.com/jonsamwell/dart_gherkin.git"
source: git
version: "1.1.10"
name: gherkin
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
glob:
dependency: transitive
description:
@ -191,90 +154,62 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
http:
dependency: transitive
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
version: "3.0.1"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.4"
integration_test:
dependency: transitive
description:
name: integration_test
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "4.0.0"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.4"
version: "1.0.0"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3-nullsafety.2"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.1"
json_rpc_2:
dependency: transitive
description:
name: json_rpc_2
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.2"
version: "0.6.3"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "0.11.4"
version: "1.0.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10-nullsafety.1"
version: "0.12.10"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.3"
version: "1.3.0"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.7"
version: "1.0.0"
node_interop:
dependency: transitive
description:
@ -295,7 +230,7 @@ packages:
name: node_preamble
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.12"
version: "1.4.13"
package_config:
dependency: transitive
description:
@ -309,35 +244,35 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety.1"
version: "1.8.0"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0-nullsafety.2"
version: "1.11.0"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0-nullsafety.2"
version: "3.0.0"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.0-nullsafety.2"
version: "1.5.0"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0-nullsafety.2"
version: "4.0.0"
pub_semver:
dependency: transitive
description:
@ -345,95 +280,81 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.4"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.7"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.9"
version: "1.1.1"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.0.1"
shelf_static:
dependency: transitive
description:
name: shelf_static
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.9+1"
version: "0.2.9+2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.3"
version: "0.2.4+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: "0.9.10+1"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.3"
version: "2.1.0"
source_maps:
dependency: transitive
description:
name: source_maps
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.10-nullsafety.2"
version: "0.10.10"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety.2"
version: "1.8.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0-nullsafety.1"
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.1"
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.1"
version: "1.1.0"
sync_http:
dependency: transitive
description:
@ -447,42 +368,42 @@ packages:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.1"
version: "1.2.0"
test:
dependency: transitive
dependency: "direct overridden"
description:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0-nullsafety.5"
version: "1.16.5"
test_api:
dependency: transitive
dependency: "direct overridden"
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.19-nullsafety.2"
version: "0.3.0"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.12-nullsafety.5"
version: "0.3.15"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.3"
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.3"
version: "2.1.0"
vm_service:
dependency: transitive
description:
@ -490,13 +411,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.5.0"
vm_service_client:
dependency: transitive
description:
name: vm_service_client
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.6+2"
watcher:
dependency: transitive
description:
@ -510,7 +424,7 @@ packages:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.0"
webdriver:
dependency: transitive
description:
@ -524,7 +438,7 @@ packages:
name: webkit_inspection_protocol
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.4"
version: "1.0.0"
yaml:
dependency: transitive
description:
@ -533,5 +447,5 @@ packages:
source: hosted
version: "2.2.1"
sdks:
dart: ">=2.10.0 <2.11.0"
flutter: ">=1.17.0 <2.0.0"
dart: ">=2.12.3 <3.0.0"
flutter: ">=2.0.0"

View File

@ -6,13 +6,15 @@ publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.0
dependency_overrides:
test: ^1.16.5 # gherkin still depends on 1.16.8 :-(
test_api: ^0.3.0 # flutter_test still depends on 0.2.19 :-(
dev_dependencies:
flutter_test:
@ -21,5 +23,4 @@ dev_dependencies:
path: ../
flutter:
uses-material-design: true

View File

@ -1,29 +1,24 @@
import 'dart:async';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_gherkin/flutter_gherkin_with_driver.dart';
import 'package:gherkin/gherkin.dart';
Future<void> main() {
final steps = <StepDefinitionGeneric>[];
final config = FlutterDriverTestConfiguration.DEFAULT(
steps,
Iterable.empty(),
featurePath: 'features/**.feature',
targetAppPath: 'test_driver/app.dart',
)
..hooks = []
..customStepParameterDefinitions = []
..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
..exitAfterTestRun = true; // set to false if debugging to exit cleanly
..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
return GherkinRunner().execute(config);
}

View File

@ -1,3 +0,0 @@
{
"gherkin_results": "{\"test\":\"moo1\"}"
}

View File

@ -1,7 +1,7 @@
import 'package:example_with_integration_test/main.dart';
import 'package:example_with_integration_test/services/external_application_manager.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_simple_dependency_injection/injector.dart';
import 'package:gherkin/gherkin.dart';

View File

@ -1,6 +1,5 @@
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';
@ -8,7 +7,7 @@ final thenIExpectTheTodos = then1<GherkinTable, FlutterWorld>(
'I expect the todo list',
(table, context) async {
expect(context.configuration.timeout, isNotNull);
expect(context.configuration.timeout.inSeconds, 5);
expect(context.configuration.timeout!.inSeconds, 5);
// get the parent list
final listTileFinder = context.world.appDriver.findBy(

View File

@ -1,6 +1,6 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:gherkin/gherkin.dart';
import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart';
import 'package:flutter_test/flutter_test.dart';
/// Shows are example of using the `WidgetTester` from the `World` context rather than

View File

@ -1,5 +1,5 @@
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:gherkin/gherkin.dart';
import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart';
import 'package:flutter_test/flutter_test.dart';
final whenStepHasTimeout = when<FlutterWidgetTesterWorld>(

View File

@ -1,11 +1,11 @@
import 'package:example_with_integration_test/services/external_application_manager.dart';
import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
class CustomWorld extends FlutterWidgetTesterWorld {
ExternalApplicationManager _externalApplicationManager;
ExternalApplicationManager? _externalApplicationManager;
ExternalApplicationManager get externalApplicationManager =>
_externalApplicationManager;
_externalApplicationManager!;
void setExternalApplicationManager(ExternalApplicationManager manager) {
_externalApplicationManager = manager;

View File

@ -1,14 +1,12 @@
import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:gherkin/gherkin.dart';
import 'gherkin/configuration.dart';
part 'gherkin_suite_test.g.dart';
@GherkinTestSuite()
void main() {
executeTestSuite(
gherkinTestConfiguration,
appInitializationFn,
);
// executeTestSuite(
// gherkinTestConfiguration,
// appInitializationFn,
// );
}

View File

@ -109,6 +109,13 @@ class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
null,
dependencies,
);
await runStep(
'When I test the default step timeout is not applied to step with custom timeout',
<String>[],
null,
dependencies,
);
},
);
},

View File

@ -7,7 +7,7 @@ class TodoBloc {
final Subject<void> _dataRefresher = BehaviorSubject.seeded(null);
final Subject<Todo> _newModel = ReplaySubject(maxSize: 1);
final TodoRepository _repository;
Stream<Iterable<Todo>> _todos;
late final Stream<Iterable<Todo>> _todos;
Stream<Iterable<Todo>> get todos => _todos;
Stream<Todo> get newModel => _newModel;

View File

@ -14,11 +14,11 @@ void main() {
}
class TodoApp extends StatelessWidget {
final ExternalApplicationManager externalApplicationManager;
final ExternalApplicationManager? externalApplicationManager;
final Injector injector;
TodoApp({
this.injector,
required this.injector,
this.externalApplicationManager,
}) : super() {
ModuleContainer().initialise(injector);
@ -40,7 +40,7 @@ class TodoApp extends StatelessWidget {
return externalApplicationManager == null
? app
: StreamBuilder(
stream: externalApplicationManager.applicationReset,
stream: externalApplicationManager!.applicationReset,
builder: (ctx, _) => app,
);
}

View File

@ -5,11 +5,11 @@ part 'todo_model.g.dart';
@JsonSerializable()
class Todo {
String id;
DateTime created;
DateTime updated;
String action;
TodoStatus status;
late String id;
late DateTime created;
late DateTime updated;
late String action;
late TodoStatus status;
Todo();

View File

@ -9,54 +9,44 @@ part of 'todo_model.dart';
Todo _$TodoFromJson(Map<String, dynamic> json) {
return Todo()
..id = json['id'] as String
..created = json['created'] == null
? null
: DateTime.parse(json['created'] as String)
..updated = json['updated'] == null
? null
: DateTime.parse(json['updated'] as String)
..created = DateTime.parse(json['created'] as String)
..updated = DateTime.parse(json['updated'] as String)
..action = json['action'] as String
..status = _$enumDecodeNullable(_$TodoStatusEnumMap, json['status']);
..status = _$enumDecode(_$TodoStatusEnumMap, json['status']);
}
Map<String, dynamic> _$TodoToJson(Todo instance) => <String, dynamic>{
'id': instance.id,
'created': instance.created?.toIso8601String(),
'updated': instance.updated?.toIso8601String(),
'created': instance.created.toIso8601String(),
'updated': instance.updated.toIso8601String(),
'action': instance.action,
'status': _$TodoStatusEnumMap[instance.status],
};
T _$enumDecode<T>(
Map<T, dynamic> enumValues,
dynamic source, {
T unknownValue,
K _$enumDecode<K, V>(
Map<K, V> enumValues,
Object? source, {
K? unknownValue,
}) {
if (source == null) {
throw ArgumentError('A value must be provided. Supported values: '
'${enumValues.values.join(', ')}');
throw ArgumentError(
'A value must be provided. Supported values: '
'${enumValues.values.join(', ')}',
);
}
final value = enumValues.entries
.singleWhere((e) => e.value == source, orElse: () => null)
?.key;
if (value == null && unknownValue == null) {
throw ArgumentError('`$source` is not one of the supported values: '
'${enumValues.values.join(', ')}');
}
return value ?? unknownValue;
}
T _$enumDecodeNullable<T>(
Map<T, dynamic> enumValues,
dynamic source, {
T unknownValue,
}) {
if (source == null) {
return null;
}
return _$enumDecode<T>(enumValues, source, unknownValue: unknownValue);
return enumValues.entries.singleWhere(
(e) => e.value == source,
orElse: () {
if (unknownValue == null) {
throw ArgumentError(
'`$source` is not one of the supported values: '
'${enumValues.values.join(', ')}',
);
}
return MapEntry(unknownValue, enumValues.values.first);
},
).key;
}
const _$TodoStatusEnumMap = {

View File

@ -15,7 +15,7 @@ class TodoRepository {
ReplaySubject<SharedPreferences>(maxSize: 1);
TodoRepository() {
StreamSubscription<SharedPreferences> sub;
StreamSubscription<SharedPreferences>? sub;
sub = SharedPreferences.getInstance().asStream().listen(
(sp) {
_storage.add(sp);

View File

@ -9,8 +9,8 @@ class AddTodoComponent extends StatefulWidget {
final Stream<Todo> todo;
const AddTodoComponent({
@required this.todo,
@required this.onAdded,
required this.todo,
required this.onAdded,
}) : super();
@override
@ -63,12 +63,12 @@ class _AddTodoComponentState extends State<AddTodoComponent>
),
),
FloatingActionButton(
child: Icon(Icons.add),
key: const Key('add'),
onPressed: onAdd,
backgroundColor: Theme.of(context).primaryColor,
focusElevation: 3,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
child: const Icon(Icons.add),
),
],
),

View File

@ -7,11 +7,11 @@ class ViewUtilsMixin {
@protected
StreamSubscription<T> subscribeOnce<T>(
Stream<T> stream, {
void Function(T data) onData,
void Function(Object, StackTrace) onError,
void Function() onDone,
void Function(T data)? onData,
void Function(Object, StackTrace)? onError,
void Function()? onDone,
}) {
StreamSubscription<T> sub;
StreamSubscription<T>? sub;
var hasErrored = false;
return sub = stream.listen(
(data) {

View File

@ -11,8 +11,8 @@ class HomeView extends StatefulWidget {
final TodoBloc Function() blocFactory;
const HomeView({
@required this.blocFactory,
Key key,
required this.blocFactory,
Key? key,
}) : super(key: key);
@override
@ -56,7 +56,7 @@ class _HomeViewState extends State<HomeView> with ViewUtilsMixin {
stream: bloc.todos,
builder: (_, snapshot) {
if (snapshot.hasData) {
final data = snapshot.data;
final data = snapshot.data!;
if (data.isEmpty) {
return Center(
child: Column(
@ -108,7 +108,7 @@ class _HomeViewState extends State<HomeView> with ViewUtilsMixin {
subscribeOnce(
bloc.remove(todo),
onDone: () {
Scaffold.of(context).showSnackBar(
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Todo deleted'),
),
@ -121,7 +121,7 @@ class _HomeViewState extends State<HomeView> with ViewUtilsMixin {
todo.action,
style: Theme.of(context)
.textTheme
.bodyText1
.bodyText1!
.copyWith(
decoration:
todo.status == TodoStatus.complete

View File

@ -7,14 +7,14 @@ packages:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "12.0.0"
version: "21.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "0.40.6"
version: "1.5.0"
archive:
dependency: transitive
description:
@ -35,56 +35,56 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0-nullsafety.1"
version: "2.5.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.1"
version: "2.1.0"
build:
dependency: transitive
description:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.0"
version: "2.0.1"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.5"
version: "0.4.7"
build_daemon:
dependency: transitive
description:
name: build_daemon
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
version: "2.1.10"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.1"
version: "2.0.0"
build_runner:
dependency: "direct dev"
description:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.11"
version: "1.12.2"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.5"
version: "6.1.12"
built_collection:
dependency: transitive
description:
@ -105,35 +105,35 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.3"
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.1"
version: "1.2.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "2.0.1"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
version: "0.3.0"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.1"
version: "1.1.0"
code_builder:
dependency: transitive
description:
@ -147,63 +147,56 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0-nullsafety.3"
version: "1.15.0"
convert:
dependency: transitive
dependency: "direct overridden"
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "3.0.0"
coverage:
dependency: transitive
description:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.2"
version: "0.15.2"
crypto:
dependency: transitive
dependency: "direct overridden"
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.5"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
version: "3.0.1"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.10"
version: "2.0.1"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.1"
version: "1.2.0"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
version: "1.0.0"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.0-nullsafety.2"
version: "6.0.0"
fixnum:
dependency: transitive
description:
@ -234,7 +227,7 @@ packages:
name: flutter_simple_dependency_injection
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "2.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
@ -253,33 +246,24 @@ packages:
gherkin:
dependency: transitive
description:
path: "."
ref: f0b3f955191b53a78075ed2c9387ea8bee12f111
resolved-ref: f0b3f955191b53a78075ed2c9387ea8bee12f111
url: "https://github.com/jonsamwell/dart_gherkin.git"
source: git
version: "1.1.10"
name: gherkin
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "2.0.1"
graphs:
dependency: transitive
description:
name: graphs
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
http:
dependency: transitive
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.2"
version: "1.0.0"
http_multi_server:
dependency: transitive
description:
@ -296,11 +280,9 @@ packages:
version: "3.1.4"
integration_test:
dependency: "direct dev"
description:
name: integration_test
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
description: flutter
source: sdk
version: "0.9.2+2"
io:
dependency: transitive
description:
@ -314,49 +296,42 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3-nullsafety.2"
version: "0.6.3"
json_annotation:
dependency: "direct main"
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.1"
json_rpc_2:
dependency: transitive
description:
name: json_rpc_2
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.2"
version: "4.0.1"
json_serializable:
dependency: "direct main"
description:
name: json_serializable
url: "https://pub.dartlang.org"
source: hosted
version: "3.5.1"
version: "4.1.1"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "0.11.4"
version: "1.0.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10-nullsafety.1"
version: "0.12.10"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.3"
version: "1.3.0"
mime:
dependency: transitive
description:
@ -364,111 +339,97 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.7"
node_interop:
dependency: transitive
description:
name: node_interop
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
node_io:
dependency: transitive
description:
name: node_io
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
node_preamble:
dependency: transitive
description:
name: node_preamble
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.12"
version: "1.4.13"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.3"
version: "2.0.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety.1"
version: "1.8.0"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+2"
version: "2.0.0"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "2.0.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4+3"
version: "2.0.1"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0-nullsafety.2"
version: "1.11.0"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0-nullsafety.2"
version: "3.0.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "2.0.0"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.0-nullsafety.2"
version: "1.5.0"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0-nullsafety.2"
version: "4.0.0"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.4"
version: "2.0.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.7"
version: "1.0.0"
quiver:
dependency: transitive
description:
@ -482,49 +443,49 @@ packages:
name: rxdart
url: "https://pub.dartlang.org"
source: hosted
version: "0.25.0"
version: "0.26.0"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.12+4"
version: "2.0.5"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2+4"
version: "2.0.0"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+11"
version: "2.0.0"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "2.0.0"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2+7"
version: "2.0.0"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+3"
version: "2.0.0"
shelf:
dependency: transitive
description:
@ -538,14 +499,14 @@ packages:
name: shelf_packages_handler
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.0.1"
shelf_static:
dependency: transitive
description:
name: shelf_static
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.9+1"
version: "0.2.9+2"
shelf_web_socket:
dependency: transitive
description:
@ -564,56 +525,56 @@ packages:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.10+1"
version: "1.0.0"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.3"
version: "2.1.0"
source_maps:
dependency: transitive
description:
name: source_maps
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.10-nullsafety.2"
version: "0.10.10"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety.2"
version: "1.8.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0-nullsafety.1"
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.1"
version: "2.1.0"
stream_transform:
dependency: transitive
description:
name: stream_transform
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "2.0.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.1"
version: "1.1.0"
sync_http:
dependency: transitive
description:
@ -627,28 +588,28 @@ packages:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.1"
version: "1.2.0"
test:
dependency: transitive
dependency: "direct overridden"
description:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0-nullsafety.5"
version: "1.16.5"
test_api:
dependency: transitive
dependency: "direct overridden"
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.19-nullsafety.2"
version: "0.3.0"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.12-nullsafety.5"
version: "0.3.15"
timing:
dependency: transitive
description:
@ -662,21 +623,21 @@ packages:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.3"
version: "1.3.0"
uuid:
dependency: "direct main"
description:
name: uuid
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.2"
version: "3.0.4"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.3"
version: "2.1.0"
vm_service:
dependency: transitive
description:
@ -684,20 +645,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.5.0"
vm_service_client:
dependency: transitive
description:
name: vm_service_client
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.6+2"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.7+15"
version: "1.0.0"
web_socket_channel:
dependency: transitive
description:
@ -718,28 +672,28 @@ packages:
name: webkit_inspection_protocol
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.4"
version: "1.0.0"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.4"
version: "2.0.5"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2"
version: "0.2.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
version: "3.1.0"
sdks:
dart: ">=2.10.0 <2.11.0"
flutter: ">=1.17.0 <2.0.0"
dart: ">=2.12.3 <3.0.0"
flutter: ">=2.0.0"

View File

@ -6,26 +6,30 @@ publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
json_serializable: ^4.1.1
json_annotation: ^4.0.1
rxdart: ^0.26.0
shared_preferences: ^2.0.5
uuid: ^3.0.4
flutter_simple_dependency_injection: ^2.0.0
cupertino_icons: ^1.0.0
json_serializable: ^3.5.1
json_annotation: ^3.1.1
rxdart: ^0.25.0
shared_preferences: ^0.5.12+4
uuid: ^2.2.2
flutter_simple_dependency_injection: ^1.0.4
dependency_overrides:
crypto: ^3.0.0 # flutter_driver still depends on 2.1.5 :-(
convert: ^3.0.0 # flutter_driver still depends on >=2.0.0 :-(
test: ^1.16.5 # gherkin still depends on 1.16.8 :-(
test_api: ^0.3.0 # flutter_test still depends on 0.2.19 :-(
dev_dependencies:
flutter_test:
sdk: flutter
integration_test: ^1.0.0
build_runner: ^1.10.11
integration_test:
sdk: flutter
build_runner:
flutter_gherkin:
path: ../

View File

@ -1,5 +1,10 @@
library flutter_gherkin;
/// ***************************************
/// Library export for use with integration_test with include reference to flutter_test which reference dart:ui
/// which are not allowed when running tests with flutter_driver hence this separate library declaration file
/// ***************************************
// Flutter specific implementations
export 'src/flutter/configuration/build_mode.dart';
export 'src/flutter/world/flutter_world.dart';
@ -28,11 +33,8 @@ export 'src/flutter/steps/wait_until_type_exists_step.dart';
// Hooks
export 'src/flutter/hooks/attach_screenshot_on_failed_step_hook.dart';
// Flutter WidgetTester specific implementations
// integration_test specific exports
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';
// Flutter driver specific implementations
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

@ -1,10 +1,5 @@
library flutter_gherkin;
/// ***************************************
/// Library export for use with integration_test with include reference to flutter_test which reference dart:ui
/// which are not allowed when running tests with flutter_driver hence this separate library declaration file
/// ***************************************
// Flutter specific implementations
export 'src/flutter/configuration/build_mode.dart';
export 'src/flutter/world/flutter_world.dart';
@ -33,8 +28,8 @@ export 'src/flutter/steps/wait_until_type_exists_step.dart';
// Hooks
export 'src/flutter/hooks/attach_screenshot_on_failed_step_hook.dart';
// integration_test specific exports
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';
// Flutter driver specific implementations
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

@ -61,34 +61,34 @@ abstract class AppDriverAdapter<TRawAdapter, TFinderType, TWidgetBaseType> {
Future<bool> isPresent(
TFinderType finder, {
Duration timeout = const Duration(seconds: 1),
Duration? timeout = const Duration(seconds: 1),
});
Future<bool> isAbsent(
TFinderType finder, {
Duration timeout = const Duration(seconds: 1),
Duration? timeout = const Duration(seconds: 1),
});
Future<void> enterText(
TFinderType finder,
String text, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
});
Future<String> getText(
Future<String?> getText(
TFinderType finder, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
});
Future<void> tap(
TFinderType finder, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
});
Future<void> longPress(
TFinderType finder, {
Duration pressDuration = const Duration(milliseconds: 500),
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
});
Future<void> pageBack();
@ -98,8 +98,8 @@ abstract class AppDriverAdapter<TRawAdapter, TFinderType, TWidgetBaseType> {
TFinderType finder, {
double dx,
double dy,
Duration duration = const Duration(milliseconds: 200),
Duration timeout = const Duration(seconds: 30),
Duration? duration = const Duration(milliseconds: 200),
Duration? timeout = const Duration(seconds: 30),
});
/// Repeatedly scrolls a [Scrollable] by delta until finder is visible.
@ -110,26 +110,26 @@ abstract class AppDriverAdapter<TRawAdapter, TFinderType, TWidgetBaseType> {
TFinderType scrollable,
double dx,
double dy,
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
});
Future<void> scrollIntoView(
TFinderType finder, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
});
/// Will wait until the give condition returns `true` polling every `pollInterval`. If `condition` has not returned true
/// within the given `timeout` this will cause the returned future to complete with a [TimeoutException].
Future<void> waitUntil(
Future<bool> Function() condition, {
Duration timeout = const Duration(seconds: 10),
Duration pollInterval = const Duration(milliseconds: 500),
Duration? timeout = const Duration(seconds: 10),
Duration? pollInterval = const Duration(milliseconds: 500),
}) async {
return Future.microtask(
() async {
final completer = Completer<void>();
var maxAttempts =
(timeout.inMilliseconds / pollInterval.inMilliseconds).round();
(timeout!.inMilliseconds / pollInterval!.inMilliseconds).round();
var attempts = 0;
while (attempts < maxAttempts) {
@ -143,7 +143,7 @@ abstract class AppDriverAdapter<TRawAdapter, TFinderType, TWidgetBaseType> {
}
},
).timeout(
timeout,
timeout!,
);
}

View File

@ -10,25 +10,26 @@ class FlutterDriverAppDriverAdapter
@override
Future<int> waitForAppToSettle({
Duration duration = const Duration(milliseconds: 100),
Duration timeout = const Duration(seconds: 30),
Duration? duration = const Duration(milliseconds: 100),
Duration? timeout = const Duration(seconds: 30),
}) async {
try {
await rawDriver.waitUntilNoTransientCallbacks(timeout: timeout);
} catch (_) {
return null;
return 1;
}
return null;
return 0;
}
@override
Future<T> widget<T extends Object>(
Future<T> widget<T extends dynamic>(
SerializableFinder finder, [
ExpectedWidgetResultType expectResultType = ExpectedWidgetResultType.first,
]) {
throw UnimplementedError(
'Flutter driver does not support directly interacting with the widget tree');
'Flutter driver does not support directly interacting with the widget tree',
);
}
@override
@ -49,7 +50,7 @@ class FlutterDriverAppDriverAdapter
@override
Future<bool> isPresent(
SerializableFinder finder, {
Duration timeout = const Duration(seconds: 1),
Duration? timeout = const Duration(seconds: 1),
}) async {
try {
await rawDriver.waitFor(
@ -65,7 +66,7 @@ class FlutterDriverAppDriverAdapter
@override
Future<bool> isAbsent(
SerializableFinder finder, {
Duration timeout = const Duration(seconds: 1),
Duration? timeout = const Duration(seconds: 1),
}) async {
try {
await rawDriver.waitForAbsent(
@ -79,9 +80,9 @@ class FlutterDriverAppDriverAdapter
}
@override
Future<String> getText(
Future<String?> getText(
SerializableFinder finder, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
}) async {
await waitForAppToSettle(timeout: timeout);
@ -95,7 +96,7 @@ class FlutterDriverAppDriverAdapter
Future<void> enterText(
SerializableFinder finder,
String text, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
}) async {
await tap(
finder,
@ -110,7 +111,7 @@ class FlutterDriverAppDriverAdapter
@override
Future<void> tap(
SerializableFinder finder, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
}) async {
await rawDriver.tap(finder, timeout: timeout);
await waitForAppToSettle(timeout: timeout);
@ -119,8 +120,8 @@ class FlutterDriverAppDriverAdapter
@override
Future<void> longPress(
SerializableFinder finder, {
Duration pressDuration = const Duration(milliseconds: 500),
Duration timeout = const Duration(seconds: 30),
Duration? pressDuration = const Duration(milliseconds: 500),
Duration? timeout = const Duration(seconds: 30),
}) async {
await scroll(
finder,
@ -135,10 +136,10 @@ class FlutterDriverAppDriverAdapter
@override
Future<void> scroll(
SerializableFinder finder, {
double dx,
double dy,
Duration duration = const Duration(milliseconds: 200),
Duration timeout = const Duration(seconds: 30),
double? dx,
double? dy,
Duration? duration = const Duration(milliseconds: 200),
Duration? timeout = const Duration(seconds: 30),
}) async {
await rawDriver.scroll(
finder,
@ -165,8 +166,6 @@ class FlutterDriverAppDriverAdapter
case FindType.type:
return find.byType(data.toString());
}
throw Exception('unknown finder');
}
@override
@ -202,10 +201,10 @@ class FlutterDriverAppDriverAdapter
@override
Future<void> scrollUntilVisible(
SerializableFinder item, {
SerializableFinder scrollable,
double dx,
double dy,
Duration timeout = const Duration(seconds: 30),
SerializableFinder? scrollable,
double? dx,
double? dy,
Duration? timeout = const Duration(seconds: 30),
}) async {
await rawDriver.scrollUntilVisible(
scrollable,
@ -219,7 +218,7 @@ class FlutterDriverAppDriverAdapter
@override
Future<void> scrollIntoView(
SerializableFinder finder, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
}) async {
await rawDriver.scrollIntoView(
finder,

View File

@ -14,20 +14,18 @@ class WidgetTesterAppDriverAdapter
@override
Future<int> waitForAppToSettle({
Duration duration = const Duration(milliseconds: 100),
Duration timeout = const Duration(seconds: 30),
Duration? duration = const Duration(milliseconds: 100),
Duration? timeout = const Duration(seconds: 30),
}) async {
try {
await rawDriver.pumpAndSettle(
duration,
EnginePhase.sendSemanticsUpdate,
timeout,
return await rawDriver.pumpAndSettle(
duration!,
EnginePhase.paint,
timeout!,
);
} catch (_) {
return null;
return 1;
}
return null;
}
@override
@ -48,24 +46,25 @@ class WidgetTesterAppDriverAdapter
@override
Future<List<int>> screenshot() {
var renderObject = rawDriver.binding.renderViewElement.renderObject;
while (!renderObject.isRepaintBoundary) {
var renderObject = rawDriver.binding.renderViewElement?.renderObject;
while (renderObject != null && !renderObject.isRepaintBoundary) {
renderObject = renderObject.parent as RenderObject;
assert(renderObject != null);
}
assert(!renderObject.debugNeedsPaint);
final layer = renderObject.debugLayer as OffsetLayer;
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());
.then((value) => value?.buffer.asUint8List() ?? List<int>.empty());
}
@override
Future<bool> isPresent(
Finder finder, {
Duration timeout = const Duration(seconds: 1),
Duration? timeout = const Duration(seconds: 1),
}) async {
return finder.evaluate().isNotEmpty;
}
@ -73,15 +72,15 @@ class WidgetTesterAppDriverAdapter
@override
Future<bool> isAbsent(
Finder finder, {
Duration timeout = const Duration(seconds: 1),
Duration? timeout = const Duration(seconds: 1),
}) async {
return await isPresent(finder).then((value) => !value);
}
@override
Future<String> getText(
Future<String?> getText(
Finder finder, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
}) async {
await waitForAppToSettle(timeout: timeout);
@ -102,7 +101,7 @@ class WidgetTesterAppDriverAdapter
Future<void> enterText(
Finder finder,
String text, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
}) async {
await tap(
finder,
@ -117,7 +116,7 @@ class WidgetTesterAppDriverAdapter
@override
Future<void> tap(
Finder finder, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
}) async {
await rawDriver.tap(finder);
await waitForAppToSettle(timeout: timeout);
@ -126,8 +125,8 @@ class WidgetTesterAppDriverAdapter
@override
Future<void> longPress(
Finder finder, {
Duration pressDuration = const Duration(milliseconds: 500),
Duration timeout = const Duration(seconds: 30),
Duration? pressDuration = const Duration(milliseconds: 500),
Duration? timeout = const Duration(seconds: 30),
}) async {
await scroll(
finder,
@ -142,10 +141,10 @@ class WidgetTesterAppDriverAdapter
@override
Future<void> scroll(
Finder finder, {
double dx,
double dy,
Duration duration = const Duration(milliseconds: 200),
Duration timeout = const Duration(seconds: 30),
double? dx,
double? dy,
Duration? duration = const Duration(milliseconds: 200),
Duration? timeout = const Duration(seconds: 30),
}) async {
final scrollableFinder = findByDescendant(
finder,
@ -154,7 +153,7 @@ class WidgetTesterAppDriverAdapter
);
final state = rawDriver.state(scrollableFinder) as ScrollableState;
final position = state.position;
position.jumpTo(dy ?? dx);
position.jumpTo(dy ?? dx ?? 0);
await rawDriver.pump();
}
@ -174,8 +173,6 @@ class WidgetTesterAppDriverAdapter
case FindType.type:
return find.byType(data);
}
throw Exception('unknown finder');
}
@override
@ -209,14 +206,14 @@ class WidgetTesterAppDriverAdapter
@override
Future<void> scrollUntilVisible(
Finder item, {
Finder scrollable,
double dx,
double dy,
Duration timeout = const Duration(seconds: 30),
Finder? scrollable,
double? dx,
double? dy,
Duration? timeout = const Duration(seconds: 30),
}) async {
await rawDriver.scrollUntilVisible(
item,
dy ?? dx,
dy ?? dx ?? 0,
scrollable: scrollable,
);
}
@ -224,7 +221,7 @@ class WidgetTesterAppDriverAdapter
@override
Future<void> scrollIntoView(
Finder finder, {
Duration timeout = const Duration(seconds: 30),
Duration? timeout = const Duration(seconds: 30),
}) async {
await rawDriver.ensureVisible(finder);
await waitForAppToSettle();

View File

@ -4,7 +4,7 @@ import 'package:gherkin/gherkin.dart';
/// to the style required by the integration_test package
class GherkinTestSuite {
/// Path to the feature files to generate tests for
final Iterable<String> featurePaths;
final Iterable<Pattern> featurePaths;
/// The execution order of features - this default to random to avoid any inter-test dependencies
final ExecutionOrder executionOrder;

View File

@ -5,6 +5,7 @@ import 'package:analyzer/dart/element/element.dart';
import 'package:flutter_gherkin/src/flutter/code_generation/annotations/gherkin_full_test_suite_annotation.dart';
import 'package:gherkin/gherkin.dart';
import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';
import 'package:source_gen/source_gen.dart';
class NoOpReporter extends Reporter {}
@ -45,25 +46,25 @@ void executeTestSuite(
_languageService.initialise(
annotation.read('featureDefaultLanguage').literalValue.toString(),
);
final executionOrder = ExecutionOrder.values[annotation
final idx = annotation
.read('executionOrder')
.objectValue
.getField('index')
.toIntValue()];
.getField('index')!
.toIntValue()!;
final executionOrder = ExecutionOrder.values[idx];
final featureFiles = annotation
.read('featurePaths')
.listValue
.map((path) => Glob(path.toStringValue()))
.map((path) => Glob(path.toStringValue()!))
.map(
(glob) => glob
.listSync()
(glob) => glob.listSync()
.map((entity) => File(entity.path).readAsStringSync())
.toList(),
)
.reduce((value, element) => value..addAll(element));
if (executionOrder == ExecutionOrder.random) {
featureFiles..shuffle();
featureFiles.shuffle();
}
final featureExecutionFunctionsBuilder = StringBuffer();
@ -146,9 +147,9 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
''';
final StringBuffer _buffer = StringBuffer();
int _id;
String _currentFeatureCode;
String _currentScenarioCode;
int? _id;
String? _currentFeatureCode;
String? _currentScenarioCode;
final StringBuffer _scenarioBuffer = StringBuffer();
final StringBuffer _stepBuffer = StringBuffer();
@ -176,18 +177,21 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
@override
Future<void> visitFeature(
String name,
String description,
String? description,
Iterable<String> tags,
) async {
_currentFeatureCode =
_replaceVariable(FUNCTION_TEMPLATE, 'feature_number', _id.toString());
_currentFeatureCode = _replaceVariable(
_currentFeatureCode,
FUNCTION_TEMPLATE,
'feature_number',
_id.toString(),
);
_currentFeatureCode = _replaceVariable(
_currentFeatureCode!,
'feature_name',
_escapeText(name),
);
_currentFeatureCode = _replaceVariable(
_currentFeatureCode,
_currentFeatureCode!,
'tags',
'<String>[${tags.map((e) => "'$e'").join(', ')}]',
);
@ -202,7 +206,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
_escapeText(name),
);
_currentScenarioCode = _replaceVariable(
_currentScenarioCode,
_currentScenarioCode!,
'tags',
'<String>[${tags.map((e) => "'$e'").join(', ')}]',
);
@ -212,7 +216,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
Future<void> visitScenarioStep(
String name,
Iterable<String> multiLineStrings,
GherkinTable table,
GherkinTable? table,
) async {
var code = _replaceVariable(
STEP_TEMPLATE,
@ -236,7 +240,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
void _flushFeature() {
if (_currentFeatureCode != null) {
_currentFeatureCode = _replaceVariable(
_currentFeatureCode,
_currentFeatureCode!,
'scenarios',
_scenarioBuffer.toString(),
);
@ -252,7 +256,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
if (_currentScenarioCode != null) {
if (_stepBuffer.isNotEmpty) {
_currentScenarioCode = _replaceVariable(
_currentScenarioCode,
_currentScenarioCode!,
'steps',
_stepBuffer.toString(),
);

View File

@ -1,6 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_gherkin/flutter_gherkin_with_driver.dart';
import 'package:flutter_gherkin/src/flutter/configuration/build_mode.dart';
import 'package:flutter_gherkin/src/flutter/world/flutter_driver_world.dart';
import 'package:flutter_gherkin/src/flutter/world/flutter_world.dart';
@ -12,7 +12,7 @@ import 'package:glob/glob.dart';
import 'flutter_test_configuration.dart';
class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
String _observatoryDebuggerUri;
String? _observatoryDebuggerUri;
/// 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.
@ -36,8 +36,7 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
]
..targetAppPath = targetAppPath
..stepDefinitions = steps
..restartAppBetweenScenarios = true
..exitAfterTestRun = true;
..restartAppBetweenScenarios = true;
}
/// restarts the application under test between each scenario.
@ -51,11 +50,11 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
/// Option to define the working directory for the process that runs the app under test (optional)
/// Handy if your app is separated from your tests as flutter needs to be able to find a pubspec file
String targetAppWorkingDirectory;
String? targetAppWorkingDirectory;
/// The build flavor to run the tests against (optional)
/// Defaults to empty
String buildFlavor = '';
/// Defaults to null
String? buildFlavor;
/// The default build mode used for running tests is --debug.
/// We are exposing the option to run the tests also in --profile mode
@ -66,8 +65,8 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
bool build = true;
/// The target device id to run the tests against when multiple devices detected
/// Defaults to empty
String targetDeviceId = '';
/// Defaults to null
String? targetDeviceId;
/// Will keep the Flutter application running when done testing
/// Defaults to false
@ -100,19 +99,19 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
/// An observatory url that the test runner can connect to instead of creating a new running instance of the target application
/// Url takes the form of `http://127.0.0.1:51540/EM72VtRsUV0=/` and usually printed to stdout in the form `Connecting to service protocol: http://127.0.0.1:51540/EM72VtRsUV0=/`
/// You will have to add the `--verbose` flag to the command to start your flutter app to see this output and ensure `enableFlutterDriverExtension()` is called by the running app
String runningAppProtocolEndpointUri;
String? runningAppProtocolEndpointUri;
/// Called before any attempt to connect Flutter driver to the running application, Depending on your configuration this
/// method will be called before each scenario is run.
Future<void> Function() onBeforeFlutterDriverConnect;
Future<void> Function()? onBeforeFlutterDriverConnect;
/// Called after the successful connection of Flutter driver to the running application. Depending on your configuration this
/// method will be called on each new connection usually before each scenario is run.
Future<void> Function(FlutterDriver driver) onAfterFlutterDriverConnect;
Future<void> Function(FlutterDriver driver)? onAfterFlutterDriverConnect;
void setObservatoryDebuggerUri(String uri) => _observatoryDebuggerUri = uri;
Future<FlutterDriver> createFlutterDriver([String dartVmServiceUrl]) async {
Future<FlutterDriver> createFlutterDriver([String? dartVmServiceUrl]) async {
final completer = Completer<FlutterDriver>();
dartVmServiceUrl = (dartVmServiceUrl ?? _observatoryDebuggerUri) ??
Platform.environment['VM_SERVICE_URL'];
@ -120,12 +119,12 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
await runZonedGuarded(
() async {
if (onBeforeFlutterDriverConnect != null) {
await onBeforeFlutterDriverConnect();
await onBeforeFlutterDriverConnect!();
}
final driver = await _attemptDriverConnection(dartVmServiceUrl, 1, 3);
if (onAfterFlutterDriverConnect != null) {
await onAfterFlutterDriverConnect(driver);
await onAfterFlutterDriverConnect!(driver);
}
completer.complete(driver);
@ -142,14 +141,15 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
Future<FlutterWorld> createFlutterWorld(
TestConfiguration config,
FlutterWorld world,
FlutterWorld? world,
) async {
var flutterConfig = config as FlutterDriverTestConfiguration;
world = world ?? FlutterDriverWorld();
final driver = await createFlutterDriver(
flutterConfig.runningAppProtocolEndpointUri != null &&
flutterConfig.runningAppProtocolEndpointUri.isNotEmpty
flutterConfig.runningAppProtocolEndpointUri != null &&
flutterConfig.runningAppProtocolEndpointUri!.isNotEmpty
? flutterConfig.runningAppProtocolEndpointUri
: null,
);
@ -165,19 +165,19 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
_ensureCorrectConfiguration();
final providedCreateWorld = createWorld;
createWorld = (config) async {
FlutterWorld world;
FlutterWorld? world;
if (providedCreateWorld != null) {
world = await providedCreateWorld(config);
world = await providedCreateWorld(config) as FlutterWorld;
}
return await createFlutterWorld(config, world);
};
hooks = List.from(hooks ?? [])..add(FlutterAppRunnerHook());
hooks = List.from(hooks ?? Iterable.empty())..add(FlutterAppRunnerHook());
}
Future<FlutterDriver> _attemptDriverConnection(
String dartVmServiceUrl,
String? dartVmServiceUrl,
int attempt,
int maxAttempts,
) async {
@ -189,7 +189,8 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
throw e;
} else {
print(
'Fluter driver error connecting to application at `$dartVmServiceUrl`, retrying after delay of $flutterDriverReconnectionDelay',
'Fluter driver error connecting to application at `$dartVmServiceUrl`,'
'retrying after delay of $flutterDriverReconnectionDelay',
);
await Future<void>.delayed(flutterDriverReconnectionDelay);
@ -205,15 +206,17 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
void _ensureCorrectConfiguration() {
if (runningAppProtocolEndpointUri != null &&
runningAppProtocolEndpointUri.isNotEmpty) {
runningAppProtocolEndpointUri!.isNotEmpty) {
if (restartAppBetweenScenarios) {
throw AssertionError(
'Cannot restart app between scenarios if using runningAppProtocolEndpointUri');
'Cannot restart app between scenarios if using runningAppProtocolEndpointUri',
);
}
if (targetDeviceId != null && targetDeviceId.isNotEmpty) {
if (targetDeviceId != null && targetDeviceId!.isNotEmpty) {
throw AssertionError(
'Cannot target specific device id if using runningAppProtocolEndpointUri');
'Cannot target specific device id if using runningAppProtocolEndpointUri',
);
}
}
}

View File

@ -1,4 +1,4 @@
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_gherkin/flutter_gherkin_with_driver.dart';
import 'package:flutter_gherkin/src/flutter/parameters/existence_parameter.dart';
import 'package:flutter_gherkin/src/flutter/parameters/swipe_direction_parameter.dart';
import 'package:flutter_gherkin/src/flutter/steps/given_i_open_the_drawer_step.dart';
@ -45,12 +45,12 @@ class FlutterTestConfiguration extends TestConfiguration {
@override
void prepare() {
customStepParameterDefinitions =
List.from(customStepParameterDefinitions ?? [])
List.from(customStepParameterDefinitions ?? Iterable.empty())
..addAll([
ExistenceParameter(),
SwipeDirectionParameter(),
]);
stepDefinitions = List.from(stepDefinitions ?? [])
stepDefinitions = List.from(stepDefinitions ?? Iterable.empty())
..addAll([
ThenExpectElementToHaveValue(),
WhenTapBackButtonWidget(),

View File

@ -7,7 +7,7 @@ import 'package:gherkin/gherkin.dart';
/// A hook that manages running the target flutter application
/// that is under test
class FlutterAppRunnerHook extends Hook {
FlutterRunProcessHandler _flutterRunProcessHandler;
FlutterRunProcessHandler? _flutterRunProcessHandler;
bool haveRunFirstScenario = false;
@override
@ -55,17 +55,18 @@ class FlutterAppRunnerHook extends Hook {
Iterable<Tag> tags,
) async {
if (world is FlutterDriverWorld) {
world.setFlutterProcessHandler(_flutterRunProcessHandler);
world.setFlutterProcessHandler(_flutterRunProcessHandler!);
}
}
Future<void> _runApp(FlutterDriverTestConfiguration config) async {
if (config.runningAppProtocolEndpointUri != null &&
config.runningAppProtocolEndpointUri.isNotEmpty) {
config.runningAppProtocolEndpointUri!.isNotEmpty) {
stdout.writeln(
"Connecting to running Flutter app under test at '${config.runningAppProtocolEndpointUri}', this might take a few moments",
"Connecting to running Flutter app under test at '${config.runningAppProtocolEndpointUri}', "
'this might take a few moments',
);
config.setObservatoryDebuggerUri(config.runningAppProtocolEndpointUri);
config.setObservatoryDebuggerUri(config.runningAppProtocolEndpointUri!);
} else {
_flutterRunProcessHandler = FlutterRunProcessHandler()
..setLogFlutterProcessOutput(config.logFlutterProcessOutput)
@ -81,8 +82,8 @@ class FlutterAppRunnerHook extends Hook {
stdout.writeln(
"Starting Flutter app under test '${config.targetAppPath}', this might take a few moments");
await _flutterRunProcessHandler.run();
final observatoryUri = await _flutterRunProcessHandler
await _flutterRunProcessHandler!.run();
final observatoryUri = await _flutterRunProcessHandler!
.waitForObservatoryDebuggerUri(config.flutterBuildTimeout);
config.setObservatoryDebuggerUri(observatoryUri);
}
@ -91,7 +92,7 @@ class FlutterAppRunnerHook extends Hook {
Future<void> _terminateApp() async {
if (_flutterRunProcessHandler != null) {
stdout.writeln('Terminating Flutter app under test');
await _flutterRunProcessHandler.terminate();
await _flutterRunProcessHandler!.terminate();
_flutterRunProcessHandler = null;
}
}
@ -99,7 +100,7 @@ class FlutterAppRunnerHook extends Hook {
Future<void> _restartApp() async {
if (_flutterRunProcessHandler != null) {
stdout.writeln('Restarting Flutter app under test');
await _flutterRunProcessHandler.restart();
await _flutterRunProcessHandler!.restart();
}
}

View File

@ -31,7 +31,7 @@ class FlutterDriverReporter extends Reporter {
@override
Future<void> dispose() async {
driverLog = null;
// driverLog = null;
}
void _driverLogMessageHandler(String source, String message) {

View File

@ -41,20 +41,20 @@ class FlutterRunProcessHandler extends ProcessHandler {
multiLine: false,
);
Process _runningProcess;
Stream<String> _processStdoutStream;
Process? _runningProcess;
Stream<String>? _processStdoutStream;
final List<StreamSubscription> _openSubscriptions = <StreamSubscription>[];
bool _buildApp = true;
bool _logFlutterProcessOutput = false;
bool _verboseFlutterLogs = false;
bool _keepAppRunning = false;
BuildMode _buildMode = BuildMode.Debug;
String _workingDirectory;
String _appTarget;
String _buildFlavor;
String _deviceTargetId;
String? _workingDirectory;
String? _appTarget;
String? _buildFlavor;
String? _deviceTargetId;
Duration _driverConnectionDelay = const Duration(seconds: 2);
String currentObservatoryUri;
String? currentObservatoryUri;
void setLogFlutterProcessOutput(bool logFlutterProcessOutput) {
_logFlutterProcessOutput = logFlutterProcessOutput;
@ -64,15 +64,15 @@ class FlutterRunProcessHandler extends ProcessHandler {
_appTarget = targetPath;
}
void setDriverConnectionDelay(Duration duration) {
void setDriverConnectionDelay(Duration? duration) {
_driverConnectionDelay = duration ?? _driverConnectionDelay;
}
void setWorkingDirectory(String workingDirectory) {
void setWorkingDirectory(String? workingDirectory) {
_workingDirectory = workingDirectory;
}
void setBuildFlavor(String buildFlavor) {
void setBuildFlavor(String? buildFlavor) {
_buildFlavor = buildFlavor;
}
@ -80,7 +80,7 @@ class FlutterRunProcessHandler extends ProcessHandler {
_buildMode = buildMode;
}
void setDeviceTargetId(String deviceTargetId) {
void setDeviceTargetId(String? deviceTargetId) {
_deviceTargetId = deviceTargetId;
}
@ -110,11 +110,11 @@ class FlutterRunProcessHandler extends ProcessHandler {
arguments.add('--no-build');
}
if (_buildFlavor != null && _buildFlavor.isNotEmpty) {
if (_buildFlavor != null && _buildFlavor!.isNotEmpty) {
arguments.add('--flavor=$_buildFlavor');
}
if (_deviceTargetId != null && _deviceTargetId.isNotEmpty) {
if (_deviceTargetId != null && _deviceTargetId!.isNotEmpty) {
arguments.add('--device-id=$_deviceTargetId');
}
@ -140,16 +140,16 @@ class FlutterRunProcessHandler extends ProcessHandler {
);
_processStdoutStream =
_runningProcess.stdout.transform(utf8.decoder).asBroadcastStream();
_runningProcess!.stdout.transform(utf8.decoder).asBroadcastStream();
_openSubscriptions.add(_runningProcess.stderr
_openSubscriptions.add(_runningProcess!.stderr
.map((events) => String.fromCharCodes(events).trim())
.where((event) => event.isNotEmpty)
.listen((event) {
if (event.contains(_errorMessageRegex)) {
stderr.writeln('${FAIL_COLOR}Flutter build error: $event$RESET_COLOR');
} else {
// This is most likely a depricated api usage warnings (from Gradle) and should not
// 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');
}
@ -161,19 +161,21 @@ class FlutterRunProcessHandler extends ProcessHandler {
var exitCode = -1;
_ensureRunningProcess();
if (_runningProcess != null) {
_runningProcess.stdin.write('q');
_runningProcess!.stdin.write('q');
_openSubscriptions.forEach((s) => s.cancel());
_openSubscriptions.clear();
exitCode = await _runningProcess.exitCode;
exitCode = await _runningProcess!.exitCode;
_runningProcess = null;
}
return exitCode;
}
Future<bool> restart({Duration timeout = const Duration(seconds: 90)}) async {
Future<bool> restart({
Duration? timeout = const Duration(seconds: 90),
}) async {
_ensureRunningProcess();
_runningProcess.stdin.write('R');
_runningProcess!.stdin.write('R');
await _waitForStdOutMessage(
_restartedApplicationSuccessRegex,
'Timeout waiting for app restart',
@ -196,18 +198,18 @@ class FlutterRunProcessHandler extends ProcessHandler {
timeout,
);
return currentObservatoryUri;
return currentObservatoryUri!;
}
Future<String> _waitForStdOutMessage(
RegExp matcher,
String timeoutMessage, [
Duration timeout = const Duration(seconds: 90),
Duration? timeout = const Duration(seconds: 90),
]) {
_ensureRunningProcess();
final completer = Completer<String>();
StreamSubscription sub;
sub = _processStdoutStream.timeout(
StreamSubscription? sub;
sub = _processStdoutStream!.timeout(
timeout ?? const Duration(seconds: 90),
onTimeout: (_) {
sub?.cancel();
@ -223,7 +225,7 @@ class FlutterRunProcessHandler extends ProcessHandler {
if (matcher.hasMatch(logLine)) {
sub?.cancel();
if (!completer.isCompleted) {
completer.complete(matcher.firstMatch(logLine).group(1));
completer.complete(matcher.firstMatch(logLine)!.group(1));
}
} else if (_noConnectedDeviceRegex.hasMatch(logLine)) {
sub?.cancel();

View File

@ -1,10 +1,11 @@
import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_gherkin/src/flutter/adapters/widget_tester_app_driver_adapter.dart';
import 'package:flutter_gherkin/src/flutter/world/flutter_world.dart';
import 'package:gherkin/gherkin.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:collection/collection.dart';
class TestDependencies {
final World world;
@ -21,15 +22,15 @@ abstract class GherkinIntegrationTestRunner {
TagExpressionEvaluator();
final TestConfiguration configuration;
final void Function(World world) appMainFunction;
Reporter _reporter;
Hook _hook;
Iterable<ExecutableStep> _executableSteps;
Iterable<CustomParameter> _customParameters;
Reporter? _reporter;
Hook? _hook;
Iterable<ExecutableStep>? _executableSteps;
Iterable<CustomParameter>? _customParameters;
IntegrationTestWidgetsFlutterBinding _binding;
IntegrationTestWidgetsFlutterBinding? _binding;
Reporter get reporter => _reporter;
Hook get hook => _hook;
Reporter get reporter => _reporter!;
Hook get hook => _hook!;
Timeout scenarioExecutionTimeout = const Timeout(Duration(minutes: 10));
@ -43,8 +44,8 @@ abstract class GherkinIntegrationTestRunner {
_customParameters =
_registerCustomParameters(configuration.customStepParameterDefinitions);
_executableSteps = _registerStepDefinitions(
configuration.stepDefinitions,
_customParameters,
configuration.stepDefinitions!,
_customParameters!,
);
}
@ -68,7 +69,7 @@ abstract class GherkinIntegrationTestRunner {
void onRunComplete() {
_safeInvokeFuture(() async => await reporter.onTestRunFinished());
_safeInvokeFuture(() async => await hook.onAfterRun(configuration));
setTestResultData(_binding);
setTestResultData(_binding!);
_safeInvokeFuture(() async => await reporter.dispose());
}
@ -82,7 +83,7 @@ abstract class GherkinIntegrationTestRunner {
@protected
void runFeature(
String name,
Iterable<String> tags,
Iterable<String>? tags,
void Function() runFeature,
) {
group(
@ -90,7 +91,7 @@ abstract class GherkinIntegrationTestRunner {
() {
final debugInformation = RunnableDebugInformation('', 0, name);
final featureTags =
(tags ?? Iterable<Tag>.empty()).map((t) => Tag(t, 0));
(tags ?? Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0));
_safeInvokeFuture(
() async => await reporter.onFeatureStarted(
StartedMessage(
@ -120,7 +121,7 @@ abstract class GherkinIntegrationTestRunner {
@protected
void runScenario(
String name,
Iterable<String> tags,
Iterable<String>? tags,
Future<void> Function(TestDependencies dependencies) runTest,
) {
if (_evaluateTagFilterExpression(configuration.tagExpression, tags)) {
@ -129,13 +130,13 @@ abstract class GherkinIntegrationTestRunner {
(WidgetTester tester) async {
final debugInformation = RunnableDebugInformation('', 0, name);
final scenarioTags =
(tags ?? Iterable<Tag>.empty()).map((t) => Tag(t, 0));
(tags ?? Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0));
final dependencies = await createTestDependencies(
configuration,
tester,
);
await _hook.onBeforeScenario(
await hook.onBeforeScenario(
configuration,
name,
scenarioTags,
@ -146,7 +147,7 @@ abstract class GherkinIntegrationTestRunner {
dependencies.world,
);
await _hook.onAfterScenarioWorldCreated(
await hook.onAfterScenarioWorldCreated(
dependencies.world,
name,
scenarioTags,
@ -163,7 +164,7 @@ abstract class GherkinIntegrationTestRunner {
await runTest(dependencies);
await _reporter.onScenarioFinished(
await reporter.onScenarioFinished(
ScenarioFinishedMessage(
name,
debugInformation,
@ -171,7 +172,7 @@ abstract class GherkinIntegrationTestRunner {
),
);
await _hook.onAfterScenario(
await hook.onAfterScenario(
configuration,
name,
scenarioTags,
@ -184,7 +185,7 @@ abstract class GherkinIntegrationTestRunner {
} else {
_safeInvokeFuture(
() async => reporter.message(
'Ignoring scenario `${name}` as tag expression `${configuration.tagExpression}` not satisfied',
'Ignoring scenario `$name` as tag expression `${configuration.tagExpression}` not satisfied',
MessageLevel.info,
),
);
@ -205,12 +206,12 @@ abstract class GherkinIntegrationTestRunner {
TestConfiguration configuration,
WidgetTester tester,
) async {
World world;
World? world;
final attachmentManager =
await configuration.getAttachmentManager(configuration);
if (configuration.createWorld != null) {
world = await configuration.createWorld(configuration);
world = await configuration.createWorld!(configuration);
}
world = world ?? FlutterWidgetTesterWorld();
@ -231,9 +232,8 @@ abstract class GherkinIntegrationTestRunner {
dynamic table,
TestDependencies dependencies,
) async {
final executable = _executableSteps.firstWhere(
final executable = _executableSteps!.firstWhereOrNull(
(s) => s.expression.isMatch(step),
orElse: () => null,
);
if (executable == null) {
@ -280,11 +280,14 @@ abstract class GherkinIntegrationTestRunner {
@protected
void cleanupScenarioRun(TestDependencies dependencies) {
_safeInvokeFuture(
() async => await dependencies.attachmentManager.dispose());
_safeInvokeFuture(() async => await dependencies.world.dispose());
() async => dependencies.attachmentManager.dispose(),
);
_safeInvokeFuture(
() async => dependencies.world.dispose(),
);
}
Reporter _registerReporters(Iterable<Reporter> reporters) {
Reporter _registerReporters(Iterable<Reporter>? reporters) {
final reporter = AggregatedReporter();
if (reporters != null) {
reporters.forEach((r) => reporter.addReporter(r));
@ -293,7 +296,7 @@ abstract class GherkinIntegrationTestRunner {
return reporter;
}
Hook _registerHooks(Iterable<Hook> hooks) {
Hook _registerHooks(Iterable<Hook>? hooks) {
final hook = AggregatedHook();
if (hooks != null) {
hook.addHooks(hooks);
@ -303,7 +306,7 @@ abstract class GherkinIntegrationTestRunner {
}
Iterable<CustomParameter> _registerCustomParameters(
Iterable<CustomParameter> customParameters,
Iterable<CustomParameter>? customParameters,
) {
final parameters = <CustomParameter>[];
@ -332,7 +335,7 @@ abstract class GherkinIntegrationTestRunner {
return stepDefinitions
.map(
(s) => ExecutableStep(
GherkinExpression(s.pattern, customParameters),
GherkinExpression(s.pattern as RegExp, customParameters),
s,
),
)
@ -362,12 +365,13 @@ abstract class GherkinIntegrationTestRunner {
StepResult result,
TestDependencies dependencies,
) async {
await _hook.onAfterStep(
await hook.onAfterStep(
dependencies.world,
step,
result,
);
await _reporter.onStepFinished(
await reporter.onStepFinished(
StepFinishedMessage(
step,
RunnableDebugInformation('', 0, step),
@ -383,7 +387,7 @@ abstract class GherkinIntegrationTestRunner {
table,
Iterable<String> multiLineStrings,
) async {
await _hook.onBeforeStep(world, step);
await hook.onBeforeStep(world, step);
await reporter.onStepStarted(
StepStartedMessage(
step,
@ -402,11 +406,11 @@ abstract class GherkinIntegrationTestRunner {
}
bool _evaluateTagFilterExpression(
String tagExpression,
List<String> tags,
String? tagExpression,
Iterable<String>? tags,
) {
return tagExpression == null || tagExpression.isEmpty
? true
: _tagExpressionEvaluator.evaluate(tagExpression, tags);
: _tagExpressionEvaluator.evaluate(tagExpression, tags!.toList());
}
}

View File

@ -34,7 +34,7 @@ StepDefinitionGeneric GivenOpenDrawer() {
'Open navigation menu',
FindType.tooltip,
),
timeout: context.configuration?.timeout,
timeout: context.configuration.timeout,
);
}

View File

@ -6,7 +6,7 @@ StepDefinitionGeneric RestartAppStep() {
'I restart the app',
(context) async {
await context.world.restartApp(
timeout: context.configuration?.timeout,
timeout: context.configuration.timeout,
);
},
);

View File

@ -31,7 +31,7 @@ StepDefinitionGeneric SiblingContainsTextStep() {
final isPresent = await context.world.appDriver.isPresent(
valueWidget,
timeout: context.configuration?.timeout ?? const Duration(seconds: 20),
timeout: context.configuration.timeout ?? const Duration(seconds: 20),
);
context.expect(isPresent, true);

View File

@ -14,7 +14,7 @@ StepDefinitionGeneric TapTextWithinWidgetStep() {
r'I tap the (?:button|element|label|field|text|widget) that contains the text {string} within the {string}'),
(text, ancestorKey, context) async {
final timeout =
context.configuration?.timeout ?? const Duration(seconds: 20);
context.configuration.timeout ?? const Duration(seconds: 20);
final finder = context.world.appDriver.findByDescendant(
context.world.appDriver.findBy(ancestorKey, FindType.key),
context.world.appDriver.findBy(text, FindType.text),

View File

@ -1,12 +1,12 @@
import 'package:flutter_driver/flutter_driver.dart';
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_gherkin/flutter_gherkin_with_driver.dart';
import '../runners/flutter_run_process_handler.dart';
/// Driver version of the FlutterWorld with a typed driver
class FlutterDriverWorld extends FlutterTypedAdapterWorld<FlutterDriver,
SerializableFinder, dynamic> {
FlutterRunProcessHandler _flutterRunProcessHandler;
FlutterRunProcessHandler? _flutterRunProcessHandler;
void setFlutterDriver(FlutterDriver flutterDriver) {
setAppAdapter(FlutterDriverAppDriverAdapter(flutterDriver));
@ -20,7 +20,7 @@ class FlutterDriverWorld extends FlutterTypedAdapterWorld<FlutterDriver,
@override
Future<bool> restartApp({
Duration timeout = const Duration(seconds: 60),
Duration? timeout = const Duration(seconds: 60),
}) async {
await _closeDriver(timeout: timeout);
final result = await _flutterRunProcessHandler?.restart(
@ -28,12 +28,12 @@ class FlutterDriverWorld extends FlutterTypedAdapterWorld<FlutterDriver,
);
final driver = await FlutterDriver.connect(
dartVmServiceUrl: _flutterRunProcessHandler.currentObservatoryUri,
dartVmServiceUrl: _flutterRunProcessHandler!.currentObservatoryUri,
);
setFlutterDriver(driver);
return result;
return result!;
}
@override
@ -45,8 +45,9 @@ class FlutterDriverWorld extends FlutterTypedAdapterWorld<FlutterDriver,
}
Future<void> _closeDriver({
Duration timeout = const Duration(seconds: 60),
Duration? timeout = const Duration(seconds: 60),
}) async {
// ignore: unnecessary_null_comparison
if (rawAppDriver != null) {
await rawAppDriver.close().catchError(
(e, st) {

View File

@ -1,6 +1,6 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_gherkin/flutter_gherkin_integration_test.dart';
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`

View File

@ -7,10 +7,10 @@ import '../adapters/app_driver_adapter.dart';
/// It also allows interaction with the app under test through the `appDriver`
/// which exposes an instance of `AppDriverAdapter`
class FlutterWorld extends World {
AppDriverAdapter _adapter;
AppDriverAdapter? _adapter;
/// The adapter that is used to agnostically drive the app under test
AppDriverAdapter get appDriver => _adapter;
AppDriverAdapter get appDriver => _adapter!;
/// Sets the app driver that is used to control the app under test
void setAppAdapter(AppDriverAdapter appAdapter) {
@ -19,7 +19,7 @@ class FlutterWorld extends World {
/// Restart the app under test
Future<bool> restartApp({
Duration timeout = const Duration(seconds: 60),
Duration? timeout = const Duration(seconds: 60),
}) {
throw UnimplementedError('Unable to restart the app during the test');
}
@ -34,7 +34,7 @@ class FlutterTypedAdapterWorld<TDriver, TFinder, TWidget> extends FlutterWorld {
/// 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;
TDriver get rawAppDriver => _adapter!.rawDriver as TDriver;
/// The adapter that is used to agnostically drive the app under test
@override

View File

@ -7,14 +7,14 @@ packages:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "12.0.0"
version: "21.0.0"
analyzer:
dependency: "direct main"
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "0.40.6"
version: "1.5.0"
archive:
dependency: transitive
description:
@ -35,112 +35,112 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0-nullsafety.1"
version: "2.5.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.1"
version: "2.1.0"
build:
dependency: "direct main"
dependency: "direct dev"
description:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.2"
version: "2.0.1"
build_config:
dependency: "direct main"
dependency: "direct dev"
description:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.5"
version: "1.0.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.3"
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.1"
version: "1.2.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "2.0.1"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
version: "0.3.0"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.1"
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0-nullsafety.3"
version: "1.15.0"
convert:
dependency: transitive
dependency: "direct overridden"
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "3.0.0"
coverage:
dependency: transitive
description:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.2"
version: "0.15.2"
crypto:
dependency: transitive
dependency: "direct overridden"
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.5"
version: "3.0.1"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.10"
version: "2.0.1"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.1"
version: "1.2.0"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.0-nullsafety.2"
version: "6.0.0"
flutter:
dependency: "direct main"
description: flutter
@ -164,117 +164,85 @@ packages:
gherkin:
dependency: "direct main"
description:
path: "."
ref: f0b3f955191b53a78075ed2c9387ea8bee12f111
resolved-ref: f0b3f955191b53a78075ed2c9387ea8bee12f111
url: "https://github.com/jonsamwell/dart_gherkin.git"
source: git
version: "1.1.10"
name: gherkin
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
glob:
dependency: "direct main"
dependency: "direct dev"
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
http:
dependency: transitive
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.2"
version: "2.0.1"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
version: "3.0.1"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.4"
version: "4.0.0"
integration_test:
dependency: "direct main"
description:
name: integration_test
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2+2"
dependency: "direct dev"
description: flutter
source: sdk
version: "0.9.2+2"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.4"
version: "1.0.0"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3-nullsafety.2"
version: "0.6.3"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.1"
json_rpc_2:
dependency: transitive
description:
name: json_rpc_2
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.2"
version: "4.0.1"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "0.11.4"
version: "1.0.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10-nullsafety.1"
version: "0.12.10"
meta:
dependency: "direct main"
dependency: "direct dev"
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.3"
version: "1.3.0"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.7"
node_interop:
dependency: transitive
description:
name: node_interop
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
node_io:
dependency: transitive
description:
name: node_io
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
version: "1.0.0"
node_preamble:
dependency: transitive
description:
@ -288,138 +256,138 @@ packages:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.3"
version: "2.0.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety.1"
version: "1.8.0"
pedantic:
dependency: "direct dev"
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0-nullsafety.2"
version: "1.11.0"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0-nullsafety.2"
version: "3.0.0"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.0-nullsafety.2"
version: "1.5.0"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0-nullsafety.2"
version: "4.0.0"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.4"
version: "2.0.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.8"
version: "1.0.0"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.9"
version: "1.1.1"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
version: "3.0.0"
shelf_static:
dependency: transitive
description:
name: shelf_static
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.9+2"
version: "1.0.0"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.4"
version: "1.0.1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_gen:
dependency: "direct main"
dependency: "direct dev"
description:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.10+1"
version: "1.0.0"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.3"
version: "2.1.0"
source_maps:
dependency: transitive
description:
name: source_maps
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.10-nullsafety.2"
version: "0.10.10"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety.2"
version: "1.8.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0-nullsafety.1"
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.1"
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.1"
version: "1.1.0"
sync_http:
dependency: transitive
description:
@ -433,42 +401,42 @@ packages:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.1"
version: "1.2.0"
test:
dependency: "direct dev"
description:
name: test
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0-nullsafety.5"
version: "1.16.5"
test_api:
dependency: transitive
dependency: "direct overridden"
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.19-nullsafety.2"
version: "0.3.0"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.12-nullsafety.5"
version: "0.3.15"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.3"
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.3"
version: "2.1.0"
vm_service:
dependency: transitive
description:
@ -476,27 +444,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "5.5.0"
vm_service_client:
dependency: transitive
description:
name: vm_service_client
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.6+2"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.7+15"
version: "1.0.0"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "2.0.0"
webdriver:
dependency: transitive
description:
@ -510,14 +471,14 @@ packages:
name: webkit_inspection_protocol
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.5"
version: "1.0.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
version: "3.1.0"
sdks:
dart: ">=2.10.0 <2.11.0"
flutter: ">=1.17.0"
dart: ">=2.12.3 <3.0.0"
flutter: ">=2.0.0"

View File

@ -4,8 +4,8 @@ version: 1.2.0
homepage: https://github.com/jonsamwell/flutter_gherkin
environment:
sdk: ">=2.7.0 <3.0.0"
flutter: ">=1.17.0"
sdk: '>=2.12.3 <3.0.0'
flutter: ">=2.0.0"
dependencies:
flutter:
@ -14,24 +14,29 @@ dependencies:
sdk: flutter
flutter_driver:
sdk: flutter
analyzer: ">=0.37.0 <0.41.0"
glob: ^1.1.7
meta: ">=1.1.6 <2.0.0"
source_gen: ^0.9.10+1
build: ^1.6.0
build_config: ^0.4.5
integration_test: ^1.0.1
# gherkin: ^1.1.9
gherkin: ^2.0.0
# gherkin:
# path: ../dart_gherkin
gherkin:
git:
url: https://github.com/jonsamwell/dart_gherkin.git
ref: f0b3f955191b53a78075ed2c9387ea8bee12f111
# ref: code_gen_changes
# gherkin:
# git:
# url: https://github.com/jonsamwell/dart_gherkin.git
# ref: f0b3f955191b53a78075ed2c9387ea8bee12f111
dependency_overrides:
crypto: ^3.0.0 # flutter_driver still depends on 2.1.5 :-(
convert: ^3.0.0 # flutter_driver still depends on >=2.0.0 :-(
test: ^1.16.5 # gherkin still depends on 1.16.8 :-(
test_api: ^0.3.0 # flutter_test still depends on 0.2.19 :-(
dev_dependencies:
test:
pedantic: ^1.9.2
integration_test:
sdk: flutter
test: ^1.16.5
meta: ^1.3.0
pedantic: ^1.11.0
glob: ^2.0.1
source_gen: ^1.0.0
build: ^2.0.1
build_config: ^1.0.0
flutter:

View File

@ -1,4 +1,4 @@
import 'package:flutter_gherkin/flutter_gherkin.dart';
import 'package:flutter_gherkin/flutter_gherkin_with_driver.dart';
import 'package:flutter_gherkin/src/flutter/hooks/app_runner_hook.dart';
import 'package:test/test.dart';
@ -13,8 +13,8 @@ void main() {
expect(config.hooks, isNull);
config.prepare();
expect(config.hooks, isNotNull);
expect(config.hooks.length, 1);
expect(config.hooks.elementAt(0), (x) => x is FlutterAppRunnerHook);
expect(config.hooks!.length, 1);
expect(config.hooks!.elementAt(0), (x) => x is FlutterAppRunnerHook);
});
test('common steps definition added', () {
@ -23,25 +23,25 @@ void main() {
config.prepare();
expect(config.stepDefinitions, isNotNull);
expect(config.stepDefinitions.length, 23);
expect(config.stepDefinitions!.length, 23);
expect(config.customStepParameterDefinitions, isNotNull);
expect(config.customStepParameterDefinitions.length, 2);
expect(config.customStepParameterDefinitions!.length, 2);
});
test('common step definition added to existing steps', () {
final config = FlutterTestConfiguration()
..stepDefinitions = [MockStepDefinition()]
..customStepParameterDefinitions = [MockParameter()];
expect(config.stepDefinitions.length, 1);
expect(config.stepDefinitions!.length, 1);
config.prepare();
expect(config.stepDefinitions, isNotNull);
expect(config.stepDefinitions.length, 24);
expect(config.stepDefinitions.elementAt(0),
expect(config.stepDefinitions!.length, 24);
expect(config.stepDefinitions!.elementAt(0),
(x) => x is MockStepDefinition);
expect(config.customStepParameterDefinitions, isNotNull);
expect(config.customStepParameterDefinitions.length, 3);
expect(config.customStepParameterDefinitions.elementAt(0),
expect(config.customStepParameterDefinitions!.length, 3);
expect(config.customStepParameterDefinitions!.elementAt(0),
(x) => x is MockParameter);
});
});

View File

@ -5,7 +5,7 @@ typedef OnRunCode = Future<void> Function(Iterable parameters);
class MockStepDefinition extends StepDefinitionBase<World> {
bool hasRun = false;
int runCount = 0;
final OnRunCode code;
final OnRunCode? code;
MockStepDefinition([this.code, int expectedParameterCount = 0])
: super(null, expectedParameterCount);
@ -15,10 +15,10 @@ class MockStepDefinition extends StepDefinitionBase<World> {
hasRun = true;
runCount += 1;
if (code != null) {
await code(parameters);
await code!(parameters);
}
}
@override
RegExp get pattern => null;
RegExp get pattern => RegExp('');
}