@@ -10,11 +10,12 @@ const {snapshotGather} = require('./gather/snapshot-runner.js');
10
10
const { startTimespanGather} = require ( './gather/timespan-runner.js' ) ;
11
11
const { navigationGather} = require ( './gather/navigation-runner.js' ) ;
12
12
const Runner = require ( '../runner.js' ) ;
13
+ const { initializeConfig} = require ( './config/config.js' ) ;
13
14
14
15
/** @typedef {Parameters<snapshotGather>[0] } FrOptions */
15
16
/** @typedef {Omit<FrOptions, 'page'> & {name?: string} } UserFlowOptions */
16
17
/** @typedef {Omit<FrOptions, 'page'> & {stepName?: string} } StepOptions */
17
- /** @typedef {{gatherResult: LH.Gatherer.FRGatherResult, name: string} } StepArtifact */
18
+ /** @typedef {WeakMap<LH.UserFlow.GatherStep, LH.Gatherer.FRGatherResult['runnerOptions']> } GatherStepRunnerOptions */
18
19
19
20
class UserFlow {
20
21
/**
@@ -26,8 +27,10 @@ class UserFlow {
26
27
this . options = { page, ...options } ;
27
28
/** @type {string|undefined } */
28
29
this . name = options ?. name ;
29
- /** @type {StepArtifact[] } */
30
- this . stepArtifacts = [ ] ;
30
+ /** @type {LH.UserFlow.GatherStep[] } */
31
+ this . _gatherSteps = [ ] ;
32
+ /** @type {GatherStepRunnerOptions } */
33
+ this . _gatherStepRunnerOptions = new WeakMap ( ) ;
31
34
}
32
35
33
36
/**
@@ -68,8 +71,8 @@ class UserFlow {
68
71
}
69
72
70
73
// On repeat navigations, we want to disable storage reset by default (i.e. it's not a cold load).
71
- const isSubsequentNavigation = this . stepArtifacts
72
- . some ( step => step . gatherResult . artifacts . GatherContext . gatherMode === 'navigation' ) ;
74
+ const isSubsequentNavigation = this . _gatherSteps
75
+ . some ( step => step . artifacts . GatherContext . gatherMode === 'navigation' ) ;
73
76
if ( isSubsequentNavigation ) {
74
77
if ( settingsOverrides . disableStorageReset === undefined ) {
75
78
settingsOverrides . disableStorageReset = true ;
@@ -82,23 +85,34 @@ class UserFlow {
82
85
return options ;
83
86
}
84
87
88
+ /**
89
+ *
90
+ * @param {LH.Gatherer.FRGatherResult } gatherResult
91
+ * @param {StepOptions } options
92
+ */
93
+ _addGatherStep ( gatherResult , options ) {
94
+ const providedName = options ?. stepName ;
95
+ const gatherStep = {
96
+ artifacts : gatherResult . artifacts ,
97
+ name : providedName || this . _getDefaultStepName ( gatherResult . artifacts ) ,
98
+ config : options . config ,
99
+ configContext : options . configContext ,
100
+ } ;
101
+ this . _gatherSteps . push ( gatherStep ) ;
102
+ this . _gatherStepRunnerOptions . set ( gatherStep , gatherResult . runnerOptions ) ;
103
+ }
104
+
85
105
/**
86
106
* @param {LH.NavigationRequestor } requestor
87
107
* @param {StepOptions= } stepOptions
88
108
*/
89
109
async navigate ( requestor , stepOptions ) {
90
110
if ( this . currentTimespan ) throw new Error ( 'Timespan already in progress' ) ;
91
111
92
- const gatherResult = await navigationGather (
93
- requestor ,
94
- this . _getNextNavigationOptions ( stepOptions )
95
- ) ;
112
+ const options = this . _getNextNavigationOptions ( stepOptions ) ;
113
+ const gatherResult = await navigationGather ( requestor , options ) ;
96
114
97
- const providedName = stepOptions ?. stepName ;
98
- this . stepArtifacts . push ( {
99
- gatherResult,
100
- name : providedName || this . _getDefaultStepName ( gatherResult . artifacts ) ,
101
- } ) ;
115
+ this . _addGatherStep ( gatherResult , options ) ;
102
116
103
117
return gatherResult ;
104
118
}
@@ -121,11 +135,7 @@ class UserFlow {
121
135
const gatherResult = await timespan . endTimespanGather ( ) ;
122
136
this . currentTimespan = undefined ;
123
137
124
- const providedName = options ?. stepName ;
125
- this . stepArtifacts . push ( {
126
- gatherResult,
127
- name : providedName || this . _getDefaultStepName ( gatherResult . artifacts ) ,
128
- } ) ;
138
+ this . _addGatherStep ( gatherResult , options ) ;
129
139
130
140
return gatherResult ;
131
141
}
@@ -139,11 +149,7 @@ class UserFlow {
139
149
const options = { ...this . options , ...stepOptions } ;
140
150
const gatherResult = await snapshotGather ( options ) ;
141
151
142
- const providedName = stepOptions ?. stepName ;
143
- this . stepArtifacts . push ( {
144
- gatherResult,
145
- name : providedName || this . _getDefaultStepName ( gatherResult . artifacts ) ,
146
- } ) ;
152
+ this . _addGatherStep ( gatherResult , options ) ;
147
153
148
154
return gatherResult ;
149
155
}
@@ -152,21 +158,11 @@ class UserFlow {
152
158
* @returns {Promise<LH.FlowResult> }
153
159
*/
154
160
async createFlowResult ( ) {
155
- if ( ! this . stepArtifacts . length ) {
156
- throw new Error ( 'Need at least one step before getting the result' ) ;
157
- }
158
- const url = new URL ( this . stepArtifacts [ 0 ] . gatherResult . artifacts . URL . finalUrl ) ;
159
- const flowName = this . name || `User flow (${ url . hostname } )` ;
160
-
161
- /** @type {LH.FlowResult['steps'] } */
162
- const steps = [ ] ;
163
- for ( const { gatherResult, name} of this . stepArtifacts ) {
164
- const result = await Runner . audit ( gatherResult . artifacts , gatherResult . runnerOptions ) ;
165
- if ( ! result ) throw new Error ( `Step "${ name } " did not return a result` ) ;
166
- steps . push ( { lhr : result . lhr , name} ) ;
167
- }
168
-
169
- return { steps, name : flowName } ;
161
+ return auditGatherSteps ( this . _gatherSteps , {
162
+ name : this . name ,
163
+ config : this . options . config ,
164
+ gatherStepRunnerOptions : this . _gatherStepRunnerOptions ,
165
+ } ) ;
170
166
}
171
167
172
168
/**
@@ -176,6 +172,58 @@ class UserFlow {
176
172
const flowResult = await this . createFlowResult ( ) ;
177
173
return generateFlowReportHtml ( flowResult ) ;
178
174
}
175
+
176
+ /**
177
+ * @return {LH.UserFlow.FlowArtifacts }
178
+ */
179
+ createArtifactsJson ( ) {
180
+ return {
181
+ gatherSteps : this . _gatherSteps ,
182
+ name : this . name ,
183
+ } ;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * @param {Array<LH.UserFlow.GatherStep> } gatherSteps
189
+ * @param {{name?: string, config?: LH.Config.Json, gatherStepRunnerOptions?: GatherStepRunnerOptions} } options
190
+ */
191
+ async function auditGatherSteps ( gatherSteps , options ) {
192
+ if ( ! gatherSteps . length ) {
193
+ throw new Error ( 'Need at least one step before getting the result' ) ;
194
+ }
195
+
196
+ /** @type {LH.FlowResult['steps'] } */
197
+ const steps = [ ] ;
198
+ for ( const gatherStep of gatherSteps ) {
199
+ const { artifacts, name, configContext} = gatherStep ;
200
+
201
+ let runnerOptions = options . gatherStepRunnerOptions ?. get ( gatherStep ) ;
202
+
203
+ // If the gather step is not active, we must recreate the runner options.
204
+ if ( ! runnerOptions ) {
205
+ // Step specific configs take precedence over a config for the entire flow.
206
+ const configJson = gatherStep . config || options . config ;
207
+ const { gatherMode} = artifacts . GatherContext ;
208
+ const { config} = initializeConfig ( configJson , { ...configContext , gatherMode} ) ;
209
+ runnerOptions = {
210
+ config,
211
+ computedCache : new Map ( ) ,
212
+ } ;
213
+ }
214
+
215
+ const result = await Runner . audit ( artifacts , runnerOptions ) ;
216
+ if ( ! result ) throw new Error ( `Step "${ name } " did not return a result` ) ;
217
+ steps . push ( { lhr : result . lhr , name} ) ;
218
+ }
219
+
220
+ const url = new URL ( gatherSteps [ 0 ] . artifacts . URL . finalUrl ) ;
221
+ const flowName = options . name || `User flow (${ url . hostname } )` ;
222
+ return { steps, name : flowName } ;
179
223
}
180
224
181
- module . exports = UserFlow ;
225
+
226
+ module . exports = {
227
+ UserFlow,
228
+ auditGatherSteps,
229
+ } ;
0 commit comments