@serenity-js/playwright-test 3.31.17 → 3.32.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 +37 -0
- package/README.md +6 -5
- package/lib/api/PlaywrightTestConfig.d.ts +7 -6
- package/lib/api/PlaywrightTestConfig.d.ts.map +1 -1
- package/lib/api/WorkerEventStreamReader.d.ts +13 -0
- package/lib/api/WorkerEventStreamReader.d.ts.map +1 -0
- package/lib/api/WorkerEventStreamReader.js +58 -0
- package/lib/api/WorkerEventStreamReader.js.map +1 -0
- package/lib/api/WorkerEventStreamWriter.d.ts +24 -0
- package/lib/api/WorkerEventStreamWriter.d.ts.map +1 -0
- package/lib/api/WorkerEventStreamWriter.js +86 -0
- package/lib/api/WorkerEventStreamWriter.js.map +1 -0
- package/lib/api/index.d.ts +1 -2
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.js +1 -2
- package/lib/api/index.js.map +1 -1
- package/lib/api/serenity-fixtures.d.ts +377 -0
- package/lib/api/serenity-fixtures.d.ts.map +1 -0
- package/lib/api/{SerenityOptions.js → serenity-fixtures.js} +1 -1
- package/lib/api/serenity-fixtures.js.map +1 -0
- package/lib/api/test-api.d.ts +27 -15
- package/lib/api/test-api.d.ts.map +1 -1
- package/lib/api/test-api.js +126 -104
- package/lib/api/test-api.js.map +1 -1
- package/lib/events/EventFactory.d.ts +16 -0
- package/lib/events/EventFactory.d.ts.map +1 -0
- package/lib/events/EventFactory.js +94 -0
- package/lib/events/EventFactory.js.map +1 -0
- package/lib/events/PlaywrightSceneId.d.ts +7 -0
- package/lib/events/PlaywrightSceneId.d.ts.map +1 -0
- package/lib/events/PlaywrightSceneId.js +19 -0
- package/lib/events/PlaywrightSceneId.js.map +1 -0
- package/lib/events/index.d.ts +3 -0
- package/lib/events/index.d.ts.map +1 -0
- package/lib/events/index.js +19 -0
- package/lib/events/index.js.map +1 -0
- package/lib/reporter/PlaywrightErrorParser.d.ts +7 -0
- package/lib/reporter/PlaywrightErrorParser.d.ts.map +1 -0
- package/lib/reporter/PlaywrightErrorParser.js +28 -0
- package/lib/reporter/PlaywrightErrorParser.js.map +1 -0
- package/lib/reporter/PlaywrightEventBuffer.d.ts +25 -0
- package/lib/reporter/PlaywrightEventBuffer.d.ts.map +1 -0
- package/lib/reporter/PlaywrightEventBuffer.js +147 -0
- package/lib/reporter/PlaywrightEventBuffer.js.map +1 -0
- package/lib/reporter/PlaywrightTestSceneIdFactory.d.ts +8 -0
- package/lib/reporter/PlaywrightTestSceneIdFactory.d.ts.map +1 -0
- package/lib/reporter/PlaywrightTestSceneIdFactory.js +15 -0
- package/lib/reporter/PlaywrightTestSceneIdFactory.js.map +1 -0
- package/lib/reporter/SerenityReporterForPlaywrightTest.d.ts +15 -22
- package/lib/reporter/SerenityReporterForPlaywrightTest.d.ts.map +1 -1
- package/lib/reporter/SerenityReporterForPlaywrightTest.js +62 -163
- package/lib/reporter/SerenityReporterForPlaywrightTest.js.map +1 -1
- package/lib/reporter/index.d.ts +0 -2
- package/lib/reporter/index.d.ts.map +1 -1
- package/lib/reporter/index.js +0 -2
- package/lib/reporter/index.js.map +1 -1
- package/package.json +9 -9
- package/src/api/PlaywrightTestConfig.ts +7 -6
- package/src/api/WorkerEventStreamReader.ts +27 -0
- package/src/api/WorkerEventStreamWriter.ts +117 -0
- package/src/api/index.ts +1 -2
- package/src/api/serenity-fixtures.ts +392 -0
- package/src/api/test-api.ts +187 -99
- package/src/events/EventFactory.ts +204 -0
- package/src/events/PlaywrightSceneId.ts +20 -0
- package/src/events/index.ts +2 -0
- package/src/reporter/PlaywrightErrorParser.ts +35 -0
- package/src/reporter/PlaywrightEventBuffer.ts +251 -0
- package/src/reporter/PlaywrightTestSceneIdFactory.ts +14 -0
- package/src/reporter/SerenityReporterForPlaywrightTest.ts +89 -250
- package/src/reporter/index.ts +0 -2
- package/lib/api/SerenityFixtures.d.ts +0 -130
- package/lib/api/SerenityFixtures.d.ts.map +0 -1
- package/lib/api/SerenityFixtures.js +0 -3
- package/lib/api/SerenityFixtures.js.map +0 -1
- package/lib/api/SerenityOptions.d.ts +0 -271
- package/lib/api/SerenityOptions.d.ts.map +0 -1
- package/lib/api/SerenityOptions.js.map +0 -1
- package/lib/reporter/DomainEventBuffer.d.ts +0 -11
- package/lib/reporter/DomainEventBuffer.d.ts.map +0 -1
- package/lib/reporter/DomainEventBuffer.js +0 -24
- package/lib/reporter/DomainEventBuffer.js.map +0 -1
- package/lib/reporter/PlaywrightAttachments.d.ts +0 -2
- package/lib/reporter/PlaywrightAttachments.d.ts.map +0 -1
- package/lib/reporter/PlaywrightAttachments.js +0 -5
- package/lib/reporter/PlaywrightAttachments.js.map +0 -1
- package/src/api/SerenityFixtures.ts +0 -132
- package/src/api/SerenityOptions.ts +0 -277
- package/src/reporter/DomainEventBuffer.ts +0 -28
- package/src/reporter/PlaywrightAttachments.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serenity-js/playwright-test",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.32.1",
|
|
4
4
|
"description": "Serenity/JS test runner adapter for Playwright Test, combining Playwright's developer experience with the advanced reporting and automation capabilities of Serenity/JS",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Jan Molak",
|
|
@@ -59,26 +59,26 @@
|
|
|
59
59
|
"node": "^18.12 || ^20 || ^22"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@serenity-js/core": "3.
|
|
63
|
-
"@serenity-js/playwright": "3.
|
|
64
|
-
"@serenity-js/rest": "3.
|
|
65
|
-
"@serenity-js/web": "3.
|
|
62
|
+
"@serenity-js/core": "3.32.1",
|
|
63
|
+
"@serenity-js/playwright": "3.32.1",
|
|
64
|
+
"@serenity-js/rest": "3.32.1",
|
|
65
|
+
"@serenity-js/web": "3.32.1",
|
|
66
66
|
"deepmerge": "4.3.1",
|
|
67
67
|
"tiny-types": "1.23.0"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
|
-
"@playwright/test": "~1.53.
|
|
70
|
+
"@playwright/test": "~1.53.1"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@integration/testing-tools": "3.0.0",
|
|
74
|
-
"@playwright/test": "1.53.
|
|
74
|
+
"@playwright/test": "1.53.1",
|
|
75
75
|
"@types/chai": "4.3.20",
|
|
76
76
|
"@types/mocha": "10.0.10",
|
|
77
77
|
"c8": "10.1.3",
|
|
78
|
-
"mocha": "11.
|
|
78
|
+
"mocha": "11.7.0",
|
|
79
79
|
"mocha-multi": "1.1.7",
|
|
80
80
|
"ts-node": "10.9.2",
|
|
81
81
|
"typescript": "5.8.3"
|
|
82
82
|
},
|
|
83
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "f1f288c9a75d28270dfe4ba16ca4f367a4dbba22"
|
|
84
84
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { PlaywrightTestConfig as BasePlaywrightTestConfig } from '@playwright/test';
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { SerenityFixtures, SerenityWorkerFixtures } from './serenity-fixtures';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Convenience alias for [PlaywrightTestConfig](https://playwright.dev/docs/test-configuration) object
|
|
7
|
-
* that includes [`
|
|
7
|
+
* that includes [`SerenityFixtures`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/) and
|
|
8
|
+
* [`SerenityWorkerFixtures`](https://serenity-js.org/api/playwright-test/interface/SerenityWorkerFixtures/) and allows for any other custom options when needed.
|
|
8
9
|
*
|
|
9
10
|
* #### Example
|
|
10
11
|
* Configuring Playwright Test using the standard `PlaywrightTestConfig` from `@playwright/test`:
|
|
@@ -12,9 +13,9 @@ import type { SerenityOptions } from './SerenityOptions';
|
|
|
12
13
|
* ```typescript
|
|
13
14
|
* // playwright.config.ts
|
|
14
15
|
* import type { PlaywrightTestConfig } from '@playwright/test'
|
|
15
|
-
* import type {
|
|
16
|
+
* import type { SerenityFixtures, SerenityWorkerFixtures } from '@serenity-js/playwright-test'
|
|
16
17
|
*
|
|
17
|
-
* const config: PlaywrightTestConfig<
|
|
18
|
+
* const config: PlaywrightTestConfig<SerenityFixtures & MyCustomOptions, SerenityWorkerFixtures> = {
|
|
18
19
|
* // ...
|
|
19
20
|
* }
|
|
20
21
|
*
|
|
@@ -34,9 +35,9 @@ import type { SerenityOptions } from './SerenityOptions';
|
|
|
34
35
|
* ```
|
|
35
36
|
*
|
|
36
37
|
* #### Learn more
|
|
37
|
-
* - [`SerenityOptions`](https://serenity-js.org/api/playwright-test/interface/SerenityOptions/)
|
|
38
38
|
* - [`SerenityFixtures`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/)
|
|
39
|
+
* - [`SerenityWorkerFixtures`](https://serenity-js.org/api/playwright-test/interface/SerenityWorkerFixtures/)
|
|
39
40
|
* - [`SerenityReporterForPlaywrightTestConfig`](https://serenity-js.org/api/playwright-test/interface/SerenityReporterForPlaywrightTestConfig/)
|
|
40
41
|
* - [Playwright Test configuration](https://playwright.dev/docs/test-configuration)
|
|
41
42
|
*/
|
|
42
|
-
export type PlaywrightTestConfig<TestArgs = object, WorkerArgs = object> = BasePlaywrightTestConfig<
|
|
43
|
+
export type PlaywrightTestConfig<TestArgs = object, WorkerArgs = object> = BasePlaywrightTestConfig<SerenityFixtures & TestArgs, SerenityWorkerFixtures & WorkerArgs>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
|
|
3
|
+
import type { DomainEvent } from '@serenity-js/core/lib/events';
|
|
4
|
+
import * as events from '@serenity-js/core/lib/events';
|
|
5
|
+
import type { JSONObject } from 'tiny-types';
|
|
6
|
+
|
|
7
|
+
export class WorkerEventStreamReader {
|
|
8
|
+
|
|
9
|
+
hasStream(pathToEventStreamFile: string): boolean {
|
|
10
|
+
return fs.existsSync(pathToEventStreamFile);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
read<T extends DomainEvent>(
|
|
14
|
+
pathToEventStreamFile: string,
|
|
15
|
+
mapper: (input: { type: string, value: JSONObject }) => { type: string, value: JSONObject } = input => input
|
|
16
|
+
): T[] {
|
|
17
|
+
const content = fs.readFileSync(pathToEventStreamFile, 'utf8');
|
|
18
|
+
|
|
19
|
+
return content
|
|
20
|
+
.split('\n')
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
.map(line => {
|
|
23
|
+
const { type, value } = mapper(JSON.parse(line));
|
|
24
|
+
return events[type].fromJSON(value);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { type WorkerInfo } from '@playwright/test';
|
|
5
|
+
import type { TestCase } from '@playwright/test/reporter';
|
|
6
|
+
import type { Stage, StageCrewMember } from '@serenity-js/core';
|
|
7
|
+
import { LogicError } from '@serenity-js/core';
|
|
8
|
+
import type { DomainEvent } from '@serenity-js/core/lib/events';
|
|
9
|
+
import { CorrelationId } from '@serenity-js/core/lib/model';
|
|
10
|
+
import type { JSONObject } from 'tiny-types';
|
|
11
|
+
|
|
12
|
+
export class WorkerEventStreamWriter implements StageCrewMember {
|
|
13
|
+
|
|
14
|
+
private readonly beforeAllId: CorrelationId; // = new CorrelationId('unknown');
|
|
15
|
+
private activeSceneId: CorrelationId; // = WorkerEventStreamWriter.beforeTest;
|
|
16
|
+
|
|
17
|
+
private events: Map<TestCase['id'], DomainEvent[]> = new Map();
|
|
18
|
+
|
|
19
|
+
static workerStreamIdFor(workerIndex: number): CorrelationId {
|
|
20
|
+
return new CorrelationId(`worker-${ workerIndex }`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
private readonly outputDirectory: string,
|
|
25
|
+
private readonly workerInfo: WorkerInfo,
|
|
26
|
+
private stage?: Stage,
|
|
27
|
+
) {
|
|
28
|
+
|
|
29
|
+
this.beforeAllId = WorkerEventStreamWriter.workerStreamIdFor(this.workerInfo.workerIndex);
|
|
30
|
+
this.activeSceneId = this.beforeAllId;
|
|
31
|
+
this.events.set(this.beforeAllId.value, []);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
assignedTo(stage: Stage): StageCrewMember {
|
|
35
|
+
this.stage = stage;
|
|
36
|
+
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
notifyOf(event: DomainEvent): void {
|
|
41
|
+
|
|
42
|
+
if (this.isSceneEvent(event) && ! this.activeSceneExistsFor(event)) {
|
|
43
|
+
this.activateScene(event);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.events.get(this.activeSceneId.value).push(event);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private isSceneEvent(event: DomainEvent & { sceneId?: CorrelationId }): event is DomainEvent & { sceneId: CorrelationId } {
|
|
50
|
+
return event['sceneId'] instanceof CorrelationId;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private activeSceneExistsFor(event: DomainEvent & { sceneId: CorrelationId }): boolean {
|
|
54
|
+
return this.activeSceneId.equals(event.sceneId);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private activateScene(event: DomainEvent & { sceneId: CorrelationId }): void {
|
|
58
|
+
this.activeSceneId = event.sceneId;
|
|
59
|
+
|
|
60
|
+
const testId = event.sceneId.value;
|
|
61
|
+
|
|
62
|
+
if (! this.events.has(testId)) {
|
|
63
|
+
this.events.set(testId, []);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.events.get(testId).push(
|
|
67
|
+
...this.events.get(this.beforeAllId.value),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
this.events.set(this.beforeAllId.value, []);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async persistAll(workerBeforeAllSceneId: CorrelationId): Promise<void> {
|
|
74
|
+
const testIds = [...this.events.keys()];
|
|
75
|
+
|
|
76
|
+
await Promise.all(testIds.map(testId => this.persist(testId, workerBeforeAllSceneId)));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async persist(testId: TestCase['id'], workerBeforeAllSceneId?: CorrelationId): Promise<void> {
|
|
80
|
+
const testOutputDirectory = path.join(this.outputDirectory, testId);
|
|
81
|
+
|
|
82
|
+
const filePath = path.join(testOutputDirectory, 'events.ndjson');
|
|
83
|
+
|
|
84
|
+
const events = this.flush(testId);
|
|
85
|
+
|
|
86
|
+
if (events.length === 0) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await fs.promises.mkdir(testOutputDirectory, { recursive: true })
|
|
91
|
+
|
|
92
|
+
for (const event of events) {
|
|
93
|
+
const shouldReattachToScene = event['sceneId'] && event['sceneId'].equals(workerBeforeAllSceneId);
|
|
94
|
+
|
|
95
|
+
const type = event.constructor.name;
|
|
96
|
+
|
|
97
|
+
const value = shouldReattachToScene
|
|
98
|
+
? ({ ...(event.toJSON() as JSONObject), sceneId: testId })
|
|
99
|
+
: event.toJSON();
|
|
100
|
+
|
|
101
|
+
const serialisedEvent = JSON.stringify({ type, value }, undefined, 0);
|
|
102
|
+
|
|
103
|
+
await fs.promises.appendFile(filePath, serialisedEvent + '\n');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private flush(testId: TestCase['id']): DomainEvent[] {
|
|
108
|
+
if (! this.events.has(testId)) {
|
|
109
|
+
throw new LogicError(`No events recorded for test with id ${ testId }`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const events = this.events.get(testId);
|
|
113
|
+
this.events.set(testId, []);
|
|
114
|
+
|
|
115
|
+
return events;
|
|
116
|
+
}
|
|
117
|
+
}
|
package/src/api/index.ts
CHANGED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Cast,
|
|
3
|
+
ClassDescription,
|
|
4
|
+
Duration,
|
|
5
|
+
Serenity,
|
|
6
|
+
StageCrewMember,
|
|
7
|
+
StageCrewMemberBuilder
|
|
8
|
+
} from '@serenity-js/core';
|
|
9
|
+
import type { Actor } from '@serenity-js/core';
|
|
10
|
+
import type { ExtraBrowserContextOptions } from '@serenity-js/playwright';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Serenity/JS-specific [Playwright Test fixtures](https://playwright.dev/docs/test-fixtures)
|
|
14
|
+
* injected into your [test scenarios](https://serenity-js.org/api/playwright-test/function/it/).
|
|
15
|
+
*
|
|
16
|
+
* ### Configuring Serenity/JS using a test file
|
|
17
|
+
*
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { Ensure, equals } from '@serenity-js/assertions'
|
|
20
|
+
* import { describe, it, test } from '@serenity-js/playwright-test'
|
|
21
|
+
* import { Photographer, TakePhotosOfFailures } from '@serenity-js/web'
|
|
22
|
+
*
|
|
23
|
+
* describe(`Recording items`, () => {
|
|
24
|
+
*
|
|
25
|
+
* test.use({
|
|
26
|
+
* defaultActorName: 'Serena',
|
|
27
|
+
* crew: [
|
|
28
|
+
* Photographer.whoWill(TakePhotosOfFailures),
|
|
29
|
+
* ],
|
|
30
|
+
*
|
|
31
|
+
* // Register a custom cast of Serenity/JS actors to replace the default one
|
|
32
|
+
* actors: async ({ browser, contextOptions, extraContextOptions, baseURL }, use) => {
|
|
33
|
+
* const cast = Cast.where(actor =>
|
|
34
|
+
* actor.whoCan(
|
|
35
|
+
* BrowseTheWebWithPlaywright.using(browser, contextOptions, extraContextOptions),
|
|
36
|
+
* TakeNotes.usingAnEmptyNotepad(),
|
|
37
|
+
* CallAnApi.at(baseURL),
|
|
38
|
+
* )
|
|
39
|
+
* )
|
|
40
|
+
*
|
|
41
|
+
* await use(cast)
|
|
42
|
+
* },
|
|
43
|
+
* })
|
|
44
|
+
*
|
|
45
|
+
* describe(`Todo List App`, () => {
|
|
46
|
+
*
|
|
47
|
+
* it(`should allow me to add a todo item`, async ({ actor }) => {
|
|
48
|
+
* await actor.attemptsTo(
|
|
49
|
+
* startWithAnEmptyList(),
|
|
50
|
+
*
|
|
51
|
+
* recordItem('Buy some milk'),
|
|
52
|
+
*
|
|
53
|
+
* Ensure.that(itemNames(), equals([
|
|
54
|
+
* 'Buy some milk',
|
|
55
|
+
* ])),
|
|
56
|
+
* )
|
|
57
|
+
* })
|
|
58
|
+
* })
|
|
59
|
+
* })
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* ### Configuring Serenity/JS using Playwright configuration file
|
|
63
|
+
*
|
|
64
|
+
* ```typescript
|
|
65
|
+
* // playwright.config.ts
|
|
66
|
+
* import { defineConfig } from '@playwright/test'
|
|
67
|
+
* import type { Cast, TakeNotes } from '@serenity-js/core'
|
|
68
|
+
* import type { BrowseTheWebWithPlaywright } from '@serenity-js/playwright'
|
|
69
|
+
* import type { SerenityFixtures, SerenityWorkerFixtures } from '@serenity-js/playwright-test'
|
|
70
|
+
* import type { CallAnApi } from '@serenity-js/rest'
|
|
71
|
+
*
|
|
72
|
+
* export default defineConfig<SerenityFixtures & MyCustomOptions, SerenityWorkerFixtures> = {
|
|
73
|
+
*
|
|
74
|
+
* // Register Serenity/JS reporter for Playwright Test
|
|
75
|
+
* // to enable integration with Serenity/JS stage crew members
|
|
76
|
+
* // and have them instantiated in the Playwright reporter process
|
|
77
|
+
* reporter: [
|
|
78
|
+
* [ '@serenity-js/playwright-test', {
|
|
79
|
+
* // Stage crew members instantiated in the test reporter process
|
|
80
|
+
* crew: [
|
|
81
|
+
* '@serenity-js/serenity-bdd',
|
|
82
|
+
* '@serenity-js/console-reporter',
|
|
83
|
+
* [ '@serenity-js/core:ArtifactArchiver', { outputDirectory: 'target/site/serenity' } ],
|
|
84
|
+
* ]
|
|
85
|
+
* }]
|
|
86
|
+
* ],
|
|
87
|
+
*
|
|
88
|
+
* use: {
|
|
89
|
+
*
|
|
90
|
+
* // Register Serenity/JS stage crew members
|
|
91
|
+
* // and have them instantiated in Playwright Test worker processes
|
|
92
|
+
* crew: [
|
|
93
|
+
* [ '@serenity-js/web:Photographer', { strategy: 'TakePhotosOfFailures' } ]
|
|
94
|
+
* ],
|
|
95
|
+
*
|
|
96
|
+
* // Name to be given to an actor injected via `actor` fixture
|
|
97
|
+
* defaultActorName: 'Alice',
|
|
98
|
+
*
|
|
99
|
+
* // Any other Playwright options
|
|
100
|
+
* baseURL: 'https://todo-app.serenity-js.org/',
|
|
101
|
+
* video: 'on-first-retry',
|
|
102
|
+
* trace: 'on-first-retry',
|
|
103
|
+
* },
|
|
104
|
+
* })
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* ## Learn more
|
|
108
|
+
* - [Using Serenity/JS with Playwright Test](https://serenity-js.org/handbook/test-runners/playwright-test/)
|
|
109
|
+
* - [`PlaywrightTestConfig`](https://serenity-js.org/api/playwright-test/#PlaywrightTestConfig)
|
|
110
|
+
* - [`Cast`](https://serenity-js.org/api/core/class/Cast/)
|
|
111
|
+
* - [`SerenityFixtures`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/)
|
|
112
|
+
*/
|
|
113
|
+
export interface SerenityFixtures {
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Configures the name given to the default Serenity/JS [`actor`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/#actor)
|
|
117
|
+
* injected into a [test scenario](https://serenity-js.org/api/playwright-test/function/it/).
|
|
118
|
+
*
|
|
119
|
+
* #### Learn more
|
|
120
|
+
* - Declaring a Serenity/JS [test scenario](https://serenity-js.org/api/playwright-test/function/it/)
|
|
121
|
+
* - [`SerenityFixtures`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/)
|
|
122
|
+
*/
|
|
123
|
+
defaultActorName: string;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Configures the [stage crew members](https://serenity-js.org/api/core/class/SerenityConfig/#crew)
|
|
127
|
+
* to be instantiated in Playwright Test worker processes.
|
|
128
|
+
*
|
|
129
|
+
* :::info Did you know?
|
|
130
|
+
* By default, Serenity/JS registers a [`Photographer`](https://serenity-js.org/api/web/class/Photographer/).whoWill([`TakePhotosOfFailures`](https://serenity-js.org/api/web/class/TakePhotosOfFailures/)),
|
|
131
|
+
* so that any test failures are automatically accompanied by a screenshot.
|
|
132
|
+
*
|
|
133
|
+
* If you prefer a different behaviour, you can configure the `crew` with an empty array to disable taking screenshots altogether (`crew: []`),
|
|
134
|
+
* or with a [`Photographer`](https://serenity-js.org/api/web/class/Photographer/) who uses a different [`PhotoTakingStrategy`](https://serenity-js.org/api/web/class/PhotoTakingStrategy/), like to [`TakePhotosOfInteractions`](https://serenity-js.org/api/web/class/TakePhotosOfInteractions/).
|
|
135
|
+
* :::
|
|
136
|
+
*
|
|
137
|
+
* #### Example
|
|
138
|
+
*
|
|
139
|
+
* ```typescript
|
|
140
|
+
* // playwright.config.ts
|
|
141
|
+
* import type { SerenityFixtures, SerenityWorkerFixtures } from '@serenity-js/playwright-test'
|
|
142
|
+
* import { defineConfig } from '@playwright/test'
|
|
143
|
+
*
|
|
144
|
+
* export default defineConfig<SerenityFixtures, SerenityWorkerFixtures>({
|
|
145
|
+
* use: {
|
|
146
|
+
* crew: [
|
|
147
|
+
* [ '@serenity-js/web:Photographer', { strategy: 'TakePhotosOfFailures' } ]
|
|
148
|
+
* ],
|
|
149
|
+
* },
|
|
150
|
+
* });
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* #### Learn more
|
|
154
|
+
* - [`SerenityConfig.crew`](https://serenity-js.org/api/core/class/SerenityConfig/#crew)
|
|
155
|
+
*/
|
|
156
|
+
crew: Array<ClassDescription | StageCrewMember | StageCrewMemberBuilder>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Sets the [`cueTimeout`](https://serenity-js.org/api/core/class/SerenityConfig/#cueTimeout) to a given [duration](https://serenity-js.org/api/core/class/Duration/) or a numeric value in milliseconds.
|
|
160
|
+
* Defaults to **5 seconds**.
|
|
161
|
+
*
|
|
162
|
+
* #### Learn more
|
|
163
|
+
* - [`SerenityConfig.cueTimeout`](https://serenity-js.org/api/core/class/SerenityConfig/#cueTimeout)
|
|
164
|
+
* - [`Discardable`](https://serenity-js.org/api/core/interface/Discardable/)
|
|
165
|
+
* - [`Ability`](https://serenity-js.org/api/core/class/Ability/)
|
|
166
|
+
*/
|
|
167
|
+
cueTimeout: number | Duration;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The maximum default amount of time allowed for interactions such as [`Wait.until`](https://serenity-js.org/api/core/class/Wait/#until)
|
|
171
|
+
* to complete.
|
|
172
|
+
*
|
|
173
|
+
* Defaults to **5 seconds**, can be overridden per interaction.
|
|
174
|
+
*
|
|
175
|
+
* #### Learn more
|
|
176
|
+
* - [`Wait.until`](https://serenity-js.org/api/core/class/Wait/#until)
|
|
177
|
+
*/
|
|
178
|
+
interactionTimeout: number | Duration;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Convenience properties to be used with the [ability](https://serenity-js.org/api/core/class/Ability/)
|
|
182
|
+
* to [`BrowseTheWebWithPlaywright`](https://serenity-js.org/api/playwright/class/BrowseTheWebWithPlaywright/),
|
|
183
|
+
* in addition to Playwright [BrowserContextOptions](https://playwright.dev/docs/api/class-testoptions#test-options-context-options):
|
|
184
|
+
*
|
|
185
|
+
* - [`defaultNavigationTimeout`](https://serenity-js.org/api/playwright/interface/ExtraBrowserContextOptions/#defaultNavigationTimeout)
|
|
186
|
+
* - [`defaultNavigationWaitUntil`](https://serenity-js.org/api/playwright/interface/ExtraBrowserContextOptions/#defaultNavigationWaitUntil)
|
|
187
|
+
* - [`defaultTimeout`](https://serenity-js.org/api/playwright/interface/ExtraBrowserContextOptions/#defaultTimeout)
|
|
188
|
+
*
|
|
189
|
+
* #### Using `extraContextOptions` with the default cast of Serenity/JS actors
|
|
190
|
+
*
|
|
191
|
+
* ```typescript
|
|
192
|
+
* // playwright.config.ts
|
|
193
|
+
* import type { SerenityFixtures, SerenityWorkerFixtures } from '@serenity-js/playwright-test'
|
|
194
|
+
* import { defineConfig } from '@playwright/test'
|
|
195
|
+
*
|
|
196
|
+
* export default defineConfig<SerenityFixtures, SerenityWorkerFixtures>({
|
|
197
|
+
* use: {
|
|
198
|
+
* extraContextOptions: {
|
|
199
|
+
* defaultNavigationTimeout: 30_000,
|
|
200
|
+
* }
|
|
201
|
+
*
|
|
202
|
+
* // Since `actors` property is not defined,
|
|
203
|
+
* // `extraContextOptions` will be passed to the default cast of Serenity/JS actors
|
|
204
|
+
* // and injected into the ability to `BrowseTheWebWithPlaywright`
|
|
205
|
+
* // that each actor receives.
|
|
206
|
+
* },
|
|
207
|
+
* })
|
|
208
|
+
* ```
|
|
209
|
+
*
|
|
210
|
+
* #### Using `extraContextOptions` with a custom cast of Serenity/JS actors
|
|
211
|
+
*
|
|
212
|
+
* ```typescript
|
|
213
|
+
* // playwright.config.ts
|
|
214
|
+
* import type { SerenityFixtures, SerenityWorkerFixtures } from '@serenity-js/playwright-test'
|
|
215
|
+
* import { defineConfig } from '@playwright/test'
|
|
216
|
+
*
|
|
217
|
+
* export default defineConfig<SerenityFixtures, SerenityWorkerFixtures>({
|
|
218
|
+
* use: {
|
|
219
|
+
* extraContextOptions: {
|
|
220
|
+
* defaultNavigationTimeout: 30_000,
|
|
221
|
+
* }
|
|
222
|
+
*
|
|
223
|
+
* // Custom cast of actors receives the default `contextOptions` and
|
|
224
|
+
* // the `extraContextOptions` with the additional Serenity/JS properties.
|
|
225
|
+
* actors: async ({ browser, contextOptions, extraContextOptions }, use) => {
|
|
226
|
+
* const cast = Cast.where(actor => actor.whoCan(
|
|
227
|
+
* BrowseTheWebWithPlaywright.using(browser, contextOptions, extraContextOptions),
|
|
228
|
+
* TakeNotes.usingAnEmptyNotepad(),
|
|
229
|
+
* ))
|
|
230
|
+
*
|
|
231
|
+
* await use(cast)
|
|
232
|
+
* },
|
|
233
|
+
* },
|
|
234
|
+
* })
|
|
235
|
+
* ```
|
|
236
|
+
*
|
|
237
|
+
* #### Learn more
|
|
238
|
+
* - [`SerenityFixtures`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/)
|
|
239
|
+
* - [Playwright Browser Context options](https://playwright.dev/docs/api/class-testoptions#test-options-context-options)
|
|
240
|
+
* - [Playwright Test fixtures](https://playwright.dev/docs/test-fixtures)
|
|
241
|
+
*/
|
|
242
|
+
extraContextOptions: Partial<ExtraBrowserContextOptions>;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* A cast of Serenity/JS actors to be used instead of the default cast
|
|
246
|
+
* when instantiating [`actor`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/#actor)
|
|
247
|
+
* and invoking [`actorCalled`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/#actorCalled).
|
|
248
|
+
*
|
|
249
|
+
* :::info Did you know?
|
|
250
|
+
* When you use `@serenity-js/playwright-test` [test APIs](https://serenity-js.org/api/playwright-test/function/it/), Serenity/JS already provides a default cast of actors for you.
|
|
251
|
+
* Each one of the default actors receives [abilities](https://serenity-js.org/api/core/class/Ability/) to [`BrowseTheWebWithPlaywright`](https://serenity-js.org/api/playwright/class/BrowseTheWebWithPlaywright/) and [`TakeNotes.usingAnEmptyNotepad`](https://serenity-js.org/api/core/class/TakeNotes/#usingAnEmptyNotepad).
|
|
252
|
+
*
|
|
253
|
+
* The default abilities should be sufficient in most web testing scenarios. However, you might want to override this default configuration
|
|
254
|
+
* when you need your actors to [interact with REST APIs](https://serenity-js.org/api/rest/class/CallAnApi/),
|
|
255
|
+
* [manage local servers](https://serenity-js.org/api/local-server/class/ManageALocalServer/),
|
|
256
|
+
* start with a notepad that has some [initial state](https://serenity-js.org/api/core/class/TakeNotes/#using),
|
|
257
|
+
* or receive [custom abilities](https://serenity-js.org/api/core/class/Ability/).
|
|
258
|
+
* :::
|
|
259
|
+
*
|
|
260
|
+
*
|
|
261
|
+
* #### Overriding the default cast of Serenity/JS actors
|
|
262
|
+
*
|
|
263
|
+
* ```typescript
|
|
264
|
+
* import { Cast, TakeNotes } from '@serenity-js/core'
|
|
265
|
+
* import { Ensure, equals } from '@serenity-js/assertions'
|
|
266
|
+
* import { BrowseTheWebWithPlaywright } from '@serenity-js/playwright'
|
|
267
|
+
* import { describe, it, test } from '@serenity-js/playwright-test'
|
|
268
|
+
*
|
|
269
|
+
* describe(`Recording items`, () => {
|
|
270
|
+
*
|
|
271
|
+
* test.use({
|
|
272
|
+
* extraContextOptions: {
|
|
273
|
+
* defaultNavigationTimeout: 30_000,
|
|
274
|
+
* },
|
|
275
|
+
*
|
|
276
|
+
* defaultActorName: 'Serena',
|
|
277
|
+
* actors: async ({ browser, contextOptions, extraContextOptions }, use) => {
|
|
278
|
+
* const cast = Cast.where(actor =>
|
|
279
|
+
* actor.whoCan(
|
|
280
|
+
* BrowseTheWebWithPlaywright.using(browser, contextOptions, extraContextOptions),
|
|
281
|
+
* TakeNotes.usingAnEmptyNotepad(),
|
|
282
|
+
* )
|
|
283
|
+
* )
|
|
284
|
+
*
|
|
285
|
+
* // Make sure to pass your custom cast to Playwright `use` callback
|
|
286
|
+
* await use(cast)
|
|
287
|
+
* },
|
|
288
|
+
* })
|
|
289
|
+
*
|
|
290
|
+
* describe(`Todo List App`, () => {
|
|
291
|
+
*
|
|
292
|
+
* it(`should allow me to add a todo item`, async ({ actor }) => {
|
|
293
|
+
* await actor.attemptsTo(
|
|
294
|
+
* startWithAnEmptyList(),
|
|
295
|
+
*
|
|
296
|
+
* recordItem('Buy some milk'),
|
|
297
|
+
*
|
|
298
|
+
* Ensure.that(itemNames(), equals([
|
|
299
|
+
* 'Buy some milk',
|
|
300
|
+
* ])),
|
|
301
|
+
* )
|
|
302
|
+
* })
|
|
303
|
+
* })
|
|
304
|
+
* })
|
|
305
|
+
*
|
|
306
|
+
*
|
|
307
|
+
* #### Learn more
|
|
308
|
+
* - Declaring a Serenity/JS [test scenario](https://serenity-js.org/api/playwright-test/function/it/)
|
|
309
|
+
* - [`SerenityFixtures`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/)
|
|
310
|
+
*/
|
|
311
|
+
actors: Cast;
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Default [`actor`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/#actor) injected into a [test scenario](https://serenity-js.org/api/playwright-test/function/it/).
|
|
315
|
+
*
|
|
316
|
+
* Using `actor` fixture is equivalent to invoking [`actorCalled`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/#actorCalled)
|
|
317
|
+
* with [`defaultActorName`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/#defaultActorName).
|
|
318
|
+
*
|
|
319
|
+
* #### Learn more
|
|
320
|
+
* - [`actorCalled`](https://serenity-js.org/api/playwright-test/interface/SerenityWorkerFixtures/#actorCalled)
|
|
321
|
+
* - [`defaultActorName`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/#defaultActorName)
|
|
322
|
+
* - Declaring a Serenity/JS [test scenario](https://serenity-js.org/api/playwright-test/function/it/)
|
|
323
|
+
*/
|
|
324
|
+
actor: Actor;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Serenity/JS-specific [Playwright Test fixtures](https://playwright.dev/docs/test-fixtures)
|
|
329
|
+
* injected into your [test scenarios](https://serenity-js.org/api/playwright-test/function/it/).
|
|
330
|
+
*
|
|
331
|
+
* ## Example test scenario
|
|
332
|
+
*
|
|
333
|
+
* ```typescript
|
|
334
|
+
* import { Ensure, equals } from '@serenity-js/assertions'
|
|
335
|
+
* import { describe, it, test } from '@serenity-js/playwright-test'
|
|
336
|
+
* import { Photographer, TakePhotosOfFailures } from '@serenity-js/web'
|
|
337
|
+
*
|
|
338
|
+
* describe(`Recording items`, () => {
|
|
339
|
+
*
|
|
340
|
+
* test.use({
|
|
341
|
+
* defaultActorName: 'Serena',
|
|
342
|
+
* crew: [
|
|
343
|
+
* Photographer.whoWill(TakePhotosOfFailures),
|
|
344
|
+
* ],
|
|
345
|
+
* })
|
|
346
|
+
*
|
|
347
|
+
* describe(`Todo List App`, () => {
|
|
348
|
+
*
|
|
349
|
+
* it(`should allow me to add a todo item`, async ({ actor }) => {
|
|
350
|
+
* await actor.attemptsTo(
|
|
351
|
+
* startWithAnEmptyList(),
|
|
352
|
+
*
|
|
353
|
+
* recordItem('Buy some milk'),
|
|
354
|
+
*
|
|
355
|
+
* Ensure.that(itemNames(), equals([
|
|
356
|
+
* 'Buy some milk',
|
|
357
|
+
* ])),
|
|
358
|
+
* )
|
|
359
|
+
* })
|
|
360
|
+
* })
|
|
361
|
+
* })
|
|
362
|
+
* ```
|
|
363
|
+
*
|
|
364
|
+
* ## Learn more
|
|
365
|
+
* - [Using Serenity/JS with Playwright Test](https://serenity-js.org/handbook/test-runners/playwright-test/)
|
|
366
|
+
*/
|
|
367
|
+
export interface SerenityWorkerFixtures {
|
|
368
|
+
/**
|
|
369
|
+
* Name and version of the operating system that Playwright Test worker process runs on.
|
|
370
|
+
*/
|
|
371
|
+
platform: { name: string, version: string };
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Retrieves the root object of the Serenity/JS framework.
|
|
375
|
+
*/
|
|
376
|
+
serenity: Serenity;
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Uses the provided [cast](https://serenity-js.org/api/core/class/Cast/) of [`actors`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/#actors) to instantiate an [`Actor`](https://serenity-js.org/api/core/class/Actor/) called `name`
|
|
380
|
+
* and inject it into a [test scenario](https://serenity-js.org/api/playwright-test/function/it/).
|
|
381
|
+
*
|
|
382
|
+
* Retrieves an existing actor if one has already been instantiated.
|
|
383
|
+
*
|
|
384
|
+
* #### Learn more
|
|
385
|
+
* - Declaring a Serenity/JS [test scenario](https://serenity-js.org/api/playwright-test/function/it/)
|
|
386
|
+
* - [`SerenityFixtures.actors`](https://serenity-js.org/api/playwright-test/interface/SerenityFixtures/#actors)
|
|
387
|
+
*
|
|
388
|
+
* @param name
|
|
389
|
+
*/
|
|
390
|
+
actorCalled: (name: string) => Actor;
|
|
391
|
+
}
|
|
392
|
+
|