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
47 changes: 32 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -788,32 +788,42 @@ Core workflow:

## Integrations

### iOS Simulator

Control real Mobile Safari in the iOS Simulator for authentic mobile web testing. Requires macOS with Xcode.

**Setup:**

```bash
# Install Appium and XCUITest driver
npm install -g appium
appium driver install xcuitest
```
### iOS Safari (via Appium)

agent-browser supports iOS Safari automation via Appium and the XCUITest driver.

**Prerequisites:**
1. macOS with Xcode installed
2. Install Appium and XCUITest driver:
```bash
npm install -g appium
appium driver install xcuitest
```
3. Build WebDriverAgent for simulator:
```bash
cd ~/.appium/node_modules/appium-xcuitest-driver/node_modules/appium-webdriveragent
xcodebuild build-for-testing -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -sdk iphonesimulator -derivedDataPath ./DerivedData CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
```

**Usage:**

```bash
# List available iOS simulators
agent-browser device list
# List available devices
agent-browser -p ios device list

# Launch Safari on a specific device
# Open a URL on iOS Simulator
agent-browser -p ios --device "iPhone 16 Pro" open https://example.com

# Take a screenshot
agent-browser -p ios screenshot ./ios-screenshot.png

# Run JavaScript
agent-browser -p ios eval "navigator.userAgent"

# Same commands as desktop
agent-browser -p ios snapshot -i
agent-browser -p ios tap @e1
agent-browser -p ios fill @e2 "text"
agent-browser -p ios screenshot mobile.png

# Mobile-specific commands
agent-browser -p ios swipe up
Expand All @@ -823,6 +833,8 @@ agent-browser -p ios swipe down 500
agent-browser -p ios close
```

**Supported commands:** navigate, click, type, fill, screenshot, eval, scroll, swipe, wait, and more (27+ commands).

Or use environment variables:

```bash
Expand All @@ -841,6 +853,11 @@ agent-browser open https://example.com

**Note:** The iOS provider boots the simulator, starts Appium, and controls Safari. First launch takes ~30-60 seconds; subsequent commands are fast.

**Limitations:**
- Tab/window management not supported (Safari mobile limitation)
- No PDF generation
- No screencast/video recording (requires CDP)

#### Real Device Support

Appium also supports real iOS devices connected via USB. This requires additional one-time setup:
Expand Down
1 change: 0 additions & 1 deletion src/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,6 @@ export async function startDaemon(options?: {
) {
if (isIOS && manager instanceof IOSManager) {
// Auto-launch iOS Safari
// Check for device in command first (for reused daemons), then fall back to env vars
const cmd = parseResult.command as { iosDevice?: string };
const iosDevice = cmd.iosDevice || process.env.AGENT_BROWSER_IOS_DEVICE;
await manager.launch({
Expand Down
1 change: 1 addition & 0 deletions src/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const navigateSchema = baseCommandSchema.extend({
url: z.string().min(1),
waitUntil: z.enum(['load', 'domcontentloaded', 'networkidle']).optional(),
headers: z.record(z.string()).optional(),
iosDevice: z.string().optional(),
});

const clickSchema = baseCommandSchema.extend({
Expand Down
47 changes: 47 additions & 0 deletions test/ios-manager.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describe, it, expect } from 'vitest';
import { parseCommand } from '../src/protocol.js';
import { IOSManager } from '../src/ios-manager.js';

// Helper to create command JSON string
const cmd = (obj: object) => JSON.stringify(obj);

describe('iOS provider', () => {
describe('IOSManager instantiation', () => {
it('should be instantiable', () => {
const manager = new IOSManager();
expect(manager).toBeDefined();
expect(manager).toBeInstanceOf(IOSManager);
});
});

describe('iosDevice schema parsing', () => {
it('should preserve iosDevice in navigate command', () => {
const result = parseCommand(
cmd({
id: 'test-1',
action: 'navigate',
url: 'https://example.com',
iosDevice: 'iPhone 16 Pro',
})
);
expect(result.success).toBe(true);
if (result.success) {
expect((result.command as any).iosDevice).toBe('iPhone 16 Pro');
}
});

it('should accept navigate without iosDevice', () => {
const result = parseCommand(
cmd({
id: 'test-2',
action: 'navigate',
url: 'https://example.com',
})
);
expect(result.success).toBe(true);
if (result.success) {
expect((result.command as any).iosDevice).toBeUndefined();
}
});
});
});