Skip to content
Merged
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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ See the focused guides before making task-specific changes:
- [Coding Style](docs/agents/coding-style.md)
- [Testing](docs/agents/testing.md)
- [Git Workflow](docs/agents/git-workflow.md)
- [Recreating Examples](docs/agents/recreating-examples.md)
- [Instruction Audit](docs/agents/instruction-audit.md)
142 changes: 142 additions & 0 deletions docs/agents/recreating-examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Recreating Example Apps

When an example needs to be refreshed against the latest framework version, follow this workflow: scaffold a fresh app from the official template, then restore the Reassure setup and perf tests from the existing code.

## General Workflow

1. **Capture what must be restored** — before removing anything, note the files that are not generated by the scaffold tool: `src/*.perf.tsx`, `jest-setup.js` / `jest.setup.ts`, `jest.config.*`, `babel.config.*`, `reassure-tests.sh`, `dangerfile.js`, `README.md`.
2. **Remove the old example** — delete the example directory (e.g. `examples/web-vite`).
3. **Scaffold a fresh app** — run the official getting-started command for that framework (see per-example commands below). Use the same template flags as the original.
4. **Rename / move into place** — the scaffold tool creates a subdirectory; move it to `examples/<name>` and ensure `package.json` `"name"` matches the original.
5. **Restore Reassure setup** — follow the setup steps in that example's `README.md`. See the per-example section below for specifics.
6. **Restore perf tests** — copy `src/*.perf.tsx` files back. Adjust imports if the scaffold changed any paths.
7. **Restore `README.md`** — replace the scaffold-generated README with the reassure-setup README that was there before (or update it to match the new template version).
8. **Validate** — from inside the example directory run `yarn install && yarn test && yarn perf-test && yarn typecheck`.

Always use `yarn`, not `npm`, for every install and script invocation.

---

## Per-Example Details

### `web-vite` — React + TypeScript + Vite

**Scaffold command:**
```sh
yarn create vite web-vite --template react-ts
```

**Reassure setup** (after scaffolding):
1. Install dev dependencies: `jest`, `jest-environment-jsdom`, `@types/jest`, `@testing-library/react`, `@testing-library/dom`, `@babel/preset-env`, `@babel/preset-react`, `@babel/preset-typescript`, `@types/babel__preset-env`, `reassure`, `danger`.
2. Restore `jest.config.cjs` and `babel.config.cjs` from the previous version (the scaffold does not generate these).
3. Restore `jest-setup.js` — it calls `configure({ testingLibrary: 'react' })` from `reassure`.
4. Run `yarn reassure init` — this generates `reassure-tests.sh`, `dangerfile.js`, and adds `.reassure` to `.gitignore`.
5. Add `"test": "jest"`, `"perf-test": "reassure"`, and `"typecheck": "tsc --noEmit"` to `package.json` `"scripts"`.

**Key config files to restore:** `jest.config.cjs`, `babel.config.cjs`, `jest-setup.js`.

---

### `web-nextjs` — Next.js

**Scaffold command:**
```sh
yarn create next-app web-nextjs --typescript --eslint --app --src-dir --import-alias "@/*"
```
Accept defaults for any prompts not listed above.

**Reassure setup** (after scaffolding):
1. Install dev dependencies: `jest`, `jest-environment-jsdom`, `@types/jest`, `@testing-library/react`, `@testing-library/dom`, `@testing-library/jest-dom`, `ts-node`, `reassure`, `danger`.
2. Restore `jest.config.ts` from the previous version.
3. Restore `jest.setup.ts` — it calls `configure({ testingLibrary: 'react' })` from `reassure`.
4. Run `yarn reassure init` — generates `reassure-tests.sh`, `dangerfile.js`, `.gitignore` entry.
5. Add `"test": "jest"` and `"perf-test": "reassure"` to `package.json` `"scripts"`.

**Key config files to restore:** `jest.config.ts`, `jest.setup.ts`.

---

### `native-expo` — Expo React Native

**Scaffold command:**
```sh
npx create-expo-app native-expo --template blank-typescript
```

