Started on the dart_gherkin 3.0 conversion

This commit is contained in:
Bart Vermeulen 2022-06-15 18:22:37 +02:00
parent c6b64cae2e
commit 5c15f0c2c0
7 changed files with 106 additions and 120 deletions

View File

@ -8,7 +8,18 @@ import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart'; import 'package:glob/list_local_fs.dart';
import 'package:source_gen/source_gen.dart'; import 'package:source_gen/source_gen.dart';
class NoOpReporter extends Reporter {} class NoOpReporter extends MessageReporter {
@override
Future<void> message(String message, MessageLevel level) async {
if(level == MessageLevel.info || level == MessageLevel.debug) {
print(message);
}else if(level == MessageLevel.warning) {
print('\x1B[33m$message\x1B[0m');
}else if(level == MessageLevel.error) {
print('\x1B[31m$message\x1B[0m');
}
}
}
class GherkinSuiteTestGenerator class GherkinSuiteTestGenerator
extends GeneratorForAnnotation<GherkinTestSuite> { extends GeneratorForAnnotation<GherkinTestSuite> {
@ -52,16 +63,12 @@ void executeTestSuite(
.getField('index')! .getField('index')!
.toIntValue()!; .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( .map((glob) =>
(glob) => glob glob.listSync().map((entity) => File(entity.path)).toList())
.listSync()
.map((entity) => File(entity.path).readAsStringSync())
.toList(),
)
.reduce((value, element) => value..addAll(element)); .reduce((value, element) => value..addAll(element));
if (executionOrder == ExecutionOrder.random) { if (executionOrder == ExecutionOrder.random) {
@ -73,11 +80,11 @@ void executeTestSuite(
final featuresToExecute = new StringBuffer(); final featuresToExecute = new StringBuffer();
var id = 0; var id = 0;
for (var featureFileContent in featureFiles) { for (var featureFile in featureFiles) {
final code = await generator.generate( final code = await generator.generate(
id++, id++,
featureFileContent, featureFile.readAsStringSync(),
'', featureFile.absolute.path,
_languageService, _languageService,
_reporter, _reporter,
); );
@ -104,7 +111,7 @@ class FeatureFileTestGenerator {
String featureFileContents, String featureFileContents,
String path, String path,
LanguageService languageService, LanguageService languageService,
Reporter reporter, MessageReporter reporter,
) async { ) async {
final visitor = FeatureFileTestGeneratorVisitor(); final visitor = FeatureFileTestGeneratorVisitor();
@ -148,14 +155,14 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
{{step_multi_line_strings}}, {{step_multi_line_strings}},
{{step_table}}, {{step_table}},
dependencies, dependencies,
hasToSkip hasToSkip,
);} );}
'''; ''';
static const String ON_BEFORE_SCENARIO_RUN = ''' static const String ON_BEFORE_SCENARIO_RUN = '''
onBefore: () async => onBeforeRunFeature('{{feature_name}}', {{feature_tags}},), onBefore: () async => onBeforeRunFeature('{{feature_name}}', {{feature_tags}},),
'''; ''';
static const String ON_AFTER_SCENARIO_RUN = ''' static const String ON_AFTER_SCENARIO_RUN = '''
onAfter: () async => onAfterRunFeature('{{feature_name}}',), onAfter: () async => onAfterRunFeature('{{feature_name}}', '{{path}}'),
'''; ''';
final StringBuffer _buffer = StringBuffer(); final StringBuffer _buffer = StringBuffer();
@ -171,7 +178,7 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
String featureFileContents, String featureFileContents,
String path, String path,
LanguageService languageService, LanguageService languageService,
Reporter reporter, MessageReporter reporter,
) async { ) async {
_id = id; _id = id;
await visit( await visit(
@ -214,14 +221,9 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
} }
@override @override
Future<void> visitScenario( Future<void> visitScenario(String featureName, Iterable<String> featureTags,
String featureName, String name, Iterable<String> tags, String path,
Iterable<String> featureTags, {required bool isFirst, required bool isLast}) async {
String name,
Iterable<String> tags,
bool isFirst,
bool isLast,
) async {
_flushScenario(); _flushScenario();
_currentScenarioCode = _replaceVariable( _currentScenarioCode = _replaceVariable(
SCENARIO_TEMPLATE, SCENARIO_TEMPLATE,
@ -238,6 +240,11 @@ class FeatureFileTestGeneratorVisitor extends FeatureFileVisitor {
'feature_name', 'feature_name',
_escapeText(featureName), _escapeText(featureName),
); );
_currentScenarioCode = _replaceVariable(
_currentScenarioCode!,
'path',
_escapeText(path),
);
_currentScenarioCode = _replaceVariable( _currentScenarioCode = _replaceVariable(
_currentScenarioCode!, _currentScenarioCode!,
'feature_tags', 'feature_tags',

View File

@ -24,32 +24,6 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:gherkin/gherkin.dart'; import 'package:gherkin/gherkin.dart';
class FlutterTestConfiguration extends TestConfiguration { class FlutterTestConfiguration extends TestConfiguration {
/// ~~The path(s) to all the features.~~
/// ~~All three [Pattern]s are supported: [RegExp], [String], [Glob].~~
///
/// Instead of using this variable, give the features in the `@GherkinTestSuite(features: <String>[])` option.
@deprecated
Iterable<Pattern> features = const <Pattern>[];
/// ~~The execution order of features - this default to random to avoid any inter-test dependencies~~
///
/// Instead of using this variable, give the executionOrder in the `@GherkinTestSuite(executionOrder: ExecutionOrder.random)` option.
@deprecated
ExecutionOrder order = ExecutionOrder.random;
/// ~~Lists feature files paths, which match [features] patterns.~~
///
/// Instead of using this variable, give the features in the `@GherkinTestSuite(features: <String>[])` option.
@deprecated
FeatureFileMatcher featureFileMatcher = const IoFeatureFileAccessor();
/// ~~The feature file reader.~~
/// ~~Takes files/resources paths from [featureFileIndexer] and returns their content as String.~~
///
/// Instead of using this variable, give the features in the `@GherkinTestSuite(features: <String>[])` option.
@deprecated
FeatureFileReader featureFileReader = const IoFeatureFileAccessor();
/// Enable semantics in a test by creating a [SemanticsHandle]. /// Enable semantics in a test by creating a [SemanticsHandle].
/// See: [testWidgets] and [WidgetController.ensureSemantics]. /// See: [testWidgets] and [WidgetController.ensureSemantics].
@ -63,7 +37,6 @@ class FlutterTestConfiguration extends TestConfiguration {
String targetAppPath = 'test_driver/integration_test_driver.dart', String targetAppPath = 'test_driver/integration_test_driver.dart',
}) { }) {
return FlutterTestConfiguration() return FlutterTestConfiguration()
..features = [RegExp(featurePath)]
..reporters = [ ..reporters = [
StdoutReporter(MessageLevel.error), StdoutReporter(MessageLevel.error),
ProgressReporter(), ProgressReporter(),

View File

@ -39,7 +39,7 @@ class FlutterAppRunnerHook extends Hook {
TestConfiguration config, TestConfiguration config,
String scenario, String scenario,
Iterable<Tag> tags, Iterable<Tag> tags,
bool failed, {bool? passed,}
) async { ) async {
final flutterConfig = _castConfig(config); final flutterConfig = _castConfig(config);
haveRunFirstScenario = true; haveRunFirstScenario = true;

View File

@ -1,8 +1,4 @@
import 'dart:math';
import 'package:flutter_gherkin/flutter_gherkin.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:gherkin/gherkin.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
@ -24,14 +20,14 @@ abstract class GherkinIntegrationTestRunner {
TagExpressionEvaluator(); TagExpressionEvaluator();
final TestConfiguration configuration; final TestConfiguration configuration;
final Future<void> Function(World world) appMainFunction; final Future<void> Function(World world) appMainFunction;
Reporter? _reporter; AggregatedReporter _reporter = new AggregatedReporter();
Hook? _hook; Hook? _hook;
Iterable<ExecutableStep>? _executableSteps; Iterable<ExecutableStep>? _executableSteps;
Iterable<CustomParameter>? _customParameters; Iterable<CustomParameter>? _customParameters;
IntegrationTestWidgetsFlutterBinding? _binding; IntegrationTestWidgetsFlutterBinding? _binding;
Reporter get reporter => _reporter!; AggregatedReporter get reporter => _reporter;
Hook get hook => _hook!; Hook get hook => _hook!;
LiveTestWidgetsFlutterBindingFramePolicy? get framePolicy => null; LiveTestWidgetsFlutterBindingFramePolicy? get framePolicy => null;
@ -42,7 +38,7 @@ abstract class GherkinIntegrationTestRunner {
this.appMainFunction, this.appMainFunction,
) { ) {
configuration.prepare(); configuration.prepare();
_reporter = _registerReporters(configuration.reporters); _registerReporters(configuration.reporters);
_hook = _registerHooks(configuration.hooks); _hook = _registerHooks(configuration.hooks);
_customParameters = _customParameters =
_registerCustomParameters(configuration.customStepParameterDefinitions); _registerCustomParameters(configuration.customStepParameterDefinitions);
@ -66,7 +62,7 @@ abstract class GherkinIntegrationTestRunner {
); );
_safeInvokeFuture(() async => await hook.onBeforeRun(configuration)); _safeInvokeFuture(() async => await hook.onBeforeRun(configuration));
_safeInvokeFuture(() async => await reporter.onTestRunStarted()); _safeInvokeFuture(() async => await reporter.test.onStarted.maybeCall());
onRun(); onRun();
} }
@ -74,7 +70,7 @@ abstract class GherkinIntegrationTestRunner {
void onRun(); void onRun();
void onRunComplete() { void onRunComplete() {
_safeInvokeFuture(() async => await reporter.onTestRunFinished()); _safeInvokeFuture(() async => await reporter.test.onFinished.maybeCall());
_safeInvokeFuture(() async => await hook.onAfterRun(configuration)); _safeInvokeFuture(() async => await hook.onAfterRun(configuration));
setTestResultData(_binding!); setTestResultData(_binding!);
_safeInvokeFuture(() async => await reporter.dispose()); _safeInvokeFuture(() async => await reporter.dispose());
@ -82,7 +78,7 @@ abstract class GherkinIntegrationTestRunner {
void setTestResultData(IntegrationTestWidgetsFlutterBinding binding) { void setTestResultData(IntegrationTestWidgetsFlutterBinding binding) {
if (reporter is SerializableReporter) { if (reporter is SerializableReporter) {
final json = (reporter as SerializableReporter).toJson(); final json = (reporter as SerializableReporter).serialize();
binding.reportData = {'gherkin_reports': json}; binding.reportData = {'gherkin_reports': json};
} }
} }
@ -109,12 +105,11 @@ abstract class GherkinIntegrationTestRunner {
final debugInformation = RunnableDebugInformation('', 0, name); final debugInformation = RunnableDebugInformation('', 0, name);
final featureTags = final featureTags =
(tags ?? Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0)); (tags ?? Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0));
await reporter.onFeatureStarted( await reporter.feature.onStarted.maybeCall(
StartedMessage( FeatureMessage(
Target.feature, name: name,
name, context: debugInformation,
debugInformation, tags: featureTags.toList(),
featureTags,
), ),
); );
} }
@ -122,13 +117,14 @@ abstract class GherkinIntegrationTestRunner {
@protected @protected
Future<void> onAfterRunFeature( Future<void> onAfterRunFeature(
String name, String name,
String path
) async { ) async {
final debugInformation = RunnableDebugInformation('', 0, name); final debugInformation = RunnableDebugInformation(path, 0, name);
await reporter.onFeatureFinished( await reporter.test.onFinished.maybeCall(
FinishedMessage( TestMessage(
Target.feature, target: Target.feature,
name, name: name,
debugInformation, context: debugInformation,
), ),
); );
} }
@ -138,7 +134,7 @@ abstract class GherkinIntegrationTestRunner {
String name, String name,
Iterable<String>? tags, Iterable<String>? tags,
List<Future<StepResult> Function(TestDependencies dependencies, bool skip,)> List<Future<StepResult> Function(TestDependencies dependencies, bool skip,)>
steps, { steps, String path, {
Future<void> Function()? onBefore, Future<void> Function()? onBefore,
Future<void> Function()? onAfter, Future<void> Function()? onAfter,
}) { }) {
@ -151,7 +147,7 @@ abstract class GherkinIntegrationTestRunner {
} }
var failed = false; var failed = false;
final debugInformation = RunnableDebugInformation('', 0, name); final debugInformation = RunnableDebugInformation(path, 0, name);
final scenarioTags = final scenarioTags =
(tags ?? Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0)); (tags ?? Iterable<Tag>.empty()).map((t) => Tag(t.toString(), 0));
final dependencies = await createTestDependencies( final dependencies = await createTestDependencies(
@ -177,12 +173,11 @@ abstract class GherkinIntegrationTestRunner {
scenarioTags, scenarioTags,
); );
await reporter.onScenarioStarted( await reporter.scenario.onStarted.maybeCall(
StartedMessage( ScenarioMessage(
Target.scenario, name: name,
name, context: debugInformation,
debugInformation, tags: scenarioTags.toList(),
scenarioTags,
), ),
); );
var hasToSkip = false; var hasToSkip = false;
@ -200,11 +195,11 @@ abstract class GherkinIntegrationTestRunner {
} }
} }
} finally { } finally {
await reporter.onScenarioFinished( await reporter.scenario.onFinished.maybeCall(
ScenarioFinishedMessage( ScenarioMessage(
name, name: name,
debugInformation, context: debugInformation,
!failed, hasPassed: !failed,
), ),
); );
@ -212,7 +207,7 @@ abstract class GherkinIntegrationTestRunner {
configuration, configuration,
name, name,
scenarioTags, scenarioTags,
!failed, passed: !failed,
); );
if (onAfter != null) { if (onAfter != null) {
@ -300,7 +295,7 @@ abstract class GherkinIntegrationTestRunner {
if (hasToSkip) { if (hasToSkip) {
result = new StepResult( result = new StepResult(
0, StepExecutionResult.skipped, "Previous step(s) failed."); 0, StepExecutionResult.skipped, resultReason: "Previous step(s) failed.");
} else { } else {
for (int i = 0; i < this.configuration.stepMaxRetries + 1; i++) { for (int i = 0; i < this.configuration.stepMaxRetries + 1; i++) {
result = await executable.step.run( result = await executable.step.run(
@ -345,13 +340,10 @@ abstract class GherkinIntegrationTestRunner {
); );
} }
Reporter _registerReporters(Iterable<Reporter>? reporters) { void _registerReporters(Iterable<Reporter>? reporters) {
final reporter = AggregatedReporter();
if (reporters != null) { if (reporters != null) {
reporters.forEach((r) => reporter.addReporter(r)); reporters.forEach((r) => _reporter.addReporter(r));
} }
return reporter;
} }
Hook _registerHooks(Iterable<Hook>? hooks) { Hook _registerHooks(Iterable<Hook>? hooks) {
@ -429,12 +421,12 @@ abstract class GherkinIntegrationTestRunner {
result, result,
); );
await reporter.onStepFinished( await reporter.step.onStarted.maybeCall(
StepFinishedMessage( StepMessage(
step, name: step,
RunnableDebugInformation('', 0, step), context: RunnableDebugInformation('', 0, step),
result, result: result,
dependencies.attachmentManager.getAttachmentsForContext(step), attachments: dependencies.attachmentManager.getAttachmentsForContext(step).toList(),
), ),
); );
} }
@ -446,10 +438,10 @@ abstract class GherkinIntegrationTestRunner {
Iterable<String> multiLineStrings, Iterable<String> multiLineStrings,
) async { ) async {
await hook.onBeforeStep(world, step); await hook.onBeforeStep(world, step);
await reporter.onStepStarted( await reporter.step.onStarted.maybeCall(
StepStartedMessage( StepMessage(
step, name: step,
RunnableDebugInformation('', 0, step), context: RunnableDebugInformation('', 0, step),
table: table, table: table,
multilineString: multilineString:
multiLineStrings.isNotEmpty ? multiLineStrings.first : null, multiLineStrings.isNotEmpty ? multiLineStrings.first : null,

View File

@ -22,7 +22,7 @@ StepDefinitionGeneric ThenExpectElementToHaveValue() {
context.expect(text, value); context.expect(text, value);
} catch (e) { } catch (e) {
await context.reporter.message('Step error: $e', MessageLevel.error); // await context.reporter('Step error: $e', MessageLevel.error);
rethrow; rethrow;
} }
}, },

View File

@ -21,7 +21,7 @@ packages:
name: archive name: archive
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.2" version: "3.1.11"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -35,7 +35,7 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.8.1" version: "2.8.2"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -63,7 +63,7 @@ packages:
name: characters name: characters
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.2.0"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
@ -98,7 +98,7 @@ packages:
name: collection name: collection
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.15.0" version: "1.16.0"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -126,7 +126,7 @@ packages:
name: fake_async name: fake_async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.3.0"
file: file:
dependency: transitive dependency: transitive
description: description:
@ -160,7 +160,7 @@ packages:
name: gherkin name: gherkin
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.8" version: "3.0.0"
glob: glob:
dependency: "direct main" dependency: "direct main"
description: description:
@ -193,7 +193,14 @@ packages:
name: matcher name: matcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.10" version: "0.12.11"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
meta: meta:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -214,7 +221,7 @@ packages:
name: path name: path
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.0" version: "1.8.1"
pedantic: pedantic:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -228,14 +235,14 @@ packages:
name: platform name: platform
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.0" version: "3.1.0"
process: process:
dependency: transitive dependency: transitive
description: description:
name: process name: process
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.2.3" version: "4.2.4"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:
@ -268,7 +275,7 @@ packages:
name: source_span name: source_span
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.1" version: "1.8.2"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -310,7 +317,7 @@ packages:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.2" version: "0.4.9"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -318,20 +325,27 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
uuid:
dependency: transitive
description:
name: uuid
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.6"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.2"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "7.1.1" version: "8.2.2"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:
@ -354,5 +368,5 @@ packages:
source: hosted source: hosted
version: "3.1.0" version: "3.1.0"
sdks: sdks:
dart: ">=2.14.0 <3.0.0" dart: ">=2.17.0-0 <3.0.0"
flutter: ">=2.2.0" flutter: ">=2.2.0"

View File

@ -18,7 +18,7 @@ dependencies:
sdk: flutter sdk: flutter
analyzer: ">=1.7.1 <3.0.0" analyzer: ">=1.7.1 <3.0.0"
collection: ^1.15.0 collection: ^1.15.0
gherkin: ^2.0.8 gherkin: ^3.0.0
source_gen: ^1.1.1 source_gen: ^1.1.1
build: ^2.1.1 build: ^2.1.1
glob: ^2.0.2 glob: ^2.0.2