Test native macOS apps (experimental)
Doc Detective can open and drive native macOS apps as part of a test. This experimental workflow launches an app with startSurface, acts on it with the same find, click, and type steps you already use in the browser, and closes it with closeSurface.
Experimental: Native and mobile app testing is experimental and subject to change.
Before you begin
macOS app tests run only on a macOS host. On any other platform, Doc Detective skips the test with a reason rather than failing it, so a mixed-platform spec stays green.
Doc Detective drives macOS apps through an automation driver. It installs automatically the first time a macOS app surface starts, so you don’t have to prepare anything for a first run. There’s no separate pre-install command for this driver. See the install command reference for the runtime packages that do support pre-installation.
One requirement matters before your first run. The process that runs Doc Detective needs macOS Accessibility (TCC) permission. That process is your terminal, IDE, or CI runner. Without it the driver can’t drive apps, and the test skips with a walkthrough. Grant it under System Settings before you run. For the step-by-step walkthrough, see Grant Accessibility permission on macOS.
Run your first macOS test
This test targets the mac platform, launches Calculator by its .app path, waits until a digit button exists, clicks it, and closes the app. Calculator ships with macOS, so the test needs nothing else installed.
To run this test, save it to a spec file and pass it to Doc Detective:
A few things to notice:
- The generous
timeout(180,000 ms) leaves room for the first-run WebDriverAgentMac build and for the driver to install. waitUntil.findgates readiness. The step holds until a digit button, matched byelementAria, exists. The next step then never runs against a window that hasn’t drawn its controls.- The app that
startSurfaceopens becomes the active surface, so the later steps act on it with nosurfacefield. Pass an explicitsurfaceon a step, such as{ "app": "Calculator" }, to switch to a different surface. That switch sticks for the surface-less steps that follow.closeSurfacestill names the surface to close ({ "app": "Calculator" }) because it’s a lifecycle step, not an interaction on the active surface. Doc Detective derives the default nameCalculatorfrom the.appbase name. Pass anameonstartSurfaceto choose your own. See Target an app surface. - The
screenshotstep takes a fresh screenshot of the app every run, so the picture never drifts out of sync with the current build.
To launch an app by its bundle ID instead of a .app path, set app to the identifier, such as "com.apple.TextEdit". See the app descriptor reference for the identifier syntax.
Target only macOS
Because an app targets one platform, gate the test with a context so it runs only where it can:
Doc Detective maps the host’s darwin platform to mac, so a macOS runner matches this context. See Test across platforms and browsers for the full context grammar.
Troubleshooting
macOS app tests skip instead of running
- Cause: The process that runs Doc Detective doesn’t have Accessibility (TCC) permission, so the driver can’t control apps.
- Solution: Grant the permission to your terminal, IDE, or CI runner process, then rerun. See Grant Accessibility permission on macOS.
The first app launch is slow
- Cause: The first app surface of a run builds the automation agent (WebDriverAgentMac) with Xcode, which can take several minutes on a cold machine.
- Solution: Give the first surface a generous
timeout(the default is 60,000 ms). Doc Detective already raises its own server-startup ceiling for that first build, but a highertimeoutkeeps the step from aborting early.
Element not found
- Cause: macOS exposes controls through the accessibility (AX) tree, so element criteria map onto AX properties.
elementTextmatches a control’s title, label, or value.elementIdmatches its AXIdentifier.elementAriamatches its AXRole, and its accessible name when you provide one. The worked example above matches the1button by its accessible name.elementTextandelementAriacollide when a control’s accessible name is the same for both, so setting the two fields with different values on one step fails. - Solution: Match against the AX property that carries the value you want. Specify only one of
elementTextorelementAriawhen they’d target the same accessible name. See Target an app surface and thefindreference.
Launch arguments or environment variables aren’t applied
- Cause: On macOS,
argsreaches the app as a real argument array, and Doc Detective supportsenvonly on macOS. Elsewhere,envfails the step. - Solution: Pass
argsas an array and setenvonly in macOS specs. See the app descriptor reference.
What’s next
startSurface: open an app, browser, or process surface.find: locate elements on an app surface.closeSurface: close a surface by name.- Test across platforms and browsers: gate tests by platform.
- Inline tests and Detected tests: author these startSurface, click, screenshot, and closeSurface steps inline in a doc source file instead of a standalone spec.
- Docker and headless: run app tests in CI.