@serenity-js/console-reporter 3.0.0-rc.9 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +75 -1171
- package/README.md +98 -27
- package/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +11 -1
- package/lib/index.js.map +1 -1
- package/lib/stage/crew/console-reporter/ConsoleReporter.d.ts +84 -60
- package/lib/stage/crew/console-reporter/ConsoleReporter.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/ConsoleReporter.js +233 -171
- package/lib/stage/crew/console-reporter/ConsoleReporter.js.map +1 -1
- package/lib/stage/crew/console-reporter/ConsoleReporterConfig.d.ts +13 -0
- package/lib/stage/crew/console-reporter/ConsoleReporterConfig.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/ConsoleReporterConfig.js +3 -0
- package/lib/stage/crew/console-reporter/ConsoleReporterConfig.js.map +1 -0
- package/lib/stage/crew/console-reporter/Printer.d.ts +2 -4
- package/lib/stage/crew/console-reporter/Printer.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/Printer.js +0 -3
- package/lib/stage/crew/console-reporter/Printer.js.map +1 -1
- package/lib/stage/crew/console-reporter/Summary.d.ts +7 -1
- package/lib/stage/crew/console-reporter/Summary.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/Summary.js +14 -1
- package/lib/stage/crew/console-reporter/Summary.js.map +1 -1
- package/lib/stage/crew/console-reporter/SummaryFormatter.d.ts +1 -0
- package/lib/stage/crew/console-reporter/SummaryFormatter.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/SummaryFormatter.js +4 -1
- package/lib/stage/crew/console-reporter/SummaryFormatter.js.map +1 -1
- package/lib/stage/crew/console-reporter/index.d.ts +2 -0
- package/lib/stage/crew/console-reporter/index.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/index.js +6 -1
- package/lib/stage/crew/console-reporter/index.js.map +1 -1
- package/lib/stage/crew/console-reporter/themes/TerminalTheme.d.ts +1 -7
- package/lib/stage/crew/console-reporter/themes/TerminalTheme.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/themes/TerminalTheme.js +0 -6
- package/lib/stage/crew/console-reporter/themes/TerminalTheme.js.map +1 -1
- package/lib/stage/crew/console-reporter/themes/ThemeForColourTerminals.d.ts +7 -21
- package/lib/stage/crew/console-reporter/themes/ThemeForColourTerminals.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/themes/ThemeForColourTerminals.js +9 -30
- package/lib/stage/crew/console-reporter/themes/ThemeForColourTerminals.js.map +1 -1
- package/lib/stage/crew/console-reporter/themes/ThemeForDarkTerminals.d.ts +1 -0
- package/lib/stage/crew/console-reporter/themes/ThemeForDarkTerminals.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/themes/ThemeForLightTerminals.d.ts +1 -0
- package/lib/stage/crew/console-reporter/themes/ThemeForLightTerminals.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/themes/ThemeForMonochromaticTerminals.d.ts +7 -20
- package/lib/stage/crew/console-reporter/themes/ThemeForMonochromaticTerminals.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/themes/ThemeForMonochromaticTerminals.js +6 -29
- package/lib/stage/crew/console-reporter/themes/ThemeForMonochromaticTerminals.js.map +1 -1
- package/lib/stage/crew/console-reporter/themes/index.d.ts +1 -0
- package/lib/stage/crew/console-reporter/themes/index.d.ts.map +1 -0
- package/lib/stage/crew/console-reporter/themes/index.js +5 -1
- package/lib/stage/crew/console-reporter/themes/index.js.map +1 -1
- package/lib/stage/crew/index.d.ts +1 -0
- package/lib/stage/crew/index.d.ts.map +1 -0
- package/lib/stage/crew/index.js +5 -1
- package/lib/stage/crew/index.js.map +1 -1
- package/lib/stage/index.d.ts +1 -0
- package/lib/stage/index.d.ts.map +1 -0
- package/lib/stage/index.js +5 -1
- package/lib/stage/index.js.map +1 -1
- package/package.json +19 -42
- package/src/index.ts +9 -0
- package/src/stage/crew/console-reporter/ConsoleReporter.ts +237 -173
- package/src/stage/crew/console-reporter/ConsoleReporterConfig.ts +12 -0
- package/src/stage/crew/console-reporter/Printer.ts +1 -4
- package/src/stage/crew/console-reporter/Summary.ts +23 -4
- package/src/stage/crew/console-reporter/SummaryFormatter.ts +5 -10
- package/src/stage/crew/console-reporter/index.ts +1 -0
- package/src/stage/crew/console-reporter/themes/TerminalTheme.ts +0 -8
- package/src/stage/crew/console-reporter/themes/ThemeForColourTerminals.ts +11 -42
- package/src/stage/crew/console-reporter/themes/ThemeForDarkTerminals.ts +1 -1
- package/src/stage/crew/console-reporter/themes/ThemeForLightTerminals.ts +1 -1
- package/src/stage/crew/console-reporter/themes/ThemeForMonochromaticTerminals.ts +6 -30
- package/tsconfig.build.json +10 -0
- package/tsconfig.eslint.json +0 -10
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ListensToDomainEvents, Stage, StageCrewMemberBuilder } from '@serenity-js/core';
|
|
2
|
-
import {
|
|
1
|
+
import { AssertionError, d, DomainEventQueues, Duration, ListensToDomainEvents, LogicError, Stage, StageCrewMemberBuilder, Timestamp } from '@serenity-js/core';
|
|
2
|
+
import { OutputStream } from '@serenity-js/core/lib/adapter';
|
|
3
3
|
import {
|
|
4
4
|
ActivityRelatedArtifactGenerated,
|
|
5
5
|
DomainEvent,
|
|
@@ -10,12 +10,10 @@ import {
|
|
|
10
10
|
TaskFinished,
|
|
11
11
|
TaskStarts,
|
|
12
12
|
TestRunFinished,
|
|
13
|
+
TestRunStarts,
|
|
13
14
|
} from '@serenity-js/core/lib/events';
|
|
14
|
-
import { OutputStream } from '@serenity-js/core/lib/io';
|
|
15
15
|
import {
|
|
16
|
-
AssertionReport,
|
|
17
16
|
CorrelationId,
|
|
18
|
-
Duration,
|
|
19
17
|
ExecutionCompromised,
|
|
20
18
|
ExecutionFailedWithAssertionError,
|
|
21
19
|
ExecutionFailedWithError,
|
|
@@ -27,34 +25,40 @@ import {
|
|
|
27
25
|
Name,
|
|
28
26
|
Outcome,
|
|
29
27
|
ProblemIndication,
|
|
30
|
-
Timestamp,
|
|
31
28
|
} from '@serenity-js/core/lib/model';
|
|
32
|
-
import { Instance as ChalkInstance } from 'chalk';
|
|
29
|
+
import { Instance as ChalkInstance } from 'chalk'; // eslint-disable-line unicorn/import-style
|
|
33
30
|
import { ensure, isDefined, match } from 'tiny-types';
|
|
34
31
|
|
|
32
|
+
import { ConsoleReporterConfig } from './ConsoleReporterConfig';
|
|
35
33
|
import { Printer } from './Printer';
|
|
36
34
|
import { Summary } from './Summary';
|
|
37
35
|
import { SummaryFormatter } from './SummaryFormatter';
|
|
38
36
|
import { TerminalTheme, ThemeForDarkTerminals, ThemeForLightTerminals, ThemeForMonochromaticTerminals } from './themes';
|
|
39
37
|
|
|
40
38
|
/**
|
|
41
|
-
* @
|
|
42
|
-
*
|
|
43
|
-
* to report on progress of your Serenity/JS acceptance tests.
|
|
39
|
+
* A {@apilink StageCrewMember} that uses [standard output](https://en.wikipedia.org/wiki/Standard_streams)
|
|
40
|
+
* to report on progress of your Serenity/JS acceptance tests.
|
|
44
41
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
42
|
+
* `ConsoleReporter` ships with colour themes for both dark and light terminals,
|
|
43
|
+
* as well as a monochromatic theme for those moments when you're in a noir mood
|
|
44
|
+
* (or have a terminal that doesn't support colours, like the good old `cmd.exe` on Windows).
|
|
48
45
|
*
|
|
49
|
-
*
|
|
46
|
+
* ## Registering the reporter programmatically
|
|
47
|
+
*
|
|
48
|
+
* ```ts
|
|
50
49
|
* import { configure } from '@serenity-js/core';
|
|
51
50
|
* import { ConsoleReporter } from '@serenity-js/console-reporter';
|
|
52
51
|
*
|
|
53
52
|
* configure({
|
|
54
|
-
*
|
|
53
|
+
* crew: [
|
|
54
|
+
* ConsoleReporter.withDefaultColourSupport()
|
|
55
|
+
* ],
|
|
55
56
|
* });
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* ## Redirecting output to a file
|
|
56
60
|
*
|
|
57
|
-
*
|
|
61
|
+
* ```ts
|
|
58
62
|
* import { configure } from '@serenity-js/core';
|
|
59
63
|
* import { ConsoleReporter } from '@serenity-js/console-reporter';
|
|
60
64
|
* import { createWriteStream } from 'fs';
|
|
@@ -63,93 +67,122 @@ import { TerminalTheme, ThemeForDarkTerminals, ThemeForLightTerminals, ThemeForM
|
|
|
63
67
|
* outputStream: createWriteStream('./output.log'),
|
|
64
68
|
* crew: [ ConsoleReporter.withDefaultColourSupport() ],
|
|
65
69
|
* });
|
|
70
|
+
* ```
|
|
71
|
+
* ## Registering the reporter with WebdriverIO
|
|
72
|
+
*
|
|
73
|
+
* ```ts
|
|
74
|
+
* // wdio.conf.ts
|
|
75
|
+
* import { ConsoleReporter } from '@serenity-js/console-reporter';
|
|
76
|
+
* import { WebdriverIOConfig } from '@serenity-js/webdriverio';
|
|
77
|
+
*
|
|
78
|
+
* export const config: WebdriverIOConfig = {
|
|
79
|
+
*
|
|
80
|
+
* framework: '@serenity-js/webdriverio',
|
|
81
|
+
*
|
|
82
|
+
* serenity: {
|
|
83
|
+
* crew: [
|
|
84
|
+
* ConsoleReporter.forDarkTerminals(),
|
|
85
|
+
* ]
|
|
86
|
+
* // other Serenity/JS config
|
|
87
|
+
* },
|
|
88
|
+
*
|
|
89
|
+
* // other WebdriverIO config
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* ## Registering the reporter with Protractor
|
|
66
94
|
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
95
|
+
* ```js
|
|
96
|
+
* // protractor.conf.js
|
|
97
|
+
* const { ConsoleReporter } = require('@serenity-js/console-reporter');
|
|
70
98
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
99
|
+
* exports.config = {
|
|
100
|
+
* framework: 'custom',
|
|
101
|
+
* frameworkPath: require.resolve('@serenity-js/protractor/adapter'),
|
|
74
102
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
103
|
+
* serenity: {
|
|
104
|
+
* crew: [
|
|
105
|
+
* ConsoleReporter.withDefaultColourSupport(),
|
|
106
|
+
* ],
|
|
107
|
+
* // other Serenity/JS config
|
|
108
|
+
* },
|
|
81
109
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
110
|
+
* // other Protractor config
|
|
111
|
+
* }
|
|
112
|
+
* ```
|
|
84
113
|
*
|
|
85
114
|
* @public
|
|
86
|
-
*
|
|
115
|
+
*
|
|
116
|
+
* @group Stage
|
|
87
117
|
*/
|
|
88
118
|
export class ConsoleReporter implements ListensToDomainEvents {
|
|
89
119
|
|
|
90
120
|
private startTimes = new StartTimes();
|
|
91
121
|
private artifacts = new ActivityRelatedArtifacts();
|
|
92
122
|
private summary = new Summary();
|
|
93
|
-
private
|
|
123
|
+
private readonly firstErrors: Map<string, FirstError> = new Map();
|
|
94
124
|
private readonly summaryFormatter: SummaryFormatter;
|
|
125
|
+
private readonly eventQueues = new DomainEventQueues();
|
|
126
|
+
|
|
127
|
+
static fromJSON(config: ConsoleReporterConfig): StageCrewMemberBuilder<ConsoleReporter> {
|
|
128
|
+
return new ConsoleReporterBuilder(ConsoleReporter.theme(config.theme));
|
|
129
|
+
}
|
|
95
130
|
|
|
96
131
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* for dark terminals if successful.
|
|
101
|
-
*
|
|
102
|
-
* Please note that spawning your test process from another process
|
|
103
|
-
* (by using [npm-failsafe](https://www.npmjs.com/package/npm-failsafe), for example)
|
|
104
|
-
* causes the `ConsoleReporter` to use the monochromatic colour scheme,
|
|
105
|
-
* as colour support can't be detected in child processes.
|
|
106
|
-
*
|
|
107
|
-
* If the above describes your setup, use {@link ConsoleReporter#forDarkTerminals}
|
|
108
|
-
* or {@link ConsoleReporter#forLightTerminals} to make the sub-process produce colour output.
|
|
132
|
+
* Instantiates a `ConsoleReporter` that auto-detects
|
|
133
|
+
* your terminal's support for colours and uses a colour theme
|
|
134
|
+
* for dark terminals if successful.
|
|
109
135
|
*
|
|
110
|
-
*
|
|
136
|
+
* Please note that spawning your test process from another process
|
|
137
|
+
* (by using [npm-failsafe](https://www.npmjs.com/package/npm-failsafe), for example)
|
|
138
|
+
* causes the `ConsoleReporter` to use the monochromatic colour scheme,
|
|
139
|
+
* as colour support can't be detected in child processes.
|
|
111
140
|
*/
|
|
112
141
|
static withDefaultColourSupport(): StageCrewMemberBuilder<ConsoleReporter> {
|
|
113
|
-
return new ConsoleReporterBuilder(
|
|
142
|
+
return new ConsoleReporterBuilder(ConsoleReporter.theme('auto'));
|
|
114
143
|
}
|
|
115
144
|
|
|
116
145
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* to avoid printing control characters.
|
|
122
|
-
*
|
|
123
|
-
* @returns {@serenity-js/core/lib/stage~StageCrewMemberBuilder}
|
|
146
|
+
* Instantiates a `ConsoleReporter` with a monochromatic colour theme.
|
|
147
|
+
* Good for terminals with no colour support (like the `cmd.exe` on Windows),
|
|
148
|
+
* or for times when you need to pipe the output to a text file and want
|
|
149
|
+
* to avoid printing control characters.
|
|
124
150
|
*/
|
|
125
151
|
static forMonochromaticTerminals(): StageCrewMemberBuilder<ConsoleReporter> {
|
|
126
|
-
return new ConsoleReporterBuilder(
|
|
152
|
+
return new ConsoleReporterBuilder(ConsoleReporter.theme('mono'));
|
|
127
153
|
}
|
|
128
154
|
|
|
129
155
|
/**
|
|
130
|
-
*
|
|
131
|
-
* Instantiates a `ConsoleReporter` with a colour theme optimised for terminals with dark backgrounds.
|
|
132
|
-
*
|
|
133
|
-
* @returns {@serenity-js/core/lib/stage~StageCrewMemberBuilder}
|
|
156
|
+
* Instantiates a `ConsoleReporter` with a colour theme optimised for terminals with dark backgrounds.
|
|
134
157
|
*/
|
|
135
158
|
static forDarkTerminals(): StageCrewMemberBuilder<ConsoleReporter> {
|
|
136
|
-
return new ConsoleReporterBuilder(
|
|
159
|
+
return new ConsoleReporterBuilder(ConsoleReporter.theme('dark'));
|
|
137
160
|
}
|
|
138
161
|
|
|
139
162
|
/**
|
|
140
|
-
*
|
|
141
|
-
* Instantiates a `ConsoleReporter` with a colour theme optimised for terminals with light backgrounds.
|
|
142
|
-
*
|
|
143
|
-
* @returns {@serenity-js/core/lib/stage~StageCrewMemberBuilder}
|
|
163
|
+
* Instantiates a `ConsoleReporter` with a colour theme optimised for terminals with light backgrounds.
|
|
144
164
|
*/
|
|
145
165
|
static forLightTerminals(): StageCrewMemberBuilder<ConsoleReporter> {
|
|
146
|
-
return new ConsoleReporterBuilder(
|
|
166
|
+
return new ConsoleReporterBuilder(ConsoleReporter.theme('light'));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private static theme(theme: 'light' | 'dark' | 'mono' | 'auto') {
|
|
170
|
+
switch (theme) {
|
|
171
|
+
case 'dark':
|
|
172
|
+
return new ThemeForDarkTerminals(new ChalkInstance({ level: 2 }));
|
|
173
|
+
case 'light':
|
|
174
|
+
return new ThemeForLightTerminals(new ChalkInstance({ level: 2 }));
|
|
175
|
+
case 'mono':
|
|
176
|
+
return new ThemeForMonochromaticTerminals();
|
|
177
|
+
default:
|
|
178
|
+
return new ThemeForDarkTerminals(new ChalkInstance(/* auto-detect */));
|
|
179
|
+
}
|
|
147
180
|
}
|
|
148
181
|
|
|
149
182
|
/**
|
|
150
183
|
* @param {Printer} printer
|
|
151
184
|
* @param {TerminalTheme} theme
|
|
152
|
-
* @param {
|
|
185
|
+
* @param {Stage} [stage=undefined]
|
|
153
186
|
*/
|
|
154
187
|
constructor(
|
|
155
188
|
private readonly printer: Printer,
|
|
@@ -163,167 +196,190 @@ export class ConsoleReporter implements ListensToDomainEvents {
|
|
|
163
196
|
}
|
|
164
197
|
|
|
165
198
|
/**
|
|
166
|
-
* @
|
|
167
|
-
* Handles {@link @serenity-js/core/lib/events~DomainEvent} objects emitted by the {@link @serenity-js/core/lib/stage~StageCrewMember}.
|
|
199
|
+
* Handles {@apilink DomainEvent} objects emitted by the {@apilink Stage}.
|
|
168
200
|
*
|
|
169
|
-
* @see {@
|
|
201
|
+
* @see {@apilink StageCrewMember}
|
|
170
202
|
*
|
|
171
|
-
* @listens {
|
|
203
|
+
* @listens {DomainEvent}
|
|
172
204
|
*
|
|
173
|
-
* @param {
|
|
174
|
-
* @returns {void}
|
|
205
|
+
* @param {DomainEvent} event
|
|
175
206
|
*/
|
|
176
207
|
notifyOf(event: DomainEvent): void {
|
|
177
|
-
match(event)
|
|
178
|
-
.when(SceneStarts, (e: SceneStarts) => {
|
|
179
208
|
|
|
180
|
-
|
|
181
|
-
|
|
209
|
+
if (event instanceof TestRunStarts) {
|
|
210
|
+
this.summary.recordTestRunStartedAt(event.timestamp);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (this.isSceneSpecific(event)) {
|
|
214
|
+
this.eventQueues.enqueue(event);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (event instanceof SceneStarts) {
|
|
218
|
+
this.firstErrors.set(event.sceneId.value, new FirstError());
|
|
219
|
+
|
|
220
|
+
this.startTimes.recordStartOf(event);
|
|
221
|
+
}
|
|
182
222
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
this.printer.println();
|
|
187
|
-
this.printer.println(this.theme.heading(e.details.category.value, ': ', e.details.name.value));
|
|
188
|
-
this.printer.println();
|
|
189
|
-
})
|
|
223
|
+
if (event instanceof SceneFinished) {
|
|
224
|
+
this.printScene(event.sceneId);
|
|
225
|
+
}
|
|
190
226
|
|
|
191
|
-
|
|
227
|
+
if (event instanceof TestRunFinished) {
|
|
228
|
+
this.summary.recordTestRunFinishedAt(event.timestamp);
|
|
192
229
|
|
|
193
|
-
.
|
|
230
|
+
this.printSummary(this.summary);
|
|
194
231
|
|
|
195
|
-
|
|
232
|
+
if (event.outcome instanceof ProblemIndication) {
|
|
233
|
+
this.printTestRunErrorOutcome(event.outcome);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
196
237
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
238
|
+
private printTestRunErrorOutcome(outcome: ProblemIndication): void {
|
|
239
|
+
this.printer.println(this.theme.outcome(outcome, outcome.error.stack));
|
|
240
|
+
}
|
|
200
241
|
|
|
201
|
-
|
|
202
|
-
|
|
242
|
+
private printScene(sceneId: CorrelationId): void {
|
|
243
|
+
const events = this.eventQueues.drainQueueFor(sceneId);
|
|
203
244
|
|
|
204
|
-
|
|
245
|
+
for (const event of events) {
|
|
246
|
+
match(event)
|
|
247
|
+
.when(SceneStarts, (e: SceneStarts) => {
|
|
205
248
|
|
|
206
|
-
|
|
207
|
-
.when(InteractionFinished, (e: InteractionFinished) => {
|
|
249
|
+
const category = e.details.name.value ? `${ e.details.category.value }: ` : '';
|
|
208
250
|
|
|
209
|
-
|
|
210
|
-
|
|
251
|
+
// Print scenario header
|
|
252
|
+
this.printer.println(this.theme.separator('-'));
|
|
253
|
+
this.printer.println(e.details.location.path.value, e.details.location.line ? `:${ e.details.location.line }` : '');
|
|
254
|
+
this.printer.println();
|
|
255
|
+
this.printer.println(this.theme.heading(category, e.details.name.value));
|
|
256
|
+
this.printer.println();
|
|
257
|
+
})
|
|
211
258
|
|
|
212
|
-
|
|
259
|
+
.when(TaskStarts, (e: TaskStarts) => {
|
|
213
260
|
|
|
214
|
-
|
|
215
|
-
this.firstError.recordIfNeeded(e.outcome.error);
|
|
261
|
+
this.printer.indent();
|
|
216
262
|
|
|
217
|
-
if (! (e.
|
|
218
|
-
this.printer.println(
|
|
263
|
+
if (! this.firstErrors.get(e.sceneId.value)?.alreadyRecorded()) {
|
|
264
|
+
this.printer.println(e.details.name.value);
|
|
219
265
|
}
|
|
220
|
-
}
|
|
221
266
|
|
|
222
|
-
|
|
267
|
+
})
|
|
268
|
+
.when(InteractionStarts, (e: InteractionStarts) => {
|
|
223
269
|
|
|
224
|
-
|
|
225
|
-
this.printer.println();
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
artifactGeneratedEvents.forEach(artifactGenerated => {
|
|
229
|
-
if (artifactGenerated.artifact instanceof AssertionReport) {
|
|
230
|
-
const details = artifactGenerated.artifact.map(
|
|
231
|
-
(artifactContents: { expected: string, actual: string }) =>
|
|
232
|
-
this.theme.diff(
|
|
233
|
-
artifactContents.expected,
|
|
234
|
-
artifactContents.actual,
|
|
235
|
-
),
|
|
236
|
-
);
|
|
270
|
+
this.startTimes.recordStartOf(e);
|
|
237
271
|
|
|
238
|
-
|
|
272
|
+
})
|
|
273
|
+
.when(InteractionFinished, (e: InteractionFinished) => {
|
|
239
274
|
|
|
240
|
-
|
|
275
|
+
this.printer.indent();
|
|
276
|
+
this.printer.println(this.formattedOutcome(e));
|
|
241
277
|
|
|
242
|
-
|
|
243
|
-
}
|
|
278
|
+
this.printer.indent();
|
|
244
279
|
|
|
245
|
-
if (
|
|
246
|
-
|
|
280
|
+
if (e.outcome instanceof ProblemIndication) {
|
|
281
|
+
this.firstErrors.get(e.sceneId.value)?.recordIfNeeded(e.outcome.error);
|
|
247
282
|
|
|
248
|
-
if (
|
|
249
|
-
this.printer.println(this.theme.
|
|
283
|
+
if (! (e.outcome.error instanceof AssertionError)) {
|
|
284
|
+
this.printer.println(this.theme.outcome(e.outcome, `${ e.outcome.error }`));
|
|
250
285
|
}
|
|
286
|
+
}
|
|
251
287
|
|
|
252
|
-
|
|
288
|
+
const artifactGeneratedEvents = this.artifacts.recordedFor(e.activityId);
|
|
289
|
+
|
|
290
|
+
if (artifactGeneratedEvents.some(a => a instanceof LogEntry)) {
|
|
253
291
|
this.printer.println();
|
|
254
292
|
}
|
|
255
|
-
});
|
|
256
293
|
|
|
257
|
-
|
|
294
|
+
artifactGeneratedEvents.forEach(artifactGenerated => {
|
|
295
|
+
if (artifactGenerated.artifact instanceof LogEntry) {
|
|
296
|
+
const details = artifactGenerated.artifact.map((artifactContents: { data: string }) => artifactContents.data);
|
|
258
297
|
|
|
259
|
-
|
|
298
|
+
if (artifactGenerated.name.value !== details) {
|
|
299
|
+
this.printer.println(this.theme.log(artifactGenerated.name.value, ':'));
|
|
300
|
+
}
|
|
260
301
|
|
|
261
|
-
|
|
262
|
-
|
|
302
|
+
this.printer.println(details);
|
|
303
|
+
this.printer.println();
|
|
304
|
+
}
|
|
305
|
+
});
|
|
263
306
|
|
|
264
|
-
|
|
307
|
+
this.printer.outdent();
|
|
265
308
|
|
|
266
|
-
|
|
267
|
-
.when(TaskFinished, (e: TaskFinished) => {
|
|
309
|
+
this.printer.outdent();
|
|
268
310
|
|
|
269
|
-
|
|
311
|
+
})
|
|
312
|
+
.when(ActivityRelatedArtifactGenerated, (e: ActivityRelatedArtifactGenerated) => {
|
|
270
313
|
|
|
271
|
-
|
|
272
|
-
this.printer.indent();
|
|
273
|
-
this.printer.indent();
|
|
314
|
+
this.artifacts.record(e);
|
|
274
315
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
316
|
+
})
|
|
317
|
+
.when(TaskFinished, (e: TaskFinished) => {
|
|
278
318
|
|
|
279
|
-
this.printer.outdent();
|
|
280
319
|
this.printer.outdent();
|
|
281
320
|
|
|
282
|
-
|
|
283
|
-
|
|
321
|
+
if (e.outcome instanceof ProblemIndication) {
|
|
322
|
+
this.printer.indent();
|
|
323
|
+
this.printer.indent();
|
|
284
324
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
325
|
+
if (! this.firstErrors.get(e.sceneId.value).alreadyRecorded()) {
|
|
326
|
+
this.printer.println(this.theme.outcome(e.outcome, this.iconFrom(e.outcome), `${ e.outcome.error }`));
|
|
327
|
+
}
|
|
288
328
|
|
|
289
|
-
|
|
290
|
-
|
|
329
|
+
this.printer.outdent();
|
|
330
|
+
this.printer.outdent();
|
|
331
|
+
|
|
332
|
+
this.firstErrors.get(e.sceneId.value).recordIfNeeded(e.outcome.error);
|
|
333
|
+
}
|
|
291
334
|
|
|
292
|
-
|
|
293
|
-
|
|
335
|
+
else if (! (e.outcome instanceof ExecutionSuccessful)) {
|
|
336
|
+
this.printer.indent();
|
|
337
|
+
this.printer.println(this.iconFrom(e.outcome), e.details.name.value);
|
|
294
338
|
|
|
295
|
-
|
|
339
|
+
this.printer.outdent();
|
|
340
|
+
}
|
|
296
341
|
|
|
297
|
-
|
|
298
|
-
|
|
342
|
+
})
|
|
343
|
+
.when(SceneFinished, (e: SceneFinished) => {
|
|
299
344
|
|
|
300
|
-
|
|
345
|
+
this.summary.record(e.details, e.outcome, this.startTimes.eventDurationOf(e));
|
|
301
346
|
|
|
302
347
|
this.printer.println();
|
|
348
|
+
this.printer.println(this.theme.outcome(e.outcome, this.formattedOutcome(e, this.deCamelCased(e.outcome.constructor.name))));
|
|
303
349
|
|
|
304
|
-
|
|
350
|
+
if (e.outcome instanceof ProblemIndication) {
|
|
351
|
+
|
|
352
|
+
this.printer.println();
|
|
305
353
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
354
|
+
this.printer.indent();
|
|
355
|
+
|
|
356
|
+
if (e.outcome instanceof ImplementationPending) {
|
|
357
|
+
this.printer.println(`${ e.outcome.error.name }: ${ e.outcome.error.message }`);
|
|
358
|
+
} else if (e.outcome.error?.stack) {
|
|
359
|
+
this.printer.println(e.outcome.error.stack);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
this.printer.outdent();
|
|
310
363
|
}
|
|
311
364
|
|
|
312
|
-
this.
|
|
313
|
-
}
|
|
365
|
+
this.artifacts.clear();
|
|
366
|
+
})
|
|
367
|
+
.else((_: DomainEvent) => {
|
|
368
|
+
return void 0;
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
314
372
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
.when(TestRunFinished, (_: TestRunFinished) => {
|
|
318
|
-
this.printer.println(this.theme.separator('='));
|
|
373
|
+
private printSummary(summary: Summary) {
|
|
374
|
+
this.printer.println(this.theme.separator('='));
|
|
319
375
|
|
|
320
|
-
|
|
376
|
+
this.printer.print(this.summaryFormatter.format(summary.aggregated()));
|
|
377
|
+
|
|
378
|
+
this.printer.println(this.theme.separator('='));
|
|
379
|
+
}
|
|
321
380
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
.else((_: DomainEvent) => {
|
|
325
|
-
return void 0;
|
|
326
|
-
});
|
|
381
|
+
private isSceneSpecific(event: DomainEvent): event is DomainEvent & { sceneId: CorrelationId } {
|
|
382
|
+
return Object.prototype.hasOwnProperty.call(event, 'sceneId');
|
|
327
383
|
}
|
|
328
384
|
|
|
329
385
|
private formattedOutcome(event: IdentifiableEvent & { outcome: Outcome }, description: string = event.details.name.value) {
|
|
@@ -377,14 +433,22 @@ interface IdentifiableEvent {
|
|
|
377
433
|
}
|
|
378
434
|
|
|
379
435
|
class StartTimes {
|
|
380
|
-
private times: { [
|
|
436
|
+
private times: { [key: string]: Timestamp } = {};
|
|
381
437
|
|
|
382
438
|
recordStartOf(event: IdentifiableEvent) {
|
|
383
|
-
this.times[
|
|
439
|
+
this.times[this.keyFor(event)] = event.timestamp;
|
|
384
440
|
}
|
|
385
441
|
|
|
386
442
|
eventDurationOf(event: IdentifiableEvent & { outcome: Outcome }): Duration {
|
|
387
|
-
|
|
443
|
+
if (! this.times[this.keyFor(event)]) {
|
|
444
|
+
throw new LogicError(d`StartTime missing for event ${ event }`)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return event.timestamp.diff(this.times[this.keyFor(event)]);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
private keyFor(event: IdentifiableEvent): string {
|
|
451
|
+
return event.details.toString();
|
|
388
452
|
}
|
|
389
453
|
}
|
|
390
454
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Used to configure the {@apilink ConsoleReporter}.
|
|
3
|
+
*
|
|
4
|
+
* @group Stage
|
|
5
|
+
*/
|
|
6
|
+
export interface ConsoleReporterConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Choose a colour theme optimised for light, dark, or monochromatic terminals.
|
|
9
|
+
* Or, use 'auto' to automatically pick the most suitable one.
|
|
10
|
+
*/
|
|
11
|
+
theme: 'light' | 'dark' | 'mono' | 'auto'
|
|
12
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OutputStream } from '@serenity-js/core/lib/
|
|
1
|
+
import { OutputStream } from '@serenity-js/core/lib/adapter';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @desc
|
|
@@ -10,9 +10,6 @@ import { OutputStream } from '@serenity-js/core/lib/io';
|
|
|
10
10
|
export class Printer {
|
|
11
11
|
private indentation = new Indentation(2);
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* @param {@serenity-js/core/lib/io~OutputStream} stdout
|
|
15
|
-
*/
|
|
16
13
|
constructor(private readonly stdout: OutputStream = process.stdout) {
|
|
17
14
|
}
|
|
18
15
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { Duration } from '@serenity-js/core';
|
|
1
|
+
import { Duration, Timestamp } from '@serenity-js/core';
|
|
2
2
|
import {
|
|
3
3
|
ExecutionCompromised,
|
|
4
4
|
ExecutionFailedWithAssertionError,
|
|
5
|
-
ExecutionFailedWithError,
|
|
6
|
-
|
|
5
|
+
ExecutionFailedWithError,
|
|
6
|
+
ExecutionIgnored,
|
|
7
|
+
ExecutionSkipped,
|
|
8
|
+
ExecutionSuccessful,
|
|
7
9
|
ImplementationPending,
|
|
8
10
|
Outcome,
|
|
9
11
|
ScenarioDetails,
|
|
@@ -15,6 +17,9 @@ import {
|
|
|
15
17
|
export class Summary {
|
|
16
18
|
private readonly records: SummaryRecord[] = [];
|
|
17
19
|
|
|
20
|
+
private testRunStartedAt: Timestamp = new Timestamp();
|
|
21
|
+
private testRunFinishedAt: Timestamp = new Timestamp();
|
|
22
|
+
|
|
18
23
|
record(details: ScenarioDetails, outcome: Outcome, duration: Duration): void {
|
|
19
24
|
this.records.push({ details, outcome, duration });
|
|
20
25
|
}
|
|
@@ -46,7 +51,20 @@ export class Summary {
|
|
|
46
51
|
acc.categories[categoryName].totalTime = acc.categories[categoryName].totalTime.plus(record.duration);
|
|
47
52
|
|
|
48
53
|
return acc;
|
|
49
|
-
}, {
|
|
54
|
+
}, {
|
|
55
|
+
categories: {},
|
|
56
|
+
totalTime: Duration.ofMilliseconds(0),
|
|
57
|
+
realTime: this.testRunFinishedAt.diff(this.testRunStartedAt),
|
|
58
|
+
numberOfScenarios: this.records.length
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
recordTestRunStartedAt(timestamp: Timestamp): void {
|
|
63
|
+
this.testRunStartedAt = timestamp;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
recordTestRunFinishedAt(timestamp: Timestamp): void {
|
|
67
|
+
this.testRunFinishedAt = timestamp;
|
|
50
68
|
}
|
|
51
69
|
}
|
|
52
70
|
|
|
@@ -56,6 +74,7 @@ export class Summary {
|
|
|
56
74
|
export interface AggregatedCategories {
|
|
57
75
|
categories: {[categoryName: string]: AggregatedCategory};
|
|
58
76
|
totalTime: Duration;
|
|
77
|
+
realTime: Duration;
|
|
59
78
|
numberOfScenarios: number;
|
|
60
79
|
}
|
|
61
80
|
|