**Reassure setup** (after scaffolding):
1. Install dev dependencies: `@testing-library/react-native`, `@types/jest`, `reassure`, `danger`. (Expo templates already include Jest and a basic preset.)
2. Restore `jest.config.js` from the previous version — it sets `preset: 'react-native'` and points to the setup file.
3. Restore `jest-setup.js` — it calls `configure({ testingLibrary: 'react-native' })` from `reassure`.
4. Run `yarn reassure init` — generates `reassure-tests.sh`, `dangerfile.js`, `.gitignore` entry.
5. Add `"test": "jest"`, `"perf-test": "reassure"`, and `"typecheck": "tsc --noEmit"` to `package.json` `"scripts"` if missing.

**Key config files to restore:** `jest.config.js`, `jest-setup.js`.

---

### `native-cli` — React Native CLI

**Scaffold command:**
```sh
npx @react-native-community/cli init NativeCli --template react-native-template-typescript
```
Then rename the directory to `native-cli` and set `"name"` in `package.json` to `reassure-native-example` (matching the original).

**Reassure setup** (after scaffolding):
1. Install dev dependencies: `@testing-library/react-native`, `@types/jest`, `reassure`, `danger`. (The CLI template already ships Jest via `react-native` preset.)
2. Replace the generated `jest.config.js` with the restored version — it sets `preset: 'react-native'` and points to the setup file.
3. Restore `jest-setup.js` — it calls `configure({ testingLibrary: 'react-native' })` from `reassure`.
4. Run `yarn reassure init` — generates `reassure-tests.sh`, `dangerfile.js`, `.gitignore` entry.
5. Add `"perf-test": "reassure"` and `"typecheck": "tsc --noEmit"` to `package.json` `"scripts"` if missing.

**Key config files to restore:** `jest.config.js`, `jest-setup.js`.

---

## Perf Tests

All examples contain perf tests under `src/`:

- `TestList.perf.tsx` — measures list rendering performance.
- `AsyncComponent.perf.tsx` — measures async component rendering performance.

Copy these files back after scaffolding. Check imports against the new scaffold's file layout and adjust if needed. Run `yarn perf-test` to confirm they execute without errors.

## Avoiding Image Churn

Scaffold tools regenerate image assets from their own templates on every run. These binary files often differ byte-for-byte from the ones already in the repo even when visually identical, producing noisy, meaningless diffs in git.

**Rule: prefer the existing committed images over freshly generated ones.**

After scaffolding, restore the original images by copying them from git before staging anything:

```sh
git checkout HEAD -- <path-to-image-or-directory>
```

Locations to restore per example:

| Example | Images to restore |
|---------|-------------------|
| `native-cli` | `android/app/src/main/res/mipmap-*/ic_launcher.png` and `ic_launcher_round.png` (10 files) |
| `native-expo` | `assets/adaptive-icon.png`, `assets/icon.png`, `assets/splash-icon.png`, `assets/favicon.png` |
| `web-nextjs` | `public/*.svg`, `src/app/favicon.ico` |
| `web-vite` | `public/vite.svg`, `src/assets/react.svg` |

Only commit a regenerated image if the asset itself genuinely changed (e.g. the framework updated its default branding). In that case, note it explicitly in the commit message.

---

## Files Never Generated by Scaffold Tools

These must always be restored manually:

| File | Purpose |
|------|---------|
| `src/*.perf.tsx` | Reassure performance tests |
| `jest-setup.js` / `jest.setup.ts` | Jest setup with `configure()` call |
| `jest.config.*` | Jest configuration (env, preset, setup file) |
| `babel.config.*` | Babel presets for Jest (web examples only) |
| `reassure-tests.sh` | CI script for baseline/branch comparison |
| `dangerfile.js` | Danger config for PR reporting |
| `README.md` | Reassure setup documentation |
4 changes: 4 additions & 0 deletions examples/native-expo/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,9 @@ yarn-error.*
# typescript
*.tsbuildinfo

# generated native folders
/ios
/android

