14
14
* limitations under the License.
15
15
*/
16
16
17
+ import { EventEmitter } from 'events' ;
17
18
import type { BrowserContextOptions , LaunchOptions } from '../../../..' ;
18
19
import { Frame } from '../../frames' ;
19
- import { LanguageGenerator } from './language' ;
20
+ import { LanguageGenerator , LanguageGeneratorOptions } from './language' ;
20
21
import { Action , Signal } from './recorderActions' ;
21
22
import { describeFrame } from './utils' ;
22
23
@@ -29,56 +30,55 @@ export type ActionInContext = {
29
30
committed ?: boolean ;
30
31
}
31
32
32
- export interface CodeGeneratorOutput {
33
- printLn ( text : string ) : void ;
34
- popLn ( text : string ) : void ;
35
- }
36
-
37
- export class CodeGenerator {
33
+ export class CodeGenerator extends EventEmitter {
38
34
private _currentAction : ActionInContext | null = null ;
39
35
private _lastAction : ActionInContext | null = null ;
40
- private _lastActionText : string | undefined ;
41
- private _languageGenerator : LanguageGenerator ;
42
- private _output : CodeGeneratorOutput ;
43
- private _headerText = '' ;
44
- private _footerText = '' ;
36
+ private _actions : ActionInContext [ ] = [ ] ;
37
+ private _enabled : boolean ;
38
+ private _options : LanguageGeneratorOptions ;
45
39
46
- constructor ( browserName : string , generateHeaders : boolean , launchOptions : LaunchOptions , contextOptions : BrowserContextOptions , output : CodeGeneratorOutput , languageGenerator : LanguageGenerator , deviceName : string | undefined , saveStorage : string | undefined ) {
47
- this . _output = output ;
48
- this . _languageGenerator = languageGenerator ;
40
+ constructor ( browserName : string , generateHeaders : boolean , launchOptions : LaunchOptions , contextOptions : BrowserContextOptions , deviceName : string | undefined , saveStorage : string | undefined ) {
41
+ super ( ) ;
49
42
50
43
launchOptions = { headless : false , ...launchOptions } ;
51
- if ( generateHeaders ) {
52
- this . _headerText = this . _languageGenerator . generateHeader ( browserName , launchOptions , contextOptions , deviceName ) ;
53
- this . _footerText = '\n' + this . _languageGenerator . generateFooter ( saveStorage ) ;
54
- }
44
+ this . _enabled = generateHeaders ;
45
+ this . _options = { browserName, generateHeaders, launchOptions, contextOptions, deviceName, saveStorage } ;
55
46
this . restart ( ) ;
56
47
}
57
48
58
49
restart ( ) {
59
50
this . _currentAction = null ;
60
51
this . _lastAction = null ;
61
- if ( this . _headerText ) {
62
- this . _output . printLn ( this . _headerText ) ;
63
- this . _output . printLn ( this . _footerText ) ;
64
- }
52
+ this . _actions = [ ] ;
53
+ }
54
+
55
+ setEnabled ( enabled : boolean ) {
56
+ this . _enabled = enabled ;
65
57
}
66
58
67
59
addAction ( action : ActionInContext ) {
60
+ if ( ! this . _enabled )
61
+ return ;
68
62
this . willPerformAction ( action ) ;
69
63
this . didPerformAction ( action ) ;
70
64
}
71
65
72
66
willPerformAction ( action : ActionInContext ) {
67
+ if ( ! this . _enabled )
68
+ return ;
73
69
this . _currentAction = action ;
74
70
}
75
71
76
72
performedActionFailed ( action : ActionInContext ) {
73
+ if ( ! this . _enabled )
74
+ return ;
77
75
if ( this . _currentAction === action )
78
76
this . _currentAction = null ;
79
77
}
80
78
81
79
didPerformAction ( actionInContext : ActionInContext ) {
80
+ if ( ! this . _enabled )
81
+ return ;
82
82
const { action, pageAlias } = actionInContext ;
83
83
let eraseLastAction = false ;
84
84
if ( this . _lastAction && this . _lastAction . pageAlias === pageAlias ) {
@@ -94,41 +94,39 @@ export class CodeGenerator {
94
94
}
95
95
if ( lastAction && action . name === 'navigate' && lastAction . name === 'navigate' ) {
96
96
if ( action . url === lastAction . url ) {
97
+ // Already at a target URL.
97
98
this . _currentAction = null ;
98
99
return ;
99
100
}
100
101
}
101
102
for ( const name of [ 'check' , 'uncheck' ] ) {
103
+ // Check and uncheck erase click.
102
104
if ( lastAction && action . name === name && lastAction . name === 'click' ) {
103
105
if ( ( action as any ) . selector === ( lastAction as any ) . selector )
104
106
eraseLastAction = true ;
105
107
}
106
108
}
107
109
}
108
- this . _printAction ( actionInContext , eraseLastAction ) ;
110
+
111
+ this . _lastAction = actionInContext ;
112
+ this . _currentAction = null ;
113
+ if ( eraseLastAction )
114
+ this . _actions . pop ( ) ;
115
+ this . _actions . push ( actionInContext ) ;
116
+ this . emit ( 'change' ) ;
109
117
}
110
118
111
119
commitLastAction ( ) {
120
+ if ( ! this . _enabled )
121
+ return ;
112
122
const action = this . _lastAction ;
113
123
if ( action )
114
124
action . committed = true ;
115
125
}
116
126
117
- _printAction ( actionInContext : ActionInContext , eraseLastAction : boolean ) {
118
- if ( this . _footerText )
119
- this . _output . popLn ( this . _footerText ) ;
120
- if ( eraseLastAction && this . _lastActionText )
121
- this . _output . popLn ( this . _lastActionText ) ;
122
- const performingAction = ! ! this . _currentAction ;
123
- this . _currentAction = null ;
124
- this . _lastAction = actionInContext ;
125
- this . _lastActionText = this . _languageGenerator . generateAction ( actionInContext , performingAction ) ;
126
- this . _output . printLn ( this . _lastActionText ) ;
127
- if ( this . _footerText )
128
- this . _output . printLn ( this . _footerText ) ;
129
- }
130
-
131
127
signal ( pageAlias : string , frame : Frame , signal : Signal ) {
128
+ if ( ! this . _enabled )
129
+ return ;
132
130
// Signal either arrives while action is being performed or shortly after.
133
131
if ( this . _currentAction ) {
134
132
this . _currentAction . action . signals . push ( signal ) ;
@@ -140,8 +138,9 @@ export class CodeGenerator {
140
138
return ;
141
139
if ( signal . name === 'download' && signals . length && signals [ signals . length - 1 ] . name === 'navigation' )
142
140
signals . length = signals . length - 1 ;
141
+ signal . isAsync = true ;
143
142
this . _lastAction . action . signals . push ( signal ) ;
144
- this . _printAction ( this . _lastAction , true ) ;
143
+ this . emit ( 'change' ) ;
145
144
return ;
146
145
}
147
146
@@ -154,8 +153,19 @@ export class CodeGenerator {
154
153
name : 'navigate' ,
155
154
url : frame . url ( ) ,
156
155
signals : [ ] ,
157
- }
156
+ } ,
158
157
} ) ;
159
158
}
160
159
}
160
+
161
+ generateText ( languageGenerator : LanguageGenerator ) {
162
+ const text = [ ] ;
163
+ if ( this . _options . generateHeaders )
164
+ text . push ( languageGenerator . generateHeader ( this . _options ) ) ;
165
+ for ( const action of this . _actions )
166
+ text . push ( languageGenerator . generateAction ( action ) ) ;
167
+ if ( this . _options . generateHeaders )
168
+ text . push ( languageGenerator . generateFooter ( this . _options . saveStorage ) ) ;
169
+ return text . join ( '\n' ) ;
170
+ }
161
171
}
0 commit comments