1616
1717import { EventEmitter } from 'events' ;
1818
19- import { RecorderCollection } from './recorderCollection ' ;
19+ import { RecorderSignalProcessor } from './recorderSignalProcessor ' ;
2020import * as rawRecorderSource from '../../generated/pollingRecorderSource' ;
2121import { eventsHelper , monotonicTime , quoteCSSAttributeValue } from '../../utils' ;
2222import { raceAgainstDeadline } from '../../utils/isomorphic/timeoutRunner' ;
@@ -26,13 +26,16 @@ import { Frame } from '../frames';
2626import { Page } from '../page' ;
2727import { ThrottledFile } from './throttledFile' ;
2828import { generateCode } from '../codegen/language' ;
29+ import { performAction } from './recorderRunner' ;
30+ import { collapseActions } from './recorderUtils' ;
2931
3032import type { RegisteredListener } from '../../utils' ;
3133import type { Language , LanguageGenerator , LanguageGeneratorOptions } from '../codegen/types' ;
3234import type * as channels from '@protocol/channels' ;
3335import type * as actions from '@recorder/actions' ;
3436import type { Source } from '@recorder/recorderTypes' ;
3537import type { Progress } from '@protocol/progress' ;
38+ import type { Signal } from '../../../../recorder/src/actions' ;
3639
3740type BindingSource = { frame : Frame , page : Page } ;
3841
@@ -45,7 +48,7 @@ export class ContextRecorder extends EventEmitter {
4548 Change : 'change'
4649 } ;
4750
48- private _collection : RecorderCollection ;
51+ private _collection : RecorderSignalProcessor ;
4952 private _pageAliases = new Map < Page , string > ( ) ;
5053 private _lastPopupOrdinal = 0 ;
5154 private _lastDialogOrdinal = - 1 ;
@@ -57,6 +60,9 @@ export class ContextRecorder extends EventEmitter {
5760 private _throttledOutputFile : ThrottledFile | null = null ;
5861 private _orderedLanguages : LanguageGenerator [ ] = [ ] ;
5962 private _listeners : RegisteredListener [ ] = [ ] ;
63+ private _actions : actions . ActionInContext [ ] = [ ] ;
64+ private _languageGeneratorOptions : LanguageGeneratorOptions ;
65+ private _enabled : boolean = false ;
6066
6167 constructor ( context : BrowserContext , params : channels . BrowserContextEnableRecorderParams , delegate : ContextRecorderDelegate ) {
6268 super ( ) ;
@@ -65,53 +71,76 @@ export class ContextRecorder extends EventEmitter {
6571 this . _delegate = delegate ;
6672 this . _recorderSources = [ ] ;
6773 const language = params . language || context . _browser . sdkLanguage ( ) ;
68- this . setOutput ( language , params . outputFile ) ;
6974
7075 // Make a copy of options to modify them later.
71- const languageGeneratorOptions : LanguageGeneratorOptions = {
76+ this . _languageGeneratorOptions = {
7277 browserName : context . _browser . options . name ,
7378 launchOptions : { headless : false , ...params . launchOptions , tracesDir : undefined } ,
7479 contextOptions : { ...params . contextOptions } ,
7580 deviceName : params . device ,
7681 saveStorage : params . saveStorage ,
7782 } ;
7883
79- this . _collection = new RecorderCollection ( this . _pageAliases ) ;
80- this . _collection . on ( 'change' , ( actions : actions . ActionInContext [ ] ) => {
81- this . _recorderSources = [ ] ;
82- for ( const languageGenerator of this . _orderedLanguages ) {
83- const { header, footer, actionTexts, text } = generateCode ( actions , languageGenerator , languageGeneratorOptions ) ;
84- const source : Source = {
85- isRecorded : true ,
86- label : languageGenerator . name ,
87- group : languageGenerator . groupName ,
88- id : languageGenerator . id ,
89- text,
90- header,
91- footer,
92- actions : actionTexts ,
93- language : languageGenerator . highlighter ,
94- highlight : [ ]
95- } ;
96- source . revealLine = text . split ( '\n' ) . length - 1 ;
97- this . _recorderSources . push ( source ) ;
98- if ( languageGenerator === this . _orderedLanguages [ 0 ] )
99- this . _throttledOutputFile ?. setContent ( source . text ) ;
100- }
101- this . emit ( ContextRecorder . Events . Change , {
102- sources : this . _recorderSources ,
103- actions
104- } ) ;
84+ this . _collection = new RecorderSignalProcessor ( ) ;
85+ this . _collection . on ( 'action' , ( actionInContext : actions . ActionInContext ) => {
86+ if ( ! this . _enabled )
87+ return ;
88+ this . _actions . push ( actionInContext ) ;
89+ this . _updateActions ( ) ;
90+ } ) ;
91+ this . _collection . on ( 'signal' , ( signal : Signal ) => {
92+ if ( ! this . _enabled )
93+ return ;
94+ const lastAction = this . _actions [ this . _actions . length - 1 ] ;
95+ if ( lastAction )
96+ lastAction . action . signals . push ( signal ) ;
97+ this . _updateActions ( ) ;
10598 } ) ;
99+
106100 context . on ( BrowserContext . Events . BeforeClose , ( ) => {
107101 this . _throttledOutputFile ?. flush ( ) ;
108102 } ) ;
109103 this . _listeners . push ( eventsHelper . addEventListener ( process , 'exit' , ( ) => {
110104 this . _throttledOutputFile ?. flush ( ) ;
111105 } ) ) ;
106+
107+ this . setOutput ( language , params . outputFile ) ;
112108 this . setEnabled ( params . mode === 'recording' ) ;
113109 }
114110
111+ private _resetActions ( ) {
112+ this . _actions = [ ] ;
113+ this . _updateActions ( ) ;
114+ }
115+
116+ private _updateActions ( ) {
117+ const actions = collapseActions ( this . _actions ) ;
118+ this . _recorderSources = [ ] ;
119+ for ( const languageGenerator of this . _orderedLanguages ) {
120+ const { header, footer, actionTexts, text } = generateCode ( actions , languageGenerator , this . _languageGeneratorOptions ) ;
121+ const source : Source = {
122+ isRecorded : true ,
123+ label : languageGenerator . name ,
124+ group : languageGenerator . groupName ,
125+ id : languageGenerator . id ,
126+ text,
127+ header,
128+ footer,
129+ actions : actionTexts ,
130+ language : languageGenerator . highlighter ,
131+ highlight : [ ]
132+ } ;
133+ source . revealLine = text . split ( '\n' ) . length - 1 ;
134+ this . _recorderSources . push ( source ) ;
135+ if ( languageGenerator === this . _orderedLanguages [ 0 ] )
136+ this . _throttledOutputFile ?. setContent ( source . text ) ;
137+ }
138+ this . emit ( ContextRecorder . Events . Change , {
139+ sources : this . _recorderSources ,
140+ actions
141+ } ) ;
142+ }
143+
115144 setOutput ( codegenId : string , outputFile ?: string ) {
116145 const languages = languageSet ( ) ;
117146 const primaryLanguage = [ ...languages ] . find ( l => l . id === codegenId ) ;
@@ -120,7 +149,7 @@ export class ContextRecorder extends EventEmitter {
120149 languages . delete ( primaryLanguage ) ;
121150 this . _orderedLanguages = [ primaryLanguage , ...languages ] ;
122151 this . _throttledOutputFile = outputFile ? new ThrottledFile ( outputFile ) : null ;
123- this . _collection ?. restart ( ) ;
152+ this . _resetActions ( ) ;
124153 }
125154
126155 languageName ( id ?: string ) : Language {
@@ -154,7 +183,7 @@ export class ContextRecorder extends EventEmitter {
154183 }
155184
156185 setEnabled ( enabled : boolean ) {
157- this . _collection . setEnabled ( enabled ) ;
186+ this . _enabled = enabled ;
158187 }
159188
160189 dispose ( ) {
@@ -165,7 +194,7 @@ export class ContextRecorder extends EventEmitter {
165194 // First page is called page, others are called popup1, popup2, etc.
166195 const frame = page . mainFrame ( ) ;
167196 page . on ( 'close' , ( ) => {
168- this . _collection . addRecordedAction ( {
197+ this . _collection . addAction ( {
169198 frame : this . _describeMainFrame ( page ) ,
170199 action : {
171200 name : 'closePage' ,
@@ -187,7 +216,7 @@ export class ContextRecorder extends EventEmitter {
187216 if ( page . opener ( ) ) {
188217 this . _onPopup ( page . opener ( ) ! , page ) ;
189218 } else {
190- this . _collection . addRecordedAction ( {
219+ this . _collection . addAction ( {
191220 frame : this . _describeMainFrame ( page ) ,
192221 action : {
193222 name : 'openPage' ,
@@ -200,7 +229,7 @@ export class ContextRecorder extends EventEmitter {
200229 }
201230
202231 clearScript ( ) : void {
203- this . _collection . restart ( ) ;
232+ this . _resetActions ( ) ;
204233 if ( this . _params . mode === 'recording' ) {
205234 for ( const page of this . _context . pages ( ) )
206235 this . _onFrameNavigated ( page . mainFrame ( ) , page ) ;
@@ -242,11 +271,15 @@ export class ContextRecorder extends EventEmitter {
242271 }
243272
244273 private async _performAction ( frame : Frame , action : actions . PerformOnRecordAction ) {
245- await this . _collection . performAction ( await this . _createActionInContext ( frame , action ) ) ;
274+ const actionInContext = await this . _createActionInContext ( frame , action ) ;
275+ this . _collection . addAction ( actionInContext ) ;
276+ if ( actionInContext . action . name !== 'openPage' && actionInContext . action . name !== 'closePage' )
277+ await performAction ( this . _pageAliases , actionInContext ) ;
278+ actionInContext . endTime = monotonicTime ( ) ;
246279 }
247280
248281 private async _recordAction ( frame : Frame , action : actions . Action ) {
249- this . _collection . addRecordedAction ( await this . _createActionInContext ( frame , action ) ) ;
282+ this . _collection . addAction ( await this . _createActionInContext ( frame , action ) ) ;
250283 }
251284
252285 private _onFrameNavigated ( frame : Frame , page : Page ) {
0 commit comments