### Install and Run Examples Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/snapshot/README.md Instructions for setting up and running the snapshot API examples. This involves navigating to the example directory, installing dependencies, and executing the tests. ```bash cd examples/snapshot npm install npm test ``` -------------------------------- ### Install and Run Concurrent Examples Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/concurrent/README.md Commands to install dependencies and run tests for the concurrent examples directory. Includes options for watch mode and UI testing. ```bash cd examples/concurrent # Install dependencies (from workspace root) npm install # Run tests npm test # Run tests in watch mode npm run test:watch # Run tests with UI npm run test:ui ``` -------------------------------- ### Install Project Dependencies Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/memoization/README.md Install npm packages for the memoization example directory or the entire project. ```bash # From this directory npm install # Or from the root of the project npm install --workspace=examples/memoization ``` -------------------------------- ### Install vitest-react-profiler Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/basic/README.md Install the necessary packages for vitest-react-profiler. This can be done from the example directory or the project root. ```bash # From this directory npm install # Or from the root of the project npm install --workspace=examples/basic ``` -------------------------------- ### Create Vitest Setup File Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Getting-Started Create a setup file for Vitest to import the profiler. ```typescript // vitest-setup.ts import "vitest-react-profiler"; ``` -------------------------------- ### Run All Examples Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/async/README.md Execute all tests in the project. ```bash npm test ``` -------------------------------- ### Install vitest-react-profiler Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Getting-Started Install the package using npm, yarn, or pnpm. ```bash # npm npm install --save-dev vitest-react-profiler # yarn yarn add -D vitest-react-profiler # pnpm pnpm add -D vitest-react-profiler ``` -------------------------------- ### Run Examples with UI Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/async/README.md Execute all tests with the Vitest UI enabled. ```bash npm run test:ui ``` -------------------------------- ### Install Dependencies Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CONTRIBUTING.md Install project dependencies using pnpm. ```bash pnpm install ``` -------------------------------- ### Install Dependencies Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/performance/README.md Installs the necessary project dependencies using npm. ```bash # Install dependencies npm install ``` -------------------------------- ### GitHub Actions Workflow for Performance Tests Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/basic/README.md An example GitHub Actions workflow that checks out code, sets up Node.js, installs dependencies, runs tests, and uploads performance results. Integrate this into your CI/CD pipeline. ```yaml # Example GitHub Actions workflow name: Performance Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 - run: npm ci - run: npm test - name: Upload results if: always() uses: actions/upload-artifact@v3 with: name: performance-results path: coverage/ ``` -------------------------------- ### Run Examples in Watch Mode Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/async/README.md Execute tests and automatically re-run them when files change. ```bash npm run test:watch ``` -------------------------------- ### Verify Installation with a Simple Test Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Getting-Started Run a basic test to ensure the profiler is working correctly. ```typescript import { render } from '@testing-library/react'; import { withProfiler } from 'vitest-react-profiler'; const TestComponent = () =>
Hello
; it('should work', () => { const Profiled = withProfiler(TestComponent); render(); expect(Profiled).toHaveRenderedTimes(1); }); ``` -------------------------------- ### Import Setup File in Vitest Config Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Troubleshooting Ensure your vitest setup file is imported in `vitest.config.ts` to register custom matchers. ```typescript export default defineConfig({ test: { setupFiles: ["./vitest-setup.ts"], // ✅ Must include this }, }); ``` -------------------------------- ### Import Vitest React Profiler in Setup File Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Troubleshooting Import the library in your `vitest-setup.ts` file to make its matchers available. ```typescript import "vitest-react-profiler"; // ✅ Registers matchers ``` -------------------------------- ### Configure Vitest Config Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Getting-Started Configure Vitest to use the setup file by adding it to `setupFiles`. ```typescript // vitest.config.ts import { defineConfig } from "vitest/config"; export default defineConfig({ test: { environment: "jsdom", setupFiles: ["./vitest-setup.ts"], }, }); ``` -------------------------------- ### Troubleshooting: Matchers Not Found Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Getting-Started Ensure the setup file is imported in `vitest.config.ts` if matchers are not found. ```typescript setupFiles: ["./vitest-setup.ts"] ``` -------------------------------- ### Basic Snapshot API Example Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Use this snippet to create a render baseline, trigger an action, and assert that the component rerendered exactly once. ```typescript const ProfiledComponent = withProfiler(Counter); render(); ProfiledComponent.snapshot(); fireEvent.click(screen.getByText('Increment')); expect(ProfiledComponent).toHaveRerenderedOnce(); ``` -------------------------------- ### Troubleshooting: Install React Peer Dependency Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Getting-Started Install React and React DOM as peer dependencies if you encounter 'Cannot find module "react"' errors. ```bash npm install react react-dom ``` -------------------------------- ### Conventional Commit Example Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CLAUDE.md Example of a commit message following the Conventional Commits format for a performance improvement. ```bash perf(ProfilerData): O(1) addRender complexity ``` -------------------------------- ### Project Structure Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CLAUDE.md Illustrates the directory layout for the vitest-react-profiler project, including core profiler implementation, matchers, utilities, types, tests, and examples. ```tree vitest-react-profiler/ ├── src/ │ ├── profiler/ # Core profiler implementation │ │ ├── api/ # Public API methods │ │ ├── components/ # React components (withProfiler, etc.) │ │ └── core/ # Core data structures (ProfilerData, Cache) │ ├── matchers/ # Vitest custom matchers │ │ ├── async.ts # Async matchers (toEventuallyRender, etc.) │ │ ├── sync.ts # Sync matchers (toHaveRendered, etc.) │ │ └── index.ts # ⚠️ NOT a barrel export! Registers matchers │ ├── utils/ # Utility functions │ └── types.ts # All type definitions ├── tests/ │ ├── unit/ # Unit tests (*.test.ts) │ ├── integration/ # Integration tests (*.test.tsx) │ ├── property/ # Property-based tests (*.properties.tsx) │ └── benchmarks/ # Performance benchmarks (*.bench.tsx) └── examples/ # Usage examples (npm workspace) ``` -------------------------------- ### Basic useDeferredValue Example with Profiler Source: https://github.com/greydragon888/vitest-react-profiler/wiki/React-18-Concurrent-Features Demonstrates how `useDeferredValue` defers updates to an expensive component, allowing the UI to remain responsive. This example uses `vitest-react-profiler` to track renders. ```typescript import { useState, useDeferredValue, memo } from 'react'; import { render, fireEvent, waitFor } from '@testing-library/react'; import { withProfiler } from 'vitest-react-profiler'; // Expensive component that renders large list const ExpensiveList = memo(({ query }: { query: string }) => { const items = generateItems(query, 1000); return ( ); }); function DeferredFilterComponent() { const [input, setInput] = useState(''); const deferredInput = useDeferredValue(input); return (
setInput(e.target.value)} placeholder="Filter items..." />
{input}
{deferredInput}
); } it('should track deferred value updates', async () => { const Profiled = withProfiler(DeferredFilterComponent); const { getByPlaceholderText, getByTestId } = render(); // Initial mount expect(Profiled).toHaveMountedOnce(); // Type in filter const input = getByPlaceholderText('Filter items...'); fireEvent.change(input, { target: { value: 'test' } }); // Immediate value updates instantly expect(getByTestId('immediate')).toHaveTextContent('test'); // Wait for deferred value to catch up await waitFor(() => { expect(getByTestId('deferred')).toHaveTextContent('test'); }); // Profiler tracked: mount + input update + deferred update expect(Profiled.getRenderCount()).toBeGreaterThanOrEqual(2); }); ``` -------------------------------- ### Conventional Commits Examples Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CONTRIBUTING.md Examples of commit messages following the Conventional Commits specification for different types of changes. ```git feat: add toHaveRenderedBetween matcher fix: handle undefined render history in getLastRender docs: update API reference for new matchers ``` -------------------------------- ### Real-Time Render Monitoring with Subscriptions Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Advanced-Usage Monitor component renders in real-time by subscribing to render events. This example demonstrates how to subscribe to render notifications, log detailed render information, and unsubscribe. ```typescript it('should monitor renders in real-time', () => { const Profiled = withProfiler(MyComponent); // Subscribe to all renders const unsubscribe = Profiled.onRender((info) => { console.log(` Render #${info.count} Phase: ${info.phase} History: [${info.history.join(', ')}] `); }); render(); // Trigger actions... unsubscribe(); }); ``` -------------------------------- ### Run Specific Test File Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/performance/README.md Executes a specific test file, for example, 'PerformanceTest.test.tsx', using npm test. ```bash # Run specific test file npm test PerformanceTest.test.tsx ``` -------------------------------- ### useTransition Example Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/concurrent/README.md Demonstrates marking state updates as non-urgent with useTransition for UI elements like search or filtering. Use when responsiveness is more critical than immediate completeness. ```typescript const [isPending, startTransition] = useTransition(); const handleSearch = (value: string) => { setInput(value); // Urgent: immediate UI feedback startTransition(() => { setResults(mockSearch(value)); // Non-urgent: can be interrupted }); }; ``` -------------------------------- ### Test useTransition with vitest-react-profiler Source: https://github.com/greydragon888/vitest-react-profiler/wiki/React-18-Concurrent-Features This example demonstrates how to test a component using `useTransition` for non-urgent updates. It verifies that `vitest-react-profiler` tracks renders during the transition. ```typescript import { useState, useTransition } from 'react'; import { render, fireEvent, waitFor } from '@testing-library/react'; import { withProfiler } from 'vitest-react-profiler'; function SearchComponent() { const [isPending, startTransition] = useTransition(); const [query, setQuery] = useState(''); const [results, setResults] = useState([]); const handleSearch = (value: string) => { // Urgent: Update input immediately (responsive UI) setQuery(value); // Non-urgent: Update results (can be interrupted) startTransition(() => { const filtered = performExpensiveSearch(value); setResults(filtered); }); }; return (
handleSearch(e.target.value)} placeholder="Search..." />
{isPending ? 'Searching...' : 'Ready'}
    {results.map(item =>
  • {item.name}
  • )}
); } it('should track renders during transitions', async () => { const Profiled = withProfiler(SearchComponent); const { getByPlaceholderText, getByTestId } = render(); // Initial mount expect(Profiled).toHaveMountedOnce(); // Type in search const input = getByPlaceholderText('Search...'); fireEvent.change(input, { target: { value: 'test' } }); // Wait for transition to complete await waitFor(() => { expect(getByTestId('status')).toHaveTextContent('Ready'); }); // Verify renders were tracked // React may produce 2-3 renders: // 1. query state changes (urgent) // 2. results state changes (non-urgent) expect(Profiled.getRenderCount()).toBeGreaterThanOrEqual(2); }); ``` -------------------------------- ### Event-Based API Example Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/async/README.md Shows the preferred event-based method for asserting render counts, which is faster and more accurate than polling. ```typescript // ✅ Instant notification const info = await ProfiledComponent.waitForNextRender(); expect(info.count).toBe(2); ``` -------------------------------- ### Test Rapid Typing with Transitions Source: https://github.com/greydragon888/vitest-react-profiler/wiki/React-18-Concurrent-Features This example shows how to test components with rapid user input that triggers multiple transitions. It verifies that `vitest-react-profiler` correctly handles batched updates during rapid typing. ```typescript import { useState, useTransition } from 'react'; import { render, fireEvent, waitFor, screen } from '@testing-library/react'; import { withProfiler } from 'vitest-react-profiler'; function SearchComponent() { const [isPending, startTransition] = useTransition(); const [query, setQuery] = useState(''); const [results, setResults] = useState([]); const handleSearch = (value: string) => { // Urgent: Update input immediately (responsive UI) setQuery(value); // Non-urgent: Update results (can be interrupted) startTransition(() => { const filtered = performExpensiveSearch(value); setResults(filtered); }); }; return (
handleSearch(e.target.value)} placeholder="Search..." />
{isPending ? 'Searching...' : 'Ready'}
    {results.map(item =>
  • {item.name}
  • )}
); } it('should handle rapid typing with transitions', async () => { const Profiled = withProfiler(SearchComponent); const { getByPlaceholderText } = render(); const input = getByPlaceholderText('Search...'); // Type rapidly fireEvent.change(input, { target: { value: 'a' } }); fireEvent.change(input, { target: { value: 'ab' } }); fireEvent.change(input, { target: { value: 'abc' } }); // Wait for final transition await waitFor(() => { expect(screen.getByTestId('status')).toHaveTextContent('Ready'); }); // React may batch some updates // Use toBeGreaterThanOrEqual instead of exact count expect(Profiled.getRenderCount()).toBeGreaterThan(1); }); ``` -------------------------------- ### Basic Component Render Test Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Home This snippet demonstrates how to use `withProfiler` to track component renders and assert that a component renders exactly once. Ensure `@testing-library/react` and `vitest-react-profiler` are installed. ```typescript import { render } from '@testing-library/react'; import { withProfiler } from 'vitest-react-profiler'; it('should render only once', () => { const ProfiledComponent = withProfiler(MyComponent); render(); expect(ProfiledComponent).toHaveRenderedTimes(1); }); ``` -------------------------------- ### ProfileHookOptions Example Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Configure hook profiling with `ProfileHookOptions`, particularly for providing custom `renderOptions` like context providers via React Testing Library. ```typescript const options: ProfileHookOptions = { renderOptions: { wrapper: ({ children }) => {children} } }; const { result } = profileHook(() => useMyHook(), options); ``` -------------------------------- ### Assert Component Mounted Once Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Use `toHaveMountedOnce()` to assert that a component has mounted exactly one time. This is useful for verifying initial setup. ```typescript expect(ProfiledComponent).toHaveMountedOnce(); ``` -------------------------------- ### Generate Performance Reports in CI Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Best-Practices A setup script for CI/CD that collects render counts for all profiled components after each test and logs them as JSON. This data can be used for performance analysis. ```typescript // vitest.setup.ts afterEach(() => { const results = getAllProfiledComponents().map(c => ({ name: c.displayName, renders: c.getRenderCount(), })); // Log for CI analysis console.log(JSON.stringify(results, null, 2)); }); ``` -------------------------------- ### Performance Benchmarking Between Implementations Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Advanced-Usage Compare the performance of different component implementations using vitest-react-profiler and Node.js's `perf_hooks`. This example benchmarks two implementations against defined duration budgets. ```typescript import { performance } from 'perf_hooks'; describe('Performance comparison', () => { it('implementation A', () => { const Profiled = withProfiler(ImplementationA); const start = performance.now(); render(); const duration = performance.now() - start; expect(Profiled).toHaveRenderedTimes(1); expect(duration).toBeLessThan(100); // 100ms budget }); it('implementation B', () => { const Profiled = withProfiler(ImplementationB); const start = performance.now(); render(); const duration = performance.now() - start; expect(Profiled).toHaveRenderedTimes(1); expect(duration).toBeLessThan(50); // B should be 2x faster }); }); ``` -------------------------------- ### GitHub Actions Workflow for Performance Tests Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/performance/README.md This YAML snippet shows an example GitHub Actions workflow for running performance tests and uploading the results as an artifact. It includes steps to run tests and upload artifacts regardless of test success or failure. ```yaml # Example GitHub Actions workflow - name: Run Performance Tests run: npm test - name: Upload Performance Results if: always() uses: actions/upload-artifact@v3 with: name: performance-results path: coverage/ ``` -------------------------------- ### Performance Measurements and Budgets Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/basic/README.md Measure component render duration and enforce performance budgets. This example logs the render duration and asserts that it falls within an acceptable time limit. ```tsx const ProfiledComponent = withProfiler(TodoList, "TodoList"); render(); // Get render metrics const lastRender = ProfiledComponent.getLastRender(); console.log(`Render duration: ${lastRender?.actualDuration}ms`); // Validate performance expect(ProfiledComponent).toHaveRenderedWithin(50); // 50ms budget ``` -------------------------------- ### Testing Component Performance Budgets with vitest-react-profiler Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/memoization/README.md Example of defining performance budgets for different components and using the `toHaveRenderedWithin` matcher from vitest-react-profiler to test if components render within their allocated time. ```tsx const performanceBudgets = { MemoizedList: 50, // Max 50ms MemoizedForm: 75, // Max 75ms MemoizedDataGrid: 100, // Max 100ms }; // Test each component against budget expect(ProfiledList).toHaveRenderedWithin(performanceBudgets.MemoizedList); expect(ProfiledForm).toHaveRenderedWithin(performanceBudgets.MemoizedForm); expect(ProfiledGrid).toHaveRenderedWithin(performanceBudgets.MemoizedDataGrid); ``` -------------------------------- ### Test useMemo for Memoization Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Examples Verifies that `useMemo` prevents expensive calculations when dependencies have not changed. The example shows rerendering with the same and different references for the `items` prop. ```typescript const ExpensiveCalc = ({ items }: { items: number[] }) => { const sum = useMemo(() => { return items.reduce((a, b) => a + b, 0); }, [items]); return
{sum}
; }; it('should not re-render with same items reference', () => { const ProfiledComponent = withProfiler(ExpensiveCalc); const items = [1, 2, 3]; const { rerender } = render(); // Same reference - should not recalculate rerender(); expect(ProfiledComponent).toHaveRenderedTimes(2); // New reference - should recalculate rerender(); expect(ProfiledComponent).toHaveRenderedTimes(3); }); ``` -------------------------------- ### Fail on Performance Regression in CI Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Best-Practices Example of setting up a test in CI/CD to fail if a component's render count exceeds a baseline. This helps catch performance regressions early. ```typescript describe('Performance regression tests', () => { it('should not regress on render count', () => { const Profiled = withProfiler(CriticalComponent); render(); // Hard limit based on baseline expect(Profiled.getRenderCount()).toBeLessThanOrEqual(3); }); }); ``` -------------------------------- ### Clone Repository and Navigate Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CONTRIBUTING.md Clone the repository and navigate into the project directory to begin development. ```bash git clone https://github.com/your-username/vitest-react-profiler.git cd vitest-react-profiler ``` -------------------------------- ### Use notToHaveRenderLoops to ignore initial updates Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference When a component has an expected initialization sequence, you can use `ignoreInitialUpdates` to skip a specified number of initial updates before starting loop detection. This example ignores the first 2 updates and sets a stricter consecutive update threshold. ```typescript expect(ProfiledComponent).notToHaveRenderLoops({ ignoreInitialUpdates: 2, // Skip first 2 updates maxConsecutiveUpdates: 5 }); ``` -------------------------------- ### Build Project Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CLAUDE.md Builds the project for production. ```bash npm run build ``` -------------------------------- ### Get Last Render Phase Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Use getLastRender to get the phase of the most recent render. ```typescript const lastPhase = ProfiledComponent.getLastRender(); // "update" ``` -------------------------------- ### Build the Package Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CONTRIBUTING.md Build the project package using pnpm. ```bash pnpm build ``` -------------------------------- ### Build Project Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/README.md Compile and bundle the project for deployment or distribution. This command generates the production-ready build artifacts. ```bash # Build npm run build ``` -------------------------------- ### Local Testing with Linking Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CONTRIBUTING.md Link the project locally for testing in another project. First, link the project itself, then link it into your test project. ```bash pnpm link # In your test project pnpm link vitest-react-profiler ``` -------------------------------- ### Get Full Render History Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Use getRenderHistory to obtain an array of all render phases the component has gone through. ```typescript const history = ProfiledComponent.getRenderHistory(); // ["mount", "update", "update"] ``` -------------------------------- ### Basic Performance Measurement with Profiler Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/performance/README.md Demonstrates how to use the `withProfiler` HOC to measure component render times and check if a render occurred within a specified duration. ```tsx const ProfiledComponent = withProfiler(MyComponent, "MyComponent"); render(); // Check render occurred expect(ProfiledComponent).toHaveRendered(); // Check render time expect(ProfiledComponent).toHaveRenderedWithin(100); // ms // Get specific metrics const lastRender = ProfiledComponent.getLastRender(); console.log(`Render took ${lastRender?.actualDuration}ms`); ``` -------------------------------- ### Get Total Render Count Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Use getRenderCount to retrieve the total number of times a component has rendered. ```typescript const count = ProfiledComponent.getRenderCount(); ``` -------------------------------- ### Create Snapshot and Verify Delta Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Snapshot-API Demonstrates creating a render baseline with `snapshot()` and verifying the render delta using `getRendersSinceSnapshot()`. The initial render is counted before the snapshot, and subsequent renders are measured against this baseline. ```typescript const ProfiledComponent = withProfiler(MyComponent); render(); // Before snapshot: includes initial mount expect(ProfiledComponent.getRendersSinceSnapshot()).toBe(1); ProfiledComponent.snapshot(); // Create baseline // Immediately after snapshot: delta is 0 expect(ProfiledComponent.getRendersSinceSnapshot()).toBe(0); ``` -------------------------------- ### Run Benchmarks Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CLAUDE.md Executes performance benchmarks for the project. ```bash npm run test:bench ``` -------------------------------- ### Run Unit/Integration Tests Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/README.md Execute the project's unit and integration tests using npm. This command runs a comprehensive suite of tests to ensure core functionality. ```bash # Run tests npm test # Unit/integration tests (811 tests) ``` -------------------------------- ### Get Renders by Specific Phase Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Use getRendersByPhase to filter and retrieve all render phases that match a specified phase. ```typescript const updates = ProfiledComponent.getRendersByPhase("update"); // ["update", "update", "update"] ``` -------------------------------- ### Configuration Files Reference Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CLAUDE.md Provides a reference for configuration files used in the project, detailing their purpose and key notes. ```markdown table | File | Purpose | Notes | | ------------------------------ | ---------------------- | ------------------------------------------------------------------- | | `vitest.config.common.mts` | **Base config** | Shared settings for all test configs (resolve, define, environment) | | `vitest.config.unit.mts` | Unit/Integration tests | Coverage enabled (100%), 10s timeout, 4 workers | | `vitest.config.properties.mts` | Property tests | Extends common, coverage disabled, 30s timeout | | `vitest.config.bench.mts` | Benchmarks | Extends common, forks pool, 600s timeout | | `vitest.stryker.config.mts` | Mutation testing | Extends common, forks pool, 5s timeout | | `vitest.stress.config.mts` | Stress tests | Extends common, memory/load testing | | `eslint.config.mjs` | ESLint rules | Flat config format | | `tsconfig.json` | TypeScript | Path aliases (`@/` → `src/`) | | `tsup.config.ts` | Build config | ESM + CJS bundles | | `codecov.yml` | Codecov config | 100% target, bundle analysis | | `sonar-project.properties` | SonarCloud | Quality gates, version must match `package.json` | ``` -------------------------------- ### Listener Cleanup Example Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/async/README.md Demonstrates the importance of manually unsubscribing from event listeners when they are no longer needed to prevent memory leaks. ```typescript const unsubscribe = ProfiledComponent.onRender(() => { // handle event }); // ✅ Clean up when done unsubscribe(); ``` -------------------------------- ### Tracking Render Phases with Profiler Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/performance/README.md Demonstrates how to track and verify different render phases (mount and update) for a component using the `withProfiler` HOC and its history methods. ```tsx const ProfiledComponent = withProfiler(Component, "Component"); render(); // Check mount phase const renders = ProfiledComponent.getRenderHistory(); expect(renders[0]?.phase).toBe("mount"); // Check for updates const updates = ProfiledComponent.getRendersByPhase("update"); expect(updates).toHaveLength(expectedUpdateCount); ``` -------------------------------- ### Correct Usage of waitForStabilization Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Illustrates the correct pattern for using `waitForStabilization` by initiating the wait *before* triggering renders to ensure all renders are captured. ```typescript // ✅ Correct: Start waiting first const promise = ProfiledComponent.waitForStabilization(); triggerManyRenders(); await promise; // ❌ Wrong: Waiting after renders - may resolve immediately triggerManyRenders(); await ProfiledComponent.waitForStabilization(); // May miss renders! ``` -------------------------------- ### Get Renders Since Last Snapshot Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Use getRendersSinceSnapshot to retrieve the number of renders that have occurred since the last snapshot() call. ```typescript const delta = ProfiledComponent.getRendersSinceSnapshot(); ``` -------------------------------- ### Snapshot API Quick Reference Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference A quick reference table for the Snapshot API methods used for creating render baselines and measuring render deltas. ```APIDOC ## Snapshot API ### Description Provides methods for creating render baselines and measuring render deltas. This is the primary tool for testing React optimizations like `React.memo`, `useCallback`, and ensuring single renders per user action. ### Methods - `snapshot()`: Create baseline for render counting. - `getRendersSinceSnapshot()`: Get renders since baseline. - `toHaveRerenderedOnce()`: Assert exactly 1 rerender. - `toNotHaveRerendered()`: Assert no rerenders. - `toHaveRerendered()`: Assert at least 1 rerender (v1.11.0). - `toHaveRerendered(n)`: Assert exact N rerenders (v1.11.0). - `toEventuallyRerender()`: Wait for async rerender (v1.11.0). - `toEventuallyRerenderTimes(n)`: Wait for exact N async rerenders (v1.11.0). ### Basic Example ```typescript const ProfiledComponent = withProfiler(Counter); render(); ProfiledComponent.snapshot(); fireEvent.click(screen.getByText('Increment')); expect(ProfiledComponent).toHaveRerenderedOnce(); ``` ``` -------------------------------- ### snapshot() Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Snapshot-API Creates a baseline for render counting. All subsequent `getRendersSinceSnapshot()` calls return the number of renders since this baseline. ```APIDOC ## snapshot() ### Description Creates a baseline for render counting. All subsequent `getRendersSinceSnapshot()` calls return the number of renders since this baseline. ### Signature ```typescript function snapshot(): void ``` ### Example ```typescript const ProfiledComponent = withProfiler(MyComponent); render(); // Before snapshot: includes initial mount expect(ProfiledComponent.getRendersSinceSnapshot()).toBe(1); ProfiledComponent.snapshot(); // Create baseline // Immediately after snapshot: delta is 0 expect(ProfiledComponent.getRendersSinceSnapshot()).toBe(0); ``` ``` -------------------------------- ### Count Hook Renders on Mount Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Hook-Profiling Test a custom hook to ensure it renders exactly once when initially mounted. This example uses a `useCounter` hook. ```typescript function useCounter(initialValue: number) { const [count, setCount] = useState(initialValue); return { count, increment: () => setCount(c => c + 1) }; } it('should render once on mount', () => { const { ProfiledHook } = profileHook( ({ initial }) => useCounter(initial), { initial: 0 } ); expect(ProfiledHook).toHaveRenderedTimes(1); }); ``` -------------------------------- ### Polling-Based API Example (Deprecated) Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/async/README.md Illustrates the older, less efficient polling-based approach for checking render counts, which is now superseded by the event-based API. ```typescript // ❌ Inefficient polling await waitFor(() => { expect(component.getRenderCount()).toBe(2); }); ``` -------------------------------- ### Get Render Phase by Index Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Use getRenderAt with a 0-based index to retrieve the render phase at a specific point in the component's history. ```typescript const firstRender = ProfiledComponent.getRenderAt(0); // "mount" ``` -------------------------------- ### Handle Search Updates with startTransition Source: https://github.com/greydragon888/vitest-react-profiler/wiki/React-18-Concurrent-Features Use `startTransition` to defer non-urgent state updates like search results, ensuring the UI remains responsive to urgent updates like input changes. ```typescript const handleSearch = (value: string) => { setInput(value); // Urgent: show in input immediately startTransition(() => { setResults(searchData(value)); // Non-urgent: can wait }); }; ``` -------------------------------- ### Correct Memo Wrapper for Profiled Component Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Best-Practices Demonstrates the correct way to wrap a component with `memo` and `withProfiler` to ensure `memo` is not bypassed. The incorrect approach is shown for contrast. ```typescript // ❌ Wrong - Profiler bypasses memo const Profiled = withProfiler(memo(Component)); const { rerender } = render(); rerender(); expect(Profiled).toHaveRenderedTimes(1); // ❌ Fails! // ✅ Correct - Additional memo wrapper const Profiled = withProfiler(memo(Component)); const MemoProfiled = memo(Profiled); const { rerender } = render(); rerender(); expect(Profiled).toHaveRenderedTimes(1); // ✅ Passes! ``` -------------------------------- ### Profile a Hook (Basic Usage) Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Use `profileHook` to detect extra renders in a React hook. This basic example profiles a hook without parameters. ```typescript // Basic usage const { result, ProfiledHook } = profileHook(() => useMyHook()); expect(ProfiledHook).toHaveRenderedTimes(1); ``` -------------------------------- ### Run ESLint Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CLAUDE.md Lints the project code using ESLint to enforce code style and quality. ```bash npm run lint ``` -------------------------------- ### Async Operation Performance Tracking Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/basic/README.md Track performance during asynchronous operations, such as data fetching. This example waits for data to load and checks the render phases. ```tsx const ProfiledProfile = withProfiler(UserProfile, "UserProfile"); render(); // Wait for async data await waitFor(() => { expect(screen.queryByText("Loading...")).not.toBeInTheDocument(); }); // Check render phases const renders = ProfiledProfile.getRenderHistory(); expect(renders[0]?.phase).toBe("mount"); // Initial mount expect(renders[1]?.phase).toBe("update"); // After data loaded ``` -------------------------------- ### Run Tests Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CONTRIBUTING.md Execute tests using pnpm. Use 'pnpm test' for a single run or 'pnpm dev' for watch mode. 'pnpm test:ui' opens the Vitest UI. ```bash pnpm test # Run tests once pnpm dev # Run tests in watch mode pnpm test:ui # Open Vitest UI ``` -------------------------------- ### Add vitest-react-profiler to devDependencies Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/SECURITY.md Ensure vitest-react-profiler is listed in your project's devDependencies to manage it as a development tool. This example shows how to add it using npm. ```json { "devDependencies": { "vitest-react-profiler": "^1.7.0" } } ``` -------------------------------- ### Testing useTransition Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/concurrent/README.md Example of testing a component using useTransition with vitest-react-profiler. It involves simulating user input and waiting for the transition to complete before asserting on render counts. ```typescript const ProfiledComponent = withProfiler(TransitionExample); render(); fireEvent.change(input, { target: { value: 'test' } }); // Wait for transition to settle await waitFor(() => { expect(screen.getByTestId('status')).toHaveTextContent('Ready'); }); // Profiler tracked all renders automatically expect(ProfiledComponent.getRenderCount()).toBeGreaterThanOrEqual(2); ``` -------------------------------- ### Run Tests with UI for Visual Debugging Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/performance/README.md Launches the test runner with a user interface for visual debugging, using npm run test:ui. ```bash # Run with UI for visual debugging npm run test:ui ``` -------------------------------- ### Testing useDeferredValue Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/concurrent/README.md Example of testing a component using useDeferredValue. It simulates input changes and asserts that the deferred value eventually updates, while checking the total render count. ```typescript const ProfiledComponent = withProfiler(DeferredExample); render(); fireEvent.change(input, { target: { value: 'test' } }); // Deferred value updates later await waitFor(() => { expect(screen.getByTestId('deferred-value')).toHaveTextContent('test'); }); // Profiler tracked: mount + input + deferred update expect(ProfiledComponent.getRenderCount()).toBeGreaterThanOrEqual(2); ``` -------------------------------- ### Handle Tab Switching with startTransition Source: https://github.com/greydragon888/vitest-react-profiler/wiki/React-18-Concurrent-Features Defer loading tab content using `startTransition` to keep the tab switching interaction smooth, while immediately updating the active tab. ```typescript const handleTabChange = (tabId: string) => { setActiveTab(tabId); // Urgent: highlight tab startTransition(() => { setTabContent(loadTabContent(tabId)); // Non-urgent: load content }); }; ``` -------------------------------- ### Create Hook Profiler with Context Provider Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Profile hooks that require context providers by passing them in the `renderOptions`. This allows testing hooks within specific environmental setups. ```typescript const profiler = createHookProfiler( () => useTheme(), { renderOptions: { wrapper: ThemeProvider } } ); profiler.expectRenderCount(1); expect(profiler.result.current.theme).toBe('light'); ``` -------------------------------- ### Profile Router Hook with Mock Provider Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Hook-Profiling Demonstrates profiling a router hook by providing a mock router context using the `wrapper` option. ```typescript // Mock router provider for tests const MockRouterProvider = ({ children }: { children: React.ReactNode }) => ( {} }}> {children} ); // Profile the router hook const { result, ProfiledHook } = profileHook( () => useRouter(), { renderOptions: { wrapper: MockRouterProvider } } ); expect(ProfiledHook).toHaveRenderedTimes(1); expect(result.current.params.userId).toBe('123'); ``` -------------------------------- ### Code Formatting and Linting Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CONTRIBUTING.md Ensure code quality by running formatting and linting commands. Run 'pnpm format' before committing and 'pnpm lint' to check for issues. ```bash pnpm format pnpm lint ``` -------------------------------- ### Get Renders Since Snapshot Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/snapshot/README.md Returns the number of renders that have occurred since the last `snapshot()` call. This is useful for verifying that specific actions trigger a predictable number of rerenders. ```tsx import { withProfiler } from 'vitest-react-profiler'; import { render, rerender } from '@testing-library/react'; const MyComponent = ({ value }) =>
{value}
; const ProfiledComponent = withProfiler(MyComponent); render(); ProfiledComponent.snapshot(); rerender(); rerender(); expect(ProfiledComponent.getRendersSinceSnapshot()).toBe(2); ``` -------------------------------- ### Run Coverage Report Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CLAUDE.md Generates a code coverage report for the project. ```bash npm run test:coverage ``` -------------------------------- ### toHaveMountedOnce() Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Asserts that a component has been mounted exactly one time. This is crucial for verifying correct initialization and preventing unintended remounts. ```APIDOC ## `toHaveMountedOnce()` ### Description Asserts that component mounted exactly once. ### Method `expect(ProfiledComponent).toHaveMountedOnce()` ### Example ```typescript expect(ProfiledComponent).toHaveMountedOnce(); ``` ``` -------------------------------- ### React Batches Updates Example Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/concurrent/README.md Illustrates how React may batch multiple state updates into fewer renders, especially in concurrent mode. Use `toBeGreaterThanOrEqual` for assertions on render counts. ```typescript // 3 rapid updates... fireEvent.change(input, { target: { value: "a" } }); fireEvent.change(input, { target: { value: "ab" } }); fireEvent.change(input, { target: { value: "abc" } }); // ...may produce 2-3 renders (React optimizes) expect(component.getRenderCount()).toBeGreaterThanOrEqual(2); ``` -------------------------------- ### Track Renders with Subscription Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Examples Demonstrates how to subscribe to render events using `onRender` to track component renders in real-time. The callback receives render information, and `unsubscribe` should be called to clean up the listener. ```typescript it('should track renders with subscription', () => { const ProfiledCounter = withProfiler(Counter); const { getByText } = render(); const renders: RenderEventInfo[] = []; const unsubscribe = ProfiledCounter.onRender((info) => { renders.push(info); console.log(`Render #${info.count}: ${info.phase}`); }); fireEvent.click(getByText("Increment")); fireEvent.click(getByText("Increment")); expect(renders).toHaveLength(2); expect(renders[0]!.phase).toBe("update"); expect(renders[1]!.phase).toBe("update"); unsubscribe(); }); ``` -------------------------------- ### useDeferredValue Example Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/concurrent/README.md Demonstrates using useDeferredValue to defer rendering expensive updates, improving performance for components like large lists or visualizations. The deferred value updates later than the input. ```typescript const [input, setInput] = useState(''); const deferredInput = useDeferredValue(input); // Expensive component receives deferred value ``` -------------------------------- ### Run vitest-react-profiler Tests Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/basic/README.md Commands to execute tests using vitest-react-profiler. Includes options for UI debugging, coverage, and watch mode. ```bash # Run all tests npm test # Run with UI for visual debugging npm run test:ui # Run with coverage npm run test:coverage # Watch mode for development npm run test:watch ``` -------------------------------- ### Test Batched State Updates Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Best-Practices Verify that multiple state updates are batched into a single render. This example uses `fireEvent` to trigger updates and `toHaveRenderedTimes` to assert the expected render count. ```typescript it('should batch state updates', () => { const Profiled = withProfiler(MultiStateComponent); const { getByText } = render(); // Trigger multiple state updates fireEvent.click(getByText("Update All")); // React should batch them into 1 render expect(Profiled).toHaveRenderedTimes(2); // mount + batched update }); ``` -------------------------------- ### Track Initial Render Performance Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/performance/README.md Measures the initial mount and update phases of a component. Ensures the render completes within a specified time limit. ```typescript // Track initial render performance it("should track initial render performance"); // Measures mount and update phases separately // Validates render completes within 1000ms ``` -------------------------------- ### Integrate with React Testing Library Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Advanced-Usage Use vitest-react-profiler seamlessly within your React Testing Library tests. This example shows how to profile a component and assert its render count after user interactions. ```typescript import { render, screen, fireEvent } from '@testing-library/react'; import { withProfiler } from 'vitest-react-profiler'; it('should integrate with RTL', () => { const Profiled = withProfiler(Counter); render(); const button = screen.getByRole('button'); fireEvent.click(button); expect(Profiled).toHaveRenderedTimes(2); expect(screen.getByText('1')).toBeInTheDocument(); }); ``` -------------------------------- ### Compare Virtualized vs Non-Virtualized List Performance Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/performance/README.md Compares the render performance of virtualized and non-virtualized lists for a large number of items. Expects virtualized lists to be faster. ```typescript // Virtualization comparison it("should compare virtualized vs non-virtualized list performance"); // 1000 items // Expects virtualized < non-virtualized render time ``` -------------------------------- ### Wait for Async State Update Source: https://github.com/greydragon888/vitest-react-profiler/wiki/Examples This example shows how to wait for an asynchronous state update in a component using `toEventuallyRenderTimes`. It asserts that the component renders at least twice (initial mount and after async update). ```typescript const AsyncComponent = () => { const [data, setData] = useState(null); useEffect(() => { setTimeout(() => { setData({ result: "success" }); }, 100); }, []); return
{data?.result || "Loading..."}
; }; it('should wait for async state update', async () => { const Profiled = withProfiler(AsyncComponent); render(); // Wait for mount + async update await expect(Profiled).toEventuallyRenderTimes(2); expect(Profiled).toHaveMountedOnce(); }); ``` -------------------------------- ### Fixing a Render Loop with Dependency Array Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference This example demonstrates how to fix an infinite render loop in a React component by correctly adding a dependency array to the `useEffect` hook. The `notToHaveRenderLoops` matcher can be used to detect such issues. ```jsx function Counter() { const [count, setCount] = useState(0); useEffect(() => { setCount(c => c + 1); }, []); // ✅ Added dependency array - loop fixed! return
{count}
; } ``` -------------------------------- ### Run Vitest Tests Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/examples/memoization/README.md Commands to run tests with vitest, including UI mode, coverage, and watch mode. ```bash # Run all tests npm test # Run with UI npm run test:ui # Run with coverage npm run test:coverage # Watch mode for development npm run test:watch ``` -------------------------------- ### Use notToHaveRenderLoops with custom thresholds and component name Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference Configure `notToHaveRenderLoops` with custom thresholds for stricter loop detection and provide a component name for clearer error messages. This example sets the maximum consecutive updates to 3. ```typescript expect(ProfiledComponent).notToHaveRenderLoops({ maxConsecutiveUpdates: 3, componentName: 'Counter' }); ``` -------------------------------- ### Real-world diagnostic workflow: Adding notToHaveRenderLoops to diagnose a hanging test Source: https://github.com/greydragon888/vitest-react-profiler/wiki/API-Reference This workflow shows how to use `notToHaveRenderLoops` to debug a test that hangs during rendering. By adding the matcher, you can get diagnostic information pinpointing the cause of the infinite loop, such as an `useEffect` issue. ```typescript // Step 1: Test hangs - you don't know why it('should update counter', () => { render(); // Test hangs here... ❓ }); // Step 2: Add diagnostic matcher it('should update counter', () => { const Profiled = withProfiler(Counter); render(); // Add this to diagnose the hang expect(Profiled).notToHaveRenderLoops({ maxConsecutiveUpdates: 5 }); // Now you get: "Suspicious pattern: 11 consecutive 'update' phases" // → Points to useEffect infinite loop }); // Step 3: Fix the bug ``` -------------------------------- ### Verify File Extensions Before Reading Configs Source: https://github.com/greydragon888/vitest-react-profiler/blob/master/CLAUDE.md Always check actual file extensions using `ls` before attempting to read or modify configuration files. This prevents errors caused by assuming incorrect extensions. ```bash # ✅ CORRECT: Check first ls vitest.config.* # ❌ WRONG: Assume extension Read vitest.config.ts ```