CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-lighthouse

Automated auditing, performance metrics, and best practices for the web.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

user-flows.mddocs/

User Flow Testing

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.

Capabilities

Start Flow Function

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,
      },
    },
  },
});

UserFlow Class

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();

Flow Artifact Auditing

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);
});

Types and Interfaces

UserFlow Options

interface LH.UserFlow.Options {
  name?: string;                    // Flow name for reporting
  config?: LH.Config;              // Lighthouse configuration
  flags?: LH.Flags;                // Runtime flags
}

Step 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
}

Step Result

interface LH.UserFlow.StepResult {
  lhr: LH.LighthouseResult;        // Lighthouse result for this step
  name: string;                    // Step name
  artifacts?: LH.Artifacts;        // Raw artifacts collected
}

Flow Result

interface LH.FlowResult {
  steps: LH.UserFlow.StepResult[]; // Array of all step results
  name: string;                    // Flow name
}

Flow Artifacts

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
}

Advanced Flow Patterns

Multi-Page Flow

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();

Complex Interaction Flow

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();

Conditional Flow Steps

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();

Performance Considerations

Flow Configuration

// 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',
      ],
    },
  },
});

Memory Management

// 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();
}

Error Handling

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;
}

docs

cli.md

configuration.md

core-auditing.md

index.md

report-generation.md

user-flows.md

tile.json