> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Get started: iOS · Vitest · ChatGPT subscription {#get-started}

These are the setup steps for Midscene on **iOS** with **Vitest** and a **ChatGPT subscription**. To choose another combination, see [all setup paths](/get-started.md#all-setup-paths).

## Install Node.js
Install Node.js `^20.19.0 || ^22.12.0 || >=24.0.0`, then check that `node -v` works in your terminal.
The commands below use pnpm. If it is not installed yet, run `npm install -g pnpm`.

## Connect the iOS device through WDA
Midscene communicates with iOS physical devices and simulators through a running WebDriverAgent (WDA) service. If WDA is already running and reachable from your computer, skip directly to [Verify the WDA service status](#verify-wda-status).

### Build and start WebDriverAgent

Before you begin, prepare the following iOS development environment:

- macOS (required for iOS development and device debugging)
- Xcode and Xcode Command Line Tools
- An iOS Simulator or physical iOS device

:::info Version requirement

WebDriverAgent version must be **>= 7.0.0**.

:::

Follow the official Appium documentation to build and start WebDriverAgent (WDA):

- **Simulator setup**: [Run Prebuilt WDA](https://appium.github.io/appium-xcuitest-driver/latest/guides/run-prebuilt-wda/)
- **Physical device setup**: [Real Device Configuration](https://appium.github.io/appium-xcuitest-driver/latest/getting-started/device-setup/)

### Configure a physical device and forward the port

iOS Simulators expose WDA directly on `localhost` and do not require port forwarding—if you are using a simulator or a remote WDA service, skip to [Verify the WDA service status](#verify-wda-status).

When using a physical iOS device:

1. Connect the device to your Mac via USB and tap **Trust This Computer** when prompted on the device.
2. Enable **Developer Mode** (**Settings > Privacy & Security > Developer Mode**) and **UI Automation** (**Settings > Developer > Enable UI Automation**) on the device.
3. After signing and starting WDA on the device, use `iproxy` (installable via `brew install libimobiledevice`) to forward the WDA port (`8100` by default) from the device to your Mac:

```bash
iproxy 8100 8100 YOUR_DEVICE_ID
```

Replace `YOUR_DEVICE_ID` with your device's UDID, and keep both WDA and `iproxy` running. When connecting multiple devices or using custom ports, forward each device to a distinct local port and use that port in Playground or your project configuration.

### Verify the WDA service status {#verify-wda-status}

Open the WDA `/status` endpoint in your browser or terminal from the computer running Midscene:

- **Default local URL**: `http://localhost:8100/status` (if using a custom port, remote host, or gateway, use the actual service address)
- **Expected JSON response**:

```json
{
  "value": {
    "build": {
      "version": "10.1.1",
      "time": "Sep 24 2025 18:56:41",
      "productBundleIdentifier": "com.facebook.WebDriverAgentRunner"
    },
    "os": {
      "testmanagerdVersion": 65535,
      "name": "iOS",
      "sdkVersion": "26.0",
      "version": "26.0"
    },
    "device": "iphone",
    "ios": {
      "ip": "10.91.115.63"
    },
    "message": "WebDriverAgent is ready to accept commands",
    "state": "success",
    "ready": true
  },
  "sessionId": "BCAD9603-F714-447C-A9E6-07D58267966B"
}
```

A JSON response containing `"ready": true` confirms that WDA is ready to accept commands. Note the WDA host and port (or gateway base URL) so you can use the same connection settings in Playground and your project.

## Sign in with your ChatGPT subscription
First, run the following command in your terminal:

```bash
pnpm dlx @midscene/cli model siwc login
```

Follow the terminal instructions to sign in to ChatGPT in your browser and authorize plan usage. You can choose a name for your Agent during authorization. Once authorized, the Agent appears on your ChatGPT usage page.

The command saves credentials to `~/.midscene/siwc.json`. Complete this sign-in manually before first use.

Then write these lines to a `.env` file in your project directory. If you are starting without a project, create a directory and run `pnpm init` in it first.

```bash
MIDSCENE_MODEL_AUTH_MODE="siwc"
MIDSCENE_MODEL_NAME="gpt-6-sol"
MIDSCENE_MODEL_FAMILY="gpt-6"
```
Available models depend on your ChatGPT account. For credentials and usage, see [Use a ChatGPT subscription](/chatgpt-subscription.md#usage-and-credentials).

## Set up the project
In the project directory that contains `.env`, verify the model configuration:
```bash
pnpm dlx @midscene/test@latest model verify
```
If the check fails, see [Verify model connectivity](/model-config.md#verify-model-connectivity).

### Install dependencies

```bash
pnpm add -D vitest dotenv @midscene/ios
```

### Configure Vitest
Create `vitest.config.ts`. If your project already uses Vitest for unit tests, you can save this configuration separately as `vitest.device.config.ts` and pass `--config vitest.device.config.ts` when running device tests, leaving your existing test configuration untouched.

```typescript title="vitest.config.ts"
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    include: ['e2e/**/*.test.ts'],
    environment: 'node',
    testTimeout: 90_000,
    hookTimeout: 90_000,
    fileParallelism: false,
    sequence: { concurrent: false },
  },
});
```

Because model calls and device connections take longer than standard unit tests, adjust `testTimeout` and `hookTimeout` to fit your test cases. When running against a single shared device, disable file parallelism (`fileParallelism: false`) and run tests sequentially (`sequence.concurrent: false`), and avoid `test.concurrent` or `describe.concurrent`. For details, see the Vitest documentation on [fileParallelism](https://vitest.dev/config/fileparallelism), [testTimeout](https://vitest.dev/config/testtimeout), and [hookTimeout](https://vitest.dev/config/hooktimeout).

## Write a test

Create `e2e/settings.test.ts`, initializing a new Agent for each test in `beforeEach` and destroying it in `afterEach`:

```typescript title="e2e/settings.test.ts"
import 'dotenv/config';
import { afterEach, beforeEach, test } from 'vitest';
import { agentFromWebDriverAgent } from '@midscene/ios';

let agent: Awaited<ReturnType<typeof agentFromWebDriverAgent>> | undefined;

beforeEach(async () => {
  agent = await agentFromWebDriverAgent({
    wdaHost: 'localhost',
    wdaPort: 8100,
  });
});

afterEach(async () => {
  const currentAgent = agent;
  agent = undefined;
  await currentAgent?.destroy();
});

test('scroll through settings', async () => {
  if (!agent) throw new Error('Agent was not initialized');

  await agent.launch('com.apple.Preferences');
  await agent.aiWaitFor('The Settings app is visible');
  await agent.aiAct('Scroll down one screen');
  await agent.aiAssert('The Settings app is still visible');
});
```
Replace `wdaHost` and `wdaPort` with your verified WebDriverAgent service endpoint. See the [iOS SDK guide](/sdk/ios.md) for connection options.

`dotenv/config` loads model settings from the project's `.env` file (settings in the Playground are not automatically shared with test runs).

When an `aiAssert` check fails, it throws an error, causing Vitest to mark the test as failed before running `afterEach` to clean up the Agent. You can also extract data from the screen with methods like `aiQuery` and pass the result to Vitest's `expect`. For more interaction and extraction methods, see the [Agent API reference](/reference/index.md#interaction-methods).

## Run the test and inspect the report

```bash
pnpm exec vitest run e2e/settings.test.ts
```

Vitest prints the pass or fail status of each test in the terminal, while the Midscene SDK generates an execution report for each Agent under `midscene_run/report/` by default and prints the report path. Open the generated HTML file in a browser to inspect step-by-step screenshots, AI execution details, and assertion results.

Unlike Playwright Test, Vitest integration uses the SDK's built-in report generation directly and does not require a custom reporter. Vitest test results and Midscene Agent reports are generated separately; to merge or further process report files, see [Process report files](/consume-report-file.md).

When the test passes and the report shows each step with screenshots, your model, environment, and project are all working.

## Next steps
- **Explore more examples**: [Showcases](/showcases.md) includes demo videos and full execution reports across platforms.

- **Look up APIs**: [API reference](/reference.md) lists every Agent method and its options.
- **Combine AI calls with code**: [Key APIs](/basics.md#javascript-orchestration) explains when to use `aiAct`, instant actions, and insight APIs.

- **Share a device across tests**: [Lifecycle and exclusive device access](/sdk/vitest.md#lifecycle-and-exclusive-device-access) explains how to create and destroy Agents when tests share one device.
