@serenity-js/core 3.7.2 → 3.9.0
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 +27 -0
- package/lib/events/ActivityRelatedArtifactArchived.d.ts +1 -1
- package/lib/events/ActivityRelatedArtifactArchived.d.ts.map +1 -1
- package/lib/events/ActivityRelatedArtifactArchived.js +4 -3
- package/lib/events/ActivityRelatedArtifactArchived.js.map +1 -1
- package/lib/events/ArtifactArchived.d.ts +2 -1
- package/lib/events/ArtifactArchived.d.ts.map +1 -1
- package/lib/events/ArtifactArchived.js +5 -2
- package/lib/events/ArtifactArchived.js.map +1 -1
- package/lib/screenplay/Question.d.ts +18 -5
- package/lib/screenplay/Question.d.ts.map +1 -1
- package/lib/screenplay/Question.js +22 -20
- package/lib/screenplay/Question.js.map +1 -1
- package/lib/screenplay/questions/MetaQuestion.d.ts +8 -7
- package/lib/screenplay/questions/MetaQuestion.d.ts.map +1 -1
- package/lib/stage/crew/artifact-archiver/ArtifactArchiver.d.ts +39 -19
- package/lib/stage/crew/artifact-archiver/ArtifactArchiver.d.ts.map +1 -1
- package/lib/stage/crew/artifact-archiver/ArtifactArchiver.js +41 -21
- package/lib/stage/crew/artifact-archiver/ArtifactArchiver.js.map +1 -1
- package/lib/stage/crew/stream-reporter/StreamReporter.d.ts +44 -16
- package/lib/stage/crew/stream-reporter/StreamReporter.d.ts.map +1 -1
- package/lib/stage/crew/stream-reporter/StreamReporter.js +48 -16
- package/lib/stage/crew/stream-reporter/StreamReporter.js.map +1 -1
- package/package.json +2 -2
- package/src/events/ActivityRelatedArtifactArchived.ts +4 -1
- package/src/events/ArtifactArchived.ts +4 -0
- package/src/screenplay/Question.ts +67 -8
- package/src/screenplay/questions/MetaQuestion.ts +8 -7
- package/src/stage/crew/artifact-archiver/ArtifactArchiver.ts +43 -19
- package/src/stage/crew/stream-reporter/StreamReporter.ts +51 -16
|
@@ -5,7 +5,7 @@ import type { Stage } from '../../Stage';
|
|
|
5
5
|
import type { StageCrewMember } from '../../StageCrewMember';
|
|
6
6
|
/**
|
|
7
7
|
* Serialises all the {@apilink DomainEvent} objects it receives and streams
|
|
8
|
-
* them as [ndjson](http://ndjson.org/) to the output stream.
|
|
8
|
+
* them as [ndjson](http://ndjson.org/) to the output stream or file.
|
|
9
9
|
*
|
|
10
10
|
* Useful when debugging issues related to custom Serenity/JS test runner adapters.
|
|
11
11
|
*
|
|
@@ -34,43 +34,61 @@ import type { StageCrewMember } from '../../StageCrewMember';
|
|
|
34
34
|
* })
|
|
35
35
|
* ```
|
|
36
36
|
*
|
|
37
|
-
* ##
|
|
37
|
+
* ## Using `StreamReporter` with Playwright Test
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* // playwright.config.ts
|
|
41
|
+
* import type { PlaywrightTestConfig } from '@serenity-js/playwright-test'
|
|
42
|
+
*
|
|
43
|
+
* const config: PlaywrightTestConfig = {
|
|
44
|
+
* testDir: './spec',
|
|
45
|
+
*
|
|
46
|
+
* reporter: [
|
|
47
|
+
* [ '@serenity-js/playwright-test', {
|
|
48
|
+
* crew: [
|
|
49
|
+
* [ '@serenity-js/core:StreamReporter', { outputFile: './events.ndjson' }]
|
|
50
|
+
* ]
|
|
51
|
+
* // other Serenity/JS config
|
|
52
|
+
* }]
|
|
53
|
+
* ],
|
|
54
|
+
* // other Playwright Test config
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* ## Using `StreamReporter` with Protractor
|
|
38
59
|
*
|
|
39
60
|
* ```js
|
|
40
61
|
* // protractor.conf.js
|
|
41
|
-
* const { StreamReporter } = require('@serenity-js/core');
|
|
42
|
-
*
|
|
43
62
|
* exports.config = {
|
|
44
63
|
* framework: 'custom',
|
|
45
64
|
* frameworkPath: require.resolve('@serenity-js/protractor/adapter'),
|
|
46
65
|
*
|
|
47
66
|
* serenity: {
|
|
48
67
|
* crew: [
|
|
49
|
-
*
|
|
68
|
+
* [ '@serenity-js/core:StreamReporter', { outputFile: './events.ndjson' }]
|
|
50
69
|
* ],
|
|
51
70
|
* // other Serenity/JS config
|
|
52
71
|
* },
|
|
53
72
|
* // other Protractor config
|
|
54
|
-
* }
|
|
73
|
+
* }
|
|
55
74
|
* ```
|
|
56
75
|
*
|
|
57
|
-
* ##
|
|
76
|
+
* ## Using `StreamReporter` with WebdriverIO
|
|
58
77
|
*
|
|
59
78
|
* ```ts
|
|
60
|
-
* // wdio.conf.
|
|
61
|
-
* import { StreamReporter } from '@serenity-js/core'
|
|
79
|
+
* // wdio.conf.ts
|
|
62
80
|
* import { WebdriverIOConfig } from '@serenity-js/webdriverio'
|
|
63
81
|
*
|
|
64
82
|
* export const config: WebdriverIOConfig = {
|
|
65
83
|
*
|
|
66
|
-
*
|
|
84
|
+
* framework: '@serenity-js/webdriverio',
|
|
67
85
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
86
|
+
* serenity: {
|
|
87
|
+
* crew: [
|
|
88
|
+
* '@serenity-js/serenity-bdd',
|
|
89
|
+
* [ '@serenity-js/core:StreamReporter', { outputFile: './events.ndjson' }]
|
|
90
|
+
* ]
|
|
91
|
+
* // other Serenity/JS config
|
|
74
92
|
* },
|
|
75
93
|
* // other WebdriverIO config
|
|
76
94
|
* }
|
|
@@ -81,6 +99,16 @@ import type { StageCrewMember } from '../../StageCrewMember';
|
|
|
81
99
|
export declare class StreamReporter implements StageCrewMember {
|
|
82
100
|
private readonly output;
|
|
83
101
|
private readonly stage?;
|
|
102
|
+
/**
|
|
103
|
+
* Instantiates a `StreamReporter` outputting a stream of {@apilink DomainEvent|domain events}
|
|
104
|
+
* to an `outputFile` at the given location.
|
|
105
|
+
*
|
|
106
|
+
* @param config
|
|
107
|
+
*/
|
|
108
|
+
static fromJSON(config: {
|
|
109
|
+
outputFile: string;
|
|
110
|
+
cwd?: string;
|
|
111
|
+
}): StageCrewMember;
|
|
84
112
|
/**
|
|
85
113
|
* @param {stream~Writable} output
|
|
86
114
|
* A Writable stream that should receive the output
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StreamReporter.d.ts","sourceRoot":"","sources":["../../../../src/stage/crew/stream-reporter/StreamReporter.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"StreamReporter.d.ts","sourceRoot":"","sources":["../../../../src/stage/crew/stream-reporter/StreamReporter.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4FG;AACH,qBAAa,cAAe,YAAW,eAAe;IAyB9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IAxB3B;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,eAAe;IAS9E;;;;;;OAMG;gBAEkB,MAAM,GAAE,QAAyB,EACjC,KAAK,CAAC,EAAE,KAAK;IAIlC;;;;;;;;OAQG;IACH,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,eAAe;IAIzC;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;CAKrC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.StreamReporter = void 0;
|
|
4
|
+
const tiny_types_1 = require("tiny-types");
|
|
5
|
+
const io_1 = require("../../../io");
|
|
4
6
|
/**
|
|
5
7
|
* Serialises all the {@apilink DomainEvent} objects it receives and streams
|
|
6
|
-
* them as [ndjson](http://ndjson.org/) to the output stream.
|
|
8
|
+
* them as [ndjson](http://ndjson.org/) to the output stream or file.
|
|
7
9
|
*
|
|
8
10
|
* Useful when debugging issues related to custom Serenity/JS test runner adapters.
|
|
9
11
|
*
|
|
@@ -32,43 +34,61 @@ exports.StreamReporter = void 0;
|
|
|
32
34
|
* })
|
|
33
35
|
* ```
|
|
34
36
|
*
|
|
35
|
-
* ##
|
|
37
|
+
* ## Using `StreamReporter` with Playwright Test
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* // playwright.config.ts
|
|
41
|
+
* import type { PlaywrightTestConfig } from '@serenity-js/playwright-test'
|
|
42
|
+
*
|
|
43
|
+
* const config: PlaywrightTestConfig = {
|
|
44
|
+
* testDir: './spec',
|
|
45
|
+
*
|
|
46
|
+
* reporter: [
|
|
47
|
+
* [ '@serenity-js/playwright-test', {
|
|
48
|
+
* crew: [
|
|
49
|
+
* [ '@serenity-js/core:StreamReporter', { outputFile: './events.ndjson' }]
|
|
50
|
+
* ]
|
|
51
|
+
* // other Serenity/JS config
|
|
52
|
+
* }]
|
|
53
|
+
* ],
|
|
54
|
+
* // other Playwright Test config
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* ## Using `StreamReporter` with Protractor
|
|
36
59
|
*
|
|
37
60
|
* ```js
|
|
38
61
|
* // protractor.conf.js
|
|
39
|
-
* const { StreamReporter } = require('@serenity-js/core');
|
|
40
|
-
*
|
|
41
62
|
* exports.config = {
|
|
42
63
|
* framework: 'custom',
|
|
43
64
|
* frameworkPath: require.resolve('@serenity-js/protractor/adapter'),
|
|
44
65
|
*
|
|
45
66
|
* serenity: {
|
|
46
67
|
* crew: [
|
|
47
|
-
*
|
|
68
|
+
* [ '@serenity-js/core:StreamReporter', { outputFile: './events.ndjson' }]
|
|
48
69
|
* ],
|
|
49
70
|
* // other Serenity/JS config
|
|
50
71
|
* },
|
|
51
72
|
* // other Protractor config
|
|
52
|
-
* }
|
|
73
|
+
* }
|
|
53
74
|
* ```
|
|
54
75
|
*
|
|
55
|
-
* ##
|
|
76
|
+
* ## Using `StreamReporter` with WebdriverIO
|
|
56
77
|
*
|
|
57
78
|
* ```ts
|
|
58
|
-
* // wdio.conf.
|
|
59
|
-
* import { StreamReporter } from '@serenity-js/core'
|
|
79
|
+
* // wdio.conf.ts
|
|
60
80
|
* import { WebdriverIOConfig } from '@serenity-js/webdriverio'
|
|
61
81
|
*
|
|
62
82
|
* export const config: WebdriverIOConfig = {
|
|
63
83
|
*
|
|
64
|
-
*
|
|
84
|
+
* framework: '@serenity-js/webdriverio',
|
|
65
85
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
86
|
+
* serenity: {
|
|
87
|
+
* crew: [
|
|
88
|
+
* '@serenity-js/serenity-bdd',
|
|
89
|
+
* [ '@serenity-js/core:StreamReporter', { outputFile: './events.ndjson' }]
|
|
90
|
+
* ]
|
|
91
|
+
* // other Serenity/JS config
|
|
72
92
|
* },
|
|
73
93
|
* // other WebdriverIO config
|
|
74
94
|
* }
|
|
@@ -77,6 +97,18 @@ exports.StreamReporter = void 0;
|
|
|
77
97
|
* @group Stage
|
|
78
98
|
*/
|
|
79
99
|
class StreamReporter {
|
|
100
|
+
/**
|
|
101
|
+
* Instantiates a `StreamReporter` outputting a stream of {@apilink DomainEvent|domain events}
|
|
102
|
+
* to an `outputFile` at the given location.
|
|
103
|
+
*
|
|
104
|
+
* @param config
|
|
105
|
+
*/
|
|
106
|
+
static fromJSON(config) {
|
|
107
|
+
const outputFile = (0, tiny_types_1.ensure)('outputFile', config?.outputFile, (0, tiny_types_1.isDefined)(), (0, tiny_types_1.isString)());
|
|
108
|
+
const cwd = config.cwd || process.cwd();
|
|
109
|
+
const fs = new io_1.FileSystem(io_1.Path.from(cwd));
|
|
110
|
+
return new StreamReporter(fs.createWriteStreamTo(io_1.Path.from(outputFile)));
|
|
111
|
+
}
|
|
80
112
|
/**
|
|
81
113
|
* @param {stream~Writable} output
|
|
82
114
|
* A Writable stream that should receive the output
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StreamReporter.js","sourceRoot":"","sources":["../../../../src/stage/crew/stream-reporter/StreamReporter.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"StreamReporter.js","sourceRoot":"","sources":["../../../../src/stage/crew/stream-reporter/StreamReporter.ts"],"names":[],"mappings":";;;AACA,2CAAyD;AAGzD,oCAA+C;AAI/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4FG;AACH,MAAa,cAAc;IAEvB;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,MAA4C;QACxD,MAAM,UAAU,GAAG,IAAA,mBAAM,EAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,IAAA,sBAAS,GAAE,EAAE,IAAA,qBAAQ,GAAE,CAAC,CAAC;QACrF,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAExC,MAAM,EAAE,GAAG,IAAI,eAAU,CAAC,SAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAEzC,OAAO,IAAI,cAAc,CAAC,EAAE,CAAC,mBAAmB,CAAC,SAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC7E,CAAC;IAED;;;;;;OAMG;IACH,YACqB,SAAmB,OAAO,CAAC,MAAM,EACjC,KAAa;QADb,WAAM,GAAN,MAAM,CAA2B;QACjC,UAAK,GAAL,KAAK,CAAQ;IAElC,CAAC;IAED;;;;;;;;OAQG;IACH,UAAU,CAAC,KAAY;QACnB,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;IAED;;;;;;OAMG;IACH,QAAQ,CAAC,KAAkB;QACvB,IAAI,CAAC,MAAM,CAAC,KAAK,CACb,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,IAAI,CACjF,CAAC;IACN,CAAC;CACJ;AAvDD,wCAuDC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serenity-js/core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
4
4
|
"description": "Serenity/JS Screenplay, reporting engine and core interfaces.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Jan Molak",
|
|
@@ -71,5 +71,5 @@
|
|
|
71
71
|
"engines": {
|
|
72
72
|
"node": "^16.13 || ^18.12 || ^20"
|
|
73
73
|
},
|
|
74
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "9e882e469e9c6b1617e6f645b999036ba249478b"
|
|
75
75
|
}
|
|
@@ -22,6 +22,7 @@ export class ActivityRelatedArtifactArchived extends ArtifactArchived {
|
|
|
22
22
|
Name.fromJSON(o.name as string),
|
|
23
23
|
Artifact.ofType(o.type as string),
|
|
24
24
|
Path.fromJSON(o.path as string),
|
|
25
|
+
Timestamp.fromJSON(o.artifactTimestamp as string),
|
|
25
26
|
Timestamp.fromJSON(o.timestamp as string),
|
|
26
27
|
);
|
|
27
28
|
}
|
|
@@ -32,9 +33,10 @@ export class ActivityRelatedArtifactArchived extends ArtifactArchived {
|
|
|
32
33
|
name: Name,
|
|
33
34
|
type: ArtifactType,
|
|
34
35
|
path: Path,
|
|
36
|
+
artifactTimestamp: Timestamp,
|
|
35
37
|
timestamp?: Timestamp,
|
|
36
38
|
) {
|
|
37
|
-
super(sceneId, name, type, path, timestamp);
|
|
39
|
+
super(sceneId, name, type, path, artifactTimestamp, timestamp);
|
|
38
40
|
ensure('activityId', activityId, isDefined());
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -45,6 +47,7 @@ export class ActivityRelatedArtifactArchived extends ArtifactArchived {
|
|
|
45
47
|
name: this.name.toJSON(),
|
|
46
48
|
type: this.type.name,
|
|
47
49
|
path: this.path.toJSON(),
|
|
50
|
+
artifactTimestamp: this.artifactTimestamp.toJSON(),
|
|
48
51
|
timestamp: this.timestamp.toJSON(),
|
|
49
52
|
};
|
|
50
53
|
}
|
|
@@ -17,6 +17,7 @@ export class ArtifactArchived extends DomainEvent {
|
|
|
17
17
|
Name.fromJSON(o.name as string),
|
|
18
18
|
Artifact.ofType(o.type as string),
|
|
19
19
|
Path.fromJSON(o.path as string),
|
|
20
|
+
Timestamp.fromJSON(o.artifactTimestamp as string),
|
|
20
21
|
Timestamp.fromJSON(o.timestamp as string),
|
|
21
22
|
);
|
|
22
23
|
}
|
|
@@ -26,6 +27,7 @@ export class ArtifactArchived extends DomainEvent {
|
|
|
26
27
|
public readonly name: Name,
|
|
27
28
|
public readonly type: ArtifactType,
|
|
28
29
|
public readonly path: Path,
|
|
30
|
+
public readonly artifactTimestamp: Timestamp,
|
|
29
31
|
timestamp?: Timestamp,
|
|
30
32
|
) {
|
|
31
33
|
super(timestamp);
|
|
@@ -34,6 +36,7 @@ export class ArtifactArchived extends DomainEvent {
|
|
|
34
36
|
ensure('name', name, isDefined());
|
|
35
37
|
ensure('type', type, isDefined());
|
|
36
38
|
ensure('path', path, isDefined());
|
|
39
|
+
ensure('artifactTimestamp', artifactTimestamp, isDefined());
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
toJSON(): JSONObject {
|
|
@@ -41,6 +44,7 @@ export class ArtifactArchived extends DomainEvent {
|
|
|
41
44
|
name: this.name.toJSON(),
|
|
42
45
|
type: this.type.name,
|
|
43
46
|
path: this.path.toJSON(),
|
|
47
|
+
createdAt: this.artifactTimestamp.toJSON(),
|
|
44
48
|
timestamp: this.timestamp.toJSON(),
|
|
45
49
|
};
|
|
46
50
|
}
|
|
@@ -2,12 +2,14 @@ import { isRecord } from 'tiny-types/lib/objects';
|
|
|
2
2
|
import * as util from 'util'; // eslint-disable-line unicorn/import-style
|
|
3
3
|
|
|
4
4
|
import { LogicError } from '../errors';
|
|
5
|
-
import {
|
|
5
|
+
import type { FileSystemLocation } from '../io';
|
|
6
|
+
import { d, f, inspectedObject } from '../io';
|
|
6
7
|
import type { UsesAbilities } from './abilities';
|
|
7
8
|
import type { Answerable } from './Answerable';
|
|
8
9
|
import { Interaction } from './Interaction';
|
|
9
10
|
import type { Optional } from './Optional';
|
|
10
11
|
import type { AnswersQuestions } from './questions/AnswersQuestions';
|
|
12
|
+
import type { MetaQuestion } from './questions/MetaQuestion';
|
|
11
13
|
import { Unanswered } from './questions/Unanswered';
|
|
12
14
|
import type { RecursivelyAnswered } from './RecursivelyAnswered';
|
|
13
15
|
import type { WithAnswerableProperties } from './WithAnswerableProperties';
|
|
@@ -123,9 +125,30 @@ export abstract class Question<T> {
|
|
|
123
125
|
*
|
|
124
126
|
* @param description
|
|
125
127
|
* @param body
|
|
128
|
+
* @param [metaQuestionBody]
|
|
126
129
|
*/
|
|
127
|
-
static about<
|
|
128
|
-
|
|
130
|
+
static about<Answer_Type, Supported_Context_Type>(
|
|
131
|
+
description: string,
|
|
132
|
+
body: (actor: AnswersQuestions & UsesAbilities) => Promise<Answer_Type> | Answer_Type,
|
|
133
|
+
metaQuestionBody: (answerable: Answerable<Supported_Context_Type>) => Question<Promise<Answer_Type>> | Question<Answer_Type>,
|
|
134
|
+
): MetaQuestionAdapter<Supported_Context_Type, Awaited<Answer_Type>>
|
|
135
|
+
|
|
136
|
+
static about<Answer_Type>(
|
|
137
|
+
description: string,
|
|
138
|
+
body: (actor: AnswersQuestions & UsesAbilities) => Promise<Answer_Type> | Answer_Type
|
|
139
|
+
): QuestionAdapter<Awaited<Answer_Type>>
|
|
140
|
+
|
|
141
|
+
static about<Answer_Type, Supported_Context_Type extends Answerable<any>>(
|
|
142
|
+
description: string,
|
|
143
|
+
body: (actor: AnswersQuestions & UsesAbilities) => Promise<Answer_Type> | Answer_Type,
|
|
144
|
+
metaQuestionBody?: (answerable: Supported_Context_Type) => QuestionAdapter<Answer_Type>,
|
|
145
|
+
): any
|
|
146
|
+
{
|
|
147
|
+
const statement = typeof metaQuestionBody === 'function'
|
|
148
|
+
? new MetaQuestionStatement(description, body, metaQuestionBody)
|
|
149
|
+
: new QuestionStatement(description, body);
|
|
150
|
+
|
|
151
|
+
return Question.createAdapter(statement);
|
|
129
152
|
}
|
|
130
153
|
|
|
131
154
|
/**
|
|
@@ -429,13 +452,25 @@ export type QuestionAdapterFieldDecorator<Original_Type> = {
|
|
|
429
452
|
*
|
|
430
453
|
* @group Questions
|
|
431
454
|
*/
|
|
432
|
-
export type QuestionAdapter<
|
|
433
|
-
& Question<Promise<
|
|
455
|
+
export type QuestionAdapter<Answer_Type> =
|
|
456
|
+
& Question<Promise<Answer_Type>>
|
|
434
457
|
& Interaction
|
|
435
458
|
& { isPresent(): Question<Promise<boolean>>; } // more specialised Optional
|
|
436
|
-
& QuestionAdapterFieldDecorator<
|
|
459
|
+
& QuestionAdapterFieldDecorator<Answer_Type>;
|
|
437
460
|
|
|
438
|
-
/**
|
|
461
|
+
/**
|
|
462
|
+
* An extension of {@apilink QuestionAdapter}, that in addition to proxying methods and fields
|
|
463
|
+
* of the wrapped object can also act as a {@apilink MetaQuestion}.
|
|
464
|
+
*
|
|
465
|
+
* @group Questions
|
|
466
|
+
*/
|
|
467
|
+
export type MetaQuestionAdapter<Context_Type, Answer_Type> =
|
|
468
|
+
& QuestionAdapter<Answer_Type>
|
|
469
|
+
& MetaQuestion<Context_Type, Answer_Type>
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* @package
|
|
473
|
+
*/
|
|
439
474
|
class QuestionStatement<Answer_Type> extends Interaction implements Question<Promise<Answer_Type>>, Optional {
|
|
440
475
|
|
|
441
476
|
private answer: Answer_Type | Unanswered = new Unanswered();
|
|
@@ -443,8 +478,9 @@ class QuestionStatement<Answer_Type> extends Interaction implements Question<Pro
|
|
|
443
478
|
constructor(
|
|
444
479
|
private subject: string,
|
|
445
480
|
private readonly body: (actor: AnswersQuestions & UsesAbilities, ...Parameters) => Promise<Answer_Type> | Answer_Type,
|
|
481
|
+
location: FileSystemLocation = QuestionStatement.callerLocation(4),
|
|
446
482
|
) {
|
|
447
|
-
super(subject,
|
|
483
|
+
super(subject, location);
|
|
448
484
|
}
|
|
449
485
|
|
|
450
486
|
/**
|
|
@@ -490,6 +526,29 @@ class QuestionStatement<Answer_Type> extends Interaction implements Question<Pro
|
|
|
490
526
|
}
|
|
491
527
|
}
|
|
492
528
|
|
|
529
|
+
/**
|
|
530
|
+
* @package
|
|
531
|
+
*/
|
|
532
|
+
class MetaQuestionStatement<Answer_Type, Supported_Context_Type extends Answerable<any>>
|
|
533
|
+
extends QuestionStatement<Answer_Type>
|
|
534
|
+
implements MetaQuestion<Supported_Context_Type, Answer_Type>
|
|
535
|
+
{
|
|
536
|
+
constructor(
|
|
537
|
+
subject: string,
|
|
538
|
+
body: (actor: AnswersQuestions & UsesAbilities, ...Parameters) => Promise<Answer_Type> | Answer_Type,
|
|
539
|
+
private readonly metaQuestionBody: (answerable: Answerable<Supported_Context_Type>) => QuestionAdapter<Answer_Type>,
|
|
540
|
+
) {
|
|
541
|
+
super(subject, body);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
of(answerable: Answerable<Supported_Context_Type>): Question<Promise<Answer_Type>> | Question<Answer_Type> {
|
|
545
|
+
return Question.about(
|
|
546
|
+
d`${ this.toString() } of ${ answerable }`,
|
|
547
|
+
actor => actor.answer(this.metaQuestionBody(answerable))
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
493
552
|
/**
|
|
494
553
|
* @package
|
|
495
554
|
*/
|
|
@@ -2,11 +2,12 @@ import type { Answerable } from '../Answerable';
|
|
|
2
2
|
import type { Question } from '../Question';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* A
|
|
6
|
-
*
|
|
5
|
+
* A meta-question is a {@apilink Question} that can be answered
|
|
6
|
+
* in the context of another {@apilink Answerable},
|
|
7
|
+
* typically to transform its value.
|
|
7
8
|
*
|
|
8
|
-
* For example, the question {@apilink Text.of}
|
|
9
|
-
*
|
|
9
|
+
* For example, the question {@apilink Text.of} can be answered in the context
|
|
10
|
+
* of a {@apilink PageElement} to return its text content.
|
|
10
11
|
*
|
|
11
12
|
* {@apilink MetaQuestion|Meta questions} are typically used when filtering a {@apilink List}.
|
|
12
13
|
*
|
|
@@ -15,15 +16,15 @@ import type { Question } from '../Question';
|
|
|
15
16
|
*
|
|
16
17
|
* @group Questions
|
|
17
18
|
*/
|
|
18
|
-
export interface MetaQuestion<
|
|
19
|
+
export interface MetaQuestion<Supported_Context_Type, Answer_Type> {
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
|
-
*
|
|
22
|
+
* Answers the given `MetaQuestion` in the context of another {@apilink Answerable}.
|
|
22
23
|
*
|
|
23
24
|
* #### Learn more
|
|
24
25
|
* - {@apilink List}
|
|
25
26
|
*/
|
|
26
|
-
of(
|
|
27
|
+
of(context: Answerable<Supported_Context_Type>): Question<Promise<Answer_Type>> | Question<Answer_Type>;
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Human-readable description of this {@apilink MetaQuestion},
|
|
@@ -20,7 +20,7 @@ import type { StageCrewMember } from '../../StageCrewMember';
|
|
|
20
20
|
import { Hash } from './Hash';
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Stores any {@apilink Artifact|artifacts} emitted via {@apilink ArtifactGenerated} events on the {@apilink FileSystem}
|
|
23
|
+
* Stores any {@apilink Artifact|artifacts} emitted via {@apilink ArtifactGenerated} events on the {@apilink FileSystem}.
|
|
24
24
|
*
|
|
25
25
|
* ## Registering `ArtifactArchiver` programmatically
|
|
26
26
|
*
|
|
@@ -35,49 +35,69 @@ import { Hash } from './Hash';
|
|
|
35
35
|
* })
|
|
36
36
|
* ```
|
|
37
37
|
*
|
|
38
|
-
* ##
|
|
38
|
+
* ## Using `ArtifactArchiver` with Playwright Test
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* // playwright.config.ts
|
|
42
|
+
* import type { PlaywrightTestConfig } from '@serenity-js/playwright-test'
|
|
43
|
+
*
|
|
44
|
+
* const config: PlaywrightTestConfig = {
|
|
45
|
+
* testDir: './spec',
|
|
46
|
+
*
|
|
47
|
+
* reporter: [
|
|
48
|
+
* [ '@serenity-js/playwright-test', {
|
|
49
|
+
* crew: [
|
|
50
|
+
* '@serenity-js/serenity-bdd',
|
|
51
|
+
* [ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
|
|
52
|
+
* [ '@serenity-js/core:StreamReporter', { outputFile: 'target/events.ndjson' }]
|
|
53
|
+
* ]
|
|
54
|
+
* // other Serenity/JS config
|
|
55
|
+
* }]
|
|
56
|
+
* ],
|
|
57
|
+
* // other Playwright Test config
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* ## Using `ArtifactArchiver` with Protractor
|
|
39
62
|
*
|
|
40
63
|
* ```js
|
|
41
64
|
* // protractor.conf.js
|
|
42
|
-
* const { ArtifactArchiver } = require('@serenity-js/core')
|
|
43
|
-
*
|
|
44
65
|
* exports.config = {
|
|
45
66
|
* framework: 'custom',
|
|
46
67
|
* frameworkPath: require.resolve('@serenity-js/protractor/adapter'),
|
|
47
68
|
*
|
|
48
69
|
* serenity: {
|
|
49
70
|
* crew: [
|
|
50
|
-
*
|
|
71
|
+
* '@serenity-js/serenity-bdd',
|
|
72
|
+
* [ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
|
|
51
73
|
* ],
|
|
52
74
|
* // other Serenity/JS config
|
|
53
75
|
* },
|
|
54
76
|
* // other Protractor config
|
|
55
|
-
* }
|
|
77
|
+
* }
|
|
56
78
|
* ```
|
|
57
79
|
*
|
|
58
|
-
* ##
|
|
80
|
+
* ## Using `ArtifactArchiver` with WebdriverIO
|
|
59
81
|
*
|
|
60
82
|
* ```ts
|
|
61
|
-
* // wdio.conf.
|
|
62
|
-
* import { ArtifactArchiver } from '@serenity-js/core'
|
|
83
|
+
* // wdio.conf.ts
|
|
63
84
|
* import { WebdriverIOConfig } from '@serenity-js/webdriverio'
|
|
64
85
|
*
|
|
65
86
|
* export const config: WebdriverIOConfig = {
|
|
66
87
|
*
|
|
67
|
-
*
|
|
88
|
+
* framework: '@serenity-js/webdriverio',
|
|
68
89
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
90
|
+
* serenity: {
|
|
91
|
+
* crew: [
|
|
92
|
+
* '@serenity-js/serenity-bdd',
|
|
93
|
+
* [ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
|
|
94
|
+
* ]
|
|
95
|
+
* // other Serenity/JS config
|
|
96
|
+
* },
|
|
97
|
+
* // other WebdriverIO config
|
|
76
98
|
* }
|
|
77
99
|
* ```
|
|
78
100
|
*
|
|
79
|
-
* [ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
|
|
80
|
-
*
|
|
81
101
|
* @group Stage
|
|
82
102
|
*/
|
|
83
103
|
export class ArtifactArchiver implements StageCrewMember {
|
|
@@ -209,6 +229,8 @@ export class ArtifactArchiver implements StageCrewMember {
|
|
|
209
229
|
event.name,
|
|
210
230
|
event.artifact.constructor as ArtifactType,
|
|
211
231
|
relativePathToArtifact,
|
|
232
|
+
event.timestamp,
|
|
233
|
+
this.stage.currentTime(),
|
|
212
234
|
));
|
|
213
235
|
} else if (event instanceof ArtifactGenerated) {
|
|
214
236
|
this.stage.announce(new ArtifactArchived(
|
|
@@ -216,6 +238,8 @@ export class ArtifactArchiver implements StageCrewMember {
|
|
|
216
238
|
event.name,
|
|
217
239
|
event.artifact.constructor as ArtifactType,
|
|
218
240
|
relativePathToArtifact,
|
|
241
|
+
event.timestamp,
|
|
242
|
+
this.stage.currentTime(),
|
|
219
243
|
));
|
|
220
244
|
}
|
|
221
245
|
};
|