mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
339 lines
131 KiB
HTML
339 lines
131 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head><meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Replay & E2E Testing - agent-device</title>
|
||
<script>{;const saved = localStorage.getItem('rspress-theme-appearance');const preferDark = window.matchMedia('(prefers-color-scheme: dark)').matches;const isDark = !saved || saved === 'auto' ? preferDark : saved === 'dark';document.documentElement.classList.toggle('dark', isDark);document.documentElement.classList.toggle('rp-dark', isDark);document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';}</script>
|
||
<link href="/agent-device/static/css/styles.cf5e9ab9a4.css" rel="stylesheet">
|
||
<script defer src="/agent-device/static/js/styles.308ea4bafe.js"></script>
|
||
<script defer src="/agent-device/static/js/lib-react.e84adf13b9.js"></script>
|
||
<script defer src="/agent-device/static/js/lib-router.270f17bfa6.js"></script>
|
||
<script defer src="/agent-device/static/js/476.52ed165336.js"></script>
|
||
<script defer src="/agent-device/static/js/index.8722046b0d.js"></script>
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta name="generator" content="Rspress v2.0.12">
|
||
<meta property="og:url" content="https://oss.callstack.com/agent-device">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:title" content="Replay & E2E Testing - agent-device">
|
||
<meta property="og:image" content="https://oss.callstack.com/agent-device/og-image.jpg">
|
||
<meta property="og:description" content="Agents use refs for exploration and authoring. Replay scripts are deterministic runs that can be used for E2E testing.">
|
||
<link rel="icon" href="/agent-device/logo.svg" type="image/svg+xml">
|
||
<meta name="description" content="Agents use refs for exploration and authoring. Replay scripts are deterministic runs that can be used for E2E testing."></head>
|
||
|
||
<body>
|
||
<div id="__rspress_root"><link rel="preload" as="image" href="/agent-device/logo-light.svg"/><link rel="preload" as="image" href="/agent-device/logo-dark.svg"/><link rel="preload" as="image" href="/agent-device/static/image/abstract-atom.4eca0300ff.avif"/><header class="rp-nav"><div class="rp-nav__left"><div class="rp-nav__title"><a href="/agent-device/" class="rp-nav__title__link rp-link"><div class="rp-nav__title__logo"><img src="/agent-device/logo-light.svg" alt="logo" id="logo" class="rspress-logo rp-nav__title__logo-image rp-nav__title__logo-image--light"/><img src="/agent-device/logo-dark.svg" alt="logo" id="logo" class="rspress-logo rp-nav__title__logo-image rp-nav__title__logo-image--dark"/></div></a></div></div><div class="rp-nav__right"><button class="rp-search-button"><div class="rp-search-button__content"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16" class="rp-search-button__icon"><path fill="#838289" d="M14.667 14.666h-1.333v-1.333h1.332zm-1.333-1.333H12V12h1.333zm-4-1.333H4v-1.334h5.334zM12 12h-1.335v-1.334h1.335zm-8-1.334H2.665V9.333H4zm6.665 0H9.334V9.333h1.332zm-8-1.333H1.335V4h1.333zm9.335 0h-1.335V4h1.335zM4 4H2.665V2.666H4zm6.665 0H9.334V2.666h1.332zM9.334 2.666H4V1.333h5.334z"></path></svg><span class="rp-search-button__word">Search</span></div><div class="rp-search-button__hotkey" style="opacity:0"><span></span><span>K</span></div></button><div class="rp-search-button--mobile"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16"><path fill="#838289" d="M14.667 14.666h-1.333v-1.333h1.332zm-1.333-1.333H12V12h1.333zm-4-1.333H4v-1.334h5.334zM12 12h-1.335v-1.334h1.335zm-8-1.334H2.665V9.333H4zm6.665 0H9.334V9.333h1.332zm-8-1.333H1.335V4h1.333zm9.335 0h-1.335V4h1.335zM4 4H2.665V2.666H4zm6.665 0H9.334V2.666h1.332zM9.334 2.666H4V1.333h5.334z"></path></svg></div><ul class="rp-nav-menu rp-nav-menu--right"><li class="rp-nav-menu__item"><a href="/agent-device/docs/introduction" class="rp-nav-menu__item__container rp-link">Docs</a></li></ul><div class="rp-nav__others"><div class="rp-nav-menu__divider"></div><div role="button" tabindex="0" class="rspress-nav-appearance"><div class="rp-p-1 rp-border rp-border-solid rp-border-gray-300 rp-text-gray-400 rp-cursor-pointer rp-rounded-md rp-transition-all rp-duration-300 rp-w-7 rp-h-7"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 24 24"><path fill="currentColor" d="M18 22H8v-2h10zM8 20H6v-2h2zm12 0h-2v-2h2zM6 18H4v-2h2zm16 0h-2v-4h-2v-2h2v-2h2zM4 16H2V6h2zm14 0h-6v-2h6zm-6-2h-2v-2h2zm-2-2H8V6h2zM6 6H4V4h2zm8-2h-2v2h-2V4H6V2h8z"></path></svg></div></div><div class="rp-social-links"><a href="https://github.com/callstack/agent-device" target="_blank" rel="noopener noreferrer" class="rp-social-links__item"><div class="rp-social-links__icon"><div class="rp-social-links__icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></div></div></a><a href="https://discord.gg/eYapw6F3" target="_blank" rel="noopener noreferrer" class="rp-social-links__item"><div class="rp-social-links__icon"><div class="rp-social-links__icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M20.317 4.37a19.8 19.8 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.3 18.3 0 0 0-5.487 0a13 13 0 0 0-.617-1.25a.08.08 0 0 0-.079-.037A19.7 19.7 0 0 0 3.677 4.37a.1.1 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.08.08 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.08.08 0 0 0 .084-.028a14 14 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13 13 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10 10 0 0 0 .372-.292a.07.07 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.07.07 0 0 1 .078.01q.181.149.373.292a.077.077 0 0 1-.006.127a12.3 12.3 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.08.08 0 0 0 .084.028a19.8 19.8 0 0 0 6.002-3.03a.08.08 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.06.06 0 0 0-.031-.03M8.02 15.33c-1.182 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418m7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418"/></svg></div></div></a><ul class="rp-hover-group rp-hover-group--hidden rp-hover-group--right"></ul></div></div><button aria-label="mobile hamburger" class="rp-nav-hamburger rp-nav-hamburger__sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#838289" fill-rule="evenodd" d="M1 9h6v6H1zm2 2v2h2v-2zm6-2h6v6H9zm2 2v2h2v-2zm6-2h6v6h-6zm2 2v2h2v-2z" clip-rule="evenodd"></path></svg></button><button aria-label="mobile hamburger" class="rp-nav-hamburger rp-nav-hamburger__md"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#838289" fill-rule="evenodd" d="M1 9h6v6H1zm2 2v2h2v-2zm6-2h6v6H9zm2 2v2h2v-2zm6-2h6v6h-6zm2 2v2h2v-2z" clip-rule="evenodd"></path></svg><ul class="rp-hover-group rp-hover-group--hidden rp-hover-group--right"><div class="rp-nav-menu__others-mobile__container"><div class="rp-nav-hamburger__md__hover-group"><div class="rp-nav-screen-appearance"><div class="rp-nav-screen-appearance__left">Theme</div><div class="rp-nav-screen-appearance__right"></div></div><div class="rp-nav-screen-divider"></div><div class="rp-social-links"><a href="https://github.com/callstack/agent-device" target="_blank" rel="noopener noreferrer" class="rp-social-links__item"><div class="rp-social-links__icon"><div class="rp-social-links__icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></div></div></a><a href="https://discord.gg/eYapw6F3" target="_blank" rel="noopener noreferrer" class="rp-social-links__item"><div class="rp-social-links__icon"><div class="rp-social-links__icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M20.317 4.37a19.8 19.8 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.3 18.3 0 0 0-5.487 0a13 13 0 0 0-.617-1.25a.08.08 0 0 0-.079-.037A19.7 19.7 0 0 0 3.677 4.37a.1.1 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.08.08 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.08.08 0 0 0 .084-.028a14 14 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13 13 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10 10 0 0 0 .372-.292a.07.07 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.07.07 0 0 1 .078.01q.181.149.373.292a.077.077 0 0 1-.006.127a12.3 12.3 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.08.08 0 0 0 .084.028a19.8 19.8 0 0 0 6.002-3.03a.08.08 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.06.06 0 0 0-.031-.03M8.02 15.33c-1.182 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418m7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418"/></svg></div></div></a><ul class="rp-hover-group rp-hover-group--hidden rp-hover-group--right"></ul></div></div></div></ul></button></div></header><div class="rp-doc-layout__menu"><div class="rp-sidebar-menu"><button type="button" class="rp-sidebar-menu__left"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#838289" d="M20 18H4v-2h16zm0-5H4v-2h16zm0-5H4V6h16z"></path></svg><span>Menu</span></button><button type="button" class="rp-sidebar-menu__right"><span class="rp-sidebar-menu__right__text">Contents</span><svg width="14" height="14" class="rp-progress-circle"><circle cx="7" cy="7" r="6" stroke-linecap="round" fill="none" stroke="var(--rp-c-divider-light)" stroke-width="2"></circle><circle cx="7" cy="7" r="6" stroke-linecap="round" fill="none" stroke="var(--rp-c-brand)" stroke-width="2" stroke-dasharray="37.69911184307752" stroke-dashoffset="37.69911184307752" transform="rotate(-90 7 7)" style="transition:stroke-dashoffset 0.3s"></circle></svg><svg xmlns="http://www.w3.org/2000/svg" width="12" height="13" fill="none" viewBox="0 0 12 13" class="rp-sidebar-menu__right__icon" style="transform:rotate(0deg);transition:transform 0.2s ease-out"><path fill="#BCBBC2" d="M6.5 4.5h1v-1h-1v-1h-1v1h-1v1h1v6h1zm-2 0h-1v1h1zm4 0h-1v1h1zm-5 2v-1h-1v1zm6-1h-1v1h1z"></path></svg></button></div></div><div class="rp-doc-layout__container"><aside class="rp-doc-layout__sidebar rp-scrollbar"><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span class="rp-doc">Getting Started</span></div><div class="rp-sidebar-item__right"></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/agent-device/docs/introduction" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Introduction</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/installation" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Installation</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/agent-setup" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">AI Agent Setup</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/quick-start" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Quick Start</span></div><div class="rp-sidebar-item__right"></div></a></div></div><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span class="rp-doc">Using agent-device</span></div><div class="rp-sidebar-item__right"></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/agent-device/docs/commands" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Commands</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/configuration" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Configuration</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/selectors" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Selectors</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/sessions" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Sessions</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/batching" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Batching</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/snapshots" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Snapshots</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/replay-e2e" class="rp-sidebar-item rp-sidebar-item--active rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Replay & E2E testing</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/debugging-profiling" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Debugging & Profiling</span></div><div class="rp-sidebar-item__right"></div></a></div></div><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span class="rp-doc">Integrations</span></div><div class="rp-sidebar-item__right"></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/agent-device/docs/client-api" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Node.js API</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/ai-sdk" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">AI SDK</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/eve" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Eve</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/remote-proxy" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Remote Proxy</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/device-clouds" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Device Clouds</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/browserstack" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">BrowserStack</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/aws-device-farm" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">AWS Device Farm</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/limrun" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Limrun</span></div><div class="rp-sidebar-item__right"></div></a></div></div><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span class="rp-doc">Reference</span></div><div class="rp-sidebar-item__right"></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/agent-device/docs/security-trust" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Security & Trust</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/known-limitations" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Known Limitations</span></div><div class="rp-sidebar-item__right"></div></a><a href="/agent-device/docs/migrating-gestures" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span class="rp-doc">Migrating Gestures</span></div><div class="rp-sidebar-item__right"></div></a></div></div></aside><div class="rp-doc-layout__doc"><main class="rp-doc-layout__doc-container"><div class="rp-doc rspress-doc"><!--$--><h1 class="rp-toc-include" id="replay--e2e-testing"><a href="#replay--e2e-testing" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Replay & E2E Testing<!-- --> </h1><div class="rp-not-doc rp-llms-container"><button class="rp-not-doc rp-llms-button rp-llms-copy-button"><div class="rp-llms-copy-button__icon-wrapper"><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-llms-copy-button__icon-success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-llms-copy-button__icon-copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg></div><span>Copy Markdown</span></button><button class="rp-llms-button rp-llms-view-options__trigger "><svg xmlns="http://www.w3.org/2000/svg" width="12" height="13" viewBox="0 0 12 13" class="rp-llms-view-options__arrow "><path fill="currentColor" d="M6.5 8.5h1v1h-1v1h-1v-1h-1v-1h1v-6h1zm-2 0h-1v-1h1zm4 0h-1v-1h1zm-5-2v1h-1v-1zm6 1h-1v-1h1z"></path></svg></button></div>
|
||
<p>Agents use refs for exploration and authoring. Replay scripts are deterministic runs that can be used for E2E testing.</p>
|
||
<h2 class="rp-toc-include" id="core-model"><a href="#core-model" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Core model</h2>
|
||
<p>Two-pass workflow:</p>
|
||
<ol>
|
||
<li>Agent pass: discover and interact with refs (<code>snapshot</code> -> <code>click @e..</code> / <code>fill @e..</code>).</li>
|
||
<li>Deterministic pass: run recorded <code>.ad</code> script with <code>replay</code>.</li>
|
||
</ol>
|
||
<h2 class="rp-toc-include" id="record-a-replay-script"><a href="#record-a-replay-script" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Record a replay script</h2>
|
||
<p>Enable recording during a session:</p>
|
||
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> open</span><span style="color:var(--shiki-token-string)"> Settings</span><span style="color:var(--shiki-token-string)"> --platform</span><span style="color:var(--shiki-token-string)"> ios</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e</span><span style="color:var(--shiki-token-string)"> --save-script</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> snapshot</span><span style="color:var(--shiki-token-string)"> -i</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> click</span><span style="color:var(--shiki-token-string)"> @e13</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> close</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>By default, on <code>close</code>, a replay script is written to:</p>
|
||
<div class="rp-codeblock language-text"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="text"><code><span class="line"><span>~/.agent-device/sessions/<session>-<timestamp>.ad</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>You can also provide a custom output file path:</p>
|
||
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> open</span><span style="color:var(--shiki-token-string)"> Settings</span><span style="color:var(--shiki-token-string)"> --platform</span><span style="color:var(--shiki-token-string)"> ios</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e</span><span style="color:var(--shiki-token-string)"> --save-script</span><span style="color:var(--shiki-token-string)"> ./workflows/e2e-settings.ad</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<ul>
|
||
<li><code>--save-script</code> value is treated as a file path.</li>
|
||
<li>Parent directories are created automatically when they do not exist.</li>
|
||
<li>For ambiguous bare values, use <code>--save-script=workflow.ad</code> or a path-like value such as <code>./workflow.ad</code>.</li>
|
||
</ul>
|
||
<h2 class="rp-toc-include" id="run-replay"><a href="#run-replay" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Run replay</h2>
|
||
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ~/.agent-device/sessions/e2e-2026-02-09T12-00-00-000Z.ad</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e-run</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<ul>
|
||
<li>
|
||
<p>Replay reads <code>.ad</code> scripts.</p>
|
||
</li>
|
||
<li>
|
||
<p>A script without a terminal <code>close</code> already leaves its session active. For an existing script that
|
||
does end in <code>close</code>, pass <code>--keep-session</code> to suppress only that final action and continue with
|
||
interactive commands in the same session:</p>
|
||
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ./checkout.ad</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e-run</span><span style="color:var(--shiki-token-string)"> --keep-session</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> snapshot</span><span style="color:var(--shiki-token-string)"> -i</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e-run</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>Interior <code>close</code> actions still run. The flag is intentionally unavailable to <code>test</code> because suite
|
||
attempts own cleanup, and it is rejected for Maestro YAML because that runtime owns its lifecycle.</p>
|
||
</li>
|
||
</ul>
|
||
<h2 class="rp-toc-include" id="run-maestro-compatibility-flows"><a href="#run-maestro-compatibility-flows" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Run Maestro compatibility flows</h2>
|
||
<p>Agent Device can run a supported subset of Maestro YAML through its typed Maestro compatibility runtime:</p>
|
||
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ./flow.yaml</span><span style="color:var(--shiki-token-string)"> --maestro</span><span style="color:var(--shiki-token-string)"> --platform</span><span style="color:var(--shiki-token-string)"> ios</span><span style="color:var(--shiki-token-string)"> --session</span><span style="color:var(--shiki-token-string)"> e2e-run</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./maestro-flows</span><span style="color:var(--shiki-token-string)"> --maestro</span><span style="color:var(--shiki-token-string)"> --platform</span><span style="color:var(--shiki-token-string)"> android</span><span style="color:var(--shiki-token-string)"> --artifacts-dir</span><span style="color:var(--shiki-token-string)"> ./tmp/maestro-artifacts</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>Supported subset:</p>
|
||
<ul>
|
||
<li>Flows: <code>launchApp</code>; <code>runFlow</code> file/inline with platform, visibility, and limited boolean conditions; <code>onFlowStart</code>/<code>onFlowComplete</code>; <code>repeat.times</code> and retry.</li>
|
||
<li>Interactions: <code>tapOn</code>, <code>doubleTapOn</code>, <code>longPressOn</code>, <code>inputText</code> on the focused element, <code>eraseText</code>, <code>openLink</code>, <code>hideKeyboard</code>, basic <code>pressKey</code>, and <code>back</code>; selector targets poll until available and support recursive <code>index</code>, <code>childOf</code>, <code>above</code>, <code>below</code>, <code>leftOf</code>, <code>rightOf</code>, <code>containsChild</code>, <code>containsDescendants</code>, points, and <code>optional</code>; outer command labels are metadata, not target selectors.</li>
|
||
<li>Assertions and navigation: <code>assertVisible</code>, <code>assertNotVisible</code>, <code>assertTrue</code> (literal values and <code>${VAR}</code> lookups only; <code>""</code>, <code>"false"</code>, <code>"0"</code>, <code>"null"</code>, and <code>"undefined"</code> are falsy, everything else is truthy), <code>extendedWaitUntil</code>, <code>scroll</code>, <code>scrollUntilVisible</code>, absolute/percentage/target <code>swipe</code>, <code>takeScreenshot</code>, <code>waitForAnimationToEnd</code>, <code>clearState</code>, and <code>stopApp</code>.</li>
|
||
<li>Scripts: ordered <code>runScript</code> file/env scripts with <code>http.post</code>, <code>json</code>, and <code>output</code> variables.</li>
|
||
</ul>
|
||
<p>Boundaries:</p>
|
||
<ul>
|
||
<li>Runtime: iOS and Android only; <code>launchApp.clearState</code> and standalone <code>clearState</code> support Android and iOS simulators, launch arguments are Apple-only, and other standalone device utility/state commands are unsupported.</li>
|
||
<li>Expressions: <code>when.true</code> supports boolean literals and <code>maestro.platform</code> comparisons; <code>assertTrue</code> supports literal values and <code>${VAR}</code> lookups only; <code>repeat.while</code>, <code>evalScript</code>, and broader JavaScript expressions are unsupported.</li>
|
||
<li>Environment: flow <code>env</code> is the default, <code>AD_VAR_*</code> overrides it, and CLI <code>-e KEY=VALUE</code> wins over both.</li>
|
||
<li>Failure diagnostics: resolved targets and <code>runFlow</code> paths are rendered, while <code>inputText</code> payloads remain hidden; do not place secrets in diagnostic identifiers.</li>
|
||
<li>Trust: <code>runScript</code> executes trusted scripts, may make <code>http.post</code> network requests, and is not a security sandbox; output keys cannot contain a dot.</li>
|
||
<li>Errors and tracking: unsupported commands and fields fail with source context when available; open a focused issue only when implementation work is planned.</li>
|
||
<li>Session takeover: <code>--keep-session</code> is a native <code>.ad</code> replay option and is rejected for Maestro YAML.</li>
|
||
</ul>
|
||
<p>See <a href="https://github.com/callstack/agent-device/blob/main/docs/adr/0015-direct-maestro-engine.md" target="_blank" rel="noopener noreferrer" class="rp-link">ADR 0015</a> for architecture, performance tradeoffs, and deliberate deviations. If a missing feature matters for your suite, <a href="https://github.com/callstack/agent-device/issues/new" target="_blank" rel="noopener noreferrer" class="rp-link">open a focused issue</a> with a small flow snippet.</p>
|
||
<h2 class="rp-toc-include" id="export-ad-scripts-to-maestro-yaml"><a href="#export-ad-scripts-to-maestro-yaml" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Export <code>.ad</code> scripts to Maestro YAML</h2>
|
||
<p>Replay scripts can be exported to a Maestro YAML subset when you need to hand a recorded Agent Device flow to a Maestro runner:</p>
|
||
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> export</span><span style="color:var(--shiki-token-string)"> ./workflows/checkout.ad</span><span style="color:var(--shiki-token-string)"> --out</span><span style="color:var(--shiki-token-string)"> ./maestro/checkout.yaml</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p><code>replay export</code> is a local file transform. It does not start the daemon or contact a device. If <code>--out</code> is omitted, the YAML is printed to stdout.</p>
|
||
<p>Each <code>open <appId></code> exports with an explicit <code>launchApp.appId</code>, so a flow can switch between apps and return to the original app. The first app remains the flow's default <code>appId</code>; relaunch options and app-specific deep links stay attached to their authored targets.</p>
|
||
<p>Deep links, including schemes without <code>//</code> such as <code>tel:</code> and <code>mailto:</code>, export as <code>openLink</code>. A standalone <code>open tel:+15551234567</code> emits only the link command; <code>open com.example.app mailto:agent@example.test</code> emits the app launch followed by the link.</p>
|
||
<p>The exporter is intentionally strict. It writes Maestro YAML for compatible flow actions such as app launch, taps, long press, text input, keyboard dismiss/enter, back, home, text visibility assertions, coordinate swipes, basic scroll, screenshots, and <code>.ad</code> <code>env</code> directives. <code>home</code> exports as <code>pressKey: Home</code>, so flows that visit the home screen and reopen the app can be exported. Agent-only inspection or maintenance actions such as <code>snapshot</code>, <code>get</code>, <code>record</code>, <code>trace</code>, <code>settings</code>, and unsupported selector shapes fail with the source line and action instead of being silently dropped. Known semantic differences are reported as warnings; for example, <code>.ad</code> <code>fill</code> exports as <code>tapOn</code> plus <code>inputText</code>, which may append text in Maestro rather than replacing existing field contents. Native <code>.ad</code> <code>label=</code> selectors export as Maestro <code>text:</code> selectors and warn because Maestro text matching is broader than label-only matching.</p>
|
||
<h2 class="rp-toc-include" id="run-a-lightweight-ad-suite"><a href="#run-a-lightweight-ad-suite" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Run a lightweight <code>.ad</code> suite</h2>
|
||
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./workflows</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string-expression)"> "./workflows/**/*.ad"</span><span style="color:var(--shiki-token-string)"> --platform</span><span style="color:var(--shiki-token-string)"> android</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./workflows</span><span style="color:var(--shiki-token-string)"> --timeout</span><span style="color:var(--shiki-token-constant)"> 60000</span><span style="color:var(--shiki-token-string)"> --retries</span><span style="color:var(--shiki-token-constant)"> 1</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./workflows</span><span style="color:var(--shiki-token-string)"> --artifacts-dir</span><span style="color:var(--shiki-token-string)"> ./tmp/agent-device-artifacts</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./workflows</span><span style="color:var(--shiki-token-string)"> --reporter</span><span style="color:var(--shiki-token-string)"> default</span><span style="color:var(--shiki-token-string)"> --reporter</span><span style="color:var(--shiki-token-string)"> junit:./tmp/junit.xml</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<ul>
|
||
<li><code>test</code> discovers <code>.ad</code> files from files, directories, or globs and runs them serially.</li>
|
||
<li>Quote relative globs to expand them on the caller from its working directory, including when the directory name contains glob characters such as <code>[</code> or <code>{</code>. A missing file input without glob characters reports an error.</li>
|
||
<li><code>context platform=...</code> inside each <code>.ad</code> file is the target source of truth for suite execution.</li>
|
||
<li><code>--platform</code> is a filter for suite discovery; files without platform metadata are skipped when a filter is present.</li>
|
||
<li><code>context timeout=...</code> and <code>context retries=...</code> can be declared per script; CLI flags override metadata. Retries are capped at <code>3</code>, and duplicate keys in the context header fail fast instead of silently overriding each other.</li>
|
||
<li>By default, suite artifacts are written under <code>.agent-device/test-artifacts/<run-id>/...</code>. Each attempt writes <code>replay.ad</code>, <code>result.txt</code>, and <code>replay-timing.ndjson</code>. Failed attempts also keep copied logs and artifact files when the replay produced them.</li>
|
||
<li>Copied diagnostic artifacts receive numbered filenames when their names collide with another artifact, a replay source, timing trace, or attempt manifest. <code>result.txt</code> lists the retained names in <code>copiedArtifacts</code>.</li>
|
||
<li><code>replay-timing.ndjson</code> records attempt, cleanup, and per-step start/stop events with durations. Upload it from CI even for passing runs when comparing local and CI performance.</li>
|
||
<li>Timeouts are cooperative: the runner marks the attempt failed at the timeout boundary, then gives the underlying replay a short grace period to stop before session cleanup.</li>
|
||
<li>The default text reporter streams live progress on stderr while a suite runs, then prints the final summary, failed tests, and passed-on-retry flaky tests. Use <code>--verbose</code> to include step traces in completed-test progress output.</li>
|
||
<li><code>--reporter</code> is repeatable. Built-ins are <code>default</code> for the console summary and <code>junit:<path></code> for JUnit XML. Passing any explicit reporter list replaces the implicit default reporter, so include <code>--reporter default</code> when you also want terminal output. <code>--report-junit <path></code> remains a compatibility alias for <code>--reporter junit:<path></code>.</li>
|
||
<li>JUnit reports preserve legal Unicode and whitespace, and replace characters forbidden by XML 1.0 (such as terminal ESC or NUL) with <code>U+FFFD</code> (<code><EFBFBD></code>) so CI parsers can read the report. JSON and other reporters retain the original suite values.</li>
|
||
<li>When <code>--fail-fast</code> and retries are both set, the current test still consumes its retries before the suite stops.</li>
|
||
</ul>
|
||
<h3 class="rp-toc-include" id="custom-test-reporters"><a href="#custom-test-reporters" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Custom test reporters</h3>
|
||
<p>Custom reporters are CLI-only presentation adapters. The daemon streams progress and returns the structured replay suite result; reporters run in the local CLI process and can render both live progress and final output.</p>
|
||
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./workflows</span><span style="color:var(--shiki-token-string)"> --reporter</span><span style="color:var(--shiki-token-string)"> ./scripts/replay-reporter.mjs</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>Reporter modules can export a reporter object, <code>reporter</code>, <code>createReporter</code>, or a default factory. Factories receive load context. Reporter hooks receive replay test domain objects and an IO context with <code>stdout</code> and <code>stderr</code> streams:</p>
|
||
<div class="rp-codeblock language-js"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="js"><code><span class="line"><span style="color:var(--shiki-token-comment)">// scripts/replay-reporter.mjs</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)">import</span><span style="color:var(--shiki-foreground)"> fs </span><span style="color:var(--shiki-token-keyword)">from</span><span style="color:var(--shiki-token-string-expression)"> 'node:fs'</span><span style="color:var(--shiki-foreground)">;</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)">import</span><span style="color:var(--shiki-foreground)"> path </span><span style="color:var(--shiki-token-keyword)">from</span><span style="color:var(--shiki-token-string-expression)"> 'node:path'</span><span style="color:var(--shiki-foreground)">;</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)">export</span><span style="color:var(--shiki-token-keyword)"> default</span><span style="color:var(--shiki-token-keyword)"> function</span><span style="color:var(--shiki-token-function)"> createReporter</span><span style="color:var(--shiki-foreground)">(loadContext) {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> return</span><span style="color:var(--shiki-foreground)"> {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> name</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-string-expression)"> 'summary-file'</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)"> onTestStep</span><span style="color:var(--shiki-foreground)">(test</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> context) {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-constant)"> context</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">stderr</span><span style="color:var(--shiki-token-function)">.write</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">`running </span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.file</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-keyword)"> ${</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.stepIndex</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)">/</span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.stepTotal</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)">\n`</span><span style="color:var(--shiki-foreground)">);</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)"> onSuiteEnd</span><span style="color:var(--shiki-foreground)">(suite</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> context) {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-constant)"> context</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">stdout</span><span style="color:var(--shiki-token-function)">.write</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">`finished </span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-token-constant)">suite</span><span style="color:var(--shiki-foreground)">.total</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)"> tests\n`</span><span style="color:var(--shiki-foreground)">);</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-constant)"> fs</span><span style="color:var(--shiki-token-function)">.mkdirSync</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">'./tmp'</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> { recursive</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> true</span><span style="color:var(--shiki-foreground)"> });</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-constant)"> fs</span><span style="color:var(--shiki-token-function)">.writeFileSync</span><span style="color:var(--shiki-foreground)">(</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-constant)"> path</span><span style="color:var(--shiki-token-function)">.join</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">'./tmp'</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-string-expression)"> 'report.txt'</span><span style="color:var(--shiki-foreground)">)</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-constant)"> JSON</span><span style="color:var(--shiki-token-function)">.stringify</span><span style="color:var(--shiki-foreground)">(</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> total</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> suite</span><span style="color:var(--shiki-foreground)">.total</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> passed</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> suite</span><span style="color:var(--shiki-foreground)">.passed</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> failed</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> suite</span><span style="color:var(--shiki-foreground)">.failed</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> modulePath</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> loadContext</span><span style="color:var(--shiki-foreground)">.modulePath</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-constant)"> null</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-constant)"> 2</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> )</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-string-expression)"> 'utf8'</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> );</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)"> getExitCode</span><span style="color:var(--shiki-foreground)">(suite) {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> return</span><span style="color:var(--shiki-token-constant)"> suite</span><span style="color:var(--shiki-foreground)">.failed </span><span style="color:var(--shiki-token-keyword)">></span><span style="color:var(--shiki-token-constant)"> 0</span><span style="color:var(--shiki-token-keyword)"> ?</span><span style="color:var(--shiki-token-constant)"> 1</span><span style="color:var(--shiki-token-keyword)"> :</span><span style="color:var(--shiki-token-constant)"> 0</span><span style="color:var(--shiki-foreground)">;</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> };</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>For a live terminal reporter that prints each completed test as an emoji, title, and duration:</p>
|
||
<div class="rp-codeblock language-js"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="js"><code><span class="line"><span style="color:var(--shiki-token-comment)">// scripts/emoji-reporter.mjs</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)">export</span><span style="color:var(--shiki-token-keyword)"> default</span><span style="color:var(--shiki-foreground)"> {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> name</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-string-expression)"> 'emoji-status'</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)"> onTestResult</span><span style="color:var(--shiki-foreground)">(test</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> context) {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> const</span><span style="color:var(--shiki-token-constant)"> icon</span><span style="color:var(--shiki-token-keyword)"> =</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-foreground)">.status </span><span style="color:var(--shiki-token-keyword)">===</span><span style="color:var(--shiki-token-string-expression)"> 'pass'</span><span style="color:var(--shiki-token-keyword)"> ?</span><span style="color:var(--shiki-token-string-expression)"> '✓'</span><span style="color:var(--shiki-token-keyword)"> :</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-foreground)">.status </span><span style="color:var(--shiki-token-keyword)">===</span><span style="color:var(--shiki-token-string-expression)"> 'fail'</span><span style="color:var(--shiki-token-keyword)"> ?</span><span style="color:var(--shiki-token-string-expression)"> '⨯'</span><span style="color:var(--shiki-token-keyword)"> :</span><span style="color:var(--shiki-token-string-expression)"> '-'</span><span style="color:var(--shiki-foreground)">;</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> const</span><span style="color:var(--shiki-token-constant)"> title</span><span style="color:var(--shiki-token-keyword)"> =</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">title</span><span style="color:var(--shiki-token-function)">?.trim</span><span style="color:var(--shiki-foreground)">() </span><span style="color:var(--shiki-token-keyword)">||</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-foreground)">.file;</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> const</span><span style="color:var(--shiki-token-constant)"> duration</span><span style="color:var(--shiki-token-keyword)"> =</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> typeof</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-foreground)">.durationMs </span><span style="color:var(--shiki-token-keyword)">===</span><span style="color:var(--shiki-token-string-expression)"> 'number'</span><span style="color:var(--shiki-token-keyword)"> ?</span><span style="color:var(--shiki-token-string-expression)"> ` </span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.durationMs </span><span style="color:var(--shiki-token-keyword)">/</span><span style="color:var(--shiki-token-constant)"> 1000</span><span style="color:var(--shiki-foreground)">)</span><span style="color:var(--shiki-token-function)">.toFixed</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-constant)">2</span><span style="color:var(--shiki-foreground)">)</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)">s`</span><span style="color:var(--shiki-token-keyword)"> :</span><span style="color:var(--shiki-token-string-expression)"> ''</span><span style="color:var(--shiki-foreground)">;</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="color:var(--shiki-token-constant)"> context</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">stderr</span><span style="color:var(--shiki-token-function)">.write</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">`</span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-foreground)">icon</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-keyword)"> ${</span><span style="color:var(--shiki-foreground)">title</span><span style="color:var(--shiki-token-keyword)">}${</span><span style="color:var(--shiki-foreground)">duration</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)">\n`</span><span style="color:var(--shiki-foreground)">);</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)">};</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>TypeScript reporters use the same object shape; compile them to JavaScript before passing them to <code>--reporter</code>:</p>
|
||
<div class="rp-codeblock language-ts"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="ts"><code><span class="line"><span style="color:var(--shiki-token-keyword)">const</span><span style="color:var(--shiki-token-function)"> createReporter</span><span style="color:var(--shiki-token-keyword)"> =</span><span style="color:var(--shiki-foreground)"> () </span><span style="color:var(--shiki-token-keyword)">=></span><span style="color:var(--shiki-foreground)"> ({</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> name</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-string-expression)"> 'typed-reporter'</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)"> onSuiteStart</span><span style="color:var(--shiki-foreground)">(suite</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> context) {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-constant)"> context</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">stderr</span><span style="color:var(--shiki-token-function)">.write</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">`starting </span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-token-constant)">suite</span><span style="color:var(--shiki-foreground)">.runnable</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)"> tests\n`</span><span style="color:var(--shiki-foreground)">);</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)"> onTestResult</span><span style="color:var(--shiki-foreground)">(test</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> context) {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-constant)"> context</span><span style="color:var(--shiki-token-function)">.</span><span style="color:var(--shiki-token-constant)">stderr</span><span style="color:var(--shiki-token-function)">.write</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">`</span><span style="color:var(--shiki-token-keyword)">${</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.status</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-keyword)"> ${</span><span style="color:var(--shiki-token-constant)">test</span><span style="color:var(--shiki-foreground)">.title </span><span style="color:var(--shiki-token-keyword)">??</span><span style="color:var(--shiki-token-constant)"> test</span><span style="color:var(--shiki-foreground)">.file</span><span style="color:var(--shiki-token-keyword)">}</span><span style="color:var(--shiki-token-string-expression)">\n`</span><span style="color:var(--shiki-foreground)">);</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)"> onSuiteEnd</span><span style="color:var(--shiki-foreground)">(suite) {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-comment)"> // Write artifacts, annotations, or summaries from suite.</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)">});</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)">export</span><span style="color:var(--shiki-token-keyword)"> default</span><span style="color:var(--shiki-foreground)"> createReporter;</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>The CLI loads reporter modules with Node dynamic <code>import()</code>. Use <code>.mjs</code> or <code>.js</code> files at runtime; for TypeScript, compile the reporter to JavaScript before passing it to <code>--reporter</code>. Loading <code>.ts</code> files directly depends on Node's type-stripping behavior and is not part of the supported reporter contract.</p>
|
||
<p>Live reporter hooks are semantic: <code>onSuiteStart</code>, <code>onTestStart</code>, <code>onTestStep</code>, and <code>onTestResult</code> run while the daemon request is active; generic command progress frames are not exposed to test reporters. These live hooks are synchronous — they run from the progress stream as events arrive and are not awaited, so keep their work synchronous and defer anything async to <code>onSuiteEnd</code>, which the CLI awaits before exiting. <code>onSuiteEnd</code> receives the final suite result. <code>getExitCode</code> can only raise the suite exit code, never lower it: the highest reporter-provided code wins and failed tests still exit with <code>1</code> when no reporter raises it further, so a reporter cannot mask a failing suite. Return an integer from <code>0</code> to <code>255</code>, or <code>undefined</code> to leave the exit code unchanged. Other values fail with <code>INVALID_ARGS</code>; in particular, codes such as <code>256</code> are rejected before they can wrap to a successful process exit.</p>
|
||
<h2 class="rp-toc-include" id="parametrise-ad-scripts"><a href="#parametrise-ad-scripts" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Parametrise <code>.ad</code> scripts</h2>
|
||
<p>Substitute <code>${VAR}</code> tokens in <code>.ad</code> scripts using values from the CLI, shell env, script-local <code>env</code> directives, or built-ins.</p>
|
||
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">context</span><span style="color:var(--shiki-token-string)"> platform=android</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">env</span><span style="color:var(--shiki-token-string)"> APP_ID=settings</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">env</span><span style="color:var(--shiki-token-string)"> WAIT_SHORT=</span><span style="color:var(--shiki-token-constant)">500</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">open</span><span style="color:var(--shiki-foreground)"> ${APP_ID} </span><span style="color:var(--shiki-token-string)">--relaunch</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">wait</span><span style="color:var(--shiki-foreground)"> ${WAIT_SHORT}</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> "label=${APP_ID}"</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<h3 class="rp-toc-include" id="precedence"><a href="#precedence" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Precedence</h3>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="rp-table-scroll-container rp-scrollbar"><table><thead><tr><th>Source</th><th>Priority</th><th>Example</th></tr></thead><tbody><tr><td>CLI <code>-e KEY=VALUE</code></td><td>highest</td><td><code>agent-device test flow.ad -e APP_ID=demo</code></td></tr><tr><td>Shell env prefixed <code>AD_VAR_</code></td><td></td><td><code>AD_VAR_APP_ID=demo agent-device test flow.ad</code> (imported as <code>APP_ID</code>)</td></tr><tr><td>Script <code>env KEY=VALUE</code></td><td></td><td><code>env APP_ID=settings</code> in header</td></tr><tr><td>Built-ins</td><td>runtime</td><td><code>AD_PLATFORM</code>, <code>AD_SESSION</code>, <code>AD_FILENAME</code>, <code>AD_DEVICE</code>, <code>AD_ARTIFACTS</code></td></tr></tbody></table></div>
|
||
<h3 class="rp-toc-include" id="built-ins"><a href="#built-ins" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Built-ins</h3>
|
||
<p>Built-ins are provided by replay/test runtime and use the reserved <code>AD_*</code> namespace.</p>
|
||
<ul>
|
||
<li><code>AD_PLATFORM</code> - matches <code>context platform=...</code> or the selected platform when available</li>
|
||
<li><code>AD_SESSION</code> - active session name</li>
|
||
<li><code>AD_FILENAME</code> - path of the running <code>.ad</code> file</li>
|
||
<li><code>AD_DEVICE</code> - device identifier (when <code>--device</code> is set)</li>
|
||
<li><code>AD_ARTIFACTS</code> - attempt artifacts directory (when running under <code>test</code>)</li>
|
||
</ul>
|
||
<p>User-defined keys starting with <code>AD_</code> are rejected in <code>env</code>, <code>-e</code>, and shell imports such as <code>AD_VAR_AD_FOO</code>, so built-ins cannot be overridden.</p>
|
||
<p>Substitution happens inside parsed string values. It does not create extra arguments, so quote selectors or text values that contain spaces:</p>
|
||
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">env</span><span style="color:var(--shiki-token-string)"> SETTINGS=</span><span style="color:var(--shiki-token-string-expression)">"label=Account || label=Profile"</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> "${SETTINGS}"</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<h3 class="rp-toc-include" id="fallback-and-escape"><a href="#fallback-and-escape" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Fallback and escape</h3>
|
||
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">wait</span><span style="color:var(--shiki-foreground)"> ${WAIT_MS</span><span style="color:var(--shiki-token-keyword)">:-</span><span style="color:var(--shiki-foreground)">500}</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p><code>${VAR:-default}</code> yields <code>default</code> when <code>VAR</code> is unset.</p>
|
||
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">echo</span><span style="color:var(--shiki-token-string-expression)"> "Price: \${APP}"</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p><code>\${APP}</code> emits a literal <code>${APP}</code> with no substitution.</p>
|
||
<h3 class="rp-toc-include" id="recipes"><a href="#recipes" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Recipes</h3>
|
||
<p>Run one flow against two app variants in CI:</p>
|
||
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./flows/login.ad</span><span style="color:var(--shiki-token-string)"> -e</span><span style="color:var(--shiki-token-string)"> APP_ID=com.example.debug</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> test</span><span style="color:var(--shiki-token-string)"> ./flows/login.ad</span><span style="color:var(--shiki-token-string)"> -e</span><span style="color:var(--shiki-token-string)"> APP_ID=com.example.release</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>Tune timings locally without editing the script:</p>
|
||
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-foreground)">AD_VAR_WAIT_SHORT</span><span style="color:var(--shiki-token-keyword)">=</span><span style="color:var(--shiki-token-string)">2000</span><span style="color:var(--shiki-token-function)"> agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ./flow.ad</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>Extract a reusable selector. Before:</p>
|
||
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> "label=Account || label=Profile || label=User"</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">wait</span><span style="color:var(--shiki-token-constant)"> 500</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> "label=Account || label=Profile || label=User"</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>After:</p>
|
||
<div class="rp-codeblock language-sh"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="sh"><code><span class="line"><span style="color:var(--shiki-token-function)">env</span><span style="color:var(--shiki-token-string)"> SETTINGS=</span><span style="color:var(--shiki-token-string-expression)">"label=Account || label=Profile || label=User"</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> "${SETTINGS}"</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">wait</span><span style="color:var(--shiki-token-constant)"> 500</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">click</span><span style="color:var(--shiki-token-string-expression)"> "${SETTINGS}"</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>Quote <code>${VAR}</code> inside selector expressions so the whole expression is treated as a single argument.</p>
|
||
<h3 class="rp-toc-include" id="notes"><a href="#notes" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Notes</h3>
|
||
<ul>
|
||
<li>Shell env (<code>AD_VAR_*</code>) is collected on the CLI/client side at request time, so the same values are seen whether the daemon runs locally or remotely.</li>
|
||
<li>No nested fallback. <code>${A:-${B}}</code> is not supported.</li>
|
||
<li>Unresolved <code>${VAR}</code> fails with a <code>file:line</code> reference. Typos are loud.</li>
|
||
</ul>
|
||
<h2 class="rp-toc-include" id="replay-divergence-and-resume"><a href="#replay-divergence-and-resume" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Replay divergence and resume</h2>
|
||
<p>A failing <code>replay</code>/<code>test</code> step returns a structured <code>REPLAY_DIVERGENCE</code> error instead of a bare failure. The report carries, bounded and redacted:</p>
|
||
<ul>
|
||
<li><strong><code>step</code></strong> — the 1-based plan index and its source file/line (through Maestro <code>runFlow</code> includes).</li>
|
||
<li><strong><code>screen</code></strong> — a fresh post-failure snapshot digest with actionable refs, or <code>unavailable</code> with a reason/hint when capture failed or was sparse (never a stale tree).</li>
|
||
<li><strong><code>suggestions</code></strong> — up to 5 ranked, re-resolved candidates for the failing selector (id match ranks above role+label, which ranks above label-only), each with a <code>basis</code> you can inspect before acting.</li>
|
||
<li><strong><code>resume</code></strong> — whether and how to continue without re-running the script from the top.</li>
|
||
</ul>
|
||
<div class="rp-codeblock language-jsonc"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="jsonc"><code><span class="line"><span style="color:var(--shiki-foreground)">{</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> "code"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> "REPLAY_DIVERGENCE"</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> "details"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> "divergence"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> "step"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> { </span><span style="color:var(--shiki-token-keyword)">"index"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-constant)"> 4</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-keyword)"> "source"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> { </span><span style="color:var(--shiki-token-keyword)">"path"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> "flow.ad"</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-keyword)"> "line"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-constant)"> 6</span><span style="color:var(--shiki-foreground)"> } }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> "screen"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> {</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> "state"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> "available"</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> "refsGeneration"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-constant)"> 3</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> "refs"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> [</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-comment)"> /* ... */</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> ]</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> "suggestions"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> [{ </span><span style="color:var(--shiki-token-keyword)">"selector"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> "id=\"auth_continue\""</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-keyword)"> "basis"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> "id"</span><span style="color:var(--shiki-foreground)"> }]</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-keyword)"> "resume"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-foreground)"> { </span><span style="color:var(--shiki-token-keyword)">"allowed"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-constant)"> true</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-keyword)"> "from"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-constant)"> 4</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-keyword)"> "planDigest"</span><span style="color:var(--shiki-token-punctuation)">:</span><span style="color:var(--shiki-token-string-expression)"> "…64 hex chars…"</span><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)"> }</span><span style="color:var(--shiki-token-punctuation)">,</span></span>
|
||
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>Text output prints a compact summary of the same fields; <code>--json</code>/MCP carry the full object.</p>
|
||
<h3 class="rp-toc-include" id="resuming-a-failed-replay"><a href="#resuming-a-failed-replay" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Resuming a failed replay</h3>
|
||
<p><code>replay --from <n> --plan-digest <sha256></code> resumes <strong>at</strong> plan step <code>n</code>, not after it, skipping <code>1..n-1</code> without executing them. Both flags come from a divergence report's <code>resume</code> field — <code>from</code> is the failed step, <code>planDigest</code> is the digest of the exact unchanged plan that produced it.</p>
|
||
<p>Choose one recovery workflow:</p>
|
||
<ol>
|
||
<li><strong>Change the replay script.</strong> Review the suggestion, edit the selector or include, then run a fresh full <code>replay ./flow.ad</code>. The old digest is intentionally invalid after any plan edit; do not combine it with the edited script. A later divergence supplies a new digest.</li>
|
||
<li><strong>Keep the replay plan unchanged.</strong> Repair app/device state so the reported failed step can succeed when retried, then resume with the report's unchanged <code>from</code> and <code>planDigest</code>. If you manually complete the failed action itself, the reported <code>from</code> will execute it again; only do that when repeating the action is safe.</li>
|
||
</ol>
|
||
<p>The unchanged-plan resume loop is:</p>
|
||
<ol>
|
||
<li>Run <code>replay ./flow.ad</code>. On failure, read <code>resume</code> from the divergence.</li>
|
||
<li>Leave the script, includes, platform, and target unchanged. Repair app state yourself so the failed step can be retried safely. The daemon never infers or reconstructs app state — it only skips execution of the earlier steps.</li>
|
||
<li><code>replay ./flow.ad --from <resume.from> --plan-digest <resume.planDigest></code>.</li>
|
||
</ol>
|
||
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ./flow.ad</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-comment)"># ... REPLAY_DIVERGENCE, resume: { allowed: true, from: 4, planDigest: "ab12...“ }</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-comment)"># (repair app state on the device)</span></span>
|
||
<span class="line"><span style="color:var(--shiki-token-function)">agent-device</span><span style="color:var(--shiki-token-string)"> replay</span><span style="color:var(--shiki-token-string)"> ./flow.ad</span><span style="color:var(--shiki-token-string)"> --from</span><span style="color:var(--shiki-token-constant)"> 4</span><span style="color:var(--shiki-token-string)"> --plan-digest</span><span style="color:var(--shiki-token-string)"> ab12...</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="currentColor" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="272" height="272" viewBox="0 0 272 272" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" fill-rule="evenodd" d="M45.883 23.368h123.823v22.514H68.396v146.336H45.883zm45.026 45.027H225.99v180.107H90.91zm22.514 22.513v135.08h90.053V90.908z" clip-rule="evenodd"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="271" height="271" viewBox="0 0 271 271" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="currentColor" d="M112.687 203.223H90.174v-22.514h22.512v-22.514h22.512v22.514h-22.511zm-22.514-22.514H67.66v-22.514h22.513zm-22.514-22.514H45.146v-22.512H67.66zm90.054 0h-22.514v-22.512h22.514zm22.514-22.512h-22.514v-22.514h22.514zm22.513-22.514h-22.513V90.655h22.513zm22.514-22.514H202.74V68.143h22.514z"></path></svg></button></div></div></div>
|
||
<p>For Maestro flows, <code>--from</code> addresses the immutable top-level typed plan. Compact runtime control nodes
|
||
remain single plan steps and nested commands are not independently addressable. As with generic <code>.ad</code>
|
||
replay, the caller is responsible for restoring any state and environment values established by skipped
|
||
steps before resuming.</p>
|
||
<p>Passing <code>--plan-digest</code> that no longer matches the current script — because you edited it, an include changed, or platform-conditioned expansion differs — fails <code>INVALID_ARGS</code> before any action; run a fresh full replay to get a new digest. <code>--from</code> is <code>replay</code>-only; <code>test</code> rejects it (a suite run must stay full and deterministic).</p>
|
||
<h2 class="rp-toc-include" id="--update-u-retired"><a href="#--update-u-retired" class="rp-header-anchor rp-link" aria-hidden="true">#</a><code>--update</code>/<code>-u</code> (retired)</h2>
|
||
<p><code>--update</code>/<code>-u</code> no longer rewrites <code>.ad</code> files. Historically it retried a failing step against the recorded selector's candidate material and rewrote the line in place; the audit behind <a href="https://github.com/callstack/agent-device/blob/main/docs/adr/0012-interactive-replay.md" target="_blank" rel="noopener noreferrer" class="rp-link">ADR 0012</a> found that mechanism rarely able to act (it can only recover drift the original selector still matches, never a rename) and a silent rewrite is a target-binding risk on its own. The flag is kept, accepted, and is a complete no-op: every replay divergence already carries the same ranked <code>suggestions</code> the old heal path used to apply blind, whether or not <code>--update</code> is passed. Review a suggestion, then edit the <code>.ad</code> file yourself if it's right.</p>
|
||
<h2 class="rp-toc-include" id="troubleshooting"><a href="#troubleshooting" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Troubleshooting</h2>
|
||
<ul>
|
||
<li>Replay fails after UI/layout changes:
|
||
<ul>
|
||
<li>Read the divergence report's <code>suggestions</code> and repair the selector by hand; there is no automated rewrite. Because the edit changes the plan digest, run a fresh full replay instead of using the old resume flags.</li>
|
||
</ul>
|
||
</li>
|
||
<li>Repeated re-runs are slow or the app is stateful, but the script is still correct:
|
||
<ul>
|
||
<li>Leave the replay plan unchanged, repair app state so the reported failed step can be retried, then use its <code>--from</code>/<code>--plan-digest</code>. Resume starts at <code>--from</code>; it does not skip that step.</li>
|
||
</ul>
|
||
</li>
|
||
<li>Replay file parse error:
|
||
<ul>
|
||
<li>Validate quoting in <code>.ad</code> lines (unclosed quotes are rejected).</li>
|
||
</ul>
|
||
</li>
|
||
<li>Maestro compatibility flow fails on unsupported syntax:
|
||
<ul>
|
||
<li>Check <a href="https://github.com/callstack/agent-device/blob/main/docs/adr/0015-direct-maestro-engine.md" target="_blank" rel="noopener noreferrer" class="rp-link">ADR 0015</a>. If the missing feature matters to your suite, open a focused issue with a small flow snippet.</li>
|
||
</ul>
|
||
</li>
|
||
</ul><!--/$--></div><div class="container-uhcWmO"><img class="logo-IwzwG8" src="/agent-device/static/image/abstract-atom.4eca0300ff.avif" alt="Abstract Logo" aria-hidden="true"/><div class="content-uJxcIj"><div class="headlineContainer-NHNT4y"><h1 class="headline-iCUUUl">Need React or React Native
|
||
expertise you can count on?</h1></div><div class="buttonContainer-wOj8Y6"><div class="dark"><a href="https://www.callstack.com/contact?utm_campaign=open_source&utm_source=agent-device&utm_medium=referral&utm_content=FOOTER_CTA" class="button-DogSnd brand-rT2ahT ">Let's talk<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16" class="button-icon-Okk0hT" aria-hidden="true" focusable="false"><path fill="currentColor" d="M9.333 13.333h1.334V12H9.333zm-6.666-2.666h8V12H12v-1.333h1.333V9.333H12V8h-1.333v1.333H4V2.667H2.667zM9.333 8h1.334V6.667H9.333z"></path></svg></a></div></div></div></div><footer class="rp-doc-footer"><div class="rp-doc-footer__edit"><a href="https://github.com/callstack/agent-device/edit/main/website/docs/replay-e2e.md" target="_blank" rel="noopener noreferrer" class="rp-edit-link rp-link">Edit this page on GitHub</a></div><div class="rp-doc-footer__divider"></div><div class="container-aZLxg0"><a href="/agent-device/docs/snapshots" class="pagerLink-_WIlZJ rp-link"><span class="iconBox-AUO0Le"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24" class="icon-WRLCnG"><path d="M4 20h2V4H4zm8-5v2h2v-2zm-2-4H8v2h2v2h2v-2h8v-2h-8V9h-2zm2-2h2V7h-2z"></path></svg></span><span class="textWrap-lmdyLc"><span class="desc-sp3vRh">Previous page</span><span class="title-hrit_i">Snapshots</span></span></a><a href="/agent-device/docs/debugging-profiling" class="pagerLink-_WIlZJ next-axkYXz rp-link"><span class="textWrap-lmdyLc"><span class="desc-sp3vRh">Next page</span><span class="title-hrit_i">Debugging & Profiling</span></span><span class="iconBox-AUO0Le"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24" class="icon-WRLCnG"><path d="M20 20h-2V4h2zm-8-5v2h-2v-2zm2-4h2v2h-2v2h-2v-2H4v-2h8V9h2zm-2-2h-2V7h2z"></path></svg></span></a></div></footer></main></div><aside class="rp-doc-layout__outline rp-scrollbar"><div class="rp-outline"><div class="rp-outline__title">Contents<svg width="14" height="14" class="rp-progress-circle"><circle cx="7" cy="7" r="6" stroke-linecap="round" fill="none" stroke="var(--rp-c-divider-light)" stroke-width="2"></circle><circle cx="7" cy="7" r="6" stroke-linecap="round" fill="none" stroke="var(--rp-c-brand)" stroke-width="2" stroke-dasharray="37.69911184307752" stroke-dashoffset="37.69911184307752" transform="rotate(-90 7 7)" style="transition:stroke-dashoffset 0.3s"></circle></svg></div><nav class="rp-outline__toc rp-scrollbar"><a href="#core-model" class="rp-toc-item rp-link" title="Core model" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Core model</span></a><a href="#record-a-replay-script" class="rp-toc-item rp-link" title="Record a replay script" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Record a replay script</span></a><a href="#run-replay" class="rp-toc-item rp-link" title="Run replay" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Run replay</span></a><a href="#run-maestro-compatibility-flows" class="rp-toc-item rp-link" title="Run Maestro compatibility flows" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Run Maestro compatibility flows</span></a><a href="#export-ad-scripts-to-maestro-yaml" class="rp-toc-item rp-link" title="Export .ad scripts to Maestro YAML" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Export <code>.ad</code> scripts to Maestro YAML</span></a><a href="#run-a-lightweight-ad-suite" class="rp-toc-item rp-link" title="Run a lightweight .ad suite" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Run a lightweight <code>.ad</code> suite</span></a><a href="#custom-test-reporters" class="rp-toc-item rp-link" title="Custom test reporters" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Custom test reporters</span></a><a href="#parametrise-ad-scripts" class="rp-toc-item rp-link" title="Parametrise .ad scripts" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Parametrise <code>.ad</code> scripts</span></a><a href="#precedence" class="rp-toc-item rp-link" title="Precedence" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Precedence</span></a><a href="#built-ins" class="rp-toc-item rp-link" title="Built-ins" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Built-ins</span></a><a href="#fallback-and-escape" class="rp-toc-item rp-link" title="Fallback and escape" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Fallback and escape</span></a><a href="#recipes" class="rp-toc-item rp-link" title="Recipes" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Recipes</span></a><a href="#notes" class="rp-toc-item rp-link" title="Notes" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Notes</span></a><a href="#replay-divergence-and-resume" class="rp-toc-item rp-link" title="Replay divergence and resume" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Replay divergence and resume</span></a><a href="#resuming-a-failed-replay" class="rp-toc-item rp-link" title="Resuming a failed replay" style="padding-left:12px" data-depth="1"><span class="rp-toc-item__text rp-doc">Resuming a failed replay</span></a><a href="#--update-u-retired" class="rp-toc-item rp-link" title="--update/-u (retired)" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc"><code>--update</code>/<code>-u</code> (retired)</span></a><a href="#troubleshooting" class="rp-toc-item rp-link" title="Troubleshooting" style="padding-left:0" data-depth="0"><span class="rp-toc-item__text rp-doc">Troubleshooting</span></a></nav><div class="rp-outline__divider"></div><div class="rp-outline__bottom"><a href="https://github.com/callstack/agent-device/edit/main/website/docs/replay-e2e.md" target="_blank" rel="noopener noreferrer" class="rp-outline__action-row rp-edit-link rp-link"><svg width="16" height="16" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24"><path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5z"></path></svg><span>Edit this page on GitHub</span></a></div></div><div class="container-LzbylO"><div><div class="headline-y75_LS">Curious about developing mobile apps with AI agents?</div><div class="description-muYVs3">We can help you take your agentic workflows to the next level and ship faster.</div></div><a href="https://www.callstack.com/contact?utm_campaign=open_source&utm_source=agent-device&utm_medium=referral&utm_content=OUTLINE_CTA" class="button-DogSnd brand-rT2ahT ">Book a call<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16" class="button-icon-Okk0hT" aria-hidden="true" focusable="false"><path fill="currentColor" d="M9.333 13.333h1.334V12H9.333zm-6.666-2.666h8V12H12v-1.333h1.333V9.333H12V8h-1.333v1.333H4V2.667H2.667zM9.333 8h1.334V6.667H9.333z"></path></svg></a></div></aside></div></div>
|
||
<div id="__rspress_modal_container"></div>
|
||
</body>
|
||
</html>
|