16
16
17
17
import * as fs from 'fs' ;
18
18
import * as path from 'path' ;
19
- import type { APIRequestContext , BrowserContext , Browser , BrowserContextOptions , LaunchOptions , Page , Tracing , Video , PageScreenshotOptions } from 'playwright-core' ;
19
+ import type { APIRequestContext , BrowserContext , Browser , BrowserContextOptions , LaunchOptions , Page , Tracing , Video } from 'playwright-core' ;
20
+ import * as playwrightLibrary from 'playwright-core' ;
20
21
import { createGuid , debugMode , addInternalStackPrefix , isString , asLocator , jsonStringifyForceASCII } from 'playwright-core/lib/utils' ;
21
22
import type { Fixtures , PlaywrightTestArgs , PlaywrightTestOptions , PlaywrightWorkerArgs , PlaywrightWorkerOptions , ScreenshotMode , TestInfo , TestType , VideoMode } from '../types/test' ;
22
23
import type { TestInfoImpl } from './worker/testInfo' ;
23
24
import { rootTestType } from './common/testType' ;
24
25
import type { ContextReuseMode } from './common/config' ;
25
26
import type { ClientInstrumentation , ClientInstrumentationListener } from '../../playwright-core/src/client/clientInstrumentation' ;
26
- import { currentTestInfo , setTestLifecycleInstrumentation , type TestLifecycleInstrumentation } from './common/globals' ;
27
+ import { currentTestInfo } from './common/globals' ;
27
28
export { expect } from './matchers/expect' ;
28
29
export const _baseTest : TestType < { } , { } > = rootTestType . test ;
29
30
@@ -44,12 +45,11 @@ if ((process as any)['__pw_initiator__']) {
44
45
type TestFixtures = PlaywrightTestArgs & PlaywrightTestOptions & {
45
46
_combinedContextOptions : BrowserContextOptions ,
46
47
_setupContextOptions : void ;
48
+ _setupArtifacts : void ;
47
49
_contextFactory : ( options ?: BrowserContextOptions ) => Promise < BrowserContext > ;
48
50
} ;
49
51
50
52
type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & {
51
- // Same as "playwright", but exposed so that our internal tests can override it.
52
- _playwrightImpl : PlaywrightWorkerArgs [ 'playwright' ] ;
53
53
_browserOptions : LaunchOptions ;
54
54
_optionContextReuseMode : ContextReuseMode ,
55
55
_optionConnectOptions : PlaywrightWorkerOptions [ 'connectOptions' ] ,
@@ -59,14 +59,9 @@ type WorkerFixtures = PlaywrightWorkerArgs & PlaywrightWorkerOptions & {
59
59
const playwrightFixtures : Fixtures < TestFixtures , WorkerFixtures > = ( {
60
60
defaultBrowserType : [ 'chromium' , { scope : 'worker' , option : true } ] ,
61
61
browserName : [ ( { defaultBrowserType } , use ) => use ( defaultBrowserType ) , { scope : 'worker' , option : true } ] ,
62
- _playwrightImpl : [ ( { } , use ) => use ( require ( 'playwright-core' ) ) , { scope : 'worker' } ] ,
63
-
64
- playwright : [ async ( { _playwrightImpl, screenshot } , use ) => {
65
- await connector . setPlaywright ( _playwrightImpl , screenshot ) ;
66
- await use ( _playwrightImpl ) ;
67
- await connector . setPlaywright ( undefined , screenshot ) ;
62
+ playwright : [ async ( { } , use ) => {
63
+ await use ( require ( 'playwright-core' ) ) ;
68
64
} , { scope : 'worker' , _hideStep : true } as any ] ,
69
-
70
65
headless : [ ( { launchOptions } , use ) => use ( launchOptions . headless ?? true ) , { scope : 'worker' , option : true } ] ,
71
66
channel : [ ( { launchOptions } , use ) => use ( launchOptions . channel ) , { scope : 'worker' , option : true } ] ,
72
67
launchOptions : [ { } , { scope : 'worker' , option : true } ] ,
@@ -227,7 +222,7 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
227
222
228
223
_setupContextOptions : [ async ( { playwright, _combinedContextOptions, actionTimeout, navigationTimeout, testIdAttribute } , use , testInfo ) => {
229
224
if ( testIdAttribute )
230
- playwright . selectors . setTestIdAttribute ( testIdAttribute ) ;
225
+ playwrightLibrary . selectors . setTestIdAttribute ( testIdAttribute ) ;
231
226
testInfo . snapshotSuffix = process . platform ;
232
227
if ( debugMode ( ) )
233
228
testInfo . setTimeout ( 0 ) ;
@@ -248,6 +243,58 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
248
243
}
249
244
} , { auto : 'all-hooks-included' , _title : 'context configuration' } as any ] ,
250
245
246
+ _setupArtifacts : [ async ( { playwright, screenshot } , use , testInfo ) => {
247
+ const artifactsRecorder = new ArtifactsRecorder ( playwright , tracing ( ) . artifactsDir ( ) , screenshot ) ;
248
+ await artifactsRecorder . willStartTest ( testInfo as TestInfoImpl ) ;
249
+ const csiListener : ClientInstrumentationListener = {
250
+ onApiCallBegin : ( apiName : string , params : Record < string , any > , frames : StackFrame [ ] , userData : any , out : { stepId ?: string } ) => {
251
+ const testInfo = currentTestInfo ( ) ;
252
+ if ( ! testInfo || apiName . includes ( 'setTestIdAttribute' ) )
253
+ return { userObject : null } ;
254
+ const step = testInfo . _addStep ( {
255
+ location : frames [ 0 ] as any ,
256
+ category : 'pw:api' ,
257
+ title : renderApiCall ( apiName , params ) ,
258
+ apiName,
259
+ params,
260
+ } ) ;
261
+ userData . userObject = step ;
262
+ out . stepId = step . stepId ;
263
+ } ,
264
+ onApiCallEnd : ( userData : any , error ?: Error ) => {
265
+ const step = userData . userObject ;
266
+ step ?. complete ( { error } ) ;
267
+ } ,
268
+ onWillPause : ( ) => {
269
+ currentTestInfo ( ) ?. setTimeout ( 0 ) ;
270
+ } ,
271
+ runAfterCreateBrowserContext : async ( context : BrowserContext ) => {
272
+ await artifactsRecorder ?. didCreateBrowserContext ( context ) ;
273
+ const testInfo = currentTestInfo ( ) ;
274
+ if ( testInfo )
275
+ attachConnectedHeaderIfNeeded ( testInfo , context . browser ( ) ) ;
276
+ } ,
277
+ runAfterCreateRequestContext : async ( context : APIRequestContext ) => {
278
+ await artifactsRecorder ?. didCreateRequestContext ( context ) ;
279
+ } ,
280
+ runBeforeCloseBrowserContext : async ( context : BrowserContext ) => {
281
+ await artifactsRecorder ?. willCloseBrowserContext ( context ) ;
282
+ } ,
283
+ runBeforeCloseRequestContext : async ( context : APIRequestContext ) => {
284
+ await artifactsRecorder ?. willCloseRequestContext ( context ) ;
285
+ } ,
286
+ } ;
287
+
288
+ const clientInstrumentation = ( playwright as any ) . _instrumentation as ClientInstrumentation ;
289
+ clientInstrumentation . addListener ( csiListener ) ;
290
+
291
+ await use ( ) ;
292
+
293
+ clientInstrumentation . removeListener ( csiListener ) ;
294
+ await artifactsRecorder . didFinishTest ( ) ;
295
+
296
+ } , { auto : 'all-hooks-included' , _title : 'trace recording' } as any ] ,
297
+
251
298
_contextFactory : [ async ( { browser, video, _reuseContext } , use , testInfo ) => {
252
299
const testInfoImpl = testInfo as TestInfoImpl ;
253
300
const videoMode = normalizeVideoMode ( video ) ;
@@ -424,7 +471,7 @@ class ArtifactsRecorder {
424
471
private _playwright : Playwright ;
425
472
private _artifactsDir : string ;
426
473
private _screenshotMode : ScreenshotMode ;
427
- private _screenshotOptions : { mode : ScreenshotMode } & Pick < PageScreenshotOptions , 'fullPage' | 'omitBackground' > | undefined ;
474
+ private _screenshotOptions : { mode : ScreenshotMode } & Pick < playwrightLibrary . PageScreenshotOptions , 'fullPage' | 'omitBackground' > | undefined ;
428
475
private _temporaryScreenshots : string [ ] = [ ] ;
429
476
private _temporaryArtifacts : string [ ] = [ ] ;
430
477
private _reusedContexts = new Set < BrowserContext > ( ) ;
@@ -449,6 +496,7 @@ class ArtifactsRecorder {
449
496
450
497
async willStartTest ( testInfo : TestInfoImpl ) {
451
498
this . _testInfo = testInfo ;
499
+ testInfo . _onDidFinishTestFunction = ( ) => this . didFinishTestFunction ( ) ;
452
500
453
501
// Since beforeAll(s), test and afterAll(s) reuse the same TestInfo, make sure we do not
454
502
// overwrite previous screenshots.
@@ -630,101 +678,6 @@ function tracing() {
630
678
return ( test . info ( ) as TestInfoImpl ) . _tracing ;
631
679
}
632
680
633
- class InstrumentationConnector implements TestLifecycleInstrumentation , ClientInstrumentationListener {
634
- private _playwright : PlaywrightWorkerArgs [ 'playwright' ] | undefined ;
635
- private _screenshot : ScreenshotOption = 'off' ;
636
- private _artifactsRecorder : ArtifactsRecorder | undefined ;
637
- private _testIsRunning = false ;
638
-
639
- constructor ( ) {
640
- setTestLifecycleInstrumentation ( this ) ;
641
- }
642
-
643
- async setPlaywright ( playwright : PlaywrightWorkerArgs [ 'playwright' ] | undefined , screenshot : ScreenshotOption ) {
644
- if ( this . _playwright ) {
645
- if ( this . _testIsRunning ) {
646
- // When "playwright" is destroyed during a test, collect artifacts immediately.
647
- await this . onTestEnd ( ) ;
648
- }
649
- const clientInstrumentation = ( this . _playwright as any ) . _instrumentation as ClientInstrumentation ;
650
- clientInstrumentation . removeListener ( this ) ;
651
- }
652
- this . _playwright = playwright ;
653
- this . _screenshot = screenshot ;
654
- if ( this . _playwright ) {
655
- const clientInstrumentation = ( this . _playwright as any ) . _instrumentation as ClientInstrumentation ;
656
- clientInstrumentation . addListener ( this ) ;
657
- if ( this . _testIsRunning ) {
658
- // When "playwright" is created during a test, wire it up immediately.
659
- await this . onTestBegin ( ) ;
660
- }
661
- }
662
- }
663
-
664
- async onTestBegin ( ) {
665
- this . _testIsRunning = true ;
666
- if ( this . _playwright ) {
667
- this . _artifactsRecorder = new ArtifactsRecorder ( this . _playwright , tracing ( ) . artifactsDir ( ) , this . _screenshot ) ;
668
- await this . _artifactsRecorder . willStartTest ( currentTestInfo ( ) as TestInfoImpl ) ;
669
- }
670
- }
671
-
672
- async onTestFunctionEnd ( ) {
673
- await this . _artifactsRecorder ?. didFinishTestFunction ( ) ;
674
- }
675
-
676
- async onTestEnd ( ) {
677
- await this . _artifactsRecorder ?. didFinishTest ( ) ;
678
- this . _artifactsRecorder = undefined ;
679
- this . _testIsRunning = false ;
680
- }
681
-
682
- onApiCallBegin ( apiName : string , params : Record < string , any > , frames : StackFrame [ ] , userData : any , out : { stepId ?: string } ) {
683
- const testInfo = currentTestInfo ( ) ;
684
- if ( ! testInfo || apiName . includes ( 'setTestIdAttribute' ) )
685
- return { userObject : null } ;
686
- const step = testInfo . _addStep ( {
687
- location : frames [ 0 ] as any ,
688
- category : 'pw:api' ,
689
- title : renderApiCall ( apiName , params ) ,
690
- apiName,
691
- params,
692
- } ) ;
693
- userData . userObject = step ;
694
- out . stepId = step . stepId ;
695
- }
696
-
697
- onApiCallEnd ( userData : any , error ?: Error ) {
698
- const step = userData . userObject ;
699
- step ?. complete ( { error } ) ;
700
- }
701
-
702
- onWillPause ( ) {
703
- currentTestInfo ( ) ?. setTimeout ( 0 ) ;
704
- }
705
-
706
- async runAfterCreateBrowserContext ( context : BrowserContext ) {
707
- await this . _artifactsRecorder ?. didCreateBrowserContext ( context ) ;
708
- const testInfo = currentTestInfo ( ) ;
709
- if ( testInfo )
710
- attachConnectedHeaderIfNeeded ( testInfo , context . browser ( ) ) ;
711
- }
712
-
713
- async runAfterCreateRequestContext ( context : APIRequestContext ) {
714
- await this . _artifactsRecorder ?. didCreateRequestContext ( context ) ;
715
- }
716
-
717
- async runBeforeCloseBrowserContext ( context : BrowserContext ) {
718
- await this . _artifactsRecorder ?. willCloseBrowserContext ( context ) ;
719
- }
720
-
721
- async runBeforeCloseRequestContext ( context : APIRequestContext ) {
722
- await this . _artifactsRecorder ?. willCloseRequestContext ( context ) ;
723
- }
724
- }
725
-
726
- const connector = new InstrumentationConnector ( ) ;
727
-
728
681
export const test = _baseTest . extend < TestFixtures , WorkerFixtures > ( playwrightFixtures ) ;
729
682
730
683
export { defineConfig } from './common/configLoader' ;
0 commit comments