agent-scenario-loop 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/app/profile-session.ts +812 -0
  4. package/core/config-template.json +41 -0
  5. package/dist/core/agent-summary.d.ts +15 -0
  6. package/dist/core/agent-summary.js +177 -0
  7. package/dist/core/artifact-contract.d.ts +151 -0
  8. package/dist/core/artifact-contract.js +897 -0
  9. package/dist/core/artifact-layout.d.ts +56 -0
  10. package/dist/core/artifact-layout.js +61 -0
  11. package/dist/core/artifact-writer.d.ts +44 -0
  12. package/dist/core/artifact-writer.js +55 -0
  13. package/dist/core/comparison.d.ts +133 -0
  14. package/dist/core/comparison.js +294 -0
  15. package/dist/core/evidence-interpreter.d.ts +28 -0
  16. package/dist/core/evidence-interpreter.js +69 -0
  17. package/dist/core/execution-plan.d.ts +44 -0
  18. package/dist/core/execution-plan.js +95 -0
  19. package/dist/core/planner.d.ts +132 -0
  20. package/dist/core/planner.js +812 -0
  21. package/dist/core/ports.d.ts +198 -0
  22. package/dist/core/ports.js +146 -0
  23. package/dist/core/run-index.d.ts +62 -0
  24. package/dist/core/run-index.js +143 -0
  25. package/dist/core/schema-validator.d.ts +86 -0
  26. package/dist/core/schema-validator.js +407 -0
  27. package/dist/index.d.ts +11 -0
  28. package/dist/index.js +27 -0
  29. package/dist/runner/agent-device-driver.d.ts +126 -0
  30. package/dist/runner/agent-device-driver.js +168 -0
  31. package/dist/runner/agent-device.d.ts +295 -0
  32. package/dist/runner/agent-device.js +1271 -0
  33. package/dist/runner/android-adb-driver.d.ts +175 -0
  34. package/dist/runner/android-adb-driver.js +399 -0
  35. package/dist/runner/android-adb.d.ts +254 -0
  36. package/dist/runner/android-adb.js +1618 -0
  37. package/dist/runner/argent-driver.d.ts +183 -0
  38. package/dist/runner/argent-driver.js +297 -0
  39. package/dist/runner/argent.d.ts +349 -0
  40. package/dist/runner/argent.js +1211 -0
  41. package/dist/runner/check-plan.d.ts +45 -0
  42. package/dist/runner/check-plan.js +210 -0
  43. package/dist/runner/cli.d.ts +20 -0
  44. package/dist/runner/cli.js +23 -0
  45. package/dist/runner/compare-latest.d.ts +99 -0
  46. package/dist/runner/compare-latest.js +233 -0
  47. package/dist/runner/compare.d.ts +58 -0
  48. package/dist/runner/compare.js +157 -0
  49. package/dist/runner/demo-loop.d.ts +45 -0
  50. package/dist/runner/demo-loop.js +170 -0
  51. package/dist/runner/example-android-live.d.ts +137 -0
  52. package/dist/runner/example-android-live.js +454 -0
  53. package/dist/runner/example-ios-live.d.ts +137 -0
  54. package/dist/runner/example-ios-live.js +471 -0
  55. package/dist/runner/host-doctor.d.ts +131 -0
  56. package/dist/runner/host-doctor.js +628 -0
  57. package/dist/runner/init-project.d.ts +88 -0
  58. package/dist/runner/init-project.js +263 -0
  59. package/dist/runner/ios-simctl-driver.d.ts +69 -0
  60. package/dist/runner/ios-simctl-driver.js +97 -0
  61. package/dist/runner/ios-simctl.d.ts +254 -0
  62. package/dist/runner/ios-simctl.js +1415 -0
  63. package/dist/runner/live-android.d.ts +137 -0
  64. package/dist/runner/live-android.js +539 -0
  65. package/dist/runner/live-comparison.d.ts +67 -0
  66. package/dist/runner/live-comparison.js +147 -0
  67. package/dist/runner/live-ios.d.ts +137 -0
  68. package/dist/runner/live-ios.js +460 -0
  69. package/dist/runner/live-proof-summary.d.ts +263 -0
  70. package/dist/runner/live-proof-summary.js +465 -0
  71. package/dist/runner/live-proof.d.ts +467 -0
  72. package/dist/runner/live-proof.js +920 -0
  73. package/dist/runner/local-env.d.ts +64 -0
  74. package/dist/runner/local-env.js +155 -0
  75. package/dist/runner/profile-android.d.ts +82 -0
  76. package/dist/runner/profile-android.js +671 -0
  77. package/dist/runner/profile-ios.d.ts +108 -0
  78. package/dist/runner/profile-ios.js +532 -0
  79. package/dist/runner/profile-mobile.d.ts +254 -0
  80. package/dist/runner/profile-mobile.js +1307 -0
  81. package/dist/runner/validate-project.d.ts +273 -0
  82. package/dist/runner/validate-project.js +1501 -0
  83. package/docs/adapters.md +145 -0
  84. package/docs/api.md +94 -0
  85. package/docs/authoring.md +196 -0
  86. package/docs/concepts.md +136 -0
  87. package/docs/consumer-rehearsal.md +115 -0
  88. package/docs/contracts.md +267 -0
  89. package/docs/live-proofs.md +270 -0
  90. package/docs/principles.md +46 -0
  91. package/examples/event-logs/app-startup-baseline.log +4 -0
  92. package/examples/event-logs/app-startup-current.log +4 -0
  93. package/examples/minimal-app/README.md +70 -0
  94. package/examples/mobile-app/README.md +302 -0
  95. package/examples/mobile-app/app.json +22 -0
  96. package/examples/mobile-app/asl/package-scripts.json +32 -0
  97. package/examples/mobile-app/asl.config.json +37 -0
  98. package/examples/mobile-app/event-logs/android-app-startup.log +4 -0
  99. package/examples/mobile-app/event-logs/android-open-close-cycle.log +12 -0
  100. package/examples/mobile-app/event-logs/android-scroll-settle.log +12 -0
  101. package/examples/mobile-app/event-logs/app-startup.log +4 -0
  102. package/examples/mobile-app/event-logs/open-close-cycle.log +12 -0
  103. package/examples/mobile-app/event-logs/scroll-settle.log +12 -0
  104. package/examples/mobile-app/index.ts +20 -0
  105. package/examples/mobile-app/metro.config.js +20 -0
  106. package/examples/mobile-app/package.json +62 -0
  107. package/examples/mobile-app/patches/expo-modules-jsi@56.0.10.patch +19 -0
  108. package/examples/mobile-app/plugins/with-ios-build-compat.js +271 -0
  109. package/examples/mobile-app/pnpm-lock.yaml +4440 -0
  110. package/examples/mobile-app/runner-manifests/evidence-provider.json +79 -0
  111. package/examples/mobile-app/runner-manifests/primary-runner.json +19 -0
  112. package/examples/mobile-app/scenarios/android/app-startup-video.json +73 -0
  113. package/examples/mobile-app/scenarios/android/app-startup.json +44 -0
  114. package/examples/mobile-app/scenarios/android/open-close-cycle.json +54 -0
  115. package/examples/mobile-app/scenarios/android/scroll-settle.json +49 -0
  116. package/examples/mobile-app/scenarios/ios/app-startup.json +44 -0
  117. package/examples/mobile-app/scenarios/ios/open-close-cycle.json +54 -0
  118. package/examples/mobile-app/scenarios/ios/scroll-settle.json +49 -0
  119. package/examples/mobile-app/scenarios/mobile/app-startup.json +91 -0
  120. package/examples/mobile-app/scenarios/mobile/open-close-cycle.json +160 -0
  121. package/examples/mobile-app/scenarios/mobile/scroll-settle.json +148 -0
  122. package/examples/mobile-app/scripts/asl-capture-accessibility-provider.mjs +112 -0
  123. package/examples/mobile-app/scripts/asl-capture-profiler-provider.mjs +127 -0
  124. package/examples/mobile-app/src/devtools/profile-session.ts +7 -0
  125. package/examples/mobile-app/src/example-screen.tsx +322 -0
  126. package/examples/mobile-app/tsconfig.json +16 -0
  127. package/examples/mobile-app/tsconfig.typecheck.json +13 -0
  128. package/examples/runners/README.md +44 -0
  129. package/examples/runners/adb-android.json +25 -0
  130. package/examples/runners/agent-device-android.json +27 -0
  131. package/examples/runners/agent-device-ios.json +27 -0
  132. package/examples/runners/argent-android.json +32 -0
  133. package/examples/runners/argent-ios.json +32 -0
  134. package/examples/runners/argent-react-profiler-provider.json +15 -0
  135. package/examples/runners/axe-accessibility-provider.json +24 -0
  136. package/examples/runners/manual-log-ingest.json +9 -0
  137. package/examples/runners/rozenite-profiler-provider.json +9 -0
  138. package/examples/runners/script-accessibility-provider.json +24 -0
  139. package/examples/runners/script-memory-provider.json +24 -0
  140. package/examples/runners/script-network-provider.json +24 -0
  141. package/examples/runners/script-profiler-provider.json +30 -0
  142. package/examples/runners/xcodebuildmcp-ios.json +29 -0
  143. package/examples/scenarios/ios/app-startup.json +28 -0
  144. package/examples/scenarios/ios/open-close-cycle.json +35 -0
  145. package/examples/scenarios/mobile/app-startup.json +72 -0
  146. package/examples/scenarios/mobile/media-open-close.json +141 -0
  147. package/examples/scenarios/mobile/open-close-cycle.json +135 -0
  148. package/examples/scenarios/mobile/scroll-settle.json +106 -0
  149. package/package.json +240 -0
  150. package/schemas/budget-verdict.schema.json +115 -0
  151. package/schemas/causal-run.schema.json +279 -0
  152. package/schemas/comparison.schema.json +196 -0
  153. package/schemas/health.schema.json +108 -0
  154. package/schemas/live-proof-set.schema.json +195 -0
  155. package/schemas/live-proof.schema.json +413 -0
  156. package/schemas/manifest.schema.json +204 -0
  157. package/schemas/metrics.schema.json +137 -0
  158. package/schemas/project-validation.schema.json +343 -0
  159. package/schemas/runner-capabilities.schema.json +217 -0
  160. package/schemas/scenario.schema.json +400 -0
  161. package/schemas/verdict.schema.json +88 -0
  162. package/templates/evidence-provider.json +83 -0
  163. package/templates/gitignore-snippet +9 -0
  164. package/templates/integration-readme.md +125 -0
  165. package/templates/mobile-scenario.json +133 -0
  166. package/templates/package-scripts.json +32 -0
  167. package/templates/primary-runner.json +19 -0
  168. package/templates/project.config.json +37 -0
  169. package/templates/scripts/asl-capture-accessibility-provider.mjs +112 -0
  170. package/templates/scripts/asl-capture-profiler-provider.mjs +127 -0
