Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions .github/workflows/perf-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Perf Tests

# Triggers on PRs touching source that could affect perf,
# and on pushes to main/stable for baseline updates.
on:
pull_request:
branches: [main, '*-stable']
paths:
- 'vnext/**'
- 'packages/**'
- 'vnext/Scripts/perf/**'
- '.github/workflows/perf-tests.yml'
push:
branches: [main]
paths:
- 'packages/e2e-test-app-fabric/test/__perf__/**'

# Allow manual trigger for debugging
workflow_dispatch:

# Cancel in-progress runs for the same PR
concurrency:
group: perf-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true

jobs:
perf-tests:
name: Component Performance Tests
runs-on: windows-latest
timeout-minutes: 30

permissions:
contents: read
actions: read

steps:
# ── Setup ──────────────────────────────────────────────
- name: Checkout head (PR)
uses: actions/checkout@v4
with:
fetch-depth: 0 # Need history for baseline comparison

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: yarn

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Build perf-testing package
run: yarn workspace @react-native-windows/perf-testing build

# ── Run Tests ──────────────────────────────────────────
- name: Run perf tests
id: perf-run
working-directory: packages/e2e-test-app-fabric
env:
CI: 'true'
RN_TARGET_PLATFORM: windows
run: yarn perf:ci
continue-on-error: true # Don't fail here — let comparison decide

# ── Compare & Report ───────────────────────────────────
- name: Compare against baselines
id: compare
working-directory: packages/e2e-test-app-fabric
run: yarn perf:ci:compare
continue-on-error: true

- name: Save PR number
if: github.event_name == 'pull_request'
run: echo "${{ github.event.pull_request.number }}" > packages/e2e-test-app-fabric/.perf-results/pr-number.txt

- name: Upload perf results
if: always()
uses: actions/upload-artifact@v4
with:
name: perf-results
path: |
packages/e2e-test-app-fabric/.perf-results/
packages/e2e-test-app-fabric/test/__perf__/**/__perf_snapshots__/
retention-days: 30

# ── Status Gate ────────────────────────────────────────
- name: Check for regressions
if: steps.compare.outcome == 'failure'
run: |
echo "::error::Performance regressions detected. See PR comment for details."
exit 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Performance tests for react native windows(Fabric)",
"packageName": "@react-native-windows/perf-testing",
"email": "74712637+iamAbhi-916@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Performance tests for react native windows(Fabric)",
"packageName": "react-native-windows",
"email": "74712637+iamAbhi-916@users.noreply.github.com",
"dependentChangeType": "patch"
}
13 changes: 13 additions & 0 deletions packages/@react-native-windows/perf-testing/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*
* @format
*/

module.exports = {
extends: ['@rnw-scripts/eslint-config'],
parserOptions: {
tsconfigRootDir: __dirname,
},
};
3 changes: 3 additions & 0 deletions packages/@react-native-windows/perf-testing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lib/
lib-commonjs/
*.tsbuildinfo
155 changes: 155 additions & 0 deletions packages/@react-native-windows/perf-testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# @react-native-windows/perf-testing

Performance testing utilities for React Native Windows components.

## Overview

This package provides infrastructure for measuring and tracking performance of React Native components. It enables:

- **Automated performance regression detection** on every PR
- **Component-wise performance baselines** (mount, unmount, re-render times)
- **Lightweight perf snapshots** similar to Jest snapshots
- **Easy extensibility** for new components via base classes

## Installation

```bash
npm install @react-native-windows/perf-testing --save-dev
# or
yarn add @react-native-windows/perf-testing --dev
```

## Quick Start

### 1. Create a Performance Test

```typescript
import { ComponentPerfTestBase, IScenario } from '@react-native-windows/perf-testing';
import { View } from 'react-native';

class ViewPerfTest extends ComponentPerfTestBase {
readonly componentName = 'View';
readonly category = 'core' as const;
readonly testId = 'perf-test-view';

createComponent(props?: Record<string, unknown>) {
return <View testID={this.testId} {...props} />;
}

getCustomScenarios(): IScenario[] {
return [
{
name: 'nested-views',
description: 'Test nested views performance',
run: () => this.measureNestedViews(50),
},
];
}
}
```

### 2. Write Tests

