feat(reporters): add progress reporter
This commit is contained in:
parent
03ca8d5f09
commit
508f40cc80
23
README.md
23
README.md
|
@ -248,9 +248,9 @@ Hooks are custom bits of code that can be run at certain points with the test ru
|
|||
|
||||
#### reporters
|
||||
|
||||
*Requried*
|
||||
*Required*
|
||||
|
||||
Reporters are classes that are able to report on the status of the test run. This could be a simple as merely logging scenerio result to the console. There are a number of built-in reporter:
|
||||
Reporters are classes that are able to report on the status of the test run. This could be a simple as merely logging scenario result to the console. There are a number of built-in reporter:
|
||||
|
||||
- `StdoutReporter` : Logs all messages from the test run to the standard output (console).
|
||||
- `ProgressReporter` : Logs the progress of the test run marking each step with a scenario as either passed, skipped or failed.
|
||||
|
@ -669,7 +669,24 @@ Future<void> main() {
|
|||
|
||||
A reporter is a class that is able to report on the progress of the test run. In it simplest form it could just print messages to the console or be used to tell a build server such as TeamCity of the progress of the test run. The library has a number of built in reporters.
|
||||
|
||||
- StdOut - prints all messages to the console.
|
||||
- `StdoutReporter` - prints all messages from the test run to the console.
|
||||
- `ProgressReporter` - prints the result of each scenario and step to the console - colours the output.
|
||||
|
||||
You can create your own custom reporter by inheriting from the base `Reporter` class and overriding the one or many of the methods to direct the output message. The `Reporter` defines the following methods that can be overridden. All methods must return a `Future<void>` and can be async.
|
||||
|
||||
- `onTestRunStarted`
|
||||
- `onTestRunFinished`
|
||||
- `onFeatureStarted`
|
||||
- `onFeatureFinished`
|
||||
- `onScenarioStarted`
|
||||
- `onScenarioFinished`
|
||||
- `onStepStarted`
|
||||
- `onStepFinished`
|
||||
- `onException`
|
||||
- `message`
|
||||
- `dispose`
|
||||
|
||||
Once you have created your custom reporter don't forget to add it to the `reporters` configuration file property.
|
||||
|
||||
*Note*: PR's of new reporters are *always* welcome.
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@ import 'steps/tap_button_n_times_step.dart';
|
|||
Future<void> main() {
|
||||
final config = FlutterTestConfiguration()
|
||||
..features = [Glob(r"test_driver/features/*.feature")]
|
||||
..reporters = [StdoutReporter()]
|
||||
..reporters = [ProgressReporter()]
|
||||
..hooks = [HookExample()]
|
||||
..stepDefinitions = [TapButtonNTimesStep(), GivenIPickAColour()]
|
||||
..customStepParameterDefinitions = [ColourParameter()]
|
||||
..restartAppBetweenScenarios = true
|
||||
..targetAppPath = "test_driver/app.dart"
|
||||
..exitAfterTestRun = false;
|
||||
..exitAfterTestRun = true;
|
||||
return GherkinRunner().execute(config);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_gherkin/flutter_gherkin.dart';
|
||||
|
||||
class ColourParameter extends CustomParameter<Color> {
|
||||
enum Colour {
|
||||
red,
|
||||
green,
|
||||
blue
|
||||
}
|
||||
|
||||
class ColourParameter extends CustomParameter<Colour> {
|
||||
ColourParameter()
|
||||
: super("colour", RegExp(r"red|green|blue", caseSensitive: true), (c) {
|
||||
switch (c.toLowerCase()) {
|
||||
case "red":
|
||||
return Colors.red;
|
||||
return Colour.red;
|
||||
case "green":
|
||||
return Colors.green;
|
||||
return Colour.green;
|
||||
case "blue":
|
||||
return Colors.blue;
|
||||
return Colour.blue;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_gherkin/flutter_gherkin.dart';
|
||||
import 'colour_parameter.dart';
|
||||
|
||||
class GivenIPickAColour extends Given1<Color> {
|
||||
class GivenIPickAColour extends Given1<Colour> {
|
||||
@override
|
||||
Future<void> executeStep(Color input1) async {
|
||||
Future<void> executeStep(Colour input1) async {
|
||||
// TODO: implement executeStep
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ export "src/reporters/reporter.dart";
|
|||
export "src/reporters/message_level.dart";
|
||||
export "src/reporters/messages.dart";
|
||||
export "src/reporters/stdout_reporter.dart";
|
||||
export "src/reporters/progress_reporter.dart";
|
||||
|
||||
// Hooks
|
||||
export "src/hooks/hook.dart";
|
||||
|
|
|
@ -115,9 +115,9 @@ class FeatureFileRunner {
|
|||
|
||||
world?.dispose();
|
||||
|
||||
await _reporter.onScenarioFinished(
|
||||
ScenarioFinishedMessage(scenario.name, scenario.debug, scenarioPassed));
|
||||
await _hook.onAfterScenario(_config, scenario.name);
|
||||
_reporter.onScenarioFinished(
|
||||
FinishedMessage(Target.scenario, scenario.name, scenario.debug));
|
||||
return scenarioPassed;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,9 @@ class FlutterTestConfiguration extends TestConfiguration {
|
|||
Platform.environment['VM_SERVICE_URL'];
|
||||
final driver = await FlutterDriver.connect(
|
||||
dartVmServiceUrl: dartVmServiceUrl,
|
||||
isolateReadyTimeout: Duration(seconds: 30));
|
||||
isolateReadyTimeout: Duration(seconds: 30),
|
||||
logCommunicationToFile: false,
|
||||
printCommunication: false);
|
||||
return driver;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ class AggregatedReporter extends Reporter {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> onTestRunfinished() async {
|
||||
await _invokeReporters((r) async => await r.onTestRunfinished());
|
||||
Future<void> onTestRunFinished() async {
|
||||
await _invokeReporters((r) async => await r.onTestRunFinished());
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -26,3 +26,11 @@ class StepFinishedMessage extends FinishedMessage {
|
|||
String name, RunnableDebugInformation context, this.result)
|
||||
: super(Target.step, name, context);
|
||||
}
|
||||
|
||||
class ScenarioFinishedMessage extends FinishedMessage {
|
||||
final bool passed;
|
||||
|
||||
ScenarioFinishedMessage(
|
||||
String name, RunnableDebugInformation context, this.passed)
|
||||
: super(Target.scenario, name, context);
|
||||
}
|
||||
|
|
|
@ -1,52 +1,88 @@
|
|||
import 'package:flutter_gherkin/flutter_gherkin.dart';
|
||||
import 'package:flutter_gherkin/src/gherkin/runnables/debug_information.dart';
|
||||
import 'package:flutter_gherkin/src/gherkin/steps/step_run_result.dart';
|
||||
import 'package:flutter_gherkin/src/reporters/message_level.dart';
|
||||
import 'package:flutter_gherkin/src/reporters/messages.dart';
|
||||
|
||||
class ProgressReporter extends StdoutReporter {
|
||||
Future<void> onStepFinished(FinishedMessage message) async {}
|
||||
static const String PASS_COLOR = "\u001b[33;32m"; // green
|
||||
|
||||
@override
|
||||
Future<void> onScenarioStarted(StartedMessage message) async {
|
||||
printMessage(
|
||||
"Running scenario: ${_getNameAndContext(message.name, message.context)}",
|
||||
StdoutReporter.WARN_COLOR);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onScenarioFinished(ScenarioFinishedMessage message) async {
|
||||
printMessage(
|
||||
"${message.passed ? 'PASSED' : 'FAILED'}: Scenario ${_getNameAndContext(message.name, message.context)}",
|
||||
message.passed ? PASS_COLOR : StdoutReporter.FAIL_COLOR);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onStepFinished(StepFinishedMessage message) async {
|
||||
printMessage(
|
||||
[
|
||||
" ",
|
||||
_getStatePrefixIcon(message.result.result),
|
||||
_getNameAndContext(message.name, message.context),
|
||||
_getExecutionDuration(message.result),
|
||||
_getErrorMessage(message.result)
|
||||
].join((" ")),
|
||||
_getMessageColour(message.result.result));
|
||||
}
|
||||
|
||||
Future<void> message(String message, MessageLevel level) async {
|
||||
// ignore messages
|
||||
}
|
||||
|
||||
String getStatePrefixIcon() {
|
||||
return "√|×|e!";
|
||||
String _getErrorMessage(StepResult stepResult) {
|
||||
if (stepResult is ErroredStepResult) {
|
||||
return "\n${stepResult.exception}\n${stepResult.stackTrace}";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
String getContext(RunnableDebugInformation context) {
|
||||
return "# ${context.filePath}:${context.lineNumber}";
|
||||
String _getNameAndContext(String name, RunnableDebugInformation context) {
|
||||
return "$name # ${context.filePath.replaceAll(RegExp(r"\.\\"), "")}:${context.lineNumber}";
|
||||
}
|
||||
|
||||
String _getExecutionDuration(StepResult stepResult) {
|
||||
return "took ${stepResult.elapsedMilliseconds}ms";
|
||||
}
|
||||
|
||||
String _getStatePrefixIcon(StepExecutionResult result) {
|
||||
switch (result) {
|
||||
case StepExecutionResult.pass:
|
||||
return "√";
|
||||
case StepExecutionResult.error:
|
||||
case StepExecutionResult.fail:
|
||||
case StepExecutionResult.timeout:
|
||||
return "×";
|
||||
case StepExecutionResult.skipped:
|
||||
return "-";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
String _getMessageColour(StepExecutionResult result) {
|
||||
switch (result) {
|
||||
case StepExecutionResult.pass:
|
||||
return PASS_COLOR;
|
||||
case StepExecutionResult.fail:
|
||||
return StdoutReporter.FAIL_COLOR;
|
||||
case StepExecutionResult.error:
|
||||
return StdoutReporter.FAIL_COLOR;
|
||||
case StepExecutionResult.skipped:
|
||||
return StdoutReporter.WARN_COLOR;
|
||||
case StepExecutionResult.timeout:
|
||||
return StdoutReporter.FAIL_COLOR;
|
||||
}
|
||||
|
||||
return StdoutReporter.RESET_COLOR;
|
||||
}
|
||||
}
|
||||
// √ And I click on the "change job" link # src\step-definitions\interactions\click-on-element.step.ts:13
|
||||
// × And I fill the "finish date" field with "1 December 2020" # src\step-definitions\interactions\fill-the-field-with.step.ts:11
|
||||
// WebDriverError: unknown error: cannot focus element
|
||||
// (Session info: chrome=70.0.3538.67)
|
||||
// (Driver info: chromedriver=2.43.600210 (68dcf5eebde37173d4027fa8635e332711d2874a),platform=Windows NT 10.0.16299 x86_64)
|
||||
// at Object.checkLegacyResponse (C:\easilog\webapp-tests\node_modules\selenium-webdriver\lib\error.js:546:15)
|
||||
// at parseHttpResponse (C:\easilog\webapp-tests\node_modules\selenium-webdriver\lib\http.js:509:13)
|
||||
// at doSend.then.response (C:\easilog\webapp-tests\node_modules\selenium-webdriver\lib\http.js:441:30)
|
||||
// at process._tickCallback (internal/process/next_tick.js:68:7)
|
||||
// From: Task: WebElement.sendKeys()
|
||||
// at thenableWebDriverProxy.schedule (C:\easilog\webapp-tests\node_modules\selenium-webdriver\lib\webdriver.js:807:17)
|
||||
// at WebElement.schedule_ (C:\easilog\webapp-tests\node_modules\selenium-webdriver\lib\webdriver.js:2010:25)
|
||||
// at WebElement.sendKeys (C:\easilog\webapp-tests\node_modules\selenium-webdriver\lib\webdriver.js:2174:19)
|
||||
// at actionFn (C:\easilog\webapp-tests\node_modules\protractor\built\element.js:89:44)
|
||||
// at Array.map (<anonymous>)
|
||||
// at actionResults.getWebElements.then (C:\easilog\webapp-tests\node_modules\protractor\built\element.js:461:65)
|
||||
// at ManagedPromise.invokeCallback_ (C:\easilog\webapp-tests\node_modules\selenium-webdriver\lib\promise.js:1376:14)
|
||||
// at TaskQueue.execute_ (C:\easilog\webapp-tests\node_modules\selenium-webdriver\lib\promise.js:3084:14)
|
||||
// at TaskQueue.executeNext_ (C:\easilog\webapp-tests\node_modules\selenium-webdriver\lib\promise.js:3067:27)
|
||||
// at asyncRun (C:\easilog\webapp-tests\node_modules\selenium-webdriver\lib\promise.js:2927:27)
|
||||
// at C:\easilog\webapp-tests\node_modules\selenium-webdriver\lib\promise.js:668:7
|
||||
// at process._tickCallback (internal/process/next_tick.js:68:7)Error
|
||||
// at ElementArrayFinder.applyAction_ (C:\easilog\webapp-tests\node_modules\protractor\built\element.js:459:27)
|
||||
// at ElementArrayFinder.(anonymous function).args [as sendKeys] (C:\easilog\webapp-tests\node_modules\protractor\built\element.js:91:29)
|
||||
// at ElementFinder.(anonymous function).args [as sendKeys] (C:\easilog\webapp-tests\node_modules\protractor\built\element.js:831:22)
|
||||
// at LoginPageObject.<anonymous> (C:\easilog\webapp-tests\src\pages\base.page.ts:101:23)
|
||||
// at step (C:\easilog\webapp-tests\src\pages\base.page.js:42:23)
|
||||
// at Object.next (C:\easilog\webapp-tests\src\pages\base.page.js:23:53)
|
||||
// at fulfilled (C:\easilog\webapp-tests\src\pages\base.page.js:14:58)
|
||||
// at process._tickCallback (internal/process/next_tick.js:68:7)
|
||||
// - And I fill the "started date" field with "1 October 2021" # src\step-definitions\interactions\fill-the-field-with.step.ts:11
|
||||
// - And I fill the "country" field with "United Kingdom" # src\step-definitions\interactions\fill-the-field-with.step.ts:11
|
||||
|
|
|
@ -3,11 +3,11 @@ import 'package:flutter_gherkin/src/reporters/messages.dart';
|
|||
|
||||
abstract class Reporter {
|
||||
Future<void> onTestRunStarted() async {}
|
||||
Future<void> onTestRunfinished() async {}
|
||||
Future<void> onTestRunFinished() async {}
|
||||
Future<void> onFeatureStarted(StartedMessage message) async {}
|
||||
Future<void> onFeatureFinished(FinishedMessage message) async {}
|
||||
Future<void> onScenarioStarted(StartedMessage message) async {}
|
||||
Future<void> onScenarioFinished(FinishedMessage message) async {}
|
||||
Future<void> onScenarioFinished(ScenarioFinishedMessage message) async {}
|
||||
Future<void> onStepStarted(StartedMessage message) async {}
|
||||
Future<void> onStepFinished(StepFinishedMessage message) async {}
|
||||
Future<void> onException(Exception exception, StackTrace stackTrace) async {}
|
||||
|
|
|
@ -5,13 +5,12 @@ import 'package:flutter_gherkin/src/reporters/reporter.dart';
|
|||
class StdoutReporter extends Reporter {
|
||||
static const String NEUTRAL_COLOR = "\u001b[33;34m"; // blue
|
||||
static const String DEBUG_COLOR = "\u001b[1;30m"; // gray
|
||||
static const String PASS_COLOR = "\u001b[33;32m"; // green
|
||||
static const String FAIL_COLOR = "\u001b[33;31m"; // red
|
||||
static const String WARN_COLOR = "\u001b[33;10m"; // yellow
|
||||
static const String RESET_COLOR = "\u001b[33;0m";
|
||||
|
||||
Future<void> message(String message, MessageLevel level) async {
|
||||
print(message, getColour(level));
|
||||
printMessage(message, getColour(level));
|
||||
}
|
||||
|
||||
String getColour(MessageLevel level) {
|
||||
|
@ -29,7 +28,7 @@ class StdoutReporter extends Reporter {
|
|||
}
|
||||
}
|
||||
|
||||
void print(String message, [String colour]) {
|
||||
void printMessage(String message, [String colour]) {
|
||||
stdout.writeln(
|
||||
"${colour == null ? RESET_COLOR : colour}$message$RESET_COLOR");
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ class GherkinRunner {
|
|||
await runner.run(featureFile);
|
||||
}
|
||||
} finally {
|
||||
await _reporter.onTestRunfinished();
|
||||
await _reporter.onTestRunFinished();
|
||||
}
|
||||
|
||||
await _hook.onAfterRun(config);
|
||||
|
|
|
@ -46,7 +46,7 @@ void main() {
|
|||
expect(reporter1.onStepStartedInvocationCount, 1);
|
||||
expect(reporter2.onStepStartedInvocationCount, 1);
|
||||
|
||||
await aggregatedReporter.onTestRunfinished();
|
||||
await aggregatedReporter.onTestRunFinished();
|
||||
expect(reporter1.onTestRunfinishedInvocationCount, 1);
|
||||
expect(reporter2.onTestRunfinishedInvocationCount, 1);
|
||||
|
||||
|
|
Loading…
Reference in New Issue