0
# User Flow Testing
1
2
User flow testing enables comprehensive analysis of multi-step user journeys, measuring performance and user experience across complex interactions. This capability is essential for testing real-world user scenarios and identifying performance issues that occur during user interactions.
3
4
## Capabilities
5
6
### Start Flow Function
7
8
Creates a new UserFlow instance for multi-step user journey testing.
9
10
```javascript { .api }
11
/**
12
* Start a new user flow for multi-step testing
13
* @param page - Puppeteer page instance for testing
14
* @param options - Optional flow configuration and settings
15
* @returns Promise resolving to UserFlow instance
16
*/
17
function startFlow(
18
page: LH.Puppeteer.Page,
19
options?: LH.UserFlow.Options
20
): Promise<UserFlow>;
21
```
22
23
**Usage Examples:**
24
25
```javascript
26
import { startFlow } from 'lighthouse';
27
import puppeteer from 'puppeteer';
28
29
const browser = await puppeteer.launch();
30
const page = await browser.newPage();
31
32
// Start flow with custom name and configuration
33
const flow = await startFlow(page, {
34
name: 'E-commerce Checkout Flow',
35
config: {
36
settings: {
37
throttlingMethod: 'simulate',
38
throttling: {
39
rttMs: 40,
40
throughputKbps: 10240,
41
cpuSlowdownMultiplier: 1,
42
},
43
},
44
},
45
});
46
```
47
48
### UserFlow Class
49
50
The UserFlow class provides methods for different types of measurements within a user journey.
51
52
```javascript { .api }
53
class UserFlow {
54
/**
55
* Navigate to a URL and measure navigation performance
56
* @param requestor - URL string or navigation function
57
* @param stepFlags - Optional step configuration flags
58
* @returns Promise that resolves when navigation completes
59
*/
60
navigate(
61
requestor: LH.NavigationRequestor,
62
stepFlags?: LH.UserFlow.StepFlags
63
): Promise<void>;
64
65
/**
66
* Start measuring user interactions and dynamic changes
67
* @param stepFlags - Optional step configuration flags
68
* @returns Promise that resolves when timespan starts
69
*/
70
startTimespan(stepFlags?: LH.UserFlow.StepFlags): Promise<void>;
71
72
/**
73
* End timespan measurement
74
* @returns Promise that resolves when timespan ends
75
*/
76
endTimespan(): Promise<void>;
77
78
/**
79
* Take snapshot of current page state
80
* @param stepFlags - Optional step configuration flags
81
* @returns Promise that resolves when snapshot completes
82
*/
83
snapshot(stepFlags?: LH.UserFlow.StepFlags): Promise<void>;
84
85
/**
86
* Start a user-triggered navigation (alternative to navigate())
87
* @param stepFlags - Optional step configuration flags
88
* @returns Promise that resolves when navigation setup is complete
89
*/
90
startNavigation(stepFlags?: LH.UserFlow.StepFlags): Promise<void>;
91
92
/**
93
* End a user-triggered navigation started with startNavigation()
94
* @returns Promise that resolves when navigation completes
95
*/
96
endNavigation(): Promise<void>;
97
98
/**
99
* Create final flow result with all steps
100
* @returns Promise resolving to complete flow result
101
*/
102
createFlowResult(): Promise<LH.FlowResult>;
103
104
/**
105
* Generate flow report
106
* @returns Promise resolving to HTML report string
107
*/
108
generateReport(): Promise<string>;
109
110
/**
111
* Create artifacts JSON for the flow
112
* @returns Flow artifacts object
113
*/
114
createArtifactsJson(): LH.UserFlow.FlowArtifacts;
115
}
116
```
117
118
**Usage Examples:**
119
120
```javascript
121
const flow = await startFlow(page, {name: 'Shopping Journey'});
122
123
// Step 1: Navigate to homepage
124
await flow.navigate('https://shop.example.com', {
125
stepName: 'Homepage Load',
126
});
127
128
// Step 2: Measure search interaction
129
await flow.startTimespan({stepName: 'Product Search'});
130
await page.type('.search-input', 'laptop');
131
await page.click('.search-button');
132
await page.waitForSelector('.search-results');
133
await flow.endTimespan();
134
135
// Step 3: Navigate to product page
136
await flow.navigate(async () => {
137
await page.click('.product-item:first-child a');
138
await page.waitForSelector('.product-details');
139
}, {stepName: 'Product Page Load'});
140
141
// Step 4: Take snapshot after adding to cart
142
await page.click('.add-to-cart');
143
await page.waitForSelector('.cart-confirmation');
144
await flow.snapshot({stepName: 'Added to Cart State'});
145
146
// Step 5: User-triggered navigation (alternative approach)
147
await flow.startNavigation({stepName: 'Checkout Page Load'});
148
await page.click('.checkout-button'); // This triggers navigation
149
await flow.endNavigation(); // Wait for navigation to complete
150
151
// Generate final flow result
152
const flowResult = await flow.createFlowResult();
153
```
154
155
### Flow Artifact Auditing
156
157
Audit pre-collected flow artifacts without running live tests.
158
159
```javascript { .api }
160
/**
161
* Audit collected flow artifacts
162
* @param flowArtifacts - Pre-collected flow artifacts and steps
163
* @param config - Optional Lighthouse configuration
164
* @returns Promise resolving to complete flow result
165
*/
166
function auditFlowArtifacts(
167
flowArtifacts: LH.UserFlow.FlowArtifacts,
168
config?: LH.Config
169
): Promise<LH.FlowResult>;
170
```
171
172
**Usage Examples:**
173
174
```javascript
175
import { auditFlowArtifacts } from 'lighthouse';
176
177
// Audit pre-collected artifacts (useful for CI/CD)
178
const flowResult = await auditFlowArtifacts(savedArtifacts, {
179
settings: {
180
onlyCategories: ['performance', 'accessibility'],
181
},
182
});
183
184
console.log('Flow steps:', flowResult.steps.length);
185
flowResult.steps.forEach((step, index) => {
186
console.log(`Step ${index + 1}: ${step.name}`);
187
console.log('Performance score:', step.lhr.categories.performance.score);
188
});
189
```
190
191
## Types and Interfaces
192
193
### UserFlow Options
194
195
```javascript { .api }
196
interface LH.UserFlow.Options {
197
name?: string; // Flow name for reporting
198
config?: LH.Config; // Lighthouse configuration
199
flags?: LH.Flags; // Runtime flags
200
}
201
```
202
203
### Step Flags
204
205
```javascript { .api }
206
interface LH.UserFlow.StepFlags {
207
stepName?: string; // Name of the step for reporting
208
config?: LH.Config; // Step-specific configuration
209
flags?: LH.Flags; // Step-specific flags
210
}
211
```
212
213
### Step Result
214
215
```javascript { .api }
216
interface LH.UserFlow.StepResult {
217
lhr: LH.LighthouseResult; // Lighthouse result for this step
218
name: string; // Step name
219
artifacts?: LH.Artifacts; // Raw artifacts collected
220
}
221
```
222
223
### Flow Result
224
225
```javascript { .api }
226
interface LH.FlowResult {
227
steps: LH.UserFlow.StepResult[]; // Array of all step results
228
name: string; // Flow name
229
}
230
```
231
232
### Flow Artifacts
233
234
```javascript { .api }
235
interface LH.UserFlow.FlowArtifacts {
236
gatherSteps: LH.UserFlow.GatherStep[]; // Collected gather steps
237
name: string; // Flow name
238
}
239
240
interface LH.UserFlow.GatherStep {
241
artifacts: LH.Artifacts; // Step artifacts
242
name: string; // Step name
243
config: LH.Config; // Step configuration
244
}
245
```
246
247
## Advanced Flow Patterns
248
249
### Multi-Page Flow
250
251
```javascript
252
const flow = await startFlow(page, {name: 'Multi-Page Journey'});
253
254
// Navigate through multiple pages
255
await flow.navigate('https://example.com', {stepName: 'Homepage'});
256
await flow.navigate('https://example.com/products', {stepName: 'Products Page'});
257
await flow.navigate('https://example.com/about', {stepName: 'About Page'});
258
259
const result = await flow.createFlowResult();
260
```
261
262
### Complex Interaction Flow
263
264
```javascript
265
const flow = await startFlow(page, {name: 'Form Submission Flow'});
266
267
// Initial page load
268
await flow.navigate('https://example.com/contact', {stepName: 'Contact Page Load'});
269
270
// Measure form interaction performance
271
await flow.startTimespan({stepName: 'Form Filling'});
272
await page.type('#name', 'John Doe');
273
await page.type('#email', 'john@example.com');
274
await page.type('#message', 'Hello world');
275
await flow.endTimespan();
276
277
// Measure form submission
278
await flow.startTimespan({stepName: 'Form Submission'});
279
await page.click('#submit-button');
280
await page.waitForSelector('.success-message');
281
await flow.endTimespan();
282
283
// Final state snapshot
284
await flow.snapshot({stepName: 'Success State'});
285
286
const result = await flow.createFlowResult();
287
```
288
289
### Conditional Flow Steps
290
291
```javascript
292
const flow = await startFlow(page, {name: 'Conditional User Flow'});
293
294
await flow.navigate('https://example.com/login', {stepName: 'Login Page'});
295
296
// Check if already logged in
297
const isLoggedIn = await page.$('.user-menu') !== null;
298
299
if (!isLoggedIn) {
300
await flow.startTimespan({stepName: 'Login Process'});
301
await page.type('#username', 'testuser');
302
await page.type('#password', 'password');
303
await page.click('#login-button');
304
await page.waitForSelector('.user-menu');
305
await flow.endTimespan();
306
}
307
308
await flow.navigate('https://example.com/dashboard', {stepName: 'Dashboard Load'});
309
const result = await flow.createFlowResult();
310
```
311
312
## Performance Considerations
313
314
### Flow Configuration
315
316
```javascript
317
// Optimize flow for CI/CD environments
318
const flow = await startFlow(page, {
319
name: 'CI Performance Test',
320
config: {
321
settings: {
322
// Faster execution for CI
323
throttlingMethod: 'provided',
324
screenEmulation: {disabled: true},
325
// Focus on key metrics
326
onlyCategories: ['performance'],
327
onlyAudits: [
328
'first-contentful-paint',
329
'largest-contentful-paint',
330
'cumulative-layout-shift',
331
],
332
},
333
},
334
});
335
```
336
337
### Memory Management
338
339
```javascript
340
// Clean up resources after flow completion
341
try {
342
const flow = await startFlow(page, {name: 'Resource-Intensive Flow'});
343
344
// Run your flow steps...
345
await flow.navigate('https://example.com');
346
const result = await flow.createFlowResult();
347
348
return result;
349
} finally {
350
// Ensure browser cleanup
351
await browser.close();
352
}
353
```
354
355
## Error Handling
356
357
```javascript
358
const flow = await startFlow(page, {name: 'Error-Handled Flow'});
359
360
try {
361
await flow.navigate('https://example.com', {stepName: 'Homepage'});
362
363
await flow.startTimespan({stepName: 'User Interaction'});
364
// ... user interactions
365
await flow.endTimespan();
366
367
const result = await flow.createFlowResult();
368
369
// Check for step-level errors
370
result.steps.forEach((step, index) => {
371
if (step.lhr.runtimeError) {
372
console.error(`Step ${index + 1} error:`, step.lhr.runtimeError);
373
}
374
if (step.lhr.runWarnings.length > 0) {
375
console.warn(`Step ${index + 1} warnings:`, step.lhr.runWarnings);
376
}
377
});
378
379
} catch (error) {
380
console.error('Flow execution error:', error.message);
381
throw error;
382
}
383
```