# Reassure
.reassure/
2 changes: 1 addition & 1 deletion examples/native-expo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ This example showcases Reassure setup for React Native Expo app.
* generate `reassure-tests.sh` CI script
* generate `dangerfile.js` config for `danger`
* add `.reasure` entry to `.gitignore`
5. (optional) Add `configure({ testingLibrary: 'react-native' });` to your `jest-setup.js` file
5. (optional) Add `configure({ testingLibrary: 'react-native' });` to your `jest-setup.js` file
14 changes: 5 additions & 9 deletions examples/native-expo/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,17 @@
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"newArchEnabled": true,
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
"backgroundColor": "#E6F4FE",
"foregroundImage": "./assets/android-icon-foreground.png",
"backgroundImage": "./assets/android-icon-background.png",
"monochromeImage": "./assets/android-icon-monochrome.png"
},
"edgeToEdgeEnabled": true
"predictiveBackGestureEnabled": false
},
"web": {
"favicon": "./assets/favicon.png"
Expand Down
Binary file removed examples/native-expo/assets/adaptive-icon.png
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 0 additions & 6 deletions examples/native-expo/babel.config.js

This file was deleted.

3 changes: 3 additions & 0 deletions examples/native-expo/dangerfile.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/* eslint-disable import/no-extraneous-dependencies */
import path from 'path';
import { fileURLToPath } from 'url';
import { dangerReassure } from 'reassure';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

dangerReassure({
inputFilePath: path.join(__dirname, './.reassure/output.md'),
});
2 changes: 1 addition & 1 deletion examples/native-expo/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
preset: 'react-native',
preset: 'jest-expo',
setupFilesAfterEnv: ['./jest-setup.js'],
};
19 changes: 10 additions & 9 deletions examples/native-expo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,22 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"expo": "~53.0.9",
"expo-status-bar": "~2.2.3",
"react": "19.0.0",
"react-native": "0.79.2"
"expo": "~56.0.11",
"expo-status-bar": "~56.0.4",
"react": "19.2.3",
"react-native": "0.85.3"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@react-native/jest-preset": "^0.86.0",
"@testing-library/react-native": "^14.0.0",
"@types/jest": "^30.0.0",
"@types/react": "~19.0.10",
"@types/jest": "29.5.14",
"@types/react": "~19.2.2",
"danger": "^13.0.4",
"jest": "^30.0.2",
"jest": "~29.7.0",
"jest-expo": "^56.0.5",
"reassure": "^1.5.1",
"test-renderer": "1.0.0",
"typescript": "~5.8.3"
"typescript": "~6.0.3"
},
"engines": {
"node": "^22.13.0 || >=24"
Expand Down
2 changes: 1 addition & 1 deletion examples/native-expo/reassure-tests.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
set -e
set -e

BASELINE_BRANCH=${GITHUB_BASE_REF:="main"}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
import { jest, test } from '@jest/globals';
import { fireEvent, screen } from '@testing-library/react-native';
import { measureRenders } from 'reassure';
import { AsyncComponent } from './AsyncComponent';
import { TestComponent } from './TestComponent';

jest.setTimeout(600_000);

test('React Native - Expo - AsyncComponent (10 runs)', async () => {
test('Expo - TestComponent (10 runs)', async () => {
const scenario = async () => {
const button = screen.getByText('Action');

await fireEvent.press(button);
await fireEvent.press(button);
await screen.findByText('Count: 2');
screen.getByText('Count: 2');
};

await measureRenders(<AsyncComponent />, { scenario, runs: 10 });
await measureRenders(<TestComponent />, { scenario, runs: 10 });
});

test('React Native - Expo - AsyncComponent (50 runs)', async () => {
test('Expo - TestComponent (50 runs)', async () => {
const scenario = async () => {
const button = screen.getByText('Action');

await fireEvent.press(button);
await fireEvent.press(button);
await screen.findByText('Count: 2');
screen.getByText('Count: 2');
};

await measureRenders(<AsyncComponent />, { scenario, runs: 50 });
await measureRenders(<TestComponent />, { scenario, runs: 50 });
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import * as React from 'react';
import { View, Text, Pressable } from 'react-native';
import { TestList } from './TestList';

export function AsyncComponent() {
export function TestComponent() {
const [count, setCount] = React.useState(0);


const handlePress = () => {
setTimeout(() => setCount((c) => c + 1), 10);
setCount((c) => c + 1);
};

return (
Expand Down
2 changes: 1 addition & 1 deletion examples/native-expo/src/TestList.perf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import { TestList } from './TestList';

jest.setTimeout(60_000);

test('React Native - Expo - TestList (100 items)', async () => {
test('Expo - TestList (100 items)', async () => {
await measureRenders(<TestList count={100} />, { runs: 10 });
});
3 changes: 2 additions & 1 deletion examples/native-expo/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true
"strict": true,
"types": ["jest"]
}
}
Loading
Loading