perf.test.ts

Overview

perf.test.ts is an automated performance testing script using the Playwright testing framework. Its primary purpose is to verify that a specific "Expensive Component" in a web application renders within an acceptable performance benchmark — specifically, it asserts that the component finishes rendering within 1 second after a user interaction (a checkbox click).

The test navigates to a performance-focused page (./perf), triggers a rendering process by clicking a checkbox, and uses in-browser performance marks combined with DOM mutation observation to precisely measure the render time of a complex React component containing a large number of child elements (10,000 divs). This ensures that UI performance regressions are caught early in development.


Detailed Explanation

Test Suite: performance


Test: should render expensive component within 1 second after checkbox click

Purpose

To measure and assert that the "Expensive Component" renders completely within 1000 milliseconds after a checkbox input is clicked.

Parameters

Workflow

  1. Navigate to the Performance Page

    await page.goto('./perf', { waitUntil: 'load' })
    

    Loads the performance testing page, waiting for all network activity and page load to complete.

  2. Inject Performance Measurement Script

    • Uses page.evaluate() to run client-side JavaScript in the browser context.

    • This script performs the following:

      • Selects the checkbox input element.

      • Sets up a MutationObserver on the document.body to detect when the "Expensive Component" container appears and track the number of child div elements rendered inside it.

      • Logs progress every 1000 rendered components for visibility.

      • When the total number of rendered child components reaches the target (10,000), it uses double requestAnimationFrame to ensure that the browser has painted the UI.

      • Marks the paint completion time with window.performance.mark('expensive-component-painted').

      • Adds a one-time event listener on the checkbox click to start the performance timer and mark the start time (state-change-start).

  3. Trigger Rendering

    const checkbox = page.locator('input[type="checkbox"]')
    await checkbox.click()
    

    Finds the checkbox and clicks it, triggering the UI to start rendering the expensive component.

  4. Wait for Expensive Component to Appear

    const expensiveComponentHeading = page.locator('h2:has-text("Expensive Component")')
    await expect(expensiveComponentHeading).toBeVisible({ timeout: 60_000 })
    

    Waits up to 60 seconds for the heading of the component to appear in the DOM, indicating rendering has started.

  5. Wait for Paint Completion

    await page.waitForFunction(() => {
      return window.performance.getEntriesByName('expensive-component-painted').length > 0
    }, { timeout: 5000 })
    

    Waits up to 5 seconds for the paint completion mark to be recorded, signaling rendering and paint are done.

  6. Calculate Render Time

    const renderTime = await page.evaluate(() => {
      const startMark = window.performance.getEntriesByName('state-change-start')[0]
      const paintMark = window.performance.getEntriesByName('expensive-component-painted')[0]
    
      if (!startMark || !paintMark) {
        throw new Error('Performance marks not found')
      }
    
      return paintMark.startTime - startMark.startTime
    })
    

    Retrieves the timing marks and calculates the render duration in milliseconds.

  7. Assert Performance Requirement

    expect(renderTime).toBeLessThan(1000)
    

    Fails the test if the render time exceeds 1000ms (1 second).


Important Implementation Details


Interaction with Other System Parts


Usage Example

This test script is intended to be run as part of the Playwright test suite:

npx playwright test perf.test.ts

The test will:

If the test fails, it indicates a performance regression or slowdown in rendering this component.


Mermaid Diagram: Flowchart of Main Functions and Workflow

flowchart TD
    A[Start Test] --> B[Navigate to ./perf page]
    B --> C[Inject Performance Measurement Script]
    C --> D[Set up MutationObserver to watch DOM changes]
    C --> E[Attach click listener on checkbox to start timer]
    D --> F[Detect Expensive Component container]
    F --> G[Count rendered child components]
    G -->|Rendered >= 10,000| H[Double requestAnimationFrame]
    H --> I[Mark 'expensive-component-painted']
    E --> J[Click checkbox to trigger rendering]
    J --> K[Wait for Expensive Component heading visible]
    K --> L[Wait for 'expensive-component-painted' mark]
    L --> M[Calculate render duration]
    M --> N[Assert renderTime < 1000ms]
    N --> O[End Test]

Summary