@@ -0,0 +1,183 @@
1
+ type ArgentCommandResult = {
2
+ action: string;
3
+ args: string[];
4
+ capturePath?: string;
5
+ command: string;
6
+ exitCode: number;
7
+ rawFileName: string;
8
+ stderr: string;
9
+ stdout: string;
10
+ };
11
+ type ArgentCommandExecutor = (command: string, args: string[]) => Promise<{
12
+ args: string[];
13
+ command: string;
14
+ exitCode: number;
15
+ stderr: string;
16
+ stdout: string;
17
+ }>;
18
+ type ArgentDriverOptions = {
19
+ appFlag?: string;
20
+ appId?: string;
21
+ argentCommand: string;
22
+ baseArgs?: string[];
23
+ deviceFlag?: string;
24
+ deviceId: string;
25
+ executor: ArgentCommandExecutor;
26
+ extraArgs?: string[];
27
+ screenSize?: ArgentScreenSize;
28
+ };
29
+ type ArgentDriver = {
30
+ assertVisible: (options: ArgentAssertVisibleOptions) => Promise<ArgentCommandResult>;
31
+ inspectTree: (options?: ArgentInspectTreeOptions) => Promise<ArgentCommandResult>;
32
+ launchApp: (options?: ArgentLaunchAppOptions) => Promise<ArgentCommandResult>;
33
+ openUrl: (options: ArgentOpenUrlOptions) => Promise<ArgentCommandResult>;
34
+ screenshot: (options?: ArgentScreenshotOptions) => Promise<ArgentCommandResult>;
35
+ scroll: (options: ArgentScrollOptions) => Promise<ArgentCommandResult>;
36
+ tap: (options: ArgentTapOptions) => Promise<ArgentCommandResult>;
37
+ };
38
+ type ArgentScreenSize = {
39
+ height: number;
40
+ width: number;
41
+ };
42
+ type ArgentPointInput = {
43
+ screenSize?: ArgentScreenSize;
44
+ x: number;
45
+ y: number;
46
+ };
47
+ type ArgentNormalizedPoint = {
48
+ x: number;
49
+ y: number;
50
+ };
51
+ type ArgentSelector = {
52
+ kind: string;
53
+ match?: string;
54
+ value: string;
55
+ };
56
+ type ArgentAssertVisibleOptions = {
57
+ appId?: string;
58
+ rawFileName?: string;
59
+ selector: ArgentSelector;
60
+ };
61
+ type ArgentInspectTreeOptions = {
62
+ appId?: string;
63
+ rawFileName?: string;
64
+ };
65
+ type ArgentLaunchAppOptions = {
66
+ appId?: string;
67
+ rawFileName?: string;
68
+ };
69
+ type ArgentOpenUrlOptions = {
70
+ rawFileName?: string;
71
+ url: string;
72
+ };
73
+ type ArgentScreenshotOptions = {
74
+ rawFileName?: string;
75
+ };
76
+ type ArgentScrollOptions = {
77
+ durationMs?: number;
78
+ endX: number;
79
+ endY: number;
80
+ rawFileName?: string;
81
+ screenSize?: ArgentScreenSize;
82
+ startX: number;
83
+ startY: number;
84
+ };
85
+ type ArgentTapOptions = {
86
+ rawFileName?: string;
87
+ screenSize?: ArgentScreenSize;
88
+ x: number;
89
+ y: number;
90
+ };
91
+ /**
92
+ * Returns true when Argent emitted a complete action result before a wrapper timeout.
93
+ *
94
+ * @param {string} action
95
+ * @param {string} stdout
96
+ * @param {string | undefined} capturePath
97
+ * @returns {boolean}
98
+ */
99
+ declare function hasCompletedArgentOutput(action: string, stdout: string, capturePath?: string): boolean;
100
+ /**
101
+ * Keeps successful Argent output from being failed only because a wrapper helper lingered.
102
+ *
103
+ * @param {ArgentCommandResult} result
104
+ * @returns {ArgentCommandResult}
105
+ */
106
+ declare function normalizeArgentResultExitCode(result: ArgentCommandResult): ArgentCommandResult;
107
+ /**
108
+ * Combines stdout and stderr into raw evidence text.
109
+ *
110
+ * @param {{stdout: string, stderr: string}} result
111
+ * @returns {string}
112
+ */
113
+ declare function formatArgentRawOutput(result: {
114
+ stdout: string;
115
+ stderr: string;
116
+ }): string;
117
+ /**
118
+ * Builds the command argv for one `argent run <tool>` style invocation.
119
+ *
120
+ * @param {ArgentDriverOptions} options
121
+ * @param {string} tool
122
+ * @param {string[]} [args]
123
+ * @returns {string[]}
124
+ */
125
+ declare function buildArgentRunArgs(options: ArgentDriverOptions, tool: string, args?: string[]): string[];
126
+ /**
127
+ * Extracts a local screenshot path from common Argent CLI output.
128
+ *
129
+ * @param {string} text
130
+ * @returns {string | null}
131
+ */
132
+ declare function extractArgentScreenshotPath(text: string): string | null;
133
+ /**
134
+ * Parses a JSON object from Argent output when the tool returns structured data.
135
+ *
136
+ * @param {string} text
137
+ * @returns {Record<string, unknown> | null}
138
+ */
139
+ declare function parseArgentRunJson(text: string): Record<string, unknown> | null;
140
+ /**
141
+ * Returns the Argent description text from either plain or JSON command output.
142
+ *
143
+ * @param {string} text
144
+ * @returns {string}
145
+ */
146
+ declare function readArgentDescription(text: string): string;
147
+ /**
148
+ * Detects collapsed UI descriptions that should fail scenario health upstream.
149
+ *
150
+ * @param {string} text
151
+ * @returns {boolean}
152
+ */
153
+ declare function isArgentRootOnlyDescription(text: string): boolean;
154
+ /**
155
+ * Converts pixel coordinates into Argent's normalized 0-1 coordinate space.
156
+ *
157
+ * @param {ArgentPointInput} point
158
+ * @returns {ArgentNormalizedPoint}
159
+ */
160
+ declare function normalizeArgentPoint({ screenSize, x, y }: ArgentPointInput): ArgentNormalizedPoint;
161
+ /**
162
+ * Checks whether an Argent UI description contains a portable selector target.
163
+ *
164
+ * @param {{description: string, selector: ArgentSelector}} options
165
+ * @returns {boolean}
166
+ */
167
+ declare function matchesArgentSelector({ description, selector, }: {
168
+ description: string;
169
+ selector: ArgentSelector;
170
+ }): boolean;
171
+ /**
172
+ * Creates an Argent-backed driver for portable interaction actions.
173
+ *
174
+ * The adapter shells out through an injected executor so consumers can use a
175
+ * global `argent` binary, `npx --yes @swmansion/argent run`, or a test double
176
+ * without making Argent a package dependency.
177
+ *
178
+ * @param {ArgentDriverOptions} options
179
+ * @returns {ArgentDriver}
180
+ */
181
+ declare function createArgentDriver(options: ArgentDriverOptions): ArgentDriver;
182
+ export { buildArgentRunArgs, createArgentDriver, extractArgentScreenshotPath, formatArgentRawOutput, hasCompletedArgentOutput, isArgentRootOnlyDescription, matchesArgentSelector, normalizeArgentPoint, normalizeArgentResultExitCode, parseArgentRunJson, readArgentDescription, };
183
+ export type { ArgentAssertVisibleOptions, ArgentCommandExecutor, ArgentCommandResult, ArgentDriver, ArgentDriverOptions, ArgentInspectTreeOptions, ArgentLaunchAppOptions, ArgentNormalizedPoint, ArgentOpenUrlOptions, ArgentPointInput, ArgentScreenshotOptions, ArgentScreenSize, ArgentScrollOptions, ArgentSelector, ArgentTapOptions, };
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildArgentRunArgs = buildArgentRunArgs;
4
+ exports.createArgentDriver = createArgentDriver;
5
+ exports.extractArgentScreenshotPath = extractArgentScreenshotPath;
6
+ exports.formatArgentRawOutput = formatArgentRawOutput;
7
+ exports.hasCompletedArgentOutput = hasCompletedArgentOutput;
8
+ exports.isArgentRootOnlyDescription = isArgentRootOnlyDescription;
9
+ exports.matchesArgentSelector = matchesArgentSelector;
10
+ exports.normalizeArgentPoint = normalizeArgentPoint;
11
+ exports.normalizeArgentResultExitCode = normalizeArgentResultExitCode;
12
+ exports.parseArgentRunJson = parseArgentRunJson;
13
+ exports.readArgentDescription = readArgentDescription;
14
+ /**
15
+ * Adds stable driver metadata to one Argent command result.
16
+ *
17
+ * @param {{action: string, capturePath?: string, rawFileName: string, result: Awaited<ReturnType<ArgentCommandExecutor>>}} options
18
+ * @returns {ArgentCommandResult}
19
+ */
20
+ function buildArgentResult({ action, capturePath, rawFileName, result, }) {
21
+ return {
22
+ action,
23
+ args: result.args,
24
+ command: result.command,
25
+ exitCode: result.exitCode,
26
+ rawFileName,
27
+ stderr: result.stderr,
28
+ stdout: result.stdout,
29
+ ...(capturePath ? { capturePath } : {}),
30
+ };
31
+ }
32
+ /**
33
+ * Returns true when Argent emitted a complete action result before a wrapper timeout.
34
+ *
35
+ * @param {string} action
36
+ * @param {string} stdout
37
+ * @param {string | undefined} capturePath
38
+ * @returns {boolean}
39
+ */
40
+ function hasCompletedArgentOutput(action, stdout, capturePath) {
41
+ const parsed = parseArgentRunJson(stdout);
42
+ if (action === 'screenshot') {
43
+ return typeof capturePath === 'string' && capturePath.length > 0;
44
+ }
45
+ if (['assertVisible', 'inspectTree'].includes(action)) {
46
+ return readArgentDescription(stdout).trim().length > 0;
47
+ }
48
+ if (parsed?.success === true || parsed?.launched === true) {
49
+ return true;
50
+ }
51
+ if (action === 'launch' && typeof parsed?.bundleId === 'string') {
52
+ return true;
53
+ }
54
+ return false;
55
+ }
56
+ /**
57
+ * Keeps successful Argent output from being failed only because a wrapper helper lingered.
58
+ *
59
+ * @param {ArgentCommandResult} result
60
+ * @returns {ArgentCommandResult}
61
+ */
62
+ function normalizeArgentResultExitCode(result) {
63
+ if (result.exitCode !== 0 &&
64
+ /timed out after \d+ms/iu.test(result.stderr) &&
65
+ hasCompletedArgentOutput(result.action, result.stdout, result.capturePath)) {
66
+ return {
67
+ ...result,
68
+ exitCode: 0,
69
+ };
70
+ }
71
+ return result;
72
+ }
73
+ /**
74
+ * Combines stdout and stderr into raw evidence text.
75
+ *
76
+ * @param {{stdout: string, stderr: string}} result
77
+ * @returns {string}
78
+ */
79
+ function formatArgentRawOutput(result) {
80
+ return [result.stdout, result.stderr].filter(Boolean).join('\n');
81
+ }
82
+ /**
83
+ * Builds the command argv for one `argent run <tool>` style invocation.
84
+ *
85
+ * @param {ArgentDriverOptions} options
86
+ * @param {string} tool
87
+ * @param {string[]} [args]
88
+ * @returns {string[]}
89
+ */
90
+ function buildArgentRunArgs(options, tool, args = []) {
91
+ return [
92
+ ...(options.baseArgs ?? ['run']),
93
+ tool,
94
+ options.deviceFlag ?? '--udid',
95
+ options.deviceId,
96
+ ...args,
97
+ ...(options.extraArgs ?? []),
98
+ ];
99
+ }
100
+ /**
101
+ * Extracts a local screenshot path from common Argent CLI output.
102
+ *
103
+ * @param {string} text
104
+ * @returns {string | null}
105
+ */
106
+ function extractArgentScreenshotPath(text) {
107
+ const parsed = parseArgentRunJson(text);
108
+ for (const key of ['path', 'image']) {
109
+ const jsonPath = parsed?.[key];
110
+ if (typeof jsonPath === 'string' && jsonPath.length > 0) {
111
+ return jsonPath;
112
+ }
113
+ }
114
+ const savedMatch = /(?:Saved screenshot|Screenshot saved(?: to)?):\s*(?<path>\S.+)$/imu.exec(text);
115
+ return savedMatch?.groups?.path?.trim() ?? null;
116
+ }
117
+ /**
118
+ * Parses a JSON object from Argent output when the tool returns structured data.
119
+ *
120
+ * @param {string} text
121
+ * @returns {Record<string, unknown> | null}
122
+ */
123
+ function parseArgentRunJson(text) {
124
+ const trimmed = text.trim();
125
+ if (!trimmed) {
126
+ return null;
127
+ }
128
+ try {
129
+ const parsed = JSON.parse(trimmed);
130
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
131
+ ? parsed
132
+ : null;
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ }
138
+ /**
139
+ * Returns the Argent description text from either plain or JSON command output.
140
+ *
141
+ * @param {string} text
142
+ * @returns {string}
143
+ */
144
+ function readArgentDescription(text) {
145
+ const parsed = parseArgentRunJson(text);
146
+ return typeof parsed?.description === 'string' ? parsed.description : text;
147
+ }
148
+ /**
149
+ * Detects collapsed UI descriptions that should fail scenario health upstream.
150
+ *
151
+ * @param {string} text
152
+ * @returns {boolean}
153
+ */
154
+ function isArgentRootOnlyDescription(text) {
155
+ const description = readArgentDescription(text).trim();
156
+ return /^root$/iu.test(description) || /^<root\s*\/?>$/iu.test(description);
157
+ }
158
+ /**
159
+ * Converts pixel coordinates into Argent's normalized 0-1 coordinate space.
160
+ *
161
+ * @param {ArgentPointInput} point
162
+ * @returns {ArgentNormalizedPoint}
163
+ */
164
+ function normalizeArgentPoint({ screenSize, x, y }) {
165
+ if (!screenSize) {
166
+ return { x, y };
167
+ }
168
+ if (!Number.isFinite(screenSize.width) ||
169
+ !Number.isFinite(screenSize.height) ||
170
+ screenSize.width <= 0 ||
171
+ screenSize.height <= 0) {
172
+ throw new Error('Argent screen size must have positive width and height.');
173
+ }
174
+ return {
175
+ x: Math.min(Math.max(x / screenSize.width, 0), 1),
176
+ y: Math.min(Math.max(y / screenSize.height, 0), 1),
177
+ };
178
+ }
179
+ /**
180
+ * Normalizes a point while preserving exact optional property semantics.
181
+ *
182
+ * @param {{screenSize?: ArgentScreenSize, x: number, y: number}} point
183
+ * @returns {ArgentNormalizedPoint}
184
+ */
185
+ function normalizeArgentPointWithOptionalScreenSize({ screenSize, x, y, }) {
186
+ return screenSize
187
+ ? normalizeArgentPoint({ screenSize, x, y })
188
+ : normalizeArgentPoint({ x, y });
189
+ }
190
+ /**
191
+ * Checks whether an Argent UI description contains a portable selector target.
192
+ *
193
+ * @param {{description: string, selector: ArgentSelector}} options
194
+ * @returns {boolean}
195
+ */
196
+ function matchesArgentSelector({ description, selector, }) {
197
+ const descriptionText = readArgentDescription(description);
198
+ const matchMode = selector.match ?? (selector.kind === 'regex' ? 'regex' : undefined);
199
+ if (matchMode === 'regex') {
200
+ try {
201
+ return new RegExp(selector.value, 'u').test(descriptionText);
202
+ }
203
+ catch {
204
+ return false;
205
+ }
206
+ }
207
+ return descriptionText.includes(selector.value);
208
+ }
209
+ /**
210
+ * Creates an Argent-backed driver for portable interaction actions.
211
+ *
212
+ * The adapter shells out through an injected executor so consumers can use a
213
+ * global `argent` binary, `npx --yes @swmansion/argent run`, or a test double
214
+ * without making Argent a package dependency.
215
+ *
216
+ * @param {ArgentDriverOptions} options
217
+ * @returns {ArgentDriver}
218
+ */
219
+ function createArgentDriver(options) {
220
+ const run = async (action, tool, rawFileName, args = [], capturePath) => {
221
+ const result = await options.executor(options.argentCommand, buildArgentRunArgs(options, tool, args));
222
+ return normalizeArgentResultExitCode(buildArgentResult({
223
+ action,
224
+ rawFileName,
225
+ result,
226
+ ...(capturePath ? { capturePath } : {}),
227
+ }));
228
+ };
229
+ const appArgs = (appId) => {
230
+ const resolvedAppId = appId ?? options.appId;
231
+ return resolvedAppId ? [options.appFlag ?? '--bundleId', resolvedAppId] : [];
232
+ };
233
+ return {
234
+ async assertVisible({ appId, rawFileName = 'argent-assert-visible.txt', selector, }) {
235
+ const result = await run('assertVisible', 'describe', rawFileName, appArgs(appId));
236
+ if (!matchesArgentSelector({ description: formatArgentRawOutput(result), selector })) {
237
+ return {
238
+ ...result,
239
+ exitCode: result.exitCode === 0 ? 1 : result.exitCode,
240
+ stderr: [result.stderr, `Argent description did not include ${selector.kind} \`${selector.value}\`.`]
241
+ .filter(Boolean)
242
+ .join('\n'),
243
+ };
244
+ }
245
+ return {
246
+ ...result,
247
+ exitCode: 0,
248
+ };
249
+ },
250
+ async inspectTree({ appId, rawFileName = 'argent-describe.txt', } = {}) {
251
+ return run('inspectTree', 'describe', rawFileName, appArgs(appId));
252
+ },
253
+ async launchApp({ appId, rawFileName = 'argent-launch-app.txt', } = {}) {
254
+ return run('launch', 'launch-app', rawFileName, appArgs(appId));
255
+ },
256
+ async openUrl({ rawFileName = 'argent-open-url.txt', url, }) {
257
+ return run('openUrl', 'open-url', rawFileName, ['--url', url]);
258
+ },
259
+ async screenshot({ rawFileName = 'argent-screenshot.txt', } = {}) {
260
+ const result = await run('screenshot', 'screenshot', rawFileName, ['--includeImageInContext', 'false']);
261
+ const capturePath = extractArgentScreenshotPath(result.stdout);
262
+ return normalizeArgentResultExitCode(capturePath ? { ...result, capturePath } : result);
263
+ },
264
+ async scroll({ durationMs = 300, endX, endY, rawFileName = 'argent-swipe.txt', screenSize, startX, startY, }) {
265
+ const from = normalizeArgentPointWithOptionalScreenSize({
266
+ ...(screenSize ?? options.screenSize ? { screenSize: screenSize ?? options.screenSize } : {}),
267
+ x: startX,
268
+ y: startY,
269
+ });
270
+ const to = normalizeArgentPointWithOptionalScreenSize({
271
+ ...(screenSize ?? options.screenSize ? { screenSize: screenSize ?? options.screenSize } : {}),
272
+ x: endX,
273
+ y: endY,
274
+ });
275
+ return run('scroll', 'gesture-swipe', rawFileName, [
276
+ '--fromX',
277
+ String(from.x),
278
+ '--fromY',
279
+ String(from.y),
280
+ '--toX',
281
+ String(to.x),
282
+ '--toY',
283
+ String(to.y),
284
+ '--durationMs',
285
+ String(durationMs),
286
+ ]);
287
+ },
288
+ async tap({ rawFileName = 'argent-tap.txt', screenSize, x, y, }) {
289
+ const point = normalizeArgentPointWithOptionalScreenSize({
290
+ ...(screenSize ?? options.screenSize ? { screenSize: screenSize ?? options.screenSize } : {}),
291
+ x,
292
+ y,
293
+ });
294
+ return run('tap', 'gesture-tap', rawFileName, ['--x', String(point.x), '--y', String(point.y)]);
295
+ },
296
+ };
297
+ }