Skip to content

Commit e98bb92

Browse files
committed
test(test-app): add a perf lab with seeded React render problems
1 parent 562e798 commit e98bb92

4 files changed

Lines changed: 380 additions & 0 deletions

File tree

‎examples/test-app/README.md‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ It is intentionally small, but each surface is dense with durable accessibility
1818
- `Settings`: switch rows, accordion content, loading and error states, retry flow, destructive-confirm modal
1919
- `Automation lab`: long-press, alert-result, app-event, app-state, appearance, orientation, permission-recovery, log canaries, a flattened (`accessible={true}`) text input, and an Apple Pay sheet hosted in `com.apple.PassbookUIService`
2020
- `WebView accessibility`: a deterministic semantic fixture plus live websites with varied HTML for native accessibility snapshot verification
21+
- `Perf lab`: seeded JavaScript render problems for `react-devtools profile`, one per scenario (see [Perf lab](#perf-lab))
2122

2223
Navigation uses Expo Router native bottom tabs, so the tab bar itself is also part of the test surface.
2324

@@ -234,6 +235,32 @@ installed development build is connected to that server. For Android local
234235
device/emulator runs, also run `adb reverse tcp:8082 tcp:8082` when the device
235236
needs host port forwarding.
236237

238+
## Perf lab
239+
240+
`agent-device-test-app://perf-lab?scenario=<id>` opens one seeded render problem. **Run** (`perf-run`) replays the slow interaction as a fixed burst and sets `perf-status` to `done`. Profile it with:
241+
242+
```bash
243+
agent-device open "agent-device-test-app://perf-lab?scenario=memo-defeated" --platform android
244+
agent-device react-devtools profile start
245+
agent-device click 'id="perf-run"'
246+
agent-device wait 'id="perf-status" text="done"' 10000
247+
agent-device react-devtools profile stop
248+
```
249+
250+
`profile stop` should report this finding first:
251+
252+
| Scenario | Seeded problem | Expected finding |
253+
| --- | --- | --- |
254+
| `expensive-render` | `PriceHistoryChart` recomputes its trend on every render | `slow-render PriceHistoryChart` |
255+
| `memo-defeated` | `ContactList` passes a new callback and style object to memoized rows | `memo-defeated ContactRow` |
256+
| `context-cascade` | `SessionProvider` builds a new context value on every update | `context-cascade AvatarBadge` |
257+
| `parent-cascade` | Typing re-renders every unmemoized result row | `parent-cascade ResultRow` |
258+
| `render-storm` | `LiveClock` sets state every 16 ms | `render-storm LiveClock` |
259+
| `effect-chain` | `FilteredContacts` copies derived state in an effect | `effect-chain FilteredContacts` |
260+
| `eager-list` | 1,200 tiles mount in one commit instead of a virtualized list | `eager-mount ProductTile` |
261+
262+
`src/screens/PerfLabScreen.tsx` imports only React and React Native, so it can be copied into a bare React Native app to check the profiler on other React Native versions.
263+
237264
## Local Agent Device suites
238265

239266
The repo includes two local suites for iterating on the fixture app:

‎examples/test-app/app/_layout.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ function RootLayoutContent() {
2929
<Stack.Screen name="snapshot-depth" />
3030
<Stack.Screen name="deep-tree" />
3131
<Stack.Screen name="ticker" />
32+
<Stack.Screen name="perf-lab" />
3233
<Stack.Screen name="product/[productId]" />
3334
<Stack.Screen name="webview" />
3435
</Stack>

‎examples/test-app/app/perf-lab.tsx‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { useLocalSearchParams } from 'expo-router';
2+
3+
import { isPerfScenario, PerfLabScreen } from '../src/screens/PerfLabScreen';
4+
5+
export default function PerfLabRoute() {
6+
const { scenario } = useLocalSearchParams<{ scenario?: string }>();
7+
const initialScenario = isPerfScenario(scenario) ? scenario : undefined;
8+
return <PerfLabScreen initialScenario={initialScenario} key={initialScenario} />;
9+
}

0 commit comments

Comments
 (0)