Automated auditing, performance metrics, and best practices for the web.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
Creates a new UserFlow instance for multi-step user journey testing.
/**
* Start a new user flow for multi-step testing
* @param page - Puppeteer page instance for testing
* @param options - Optional flow configuration and settings
* @returns Promise resolving to UserFlow instance
*/
function startFlow(
page: LH.Puppeteer.Page,
options?: LH.UserFlow.Options
): Promise<UserFlow>;Usage Examples:
import { startFlow } from 'lighthouse';
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Start flow with custom name and configuration
const flow = await startFlow(page, {
name: 'E-commerce Checkout Flow',
config: {
settings: {
throttlingMethod: 'simulate',
throttling: {
rttMs: 40,
throughputKbps: 10240,
cpuSlowdownMultiplier: 1,
},
},
},
});The UserFlow class provides methods for different types of measurements within a user journey.
class UserFlow {
/**
* Navigate to a URL and measure navigation performance
* @param requestor - URL string or navigation function
* @param stepFlags - Optional step configuration flags
* @returns Promise that resolves when navigation completes
*/
navigate(
requestor: LH.NavigationRequestor,
stepFlags?: LH.UserFlow.StepFlags
): Promise<void>;
/**
* Start measuring user interactions and dynamic changes
* @param stepFlags - Optional step configuration flags
* @returns Promise that resolves when timespan starts
*/
startTimespan(stepFlags?: LH.UserFlow.StepFlags): Promise<void>;
/**
* End timespan measurement
* @returns Promise that resolves when timespan ends
*/
endTimespan(): Promise<void>;
/**
* Take snapshot of current page state
* @param stepFlags - Optional step configuration flags
* @returns Promise that resolves when snapshot completes
*/
snapshot(stepFlags?: LH.UserFlow.StepFlags): Promise<void>;
/**
* Start a user-triggered navigation (alternative to navigate())
* @param stepFlags - Optional step configuration flags
* @returns Promise that resolves when navigation setup is complete
*/
startNavigation(stepFlags?: LH.UserFlow.StepFlags): Promise<void>;
/**
* End a user-triggered navigation started with startNavigation()
* @returns Promise that resolves when navigation completes
*/
endNavigation(): Promise<void>;
/**
* Create final flow result with all steps
* @returns Promise resolving to complete flow result
*/
createFlowResult(): Promise<LH.FlowResult>;
/**
* Generate flow report
* @returns Promise resolving to HTML report string
*/
generateReport(): Promise<string>;
/**
* Create artifacts JSON for the flow
* @returns Flow artifacts object
*/
createArtifactsJson(): LH.UserFlow.FlowArtifacts;
}Usage Examples:
const flow = await startFlow(page, {name: 'Shopping Journey'});
// Step 1: Navigate to homepage
await flow.navigate('https://shop.example.com', {
stepName: 'Homepage Load',
});
// Step 2: Measure search interaction
await flow.startTimespan({stepName: 'Product Search'});
await page.type('.search-input', 'laptop');
await page.click('.search-button');
await page.waitForSelector('.search-results');
await flow.endTimespan();
// Step 3: Navigate to product page
await flow.navigate(async () => {
await page.click('.product-item:first-child a');
await page.waitForSelector('.product-details');
}, {stepName: 'Product Page Load'});
// Step 4: Take snapshot after adding to cart
await page.click('.add-to-cart');
await page.waitForSelector('.cart-confirmation');
await flow.snapshot({stepName: 'Added to Cart State'});
// Step 5: User-triggered navigation (alternative approach)
await flow.startNavigation({stepName: 'Checkout Page Load'});
await page.click('.checkout-button'); // This triggers navigation
await flow.endNavigation(); // Wait for navigation to complete
// Generate final flow result
const flowResult = await flow.createFlowResult();Audit pre-collected flow artifacts without running live tests.
/**
* Audit collected flow artifacts
* @param flowArtifacts - Pre-collected flow artifacts and steps
* @param config - Optional Lighthouse configuration
* @returns Promise resolving to complete flow result
*/
function auditFlowArtifacts(
flowArtifacts: LH.UserFlow.FlowArtifacts,
config?: LH.Config
): Promise<LH.FlowResult>;Usage Examples:
import { auditFlowArtifacts } from 'lighthouse';
// Audit pre-collected artifacts (useful for CI/CD)
const flowResult = await auditFlowArtifacts(savedArtifacts, {
settings: {
onlyCategories: ['performance', 'accessibility'],
},
});
console.log('Flow steps:', flowResult.steps.length);
flowResult.steps.forEach((step, index) => {
console.log(`Step ${index + 1}: ${step.name}`);
console.log('Performance score:', step.lhr.categories.performance.score);
});interface LH.UserFlow.Options {
name?: string; // Flow name for reporting
config?: LH.Config; // Lighthouse configuration
flags?: LH.Flags; // Runtime flags
}interface LH.UserFlow.StepFlags {
stepName?: string; // Name of the step for reporting
config?: LH.Config; // Step-specific configuration
flags?: LH.Flags; // Step-specific flags
}interface LH.UserFlow.StepResult {
lhr: LH.LighthouseResult; // Lighthouse result for this step
name: string; // Step name
artifacts?: LH.Artifacts; // Raw artifacts collected
}interface LH.FlowResult {
steps: LH.UserFlow.StepResult[]; // Array of all step results
name: string; // Flow name
}interface LH.UserFlow.FlowArtifacts {
gatherSteps: LH.UserFlow.GatherStep[]; // Collected gather steps
name: string; // Flow name
}
interface LH.UserFlow.GatherStep {
artifacts: LH.Artifacts; // Step artifacts
name: string; // Step name
config: LH.Config; // Step configuration
}const flow = await startFlow(page, {name: 'Multi-Page Journey'});
// Navigate through multiple pages
await flow.navigate('https://example.com', {stepName: 'Homepage'});
await flow.navigate('https://example.com/products', {stepName: 'Products Page'});
await flow.navigate('https://example.com/about', {stepName: 'About Page'});
const result = await flow.createFlowResult();const flow = await startFlow(page, {name: 'Form Submission Flow'});
// Initial page load
await flow.navigate('https://example.com/contact', {stepName: 'Contact Page Load'});
// Measure form interaction performance
await flow.startTimespan({stepName: 'Form Filling'});
await page.type('#name', 'John Doe');
await page.type('#email', 'john@example.com');
await page.type('#message', 'Hello world');
await flow.endTimespan();
// Measure form submission
await flow.startTimespan({stepName: 'Form Submission'});
await page.click('#submit-button');
await page.waitForSelector('.success-message');
await flow.endTimespan();
// Final state snapshot
await flow.snapshot({stepName: 'Success State'});
const result = await flow.createFlowResult();const flow = await startFlow(page, {name: 'Conditional User Flow'});
await flow.navigate('https://example.com/login', {stepName: 'Login Page'});
// Check if already logged in
const isLoggedIn = await page.$('.user-menu') !== null;
if (!isLoggedIn) {
await flow.startTimespan({stepName: 'Login Process'});
await page.type('#username', 'testuser');
await page.type('#password', 'password');
await page.click('#login-button');
await page.waitForSelector('.user-menu');
await flow.endTimespan();
}
await flow.navigate('https://example.com/dashboard', {stepName: 'Dashboard Load'});
const result = await flow.createFlowResult();// Optimize flow for CI/CD environments
const flow = await startFlow(page, {
name: 'CI Performance Test',
config: {
settings: {
// Faster execution for CI
throttlingMethod: 'provided',
screenEmulation: {disabled: true},
// Focus on key metrics
onlyCategories: ['performance'],
onlyAudits: [
'first-contentful-paint',
'largest-contentful-paint',
'cumulative-layout-shift',
],
},
},
});// Clean up resources after flow completion
try {
const flow = await startFlow(page, {name: 'Resource-Intensive Flow'});
// Run your flow steps...
await flow.navigate('https://example.com');
const result = await flow.createFlowResult();
return result;
} finally {
// Ensure browser cleanup
await browser.close();
}const flow = await startFlow(page, {name: 'Error-Handled Flow'});
try {
await flow.navigate('https://example.com', {stepName: 'Homepage'});
await flow.startTimespan({stepName: 'User Interaction'});
// ... user interactions
await flow.endTimespan();
const result = await flow.createFlowResult();
// Check for step-level errors
result.steps.forEach((step, index) => {
if (step.lhr.runtimeError) {
console.error(`Step ${index + 1} error:`, step.lhr.runtimeError);
}
if (step.lhr.runWarnings.length > 0) {
console.warn(`Step ${index + 1} warnings:`, step.lhr.runWarnings);
}
});
} catch (error) {
console.error('Flow execution error:', error.message);
throw error;
}