You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -390,6 +391,120 @@ All timing and numeric constants can be overridden via environment variables. Th
390
391
|----------|---------|-------------|
391
392
|`IPHONE_KEYBOARD_LAYOUT`|*(not set)*| Opt-in non-US keyboard layout for character translation (e.g., `Canadian-CSA` or `com.apple.keylayout.Canadian-CSA`). When unset, US QWERTY keycodes are sent. |
392
393
394
+
## FakeMirroring
395
+
396
+
FakeMirroring is a real macOS app that stands in for iPhone Mirroring during testing. It renders OCR-detectable text at known positions, responds to CGEvent taps, and supports scrolling — everything the real mirroring window does, without needing a physical iPhone.
397
+
398
+
### Build & Run
399
+
400
+
```bash
401
+
swift build -c release --product FakeMirroring
402
+
./scripts/package-fake-app.sh
403
+
open .build/release/FakeMirroring.app
404
+
```
405
+
406
+
The app window is 410x898pt (matching iPhone screen dimensions) and floats above other windows so CGEvent taps always land on it.
407
+
408
+
### Scenarios
409
+
410
+
FakeMirroring renders different screen layouts via **scenarios**. Switch scenarios from the Scenario menu or programmatically via `bridge.triggerMenuAction(menu: "Scenario", item: "Settings")`.
411
+
412
+
Key scenarios:
413
+
414
+
| Scenario | Content | Navigation |
415
+
|----------|---------|------------|
416
+
|`settings`| 6 rows with chevrons (General, Privacy, etc.) | General → detail, Notifications → notifications |
417
+
|`detailWithBack`| Detail screen with `<` back button |`<` → back to source |
`NavigationMap.destination(from: scenario, tapping: label)` defines what happens when a label is tapped. Returns the target `FakeScenario` for navigation, or `nil` if the tap is a dead tap (no screen change). The BFS explorer uses this to discover new screens during integration tests.
434
+
435
+
### Input Handling
436
+
437
+
FakeMirroring handles: mouse clicks (tap), `scrollWheel` (swipe/scroll), `mouseDragged` (drag), long press (0.4s threshold), double tap (0.3s gap), and `keyDown` (text field typing). Hit regions are computed from rendered element positions. The `AlwaysAcceptingWindow` subclass accepts mouse events even when not the key window, so CGEvent-posted taps work during integration tests.
438
+
439
+
## Integration Tests
440
+
441
+
Integration tests in `Tests/IntegrationTests/` run real OCR against FakeMirroring's rendered text. They exercise the full pipeline: OCR → coordinate mapping → CGEvent tap → verify screen change.
442
+
443
+
### Running
444
+
445
+
```bash
446
+
# Build and launch FakeMirroring first
447
+
swift build -c release --product FakeMirroring
448
+
./scripts/package-fake-app.sh
449
+
open .build/release/FakeMirroring.app
450
+
451
+
# Run integration tests (FakeMirroring must be visible)
452
+
swift test --filter IntegrationTests
453
+
454
+
# Run a single test
455
+
swift test --filter BFSExplorationIntegrationTests/testMultiViewportExploration
456
+
```
457
+
458
+
Integration tests are **skipped in CI** (`swift test --skip IntegrationTests`) because they require a visible macOS window and CGEvent access. They run locally before merging.
Tests create `ScreenDescriber`, `InputSimulation`, and `ExplorationSession` directly — no MCP transport needed.
486
+
487
+
### Testing BFS Exploration
488
+
489
+
Two approaches, use the right one:
490
+
491
+
| Approach | When to use | Speed |
492
+
|----------|-------------|-------|
493
+
|**Unit tests** (`MockExplorerDescriber`) | Testing scroll logic, plan building, action counters, specific code paths | Fast (~2s) |
494
+
|**Integration tests** (FakeMirroring) | Testing full exploration loop with real OCR, tap routing, backtracking | Slow (~2min) |
495
+
496
+
Unit test mocks return a pre-defined sequence of screens. Integration tests use real OCR output that varies slightly between runs. Use `seed: 42` for deterministic tap ordering in integration tests.
497
+
498
+
## Component Skills
499
+
500
+
Component definitions are `.md` files that describe iOS UI patterns (table rows, summary cards, modal sheets). The BFS explorer matches OCR elements against these definitions to decide what to tap.
501
+
502
+
Definitions live in the sibling [mirroir-skills](https://github.com/jfarcand/mirroir-skills) repo at `components/ios/`. They're loaded at runtime from `~/.mirroir-mcp/skills/components/ios/` or `<cwd>/.mirroir-mcp/skills/components/ios/` or `../mirroir-skills/components/ios/`.
503
+
504
+
Each definition has: Match Rules (zone, element count, chevron/numeric patterns), Interaction (click target, expected result), Exploration (explorable flag, role, priority), and Grouping (row absorption).
505
+
506
+
Test a definition against the current live screen with `calibrate_component`. See [Component Detection](docs/components.md) for the full format.
0 commit comments