```typescript
const viewPerfTest = new ViewPerfTest();

describe('View Performance', () => {
test('mount time', async () => {
const perf = await viewPerfTest.measureMount();
expect(perf).toMatchPerfSnapshot();
});

test('rerender time', async () => {
const perf = await viewPerfTest.measureRerender();
expect(perf).toMatchPerfSnapshot();
});
});
```

### 3. Run Tests

```bash
# Run perf tests
yarn perf

# Update baselines
yarn perf:update
```

## API Reference

### Core Functions

| Function | Description |
|----------|-------------|
| `measurePerf(component, options)` | Core measurement function |
| `toMatchPerfSnapshot(threshold?)` | Jest matcher for perf snapshots |

### Base Classes

| Class | Description |
|-------|-------------|
| `ComponentPerfTestBase` | Abstract base class for component perf tests |

### Interfaces

| Interface | Description |
|-----------|-------------|
| `PerfMetrics` | Measurement results |
| `PerfThreshold` | Pass/fail thresholds |
| `IComponentPerfTest` | Contract for component tests |
| `IScenario` | Custom scenario interface |

### Threshold Presets

```typescript
import { ThresholdPresets } from '@react-native-windows/perf-testing';

// Available presets
ThresholdPresets.core // 10% regression threshold
ThresholdPresets.list // 15% for list components
ThresholdPresets.interactive // 20% for animated components
ThresholdPresets.community // 25% relaxed threshold for external use
```

## Extending for Custom Components

Community developers can create perf tests for their custom native components:

```typescript
import { ComponentPerfTestBase } from '@react-native-windows/perf-testing';
import { MyCustomSlider } from 'my-custom-component';

class MyCustomSliderPerfTest extends ComponentPerfTestBase {
readonly componentName = 'MyCustomSlider';
readonly category = 'custom' as const;
readonly testId = 'perf-test-slider';

createComponent(props?: Record<string, unknown>) {
return <MyCustomSlider testID={this.testId} {...props} />;
}
}
```

## Configuration

### Jest Setup

Add to your Jest setup file:

```typescript
import '@react-native-windows/perf-testing/matchers';
```

### Custom Thresholds

```typescript
expect(perf).toMatchPerfSnapshot({
maxDurationIncrease: 15, // Allow 15% regression
maxRenderCount: 5,
});
```

## License

MIT
9 changes: 9 additions & 0 deletions packages/@react-native-windows/perf-testing/just-task.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*
* @format
* @ts-check
*/

require('@rnw-scripts/just-task');
67 changes: 67 additions & 0 deletions packages/@react-native-windows/perf-testing/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"name": "@react-native-windows/perf-testing",
"version": "0.0.0-canary.1031",
"description": "Performance testing utilities for React Native Windows components",
"main": "lib-commonjs/index.js",
"types": "lib-commonjs/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/react-native-windows",
"directory": "packages/@react-native-windows/perf-testing"
},
"license": "MIT",
"private": false,
"scripts": {
"build": "rnw-scripts build",
"clean": "rnw-scripts clean",
"lint": "rnw-scripts lint",
"lint:fix": "rnw-scripts lint:fix",
"watch": "rnw-scripts watch"
},
"dependencies": {
"@react-native-windows/fs": "0.82.0-preview.1"
},
"devDependencies": {
"@rnw-scripts/eslint-config": "1.2.38",
"@rnw-scripts/just-task": "2.3.58",
"@rnw-scripts/ts-config": "2.0.6",
"@types/jest": "^29.2.2",
"@types/node": "^22.14.0",
"@types/react": "^19.0.0",
"@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1",
"eslint": "^8.19.0",
"prettier": "2.8.8",
"typescript": "5.0.4"
},
"peerDependencies": {
"jest": ">=29.0.3",
"react": ">=18.0.0",
"react-native": ">=0.72.0",
"react-test-renderer": ">=18.0.0"
},
"peerDependenciesMeta": {
"react-test-renderer": {
"optional": true
}
},
"files": [
"lib-commonjs",
"README.md"
],
"beachball": {
"defaultNpmTag": "preview",
"disallowedChangeTypes": [
"major",
"minor",
"patch",
"premajor",
"preminor",
"prepatch"
]
},
"promoteRelease": true,
"engines": {
"node": ">= 22"
}
}
Loading
Loading