From 3772e89b9c89403d80d2653e8fa3e413c7bf8e67 Mon Sep 17 00:00:00 2001 From: Bart Vermeulen Date: Tue, 3 May 2022 19:39:22 +0200 Subject: [PATCH] The test case gets retried 3 times and the other steps will get skipped --- .../gherkin_suite_test_generator.dart | 36 +++++++--- lib/src/flutter/hooks/app_runner_hook.dart | 1 + .../gherkin_integration_test_runner.dart | 70 +++++++++++++------ 3 files changed, 75 insertions(+), 32 deletions(-) diff --git a/lib/src/flutter/code_generation/generators/gherkin_suite_test_generator.dart b/lib/src/flutter/code_generation/generators/gherkin_suite_test_generator.dart index 6bcc4c5..fb7176f 100644 --- a/lib/src/flutter/code_generation/generators/gherkin_suite_test_generator.dart +++ b/lib/src/flutter/code_generation/generators/gherkin_suite_test_generator.dart @@ -10,7 +10,8 @@ import 'package:source_gen/source_gen.dart'; class NoOpReporter extends Reporter {} -class GherkinSuiteTestGenerator extends GeneratorForAnnotation { +class GherkinSuiteTestGenerator + extends GeneratorForAnnotation { static const String TEMPLATE = ''' class _CustomGherkinIntegrationTestRunner extends GherkinIntegrationTestRunner { _CustomGherkinIntegrationTestRunner( @@ -45,14 +46,21 @@ void executeTestSuite( _languageService.initialise( annotation.read('featureDefaultLanguage').literalValue.toString(), ); - final idx = annotation.read('executionOrder').objectValue.getField('index')!.toIntValue()!; + final idx = annotation + .read('executionOrder') + .objectValue + .getField('index')! + .toIntValue()!; final executionOrder = ExecutionOrder.values[idx]; final featureFiles = annotation .read('featurePaths') .listValue .map((path) => Glob(path.toStringValue()!)) .map( - (glob) => glob.listSync().map((entity) => File(entity.path).readAsStringSync()).toList(), + (glob) => glob + .listSync() + .map((entity) => File(entity.path).readAsStringSync()) + .toList(), ) .reduce((value, element) => value..addAll(element)); @@ -80,7 +88,10 @@ void executeTestSuite( } } - return TEMPLATE.replaceAll('{{feature_functions}}', featureExecutionFunctionsBuilder.toString()).replaceAll( + return TEMPLATE + .replaceAll('{{feature_functions}}', + featureExecutionFunctionsBuilder.toString()) + .replaceAll( '{{features_to_execute}}', featuresToExecute.toString(), ); @@ -123,20 +134,22 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor { runScenario( '{{scenario_name}}', {{tags}}, - (TestDependencies dependencies) async { + [ {{steps}} - }, + ], {{onBefore}} {{onAfter}} ); '''; static const String STEP_TEMPLATE = ''' - await runStep( +(TestDependencies dependencies, bool hasToSkip) async { + return await runStep( '{{step_name}}', {{step_multi_line_strings}}, {{step_table}}, dependencies, - ); + hasToSkip + );} '''; static const String ON_BEFORE_SCENARIO_RUN = ''' onBefore: () async => onBeforeRunFeature('{{feature_name}}', {{feature_tags}},), @@ -151,6 +164,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor { String? _currentScenarioCode; final StringBuffer _scenarioBuffer = StringBuffer(); final StringBuffer _stepBuffer = StringBuffer(); + final _steps = []; Future generateTests( int id, @@ -207,7 +221,6 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor { Iterable tags, bool isFirst, bool isLast, - String path, ) async { _flushScenario(); _currentScenarioCode = _replaceVariable( @@ -266,6 +279,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor { ); _stepBuffer.writeln(code); + _steps.add(code); } void _flushFeature() { @@ -285,11 +299,11 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor { void _flushScenario() { if (_currentScenarioCode != null) { - if (_stepBuffer.isNotEmpty) { + if (_steps.isNotEmpty) { _currentScenarioCode = _replaceVariable( _currentScenarioCode!, 'steps', - _stepBuffer.toString(), + _steps.join(','), ); } diff --git a/lib/src/flutter/hooks/app_runner_hook.dart b/lib/src/flutter/hooks/app_runner_hook.dart index a5ea002..4cc21cc 100644 --- a/lib/src/flutter/hooks/app_runner_hook.dart +++ b/lib/src/flutter/hooks/app_runner_hook.dart @@ -39,6 +39,7 @@ class FlutterAppRunnerHook extends Hook { TestConfiguration config, String scenario, Iterable tags, + bool failed, ) async { final flutterConfig = _castConfig(config); haveRunFirstScenario = true; diff --git a/lib/src/flutter/runners/gherkin_integration_test_runner.dart b/lib/src/flutter/runners/gherkin_integration_test_runner.dart index 15a539d..fd2e810 100644 --- a/lib/src/flutter/runners/gherkin_integration_test_runner.dart +++ b/lib/src/flutter/runners/gherkin_integration_test_runner.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + 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'; @@ -135,7 +137,8 @@ abstract class GherkinIntegrationTestRunner { void runScenario( String name, Iterable? tags, - Future Function(TestDependencies dependencies) runTest, { + List Function(TestDependencies dependencies, bool skip)> + steps, { Future Function()? onBefore, Future Function()? onAfter, }) { @@ -146,6 +149,7 @@ abstract class GherkinIntegrationTestRunner { if (onBefore != null) { await onBefore(); } + var failed = false; final debugInformation = RunnableDebugInformation('', 0, name); final scenarioTags = @@ -181,14 +185,26 @@ abstract class GherkinIntegrationTestRunner { scenarioTags, ), ); - - await runTest(dependencies); + var hasToSkip = false; + for (int i = 0; i < steps.length; i++) { + try { + final result = await steps[i](dependencies, hasToSkip); + if (_isNegativeResult(result.result)) { + failed = true; + hasToSkip = true; + } + } catch (e) { + failed = true; + hasToSkip = true; + // rethrow; + } + } } finally { await reporter.onScenarioFinished( ScenarioFinishedMessage( name, debugInformation, - true, + !failed, ), ); @@ -196,6 +212,7 @@ abstract class GherkinIntegrationTestRunner { configuration, name, scenarioTags, + !failed, ); if (onAfter != null) { @@ -254,12 +271,8 @@ abstract class GherkinIntegrationTestRunner { } @protected - Future runStep( - String step, - Iterable multiLineStrings, - dynamic table, - TestDependencies dependencies, - ) async { + Future runStep(String step, Iterable multiLineStrings, + dynamic table, TestDependencies dependencies, bool hasToSkip) async { final executable = _executableSteps!.firstWhereOrNull( (s) => s.expression.isMatch(step), ); @@ -283,28 +296,43 @@ abstract class GherkinIntegrationTestRunner { multiLineStrings, ); - final result = await executable.step.run( - dependencies.world, - reporter, - configuration.defaultTimeout, - parameters, - ); + StepResult? result; + if (hasToSkip) { + result = new StepResult( + 0, StepExecutionResult.skipped, "Previous step(s) failed."); + } else { + for (int i = 0; i < 3; i++) { + result = await executable.step.run( + dependencies.world, + reporter, + configuration.defaultTimeout, + parameters, + ); + if (!_isNegativeResult(result.result)) { + break; + } + } + } await _onAfterStepRun( step, - result, + result!, dependencies, ); - if (result.result == StepExecutionResult.fail) { - throw TestFailure('Step: $step \n\n${result.resultReason}'); - } else if (result is ErroredStepResult) { - throw result.exception; + if (result is ErroredStepResult) { + // result.resultReason = result.exception.toString(); } return result; } + bool _isNegativeResult(StepExecutionResult result) { + return result == StepExecutionResult.error || + result == StepExecutionResult.fail || + result == StepExecutionResult.timeout; + } + @protected void cleanupScenarioRun(TestDependencies dependencies) { _safeInvokeFuture(