Formatting + Dropdown Widgets
This commit is contained in:
parent
9b817676e4
commit
f20a14f3d8
|
@ -130,8 +130,7 @@ abstract class AppDriverAdapter<TNativeAdapter, TFinderType, TWidgetBaseType> {
|
||||||
return Future.microtask(
|
return Future.microtask(
|
||||||
() async {
|
() async {
|
||||||
final completer = Completer<void>();
|
final completer = Completer<void>();
|
||||||
var maxAttempts =
|
var maxAttempts = (timeout!.inMilliseconds / pollInterval!.inMilliseconds).round();
|
||||||
(timeout!.inMilliseconds / pollInterval!.inMilliseconds).round();
|
|
||||||
var attempts = 0;
|
var attempts = 0;
|
||||||
|
|
||||||
while (attempts < maxAttempts) {
|
while (attempts < maxAttempts) {
|
||||||
|
|
|
@ -4,8 +4,7 @@ import 'package:flutter_driver/flutter_driver.dart';
|
||||||
|
|
||||||
import 'app_driver_adapter.dart';
|
import 'app_driver_adapter.dart';
|
||||||
|
|
||||||
class FlutterDriverAppDriverAdapter
|
class FlutterDriverAppDriverAdapter extends AppDriverAdapter<FlutterDriver, SerializableFinder, dynamic> {
|
||||||
extends AppDriverAdapter<FlutterDriver, SerializableFinder, dynamic> {
|
|
||||||
FlutterDriverAppDriverAdapter(FlutterDriver rawAdapter) : super(rawAdapter);
|
FlutterDriverAppDriverAdapter(FlutterDriver rawAdapter) : super(rawAdapter);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -4,5 +4,4 @@ import 'package:build/build.dart';
|
||||||
import 'package:flutter_gherkin/src/flutter/code_generation/generators/gherkin_suite_test_generator.dart';
|
import 'package:flutter_gherkin/src/flutter/code_generation/generators/gherkin_suite_test_generator.dart';
|
||||||
import 'package:source_gen/source_gen.dart';
|
import 'package:source_gen/source_gen.dart';
|
||||||
|
|
||||||
Builder gherkinTestSuiteBuilder(BuilderOptions options) =>
|
Builder gherkinTestSuiteBuilder(BuilderOptions options) => SharedPartBuilder([GherkinSuiteTestGenerator()], 'gherkin_tests');
|
||||||
SharedPartBuilder([GherkinSuiteTestGenerator()], 'gherkin_tests');
|
|
||||||
|
|
|
@ -25,8 +25,7 @@ class NoOpReporter extends MessageReporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GherkinSuiteTestGenerator
|
class GherkinSuiteTestGenerator extends GeneratorForAnnotation<GherkinTestSuite> {
|
||||||
extends GeneratorForAnnotation<GherkinTestSuite> {
|
|
||||||
static const String template = '''
|
static const String template = '''
|
||||||
class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner {
|
||||||
_CustomGherkinIntegrationTestRunner({
|
_CustomGherkinIntegrationTestRunner({
|
||||||
|
@ -79,21 +78,15 @@ Future<void> executeTestSuite({
|
||||||
_languageService.initialise(
|
_languageService.initialise(
|
||||||
annotation.read('featureDefaultLanguage').literalValue.toString(),
|
annotation.read('featureDefaultLanguage').literalValue.toString(),
|
||||||
);
|
);
|
||||||
final idx = annotation
|
final idx = annotation.read('executionOrder').objectValue.getField('index')!.toIntValue()!;
|
||||||
.read('executionOrder')
|
|
||||||
.objectValue
|
|
||||||
.getField('index')!
|
|
||||||
.toIntValue()!;
|
|
||||||
final executionOrder = ExecutionOrder.values[idx];
|
final executionOrder = ExecutionOrder.values[idx];
|
||||||
final featureFiles = annotation
|
final featureFiles = annotation
|
||||||
.read('featurePaths')
|
.read('featurePaths')
|
||||||
.listValue
|
.listValue
|
||||||
.map((path) => Glob(path.toStringValue()!))
|
.map((path) => Glob(path.toStringValue()!))
|
||||||
.map((glob) =>
|
.map((glob) => glob.listSync().map((entity) => File(entity.path)).toList())
|
||||||
glob.listSync().map((entity) => File(entity.path)).toList())
|
|
||||||
.reduce((value, element) => value..addAll(element));
|
.reduce((value, element) => value..addAll(element));
|
||||||
final useAbsolutePaths =
|
final useAbsolutePaths = annotation.read('useAbsolutePaths').objectValue.toBoolValue();
|
||||||
annotation.read('useAbsolutePaths').objectValue.toBoolValue();
|
|
||||||
|
|
||||||
if (executionOrder == ExecutionOrder.random) {
|
if (executionOrder == ExecutionOrder.random) {
|
||||||
featureFiles.shuffle();
|
featureFiles.shuffle();
|
||||||
|
@ -119,10 +112,7 @@ Future<void> executeTestSuite({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return template
|
return template.replaceAll('{{feature_functions}}', featureExecutionFunctionsBuilder.toString()).replaceAll(
|
||||||
.replaceAll('{{feature_functions}}',
|
|
||||||
featureExecutionFunctionsBuilder.toString())
|
|
||||||
.replaceAll(
|
|
||||||
'{{features_to_execute}}',
|
'{{features_to_execute}}',
|
||||||
featuresToExecute.toString(),
|
featuresToExecute.toString(),
|
||||||
);
|
);
|
||||||
|
@ -333,9 +323,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
||||||
code = _replaceVariable(
|
code = _replaceVariable(
|
||||||
code,
|
code,
|
||||||
'step_table',
|
'step_table',
|
||||||
table == null
|
table == null ? 'null' : 'GherkinTable.fromJson(\'${_escapeText(table.toJson())}\')',
|
||||||
? 'null'
|
|
||||||
: 'GherkinTable.fromJson(\'${_escapeText(table.toJson())}\')',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
_stepBuffer.writeln(code);
|
_stepBuffer.writeln(code);
|
||||||
|
@ -380,8 +368,5 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
|
||||||
return content.replaceAll('{{$property}}', value ?? 'null');
|
return content.replaceAll('{{$property}}', value ?? 'null');
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _escapeText(String? text) => text
|
String? _escapeText(String? text) => text?.replaceAll("\\", "\\\\").replaceAll("'", "\\'").replaceAll(r"$", r"\$");
|
||||||
?.replaceAll("\\", "\\\\")
|
|
||||||
.replaceAll("'", "\\'")
|
|
||||||
.replaceAll(r"$", r"\$");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,15 +142,13 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
|
||||||
|
|
||||||
/// Called after the successful connection of Flutter driver to the running application. Depending on your configuration this
|
/// 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.
|
/// method will be called on each new connection usually before each scenario is run.
|
||||||
final Future<void> Function(FlutterDriver driver)?
|
final Future<void> Function(FlutterDriver driver)? onAfterFlutterDriverConnect;
|
||||||
onAfterFlutterDriverConnect;
|
|
||||||
|
|
||||||
void setObservatoryDebuggerUri(String uri) => _observatoryDebuggerUri = uri;
|
void setObservatoryDebuggerUri(String uri) => _observatoryDebuggerUri = uri;
|
||||||
|
|
||||||
Future<FlutterDriver> createFlutterDriver([String? dartVmServiceUrl]) async {
|
Future<FlutterDriver> createFlutterDriver([String? dartVmServiceUrl]) async {
|
||||||
final completer = Completer<FlutterDriver>();
|
final completer = Completer<FlutterDriver>();
|
||||||
dartVmServiceUrl = (dartVmServiceUrl ?? _observatoryDebuggerUri) ??
|
dartVmServiceUrl = (dartVmServiceUrl ?? _observatoryDebuggerUri) ?? Platform.environment['VM_SERVICE_URL'];
|
||||||
Platform.environment['VM_SERVICE_URL'];
|
|
||||||
|
|
||||||
await runZonedGuarded(
|
await runZonedGuarded(
|
||||||
() async {
|
() async {
|
||||||
|
@ -183,9 +181,7 @@ class FlutterDriverTestConfiguration extends FlutterTestConfiguration {
|
||||||
world = world ?? FlutterDriverWorld();
|
world = world ?? FlutterDriverWorld();
|
||||||
|
|
||||||
final driver = await createFlutterDriver(
|
final driver = await createFlutterDriver(
|
||||||
flutterConfig.runningAppProtocolEndpointUri?.isNotEmpty ?? false
|
flutterConfig.runningAppProtocolEndpointUri?.isNotEmpty ?? false ? flutterConfig.runningAppProtocolEndpointUri : null,
|
||||||
? flutterConfig.runningAppProtocolEndpointUri
|
|
||||||
: null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
(world as FlutterDriverWorld).setFlutterDriver(driver);
|
(world as FlutterDriverWorld).setFlutterDriver(driver);
|
||||||
|
|
|
@ -35,6 +35,8 @@ class FlutterTestConfiguration extends TestConfiguration {
|
||||||
tapWidgetOfTypeStep(),
|
tapWidgetOfTypeStep(),
|
||||||
tapWidgetOfTypeWithinStep(),
|
tapWidgetOfTypeWithinStep(),
|
||||||
tapWidgetWithTextStep(),
|
tapWidgetWithTextStep(),
|
||||||
|
selectDropDownWithTextStep(),
|
||||||
|
scrollDropDown(),
|
||||||
textExistsStep(),
|
textExistsStep(),
|
||||||
textExistsWithinStep(),
|
textExistsWithinStep(),
|
||||||
waitUntilKeyExistsStep(),
|
waitUntilKeyExistsStep(),
|
||||||
|
|
|
@ -21,8 +21,7 @@ class FlutterAppRunnerHook extends Hook {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onAfterRun(TestConfiguration config) async =>
|
Future<void> onAfterRun(TestConfiguration config) async => await _terminateApp();
|
||||||
await _terminateApp();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onBeforeScenario(
|
Future<void> onBeforeScenario(
|
||||||
|
@ -45,8 +44,7 @@ class FlutterAppRunnerHook extends Hook {
|
||||||
}) async {
|
}) async {
|
||||||
final flutterConfig = _castConfig(config);
|
final flutterConfig = _castConfig(config);
|
||||||
haveRunFirstScenario = true;
|
haveRunFirstScenario = true;
|
||||||
if (_flutterRunProcessHandler != null &&
|
if (_flutterRunProcessHandler != null && flutterConfig.restartAppBetweenScenarios) {
|
||||||
flutterConfig.restartAppBetweenScenarios) {
|
|
||||||
await _restartApp();
|
await _restartApp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,8 +84,7 @@ class FlutterAppRunnerHook extends Hook {
|
||||||
"Starting Flutter app under test '${config.targetAppPath}', this might take a few moments",
|
"Starting Flutter app under test '${config.targetAppPath}', this might take a few moments",
|
||||||
);
|
);
|
||||||
await _flutterRunProcessHandler!.run();
|
await _flutterRunProcessHandler!.run();
|
||||||
final observatoryUri = await _flutterRunProcessHandler!
|
final observatoryUri = await _flutterRunProcessHandler!.waitForObservatoryDebuggerUri(config.flutterBuildTimeout);
|
||||||
.waitForObservatoryDebuggerUri(config.flutterBuildTimeout);
|
|
||||||
config.setObservatoryDebuggerUri(observatoryUri);
|
config.setObservatoryDebuggerUri(observatoryUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,8 +104,7 @@ class FlutterAppRunnerHook extends Hook {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlutterDriverTestConfiguration _castConfig(TestConfiguration config) =>
|
FlutterDriverTestConfiguration _castConfig(TestConfiguration config) => config as FlutterDriverTestConfiguration;
|
||||||
config as FlutterDriverTestConfiguration;
|
|
||||||
|
|
||||||
void _log(String text) {
|
void _log(String text) {
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb) {
|
||||||
|
|
|
@ -10,9 +10,7 @@ class AttachScreenshotOnFailedStepHook extends Hook {
|
||||||
String step,
|
String step,
|
||||||
StepResult stepResult,
|
StepResult stepResult,
|
||||||
) async {
|
) async {
|
||||||
if (stepResult.result == StepExecutionResult.fail ||
|
if (stepResult.result == StepExecutionResult.fail || stepResult.result == StepExecutionResult.error || stepResult.result == StepExecutionResult.timeout) {
|
||||||
stepResult.result == StepExecutionResult.error ||
|
|
||||||
stepResult.result == StepExecutionResult.timeout) {
|
|
||||||
try {
|
try {
|
||||||
final screenshotData = await _takeScreenshot(world);
|
final screenshotData = await _takeScreenshot(world);
|
||||||
world.attach(screenshotData, 'image/png', step);
|
world.attach(screenshotData, 'image/png', step);
|
||||||
|
|
|
@ -17,8 +17,7 @@ class ExistenceParameter extends CustomParameter<Existence> {
|
||||||
case 'absent':
|
case 'absent':
|
||||||
return Existence.absent;
|
return Existence.absent;
|
||||||
default:
|
default:
|
||||||
throw ArgumentError(
|
throw ArgumentError('Value `$c` must be defined for this Existence parameter');
|
||||||
'Value `$c` must be defined for this Existence parameter');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,8 +18,7 @@ class SwipeDirectionParameter extends CustomParameter<SwipeDirection> {
|
||||||
case 'up':
|
case 'up':
|
||||||
return SwipeDirection.up;
|
return SwipeDirection.up;
|
||||||
default:
|
default:
|
||||||
throw ArgumentError(
|
throw ArgumentError('"down", "left", "right", or "up" must be defined for this parameter');
|
||||||
'"down", "left", "right", or "up" must be defined for this parameter');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -13,8 +13,7 @@ enum _FlutterDriverMessageLogLevel { info, warning, error }
|
||||||
/// This can cause problems with CI servers for example as they will mark a process as failed if it logs to the
|
/// This can cause problems with CI servers for example as they will mark a process as failed if it logs to the
|
||||||
/// stderr stream. So Flutter driver will log a normal info message to the stderr and thus make
|
/// stderr stream. So Flutter driver will log a normal info message to the stderr and thus make
|
||||||
/// the process fail from the perspective of a CI server.
|
/// the process fail from the perspective of a CI server.
|
||||||
class FlutterDriverReporter extends Reporter
|
class FlutterDriverReporter extends Reporter implements DisposableReporter, TestReporter {
|
||||||
implements DisposableReporter, TestReporter {
|
|
||||||
final bool logErrorMessages;
|
final bool logErrorMessages;
|
||||||
final bool logWarningMessages;
|
final bool logWarningMessages;
|
||||||
final bool logInfoMessages;
|
final bool logInfoMessages;
|
||||||
|
@ -48,8 +47,7 @@ class FlutterDriverReporter extends Reporter
|
||||||
|
|
||||||
if (logWarningMessages && level == _FlutterDriverMessageLogLevel.warning) {
|
if (logWarningMessages && level == _FlutterDriverMessageLogLevel.warning) {
|
||||||
stdout.writeln(log);
|
stdout.writeln(log);
|
||||||
} else if (logErrorMessages &&
|
} else if (logErrorMessages && level == _FlutterDriverMessageLogLevel.error) {
|
||||||
level == _FlutterDriverMessageLogLevel.error) {
|
|
||||||
stderr.writeln(log);
|
stderr.writeln(log);
|
||||||
} else {
|
} else {
|
||||||
stdout.writeln(log);
|
stdout.writeln(log);
|
||||||
|
|
|
@ -132,13 +132,9 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
runInShell: true,
|
runInShell: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
_processStdoutStream =
|
_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) {
|
||||||
.map((events) => String.fromCharCodes(events).trim())
|
|
||||||
.where((event) => event.isNotEmpty)
|
|
||||||
.listen((event) {
|
|
||||||
if (event.contains(_errorMessageRegex)) {
|
if (event.contains(_errorMessageRegex)) {
|
||||||
stderr.writeln(
|
stderr.writeln(
|
||||||
'${StdoutReporter.kFailColor}Flutter build error: $event${StdoutReporter.kResetColor}',
|
'${StdoutReporter.kFailColor}Flutter build error: $event${StdoutReporter.kResetColor}',
|
||||||
|
@ -257,8 +253,7 @@ class FlutterRunProcessHandler extends ProcessHandler {
|
||||||
|
|
||||||
void _ensureRunningProcess() {
|
void _ensureRunningProcess() {
|
||||||
if (_runningProcess == null) {
|
if (_runningProcess == null) {
|
||||||
throw Exception(
|
throw Exception('FlutterRunProcessHandler: flutter run process is not active');
|
||||||
'FlutterRunProcessHandler: flutter run process is not active');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,7 @@ class TestDependencies {
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class GherkinIntegrationTestRunner {
|
abstract class GherkinIntegrationTestRunner {
|
||||||
final TagExpressionEvaluator _tagExpressionEvaluator =
|
final TagExpressionEvaluator _tagExpressionEvaluator = TagExpressionEvaluator();
|
||||||
TagExpressionEvaluator();
|
|
||||||
final FlutterTestConfiguration configuration;
|
final FlutterTestConfiguration configuration;
|
||||||
final StartAppFn appMainFunction;
|
final StartAppFn appMainFunction;
|
||||||
final AppLifecyclePumpHandlerFn? appLifecyclePumpHandler;
|
final AppLifecyclePumpHandlerFn? appLifecyclePumpHandler;
|
||||||
|
@ -69,8 +68,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
configuration.prepare();
|
configuration.prepare();
|
||||||
_registerReporters(configuration.reporters);
|
_registerReporters(configuration.reporters);
|
||||||
_hook = _registerHooks(configuration.hooks);
|
_hook = _registerHooks(configuration.hooks);
|
||||||
_customParameters =
|
_customParameters = _registerCustomParameters(configuration.customStepParameterDefinitions);
|
||||||
_registerCustomParameters(configuration.customStepParameterDefinitions);
|
|
||||||
_executableSteps = _registerStepDefinitions(
|
_executableSteps = _registerStepDefinitions(
|
||||||
configuration.stepDefinitions!,
|
configuration.stepDefinitions!,
|
||||||
_customParameters!,
|
_customParameters!,
|
||||||
|
@ -130,8 +128,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
Iterable<String>? tags,
|
Iterable<String>? tags,
|
||||||
}) async {
|
}) async {
|
||||||
final debugInformation = RunnableDebugInformation(path, 0, name);
|
final debugInformation = RunnableDebugInformation(path, 0, name);
|
||||||
final featureTags =
|
final featureTags = (tags ?? const Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0));
|
||||||
(tags ?? const Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0));
|
|
||||||
await reporter.feature.onStarted.invoke(
|
await reporter.feature.onStarted.invoke(
|
||||||
FeatureMessage(
|
FeatureMessage(
|
||||||
name: name,
|
name: name,
|
||||||
|
@ -291,8 +288,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
WidgetTester tester,
|
WidgetTester tester,
|
||||||
) async {
|
) async {
|
||||||
World? world;
|
World? world;
|
||||||
final attachmentManager =
|
final attachmentManager = await configuration.getAttachmentManager(configuration);
|
||||||
await configuration.getAttachmentManager(configuration);
|
|
||||||
|
|
||||||
if (configuration.createWorld != null) {
|
if (configuration.createWorld != null) {
|
||||||
world = await configuration.createWorld!(configuration);
|
world = await configuration.createWorld!(configuration);
|
||||||
|
@ -305,9 +301,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
WidgetTesterAppDriverAdapter(
|
WidgetTesterAppDriverAdapter(
|
||||||
rawAdapter: tester,
|
rawAdapter: tester,
|
||||||
binding: _binding,
|
binding: _binding,
|
||||||
waitImplicitlyAfterAction: configuration is FlutterTestConfiguration
|
waitImplicitlyAfterAction: configuration is FlutterTestConfiguration ? (configuration).waitImplicitlyAfterAction : true,
|
||||||
? (configuration).waitImplicitlyAfterAction
|
|
||||||
: true,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -366,8 +360,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
parameters,
|
parameters,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!_isNegativeResult(result.result) ||
|
if (!_isNegativeResult(result.result) || configuration.stepMaxRetries == 0) {
|
||||||
configuration.stepMaxRetries == 0) {
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
await Future.delayed(configuration.retryDelay);
|
await Future.delayed(configuration.retryDelay);
|
||||||
|
@ -486,9 +479,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
name: step,
|
name: step,
|
||||||
context: RunnableDebugInformation('', 0, step),
|
context: RunnableDebugInformation('', 0, step),
|
||||||
result: result,
|
result: result,
|
||||||
attachments: dependencies.attachmentManager
|
attachments: dependencies.attachmentManager.getAttachmentsForContext(step).toList(),
|
||||||
.getAttachmentsForContext(step)
|
|
||||||
.toList(),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -505,8 +496,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
name: step,
|
name: step,
|
||||||
context: RunnableDebugInformation('', 0, step),
|
context: RunnableDebugInformation('', 0, step),
|
||||||
table: table,
|
table: table,
|
||||||
multilineString:
|
multilineString: multiLineStrings.isNotEmpty ? multiLineStrings.first : null,
|
||||||
multiLineStrings.isNotEmpty ? multiLineStrings.first : null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -524,9 +514,7 @@ abstract class GherkinIntegrationTestRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isNegativeResult(StepExecutionResult result) {
|
bool _isNegativeResult(StepExecutionResult result) {
|
||||||
return result == StepExecutionResult.error ||
|
return result == StepExecutionResult.error || result == StepExecutionResult.fail || result == StepExecutionResult.timeout;
|
||||||
result == StepExecutionResult.fail ||
|
|
||||||
result == StepExecutionResult.timeout;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _appLifecyclePhasePumper(
|
Future<void> _appLifecyclePhasePumper(
|
||||||
|
|
|
@ -5,16 +5,14 @@ import 'package:gherkin/gherkin.dart';
|
||||||
|
|
||||||
import '../parameters/swipe_direction_parameter.dart';
|
import '../parameters/swipe_direction_parameter.dart';
|
||||||
|
|
||||||
mixin _SwipeHelper
|
mixin _SwipeHelper on When3WithWorld<SwipeDirection, int, String, FlutterWorld> {
|
||||||
on When3WithWorld<SwipeDirection, int, String, FlutterWorld> {
|
|
||||||
Future<void> swipeOnFinder(
|
Future<void> swipeOnFinder(
|
||||||
dynamic finder,
|
dynamic finder,
|
||||||
SwipeDirection direction,
|
SwipeDirection direction,
|
||||||
int swipeAmount,
|
int swipeAmount,
|
||||||
) async {
|
) async {
|
||||||
if (direction == SwipeDirection.left || direction == SwipeDirection.right) {
|
if (direction == SwipeDirection.left || direction == SwipeDirection.right) {
|
||||||
final offset =
|
final offset = direction == SwipeDirection.right ? swipeAmount : (swipeAmount * -1);
|
||||||
direction == SwipeDirection.right ? swipeAmount : (swipeAmount * -1);
|
|
||||||
|
|
||||||
await world.appDriver.scroll(
|
await world.appDriver.scroll(
|
||||||
finder,
|
finder,
|
||||||
|
@ -23,8 +21,7 @@ mixin _SwipeHelper
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final offset =
|
final offset = direction == SwipeDirection.up ? swipeAmount : (swipeAmount * -1);
|
||||||
direction == SwipeDirection.up ? swipeAmount : (swipeAmount * -1);
|
|
||||||
|
|
||||||
await world.appDriver.scroll(
|
await world.appDriver.scroll(
|
||||||
finder,
|
finder,
|
||||||
|
@ -42,9 +39,7 @@ mixin _SwipeHelper
|
||||||
///
|
///
|
||||||
/// `Then I swipe up by 800 pixels on the "login_screen"`
|
/// `Then I swipe up by 800 pixels on the "login_screen"`
|
||||||
/// `Then I swipe left by 200 pixels on the "dismissible_list_item"`
|
/// `Then I swipe left by 200 pixels on the "dismissible_list_item"`
|
||||||
class SwipeOnKeyStep
|
class SwipeOnKeyStep extends When3WithWorld<SwipeDirection, int, String, FlutterWorld> with _SwipeHelper {
|
||||||
extends When3WithWorld<SwipeDirection, int, String, FlutterWorld>
|
|
||||||
with _SwipeHelper {
|
|
||||||
@override
|
@override
|
||||||
Future<void> executeStep(
|
Future<void> executeStep(
|
||||||
SwipeDirection direction,
|
SwipeDirection direction,
|
||||||
|
@ -56,8 +51,7 @@ class SwipeOnKeyStep
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RegExp get pattern =>
|
RegExp get pattern => RegExp(r'I swipe {swipe_direction} by {int} pixels on the {string}');
|
||||||
RegExp(r'I swipe {swipe_direction} by {int} pixels on the {string}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Swipes in a cardinal direction on a widget discovered by its test.
|
/// Swipes in a cardinal direction on a widget discovered by its test.
|
||||||
|
@ -65,9 +59,7 @@ class SwipeOnKeyStep
|
||||||
/// Examples:
|
/// Examples:
|
||||||
///
|
///
|
||||||
/// `Then I swipe left by 400 pixels on the widget that contains the text "Dismiss Me"`
|
/// `Then I swipe left by 400 pixels on the widget that contains the text "Dismiss Me"`
|
||||||
class SwipeOnTextStep
|
class SwipeOnTextStep extends When3WithWorld<SwipeDirection, int, String, FlutterWorld> with _SwipeHelper {
|
||||||
extends When3WithWorld<SwipeDirection, int, String, FlutterWorld>
|
|
||||||
with _SwipeHelper {
|
|
||||||
@override
|
@override
|
||||||
Future<void> executeStep(
|
Future<void> executeStep(
|
||||||
SwipeDirection direction,
|
SwipeDirection direction,
|
||||||
|
@ -79,6 +71,5 @@ class SwipeOnTextStep
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RegExp get pattern => RegExp(
|
RegExp get pattern => RegExp(r'I swipe {swipe_direction} by {int} pixels on the (?:button|element|label|field|text|widget|dialog|popup) that contains the text {string}');
|
||||||
r'I swipe {swipe_direction} by {int} pixels on the (?:button|element|label|field|text|widget|dialog|popup) that contains the text {string}');
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,9 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `Then I tap the label that contains the text "Logout" within the "user_settings_list"`
|
/// `Then I tap the label that contains the text "Logout" within the "user_settings_list"`
|
||||||
StepDefinitionGeneric tapTextWithinWidgetStep() {
|
StepDefinitionGeneric tapTextWithinWidgetStep() {
|
||||||
return given2<String, String, FlutterWorld>(
|
return given2<String, String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(r'I tap the (?:button|element|label|field|text|widget) that contains the text {string} within the {string}'),
|
||||||
r'I tap the (?:button|element|label|field|text|widget) that contains the text {string} within the {string}'),
|
|
||||||
(text, ancestorKey, context) async {
|
(text, ancestorKey, context) async {
|
||||||
final timeout =
|
final timeout = context.configuration.timeout ?? const Duration(seconds: 20);
|
||||||
context.configuration.timeout ?? const Duration(seconds: 20);
|
|
||||||
final finder = context.world.appDriver.findByDescendant(
|
final finder = context.world.appDriver.findByDescendant(
|
||||||
context.world.appDriver.findBy(ancestorKey, FindType.key),
|
context.world.appDriver.findBy(ancestorKey, FindType.key),
|
||||||
context.world.appDriver.findBy(text, FindType.text),
|
context.world.appDriver.findBy(text, FindType.text),
|
||||||
|
|
|
@ -10,8 +10,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `Then I tap the field of type "TextField"`
|
/// `Then I tap the field of type "TextField"`
|
||||||
StepDefinitionGeneric tapWidgetOfTypeStep() {
|
StepDefinitionGeneric tapWidgetOfTypeStep() {
|
||||||
return given1<String, FlutterWorld>(
|
return given1<String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(r'I tap the (?:button|element|label|icon|field|text|widget) of type {string}$'),
|
||||||
r'I tap the (?:button|element|label|icon|field|text|widget) of type {string}$'),
|
|
||||||
(input1, context) async {
|
(input1, context) async {
|
||||||
await context.world.appDriver.tap(
|
await context.world.appDriver.tap(
|
||||||
context.world.appDriver.findBy(
|
context.world.appDriver.findBy(
|
||||||
|
|
|
@ -8,8 +8,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `Then I tap the element of type "MaterialButton" within the "user_settings_list"`
|
/// `Then I tap the element of type "MaterialButton" within the "user_settings_list"`
|
||||||
StepDefinitionGeneric tapWidgetOfTypeWithinStep() {
|
StepDefinitionGeneric tapWidgetOfTypeWithinStep() {
|
||||||
return when2<String, String, FlutterWorld>(
|
return when2<String, String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(r'I tap the (?:button|element|label|icon|field|text|widget) of type {string} within the {string}$'),
|
||||||
r'I tap the (?:button|element|label|icon|field|text|widget) of type {string} within the {string}$'),
|
|
||||||
(widgetType, ancestorKey, context) async {
|
(widgetType, ancestorKey, context) async {
|
||||||
final finder = context.world.appDriver.findByDescendant(
|
final finder = context.world.appDriver.findByDescendant(
|
||||||
context.world.appDriver.findBy(ancestorKey, FindType.key),
|
context.world.appDriver.findBy(ancestorKey, FindType.key),
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_driver/flutter_driver.dart';
|
||||||
import 'package:flutter_gherkin/flutter_gherkin.dart';
|
import 'package:flutter_gherkin/flutter_gherkin.dart';
|
||||||
import 'package:gherkin/gherkin.dart';
|
import 'package:gherkin/gherkin.dart';
|
||||||
|
|
||||||
|
@ -10,8 +13,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `Then I tap the widget that contains the text "My User Profile"`
|
/// `Then I tap the widget that contains the text "My User Profile"`
|
||||||
StepDefinitionGeneric tapWidgetWithTextStep() {
|
StepDefinitionGeneric tapWidgetWithTextStep() {
|
||||||
return then1<String, FlutterWorld>(
|
return then1<String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(r'I tap the (?:button|element|label|field|text|widget) that contains the text {string}$'),
|
||||||
r'I tap the (?:button|element|label|field|text|widget) that contains the text {string}$'),
|
|
||||||
(input1, context) async {
|
(input1, context) async {
|
||||||
final finder = context.world.appDriver.findBy(input1, FindType.text);
|
final finder = context.world.appDriver.findBy(input1, FindType.text);
|
||||||
print("to tap: $finder");
|
print("to tap: $finder");
|
||||||
|
@ -20,3 +22,39 @@ StepDefinitionGeneric tapWidgetWithTextStep() {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Select and scroll a dropdown list in a given direction
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
///
|
||||||
|
/// `Then I scroll the dropdown list "LanguageList" by -10`
|
||||||
|
StepDefinitionGeneric scrollDropDown() {
|
||||||
|
return then2<String, double, FlutterWorld>(
|
||||||
|
RegExp(r'I scroll the dropdown list {string} by {num}'),
|
||||||
|
(input1, distance, context) async {
|
||||||
|
final finder = context.world.appDriver.findBy(input1, FindType.key);
|
||||||
|
await context.world.appDriver.tap(finder);
|
||||||
|
await context.world.appDriver.waitForAppToSettle();
|
||||||
|
final dropdownListFinder = context.world.appDriver.findBy(Scrollable, FindType.type);
|
||||||
|
await context.world.appDriver.scroll(dropdownListFinder.last, dy: distance);
|
||||||
|
await context.world.appDriver.waitForAppToSettle();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Taps a dropdown that contains text.
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
///
|
||||||
|
/// `Then I tap the dropdown button that contains the text "Logout"`
|
||||||
|
StepDefinitionGeneric selectDropDownWithTextStep() {
|
||||||
|
return then1<String, FlutterWorld>(
|
||||||
|
RegExp(r'I tap the dropdown button that contains the text {string}$'),
|
||||||
|
(input1, context) async {
|
||||||
|
final finder = context.world.appDriver.findBy(input1, FindType.text);
|
||||||
|
print("to tap: $finder");
|
||||||
|
await context.world.appDriver.scrollIntoView(finder.last);
|
||||||
|
await context.world.appDriver.tap(finder.last);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -11,8 +11,7 @@ import '../parameters/existence_parameter.dart';
|
||||||
/// `But I expect the text "Sign up" to be absent within the "login_screen"`
|
/// `But I expect the text "Sign up" to be absent within the "login_screen"`
|
||||||
StepDefinitionGeneric textExistsWithinStep() {
|
StepDefinitionGeneric textExistsWithinStep() {
|
||||||
return then3<String, Existence, String, FlutterWorld>(
|
return then3<String, Existence, String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(r'I expect the text {string} to be {existence} within the {string}$'),
|
||||||
r'I expect the text {string} to be {existence} within the {string}$'),
|
|
||||||
(text, exists, ancestorKey, context) async {
|
(text, exists, ancestorKey, context) async {
|
||||||
final finder = context.world.appDriver.findByDescendant(
|
final finder = context.world.appDriver.findByDescendant(
|
||||||
context.world.appDriver.findBy(ancestorKey, FindType.key),
|
context.world.appDriver.findBy(ancestorKey, FindType.key),
|
||||||
|
|
|
@ -13,8 +13,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `Then I expect the button 'save' to be present within 1 second`
|
/// `Then I expect the button 'save' to be present within 1 second`
|
||||||
StepDefinitionGeneric thenExpectWidgetToBePresent() {
|
StepDefinitionGeneric thenExpectWidgetToBePresent() {
|
||||||
return given2<String, int, FlutterWorld>(
|
return given2<String, int, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(r'I expect the (?:button|element|label|icon|field|text|widget|dialog|popup) {string} to be present within {int} second(s)$'),
|
||||||
r'I expect the (?:button|element|label|icon|field|text|widget|dialog|popup) {string} to be present within {int} second(s)$'),
|
|
||||||
(key, seconds, context) async {
|
(key, seconds, context) async {
|
||||||
await context.world.appDriver.waitUntil(
|
await context.world.appDriver.waitUntil(
|
||||||
() async {
|
() async {
|
||||||
|
@ -27,7 +26,6 @@ StepDefinitionGeneric thenExpectWidgetToBePresent() {
|
||||||
timeout: Duration(seconds: seconds),
|
timeout: Duration(seconds: seconds),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
configuration: StepDefinitionConfiguration()
|
configuration: StepDefinitionConfiguration()..timeout = const Duration(days: 1),
|
||||||
..timeout = const Duration(days: 1),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `When I long press "controlKey" widget`
|
/// `When I long press "controlKey" widget`
|
||||||
StepDefinitionGeneric whenLongPressWidget() {
|
StepDefinitionGeneric whenLongPressWidget() {
|
||||||
return when1<String, FlutterWorld>(
|
return when1<String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(r'I long press the {string} (?:button|element|label|icon|field|text|widget)$'),
|
||||||
r'I long press the {string} (?:button|element|label|icon|field|text|widget)$'),
|
|
||||||
(key, context) async {
|
(key, context) async {
|
||||||
final finder = context.world.appDriver.findBy(key, FindType.key);
|
final finder = context.world.appDriver.findBy(key, FindType.key);
|
||||||
|
|
||||||
|
@ -33,8 +32,7 @@ StepDefinitionGeneric whenLongPressWidget() {
|
||||||
/// Long presses the widget found with the given control key, without scrolling into view
|
/// Long presses the widget found with the given control key, without scrolling into view
|
||||||
StepDefinitionGeneric whenLongPressWidgetWithoutScroll() {
|
StepDefinitionGeneric whenLongPressWidgetWithoutScroll() {
|
||||||
return when1<String, FlutterWorld>(
|
return when1<String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(r'I long press the {string} (?:button|element|label|icon|field|text|widget) without scrolling it into view$'),
|
||||||
r'I long press the {string} (?:button|element|label|icon|field|text|widget) without scrolling it into view$'),
|
|
||||||
(key, context) async {
|
(key, context) async {
|
||||||
final finder = context.world.appDriver.findBy(key, FindType.key);
|
final finder = context.world.appDriver.findBy(key, FindType.key);
|
||||||
|
|
||||||
|
@ -48,8 +46,7 @@ StepDefinitionGeneric whenLongPressWidgetWithoutScroll() {
|
||||||
/// Long presses the widget found with the given control key, for the given duration
|
/// Long presses the widget found with the given control key, for the given duration
|
||||||
StepDefinitionGeneric whenLongPressWidgetForDuration() {
|
StepDefinitionGeneric whenLongPressWidgetForDuration() {
|
||||||
return when2<String, int, FlutterWorld>(
|
return when2<String, int, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(r'I long press the {string} (?:button|element|label|icon|field|text|widget) for {int} milliseconds$'),
|
||||||
r'I long press the {string} (?:button|element|label|icon|field|text|widget) for {int} milliseconds$'),
|
|
||||||
(key, milliseconds, context) async {
|
(key, milliseconds, context) async {
|
||||||
final finder = context.world.appDriver.findBy(key, FindType.key);
|
final finder = context.world.appDriver.findBy(key, FindType.key);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ StepDefinitionGeneric whenTapBackButtonWidget() {
|
||||||
RegExp(r'I tap the back (?:button|element|widget|icon|text)$'),
|
RegExp(r'I tap the back (?:button|element|widget|icon|text)$'),
|
||||||
(context) async {
|
(context) async {
|
||||||
await context.world.appDriver.pageBack();
|
await context.world.appDriver.pageBack();
|
||||||
|
await context.world.appDriver.waitForAppToSettle();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,7 @@ import 'package:gherkin/gherkin.dart';
|
||||||
/// `When I tap "controlKey" widget"`
|
/// `When I tap "controlKey" widget"`
|
||||||
StepDefinitionGeneric whenTapWidget() {
|
StepDefinitionGeneric whenTapWidget() {
|
||||||
return when1<String, FlutterWorld>(
|
return when1<String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(r'I tap the {string} (?:button|element|label|icon|field|text|widget)$'),
|
||||||
r'I tap the {string} (?:button|element|label|icon|field|text|widget)$'),
|
|
||||||
(key, context) async {
|
(key, context) async {
|
||||||
final finder = context.world.appDriver.findBy(key, FindType.key);
|
final finder = context.world.appDriver.findBy(key, FindType.key);
|
||||||
|
|
||||||
|
@ -36,11 +35,9 @@ StepDefinitionGeneric whenTapWidget() {
|
||||||
|
|
||||||
StepDefinitionGeneric whenTapWidgetWithoutScroll() {
|
StepDefinitionGeneric whenTapWidgetWithoutScroll() {
|
||||||
return when1<String, FlutterWorld>(
|
return when1<String, FlutterWorld>(
|
||||||
RegExp(
|
RegExp(r'I tap the {string} (?:button|element|label|icon|field|text|widget) without scrolling it into view$'),
|
||||||
r'I tap the {string} (?:button|element|label|icon|field|text|widget) without scrolling it into view$'),
|
|
||||||
(key, context) async {
|
(key, context) async {
|
||||||
final finder =
|
final finder = context.world.appDriver.findByDescendant(key, FindType.key);
|
||||||
context.world.appDriver.findByDescendant(key, FindType.key);
|
|
||||||
|
|
||||||
await context.world.appDriver.tap(
|
await context.world.appDriver.tap(
|
||||||
finder,
|
finder,
|
||||||
|
|
|
@ -4,8 +4,7 @@ import 'package:flutter_gherkin/flutter_gherkin_with_driver.dart';
|
||||||
import '../runners/flutter_run_process_handler.dart';
|
import '../runners/flutter_run_process_handler.dart';
|
||||||
|
|
||||||
/// Driver version of the FlutterWorld with a typed driver
|
/// Driver version of the FlutterWorld with a typed driver
|
||||||
class FlutterDriverWorld extends FlutterTypedAdapterWorld<FlutterDriver,
|
class FlutterDriverWorld extends FlutterTypedAdapterWorld<FlutterDriver, SerializableFinder, dynamic> {
|
||||||
SerializableFinder, dynamic> {
|
|
||||||
FlutterRunProcessHandler? _flutterRunProcessHandler;
|
FlutterRunProcessHandler? _flutterRunProcessHandler;
|
||||||
|
|
||||||
void setFlutterDriver(FlutterDriver flutterDriver) {
|
void setFlutterDriver(FlutterDriver flutterDriver) {
|
||||||
|
|
|
@ -6,5 +6,4 @@ import 'package:flutter_gherkin/flutter_gherkin.dart';
|
||||||
/// It also allows interaction with the app under test through the `appDriver`
|
/// It also allows interaction with the app under test through the `appDriver`
|
||||||
/// which exposes an instance of `AppDriverAdapter` and an
|
/// which exposes an instance of `AppDriverAdapter` and an
|
||||||
/// instance of `WidgetTester` via the property `rawAppDriver`
|
/// instance of `WidgetTester` via the property `rawAppDriver`
|
||||||
class FlutterWidgetTesterWorld
|
class FlutterWidgetTesterWorld extends FlutterTypedAdapterWorld<WidgetTester, Finder, Widget> {}
|
||||||
extends FlutterTypedAdapterWorld<WidgetTester, Finder, Widget> {}
|
|
||||||
|
|
|
@ -38,6 +38,5 @@ class FlutterTypedAdapterWorld<TDriver, TFinder, TWidget> extends FlutterWorld {
|
||||||
|
|
||||||
/// The adapter that is used to agnostically drive the app under test
|
/// The adapter that is used to agnostically drive the app under test
|
||||||
@override
|
@override
|
||||||
AppDriverAdapter<TDriver, TFinder, TWidget> get appDriver =>
|
AppDriverAdapter<TDriver, TFinder, TWidget> get appDriver => _adapter as AppDriverAdapter<TDriver, TFinder, TWidget>;
|
||||||
_adapter as AppDriverAdapter<TDriver, TFinder, TWidget>;
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue