@@ -6,9 +6,14 @@ import {
66} from '@seleniumhq/side-runtime'
77import { WebDriverExecutorHooks } from '@seleniumhq/side-runtime/src/webdriver'
88import { hasID } from '@seleniumhq/side-api/dist/helpers/hasID'
9+ import { randomUUID } from 'crypto'
10+ import { session } from 'electron'
911import { Session } from 'main/types'
12+ import { cpus } from 'os'
1013import BaseController from '../Base'
1114
15+ const parallelExecutions = Math . floor ( cpus ( ) . length / 2 )
16+
1217/**
1318 * This is a wrapper on the Playback construct of the side-runtime. Once again,
1419 * I am ashamed. When hoisted, the underlying playback thing is accessed at
@@ -27,48 +32,67 @@ export default class PlaybackController extends BaseController {
2732 playRange = [ 0 , - 1 ]
2833 playingSuite = ''
2934 playingTest = ''
30- playback : Playback | null = null
35+ playbacks : Playback [ ] = [ ]
36+ testQueue : string [ ] = [ ]
3137 variables : Variables = new Variables ( )
3238
3339 onBeforePlay : NonNullable < WebDriverExecutorHooks [ 'onBeforePlay' ] > = async ( {
3440 driver : executor ,
3541 } ) => {
3642 const { driver } = executor
3743 const { windows } = this . session
38- const playbackWindow = await windows . getPlaybackWindow ( )
39-
40- let success = false
41- if ( playbackWindow ) {
42- // Figure out playback window from document.title and url match
44+ const UUID = randomUUID ( )
45+ const registerWindow = async ( windowID : number ) => {
46+ let success = false
4347 const handles = await driver . getAllWindowHandles ( )
4448 for ( let i = 0 , ii = handles . length ; i !== ii ; i ++ ) {
45- try {
46- await driver . switchTo ( ) . window ( handles [ i ] )
47- const title = await driver . getTitle ( )
48- const url = await driver . getCurrentUrl ( )
49- if (
50- title === playbackWindow . getTitle ( ) &&
51- url === playbackWindow . webContents . getURL ( )
52- ) {
53- success = true
54- break
55- }
56- } catch ( e ) {
57- console . warn ( 'Failed to switch to window' , e )
49+ const handle = handles [ i ]
50+ await driver . switchTo ( ) . window ( handle )
51+ const title = await driver . getTitle ( )
52+ if ( title === UUID ) {
53+ await windows . registerPlaybackWindowHandle ( handle , windowID )
54+ success = true
55+ break
5856 }
5957 }
58+ if ( ! success ) {
59+ throw new Error ( 'Failed to switch to playback window' )
60+ }
61+ }
62+
63+ if ( this . session . playback . playingSuite ) {
64+ const window = await windows . openPlaybackWindow ( {
65+ show : false ,
66+ title : UUID ,
67+ } )
68+ await registerWindow ( window . id )
69+ await windows . arrangeWindow (
70+ window ,
71+ 'windowSizePlayback' ,
72+ 'windowPositionPlayback'
73+ )
74+ window . show ( )
75+ return
6076 }
61- if ( ! success ) {
62- await windows . initializePlaybackWindow ( )
63- await this . onBeforePlay ( { driver : executor } )
77+
78+ const playbackWindow = await windows . getPlaybackWindow ( )
79+ if ( playbackWindow ) {
80+ const handle = await windows . getPlaybackWindowHandleByID (
81+ playbackWindow . id
82+ )
83+ if ( handle ) {
84+ await driver . switchTo ( ) . window ( handle )
85+ return
86+ }
6487 }
88+
89+ const window = await windows . openPlaybackWindow ( { title : UUID } )
90+ await registerWindow ( window . id )
6591 }
6692
6793 async pause ( ) {
6894 this . isPlaying = false
69- if ( this . playback ) {
70- await this . playback . pause ( )
71- }
95+ this . playbacks . forEach ( ( playback ) => playback . pause ( ) )
7296 }
7397
7498 async stop ( ) {
@@ -82,9 +106,9 @@ export default class PlaybackController extends BaseController {
82106 }
83107
84108 async resume ( ) {
85- if ( this . playback ) {
109+ if ( this . playbacks . length ) {
86110 this . isPlaying = true
87- this . playback . resume ( )
111+ this . playbacks . forEach ( ( playback ) => playback . resume ( ) )
88112 } else {
89113 const sessionState = await this . session . state . get ( )
90114 await this . play ( sessionState . state . activeTestID )
@@ -98,28 +122,31 @@ export default class PlaybackController extends BaseController {
98122 /**
99123 * Create playback if none exists
100124 */
101- if ( ! this . playback ) {
102- const playback = new Playback ( {
103- baseUrl : this . session . projects . project . url ,
104- executor : await this . session . driver . build ( { } ) ,
105- getTestByName : ( name : string ) => this . session . tests . getByName ( name ) ,
106- logger : console ,
107- variables : this . variables ,
108- options : {
109- delay : this . session . projects . project . delay || 0
110- }
111- } )
112- const EE = playback [ 'event-emitter' ]
113- EE . addListener (
114- PlaybackEvents . PLAYBACK_STATE_CHANGED ,
115- this . handlePlaybackStateChanged
116- )
117- EE . addListener (
118- PlaybackEvents . COMMAND_STATE_CHANGED ,
119- this . handleCommandStateChanged
120- )
121- this . playback = playback
122- }
125+ // const browser = 'electron'
126+ const playback = new Playback ( {
127+ baseUrl : this . session . projects . project . url ,
128+ executor : await this . session . driver . build ( { } ) ,
129+ getTestByName : ( name : string ) => this . session . tests . getByName ( name ) ,
130+ logger : console ,
131+ variables : new Variables ( ) ,
132+ options : {
133+ delay : this . session . projects . project . delay || 0 ,
134+ } ,
135+ } )
136+ console . log ( 'playback init' )
137+ await playback . init ( )
138+ console . log ( 'playback cookies deleted' )
139+
140+ const EE = playback [ 'event-emitter' ]
141+ EE . addListener (
142+ PlaybackEvents . PLAYBACK_STATE_CHANGED ,
143+ this . handlePlaybackStateChanged ( playback , testID )
144+ )
145+ EE . addListener (
146+ PlaybackEvents . COMMAND_STATE_CHANGED ,
147+ this . handleCommandStateChanged
148+ )
149+ this . playbacks . push ( playback )
123150 /**
124151 * If not ending at end of test, use playTo command
125152 * or playSingleCommand if just one command specified.
@@ -128,12 +155,12 @@ export default class PlaybackController extends BaseController {
128155 if ( playRange [ 1 ] !== - 1 ) {
129156 const test = this . session . tests . getByID ( testID )
130157 if ( playRange [ 0 ] === playRange [ 1 ] ) {
131- this . playback . playSingleCommand ( test . commands [ playRange [ 0 ] ] )
158+ playback . playSingleCommand ( test . commands [ playRange [ 0 ] ] )
132159 } else {
133- this . playback . playTo ( test , playRange [ 1 ] , playRange [ 0 ] )
160+ playback . playTo ( test , playRange [ 1 ] , playRange [ 0 ] )
134161 }
135162 } else {
136- this . playback . play ( this . session . tests . getByID ( testID ) , {
163+ playback . play ( this . session . tests . getByID ( testID ) , {
137164 startingCommandIndex : playRange [ 0 ] ,
138165 } )
139166 }
@@ -145,28 +172,32 @@ export default class PlaybackController extends BaseController {
145172 state : { activeSuiteID } ,
146173 } = await this . session . state . get ( )
147174 this . playingSuite = activeSuiteID
148- const tests = suites . find ( hasID ( activeSuiteID ) ) ?. tests ?? [ ]
149- this . play ( tests [ 0 ] )
175+ const suite = suites . find ( hasID ( activeSuiteID ) )
176+ this . testQueue = suite ?. tests ?? [ ]
177+ if ( suite ?. parallel ) {
178+ for ( let i = 0 ; i < parallelExecutions ; i ++ ) {
179+ this . playNextTest ( )
180+ }
181+ } else {
182+ this . playNextTest ( )
183+ }
150184 }
151185
152186 async playNextTest ( ) {
153- const {
154- project : { suites } ,
155- state : { activeSuiteID, activeTestID } ,
156- } = await this . session . state . get ( )
157- const tests = suites . find ( hasID ( activeSuiteID ) ) ?. tests ?? [ ]
158- const nextTestIndex = tests . indexOf ( activeTestID ) + 1
159- const nextTest = tests [ nextTestIndex ]
187+ const nextTest = this . testQueue . shift ( )
160188 if ( nextTest ) {
161- this . session . api . state . onMutate . dispatchEvent ( 'state.setActiveTest' , {
162- params : [ nextTest ] ,
163- } )
164- this . play ( nextTest )
165- } else {
166- this . playingSuite = ''
167- this . session . api . state . onMutate . dispatchEvent ( 'playback.stop' , {
168- params : [ ] ,
169- } )
189+ const {
190+ project : { suites } ,
191+ state : { activeSuiteID } ,
192+ } = await this . session . state . get ( )
193+ const suite = suites . find ( hasID ( activeSuiteID ) )
194+ const persistSession = suite ?. persistSession ?? false
195+ if ( ! persistSession ) {
196+ await session . defaultSession . clearStorageData ( {
197+ storages : [ 'cookies' , 'localstorage' ] ,
198+ } )
199+ }
200+ await this . play ( nextTest )
170201 }
171202 }
172203
@@ -181,25 +212,36 @@ export default class PlaybackController extends BaseController {
181212 console . debug ( `${ e . state } ${ niceString } ` )
182213 }
183214
184- handlePlaybackStateChanged = (
185- e : PlaybackEventShapes [ 'PLAYBACK_STATE_CHANGED' ]
186- ) => {
187- const testName = this . session . tests . getByID ( this . playingTest ) ?. name
188- console . debug ( `Playing state changed ${ e . state } for test ${ testName } ` )
189- switch ( e . state ) {
190- case 'aborted' :
191- case 'errored' :
192- case 'failed' :
193- case 'finished' :
194- case 'stopped' :
195- const playback = this . playback as Playback
196- playback . cleanup ( )
197- this . playback = null
198- this . variables = new Variables ( )
199- if ( this . playingSuite ) {
200- this . playNextTest ( )
201- }
215+ handlePlaybackStateChanged =
216+ ( playback : Playback , testID : string ) =>
217+ async ( e : PlaybackEventShapes [ 'PLAYBACK_STATE_CHANGED' ] ) => {
218+ const testName = this . session . tests . getByID ( testID ) ?. name
219+ console . debug (
220+ `Playing state changed ${ e . state } for test ${ testName } ` ,
221+ this . playingSuite
222+ )
223+ switch ( e . state ) {
224+ case 'aborted' :
225+ case 'errored' :
226+ case 'failed' :
227+ case 'finished' :
228+ case 'stopped' :
229+ if ( this . playingSuite ) {
230+ try {
231+ await playback . executor . driver . close ( )
232+ } catch ( e ) { }
233+ this . playbacks . splice ( this . playbacks . indexOf ( playback ) , 1 )
234+ await playback . cleanup ( )
235+ if ( ! this . testQueue . length && ! this . playbacks . length ) {
236+ this . playingSuite = ''
237+ this . session . api . state . onMutate . dispatchEvent ( 'playback.stop' , {
238+ params : [ ] ,
239+ } )
240+ } else {
241+ this . playNextTest ( )
242+ }
243+ }
244+ }
245+ this . session . api . playback . onPlayUpdate . dispatchEvent ( e )
202246 }
203- this . session . api . playback . onPlayUpdate . dispatchEvent ( e )
204- }
205247}
0 commit comments