# macOS apps

> Prepare a Mac for native tests, configure an app and check its controls and task data.

Source: https://rehearsal.dev/retest/docs/platforms/macos

Use a macOS target to test your native desktop app through its accessibility elements. This page prepares the Mac for input and capture, then shows a test using XCTest and the macOS runner of appium-mac2-driver.

> [!NOTE]
> **Requirements**
>
> Use Node.js 24.12 or later on an Apple silicon Mac with Xcode 26.5 build 17F42 and your app's `.app` bundle. Native tests require this pinned Xcode build.

## Prepare the Mac

Build the native executor first, then prepare the Mac for unattended input. The executor needs clean pinned checkouts of appium-mac2-driver and WebDriverAgent, including the licence files Retest checks.

```sh
npx retest install mac2
```

The installer builds existing sources and downloads nothing. If a checkout is missing or at the wrong commit, follow the paths and commands in its failure message, then run the command again.

Automation Mode must work without an administrator answering a prompt. On a Mac dedicated to unattended tests, enable that setting once:

```sh
sudo automationmodetool enable-automationmode-without-authentication
```

For screenshots, grant Screen Recording to the terminal or agent that runs Retest in System Settings, Privacy & Security, Screen & System Audio Recording. That app may need a relaunch. A rebuilt macOS executor also needs its permissions granted again.

> [!WARNING]
> **Desktop input**
>
> Native tests use the Mac's interactive desktop and display the Automation Mode overlay. Run them on a Mac nobody is using during the test.

## Configure the app

This config launches TaskDesk from the cross-platform fixture. Its arguments ask the app to reset local state and connect to the task service; your app may use different launch settings.

```ts filename="retest.config.ts"
import { defineConfig, env } from '@rehearsal-labs/retest'

const config = defineConfig({
  apps: {
    desk: {
      platform: 'macos',
      appPath: './TaskDesk.app',
      arguments: ['-reset', '-serviceURL', 'http://127.0.0.1:4310'],
    },
  },
  secrets: { password: env('TASK_APP_PASSWORD') },
  secretOrigins: { password: ['dev.retest.fixtures.taskdesk'] },
})

export default config

declare module '@rehearsal-labs/retest' {
  interface Register {
    config: typeof config
  }
}
```

`appPath` is relative to the config. The bundle identifier in `secretOrigins` authorises password entry into that app. Native targets take no web address, browser viewport or emulation settings.

```sh
npx retest doctor
```

Doctor checks the cached executor, the app bundle's presence and Screen Recording without starting a native session. Xcode, Automation Mode and the app's launch are checked by the run.

## Check a task

TaskDesk reads tasks from the shared service. This test signs in and opens a seeded task by its id, using the accessibility identifiers the fixture app exposes.

```ts filename="tests/desk.retest.ts"
import { expect, secret, test } from '@rehearsal-labs/retest'

test('shows a saved task', { apps: ['desk'] }, async ({ desk }) => {
  await desk.getByTestId('account-field').fill('ada')
  await desk.getByTestId('password-field').fill(secret('password'))
  await desk.getByTestId('sign-in-button').click()
  await expect(desk.getByTestId('signed-in-account')).toHaveText('ada')

  await desk.getByTestId('task-id-field').fill('seed-ada-1')
  await desk.getByTestId('show-task-button').click()
  await expect(desk.getByTestId('selected-task-id')).toHaveText('seed-ada-1')
  await expect(desk.getByTestId('selected-task-state')).toHaveText('Open')
})
```

macOS elements take `click()`, while `fill()` enters a field's value. Check an editable field with `toHaveValue`, and displayed text with `toHaveText`. A check repeats its reads until the expected state arrives or its deadline expires.

```sh
npx retest run tests/desk.retest.ts
```

## State and capture

App data and keychain entries remain between tests. A reset argument asks the app to clear what it supports, so prepare backend data separately. Retest launches its own copy and leaves any copy you already opened alone.

Native macOS sessions take turns on the desktop. A test asking for two macOS apps cannot start because it would need both sessions at once. You can still combine one macOS app with web and iOS apps.

Window capture uses the app's own window number, so another window covering it does not appear in the picture. Screen Recording must be allowed for the process running Retest. Native screenshot baseline comparisons are not available.

Native finders support accessibility ids, roles, labels and text, with scoping and positional selection. CSS, placeholder lookup, `goto`, browser saved state, swipe and software keyboard helpers are unavailable on macOS. Labelled alert helpers exist, but have been exercised only with a stand-in executor.

## Next steps

- [One test across platforms](https://rehearsal.dev/retest/docs/platforms/across-platforms.md): Create a task elsewhere and verify it on the desktop.
- [Electron apps](https://rehearsal.dev/retest/docs/platforms/electron.md): Configure an Electron window as a different desktop target.
- [Debugging a failure](https://rehearsal.dev/retest/docs/essentials/debugging.md): Read the failed check and its available evidence.
