@swmansion/argent 0.22.1-next.7 → 0.22.1-next.8
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.
package/dist/cli-cmds.mjs
CHANGED
package/dist/installer.mjs
CHANGED
|
@@ -16428,7 +16428,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
16428
16428
|
var SESSION_ID = randomUUID4();
|
|
16429
16429
|
function readCliVersion() {
|
|
16430
16430
|
if (true) {
|
|
16431
|
-
return "0.22.1-next.
|
|
16431
|
+
return "0.22.1-next.8";
|
|
16432
16432
|
}
|
|
16433
16433
|
return "0.0.0";
|
|
16434
16434
|
}
|
package/dist/tool-server.cjs
CHANGED
|
@@ -95734,7 +95734,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
95734
95734
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
95735
95735
|
function readCliVersion() {
|
|
95736
95736
|
if (true) {
|
|
95737
|
-
return "0.22.1-next.
|
|
95737
|
+
return "0.22.1-next.8";
|
|
95738
95738
|
}
|
|
95739
95739
|
return "0.0.0";
|
|
95740
95740
|
}
|
|
@@ -111155,7 +111155,7 @@ function isNativeDevtoolsBlockResult(toolId, result) {
|
|
|
111155
111155
|
async function precheckNativeDevtools(api, udid, bundleId) {
|
|
111156
111156
|
if (bundleId !== void 0 && !isInjectableBundleId(bundleId)) {
|
|
111157
111157
|
throw new FailureError(
|
|
111158
|
-
`${bundleId} is an Apple system app: it is
|
|
111158
|
+
`${bundleId} is an Apple system app: it is never the app under test, so Argent native devtools refuse to read one \u2014 treat it as unavailable rather than retrying. ` + NON_INJECTABLE_RECOVERY,
|
|
111159
111159
|
{
|
|
111160
111160
|
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_NOT_INJECTABLE,
|
|
111161
111161
|
failure_stage: "native_devtools_precheck",
|
|
@@ -111854,7 +111854,7 @@ function tcpArtifactHint(err) {
|
|
|
111854
111854
|
return /TCP-transport (?:binary|dylib) not found/.test(message) ? message : void 0;
|
|
111855
111855
|
}
|
|
111856
111856
|
var TVOS_HINT = "This is an Apple TV (tvOS) simulator, which the iOS accessibility service does not support. Use the `describe` tool to read the focused and focusable elements, `tv-remote` (up/down/left/right/select/back/menu/home) to move focus, and `keyboard` to type. See the argent-tv-interact skill.";
|
|
111857
|
-
var NON_INJECTABLE_HINT = "This is an Apple system app (com.apple.*), which
|
|
111857
|
+
var NON_INJECTABLE_HINT = "This is an Apple system app (com.apple.*), which argent's native-devtools instrumentation does not support \u2014 the native view hierarchy is unavailable and restarting the app will NOT help. Take a `screenshot` to see the screen and interact by coordinate. " + NON_INJECTABLE_NATIVE_WARNING;
|
|
111858
111858
|
function emptyTree() {
|
|
111859
111859
|
return parseDescribeResult({
|
|
111860
111860
|
role: "AXGroup",
|
|
@@ -116225,10 +116225,10 @@ Returns { envSetup, appRunning, connected, requiresRestart, state, message, next
|
|
|
116225
116225
|
- state: why devtools are or aren't live, measured from the running process. "connected"; "not_running"; "stale_process" (the process cannot reach this simulator's devtools endpoint \u2014 launched either before argent's instrumentation was in place or against an earlier tool-server's listener \u2014 so restart-app fixes it); "unregistered" (the process IS injected and pointed at this simulator's devtools endpoint yet the service never registered it, so restarting the app cannot help); "connecting" (the process IS injected but launched moments ago and is still connecting, so waiting is what helps); "indeterminate" (the process could not be inspected). Omitted when injectable is false, which is terminal on its own.
|
|
116226
116226
|
- message: the remedy for that state, in full. Omitted when connected or non-injectable. Prefer it over inferring one from the booleans \u2014 it is the only field that can tell you to stop restarting the app.
|
|
116227
116227
|
- nextLaunchWillBeInjected: if you launch this bundle now, native devtools env setup is already in place (always false for a non-injectable app)
|
|
116228
|
-
- injectable: whether
|
|
116228
|
+
- injectable: whether this app is a supported target for Argent native devtools. Apple system apps (bundle ids under com.apple.) are not: they are never the app under test, so the native tools refuse to read one.
|
|
116229
116229
|
|
|
116230
116230
|
Call this before using app-scoped native hierarchy tools or native-network-logs.
|
|
116231
|
-
If injectable is false: treat this as TERMINAL \u2014
|
|
116231
|
+
If injectable is false: treat this as TERMINAL \u2014 the app is not a supported native-devtools target, and no relaunch changes that. Do NOT restart/retry. Use the standard \`describe\` tool (its accessibility path reads the screen without injection) or \`screenshot\` (then interact by coordinate). Do not fall back to the native-devtools feature tools (native-describe-screen, native-find-views, native-full-hierarchy, native-network-logs, native-view-at-point, native-user-interactable-view-at-point) \u2014 they run the same injection precheck and fail with the same non-injectable error.
|
|
116232
116232
|
If appRunning is false and nextLaunchWillBeInjected is true: use launch-app normally.
|
|
116233
116233
|
If requiresRestart is true: call restart-app once, then proceed with the native feature. Read state before acting on a second such reading \u2014 indeterminate reaches this rule too, and its line below bounds it at that one restart.
|
|
116234
116234
|
If state is unregistered: do NOT restart the app again \u2014 it already launched under the terms a restart would recreate. Restart the tool-server (\`argent server stop && argent server start --detach\`), then retry. If it reads unregistered again after that restart, stop: the process loads argent's dylib but never dials, and no further restart on either side changes it \u2014 treat native devtools as unavailable, then use \`describe\` or \`screenshot\` and drive by coordinate.
|
|
@@ -144653,7 +144653,7 @@ var FULL_HIERARCHY_FIELDS = [
|
|
|
144653
144653
|
];
|
|
144654
144654
|
async function unreadableHierarchyReason(nativeApi, bundleId) {
|
|
144655
144655
|
if (!isInjectableBundleId(bundleId)) {
|
|
144656
|
-
return `${bundleId} is an Apple system app: it is
|
|
144656
|
+
return `${bundleId} is an Apple system app: it is never the app under test, so argent's native devtools refuse to read one, and without them a flow has no view hierarchy to resolve selectors against. Replace the selector steps with coordinate ones \u2014 \`tap: { x: 0.5, y: 0.35 }\` takes a point directly and reads no tree \u2014 or target an app argent installs.`;
|
|
144657
144657
|
}
|
|
144658
144658
|
const state3 = await nativeApi.appConnectionState(bundleId).catch(() => "indeterminate");
|
|
144659
144659
|
if (state3 === "connected") {
|
|
@@ -144661,7 +144661,10 @@ async function unreadableHierarchyReason(nativeApi, bundleId) {
|
|
|
144661
144661
|
}
|
|
144662
144662
|
return `${buildAppStateMessage(bundleId, state3)} Flows resolve selectors against the full view hierarchy native devtools serve.`;
|
|
144663
144663
|
}
|
|
144664
|
-
|
|
144664
|
+
function systemAppFlowTargetRefusal(bundleId) {
|
|
144665
|
+
return `${bundleId} is an Apple system app (com.apple.*) - never a valid flow target: it is not the app under test, and argent's native devtools refuse to read one (a system process either never services the read, or describes offscreen UI as if it were the launched app), so this flow has no view hierarchy to resolve selectors against and no relaunch or retry changes this verdict. Replace the selector steps with coordinate ones - \`tap: { x: 0.5, y: 0.35 }\` takes a point directly and reads no tree - or point this flow's \`launch\` step at the app under test.`;
|
|
144666
|
+
}
|
|
144667
|
+
async function queryFullHierarchyTree(registry2, device, target) {
|
|
144665
144668
|
let nativeApi;
|
|
144666
144669
|
try {
|
|
144667
144670
|
const ndRef = nativeDevtoolsRef(device);
|
|
@@ -144672,22 +144675,113 @@ async function queryFullHierarchyTree(registry2, device, launchedNativeApp) {
|
|
|
144672
144675
|
{ cause: err }
|
|
144673
144676
|
);
|
|
144674
144677
|
}
|
|
144675
|
-
|
|
144676
|
-
|
|
144677
|
-
|
|
144678
|
-
|
|
144679
|
-
|
|
144680
|
-
|
|
144681
|
-
|
|
144682
|
-
|
|
144683
|
-
|
|
144684
|
-
|
|
144685
|
-
}
|
|
144686
|
-
|
|
144678
|
+
let bundleId;
|
|
144679
|
+
if (target?.pinned) {
|
|
144680
|
+
bundleId = target.bundleId;
|
|
144681
|
+
if (!isInjectableBundleId(bundleId)) {
|
|
144682
|
+
throw new FailureError(systemAppFlowTargetRefusal(bundleId), {
|
|
144683
|
+
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_NOT_INJECTABLE,
|
|
144684
|
+
failure_stage: "flow_tree_pinned_target",
|
|
144685
|
+
failure_area: "tool_server",
|
|
144686
|
+
error_kind: "validation"
|
|
144687
|
+
});
|
|
144688
|
+
}
|
|
144689
|
+
if (!nativeApi.isConnected(bundleId)) {
|
|
144690
|
+
throw new FailureError(
|
|
144691
|
+
`${bundleId} lost its devtools connection after launch (the app crashed, was terminated, or its socket closed) - restart it (restart-app, or a flow \`launch\` step) so the full view hierarchy is readable; launch-app recovers only the causes that killed the process, since on iOS it just foregrounds one that is still alive`,
|
|
144692
|
+
{
|
|
144693
|
+
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_NOT_CONNECTED,
|
|
144694
|
+
failure_stage: "flow_tree_pinned_target",
|
|
144695
|
+
failure_area: "tool_server",
|
|
144696
|
+
error_kind: "not_found"
|
|
144697
|
+
}
|
|
144698
|
+
);
|
|
144687
144699
|
}
|
|
144700
|
+
let pinnedState;
|
|
144701
|
+
try {
|
|
144702
|
+
pinnedState = await nativeApi.getAppState(bundleId);
|
|
144703
|
+
target.probeAnswered = true;
|
|
144704
|
+
} catch (err) {
|
|
144705
|
+
if (getFailureSignal(err)?.error_code !== FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT) {
|
|
144706
|
+
throw err;
|
|
144707
|
+
}
|
|
144708
|
+
if (target.probeAnswered) {
|
|
144709
|
+
throw new FailureError(
|
|
144710
|
+
`${bundleId} (the launched app) stopped answering Application.getState - the probe timed out although an earlier one in this run answered, so the app's main queue is no longer being serviced. For a pinned app that is usually the suspension iOS applies once a flow leaves it (e.g. a tap that opened another app), and a suspended app's hierarchy is not what is on screen; in-app work blocking the main thread past the probe timeout looks the same from here. Reading anyway parks on that same unserviced queue: certain to time out if the app is suspended, and paying the longer hierarchy timeout to find that out if it is not. If this flow's subject IS the other app, give the flow a \`launch:\` step for that app - it re-pins reads to it, and a pinned read probes only the app it names, so the silent ${bundleId} is never touched; \`tool: launch-app\` or \`tool: restart-app\` naming that app works too - each re-targets reads at the app it starts, and is how a recorded flow switches apps. A foreground-NEUTRAL raw \`tool:\` step does not work here, because demoting the pin sends reads back to auto-resolve, which probes every connection at once and is sunk by this same silent one. If the flow left ${bundleId} but is still about it, make it return before reading the UI. If it never left, the main thread is busy: raise the step's \`timeout:\` so the poll re-reads past the work, or \`launch\` ${bundleId} again.`,
|
|
144711
|
+
{
|
|
144712
|
+
// The timeout's own code, NOT
|
|
144713
|
+
// NATIVE_TARGET_SINGLE_APP_NOT_FOREGROUND: nothing answered, so no
|
|
144714
|
+
// app state was observed to classify it by.
|
|
144715
|
+
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT,
|
|
144716
|
+
failure_stage: "flow_tree_pinned_target",
|
|
144717
|
+
failure_area: "tool_server",
|
|
144718
|
+
error_kind: "timeout"
|
|
144719
|
+
},
|
|
144720
|
+
err instanceof Error ? { cause: err } : void 0
|
|
144721
|
+
);
|
|
144722
|
+
}
|
|
144723
|
+
}
|
|
144724
|
+
if (pinnedState && !chooseFrontmostConnectedApp([pinnedState])) {
|
|
144725
|
+
throw new FailureError(
|
|
144726
|
+
`${bundleId} (the launched app) has no foreground presence at all (applicationState=${pinnedState.applicationState}, foregroundActiveScenes=${pinnedState.foregroundActiveSceneCount}, foregroundInactiveScenes=${pinnedState.foregroundInactiveSceneCount}) - a step in this flow left the app (e.g. a tap that opened another app), so a read of its hierarchy would describe a screen that is not on screen. Transitional states are NOT refused here: an \`inactive\` app, or one still holding a foreground scene, is read as usual - under a system alert or mid-transition it is still the app on screen. If this flow's subject IS another app, give the flow a \`launch:\` step for that app - it re-pins reads to it, and no wedged sibling connection can sink a pinned read; a raw \`tool:\` step demotes the pin and returns reads to frontmost auto-resolve, which works when the app on screen answers but is sunk by a single wedged connection. Otherwise make the flow return to ${bundleId} before reading the UI, or \`launch\` it again.`,
|
|
144727
|
+
{
|
|
144728
|
+
error_code: FAILURE_CODES.NATIVE_TARGET_SINGLE_APP_NOT_FOREGROUND,
|
|
144729
|
+
failure_stage: "flow_tree_pinned_target",
|
|
144730
|
+
failure_area: "tool_server",
|
|
144731
|
+
error_kind: "validation"
|
|
144732
|
+
}
|
|
144733
|
+
);
|
|
144734
|
+
}
|
|
144735
|
+
} else {
|
|
144736
|
+
if (target && nativeApi.listConnectedBundleIds().length === 0) {
|
|
144737
|
+
throw new Error(await unreadableHierarchyReason(nativeApi, target.bundleId));
|
|
144738
|
+
}
|
|
144739
|
+
let resolved;
|
|
144740
|
+
try {
|
|
144741
|
+
resolved = await resolveNativeTargetApp(nativeApi, void 0);
|
|
144742
|
+
} catch (err) {
|
|
144743
|
+
const timedOut = getFailureSignal(err)?.error_code === FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT;
|
|
144744
|
+
if (!timedOut || !target) throw err;
|
|
144745
|
+
if (!isInjectableBundleId(target.bundleId)) {
|
|
144746
|
+
throw new FailureError(
|
|
144747
|
+
systemAppFlowTargetRefusal(target.bundleId),
|
|
144748
|
+
{
|
|
144749
|
+
error_code: FAILURE_CODES.NATIVE_DEVTOOLS_NOT_INJECTABLE,
|
|
144750
|
+
failure_stage: "flow_tree_unpinned_hint",
|
|
144751
|
+
failure_area: "tool_server",
|
|
144752
|
+
error_kind: "validation"
|
|
144753
|
+
},
|
|
144754
|
+
err instanceof Error ? { cause: err } : void 0
|
|
144755
|
+
);
|
|
144756
|
+
}
|
|
144757
|
+
if (!nativeApi.listConnectedBundleIds().includes(target.bundleId)) throw err;
|
|
144758
|
+
let hintState;
|
|
144759
|
+
try {
|
|
144760
|
+
hintState = await nativeApi.getAppState(target.bundleId);
|
|
144761
|
+
} catch (probeErr) {
|
|
144762
|
+
if (getFailureSignal(probeErr)?.error_code === FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT) {
|
|
144763
|
+
throw err;
|
|
144764
|
+
}
|
|
144765
|
+
throw probeErr;
|
|
144766
|
+
}
|
|
144767
|
+
if (!chooseFrontmostConnectedApp([hintState])) {
|
|
144768
|
+
throw new FailureError(
|
|
144769
|
+
`${target.bundleId} (the launched app) has no foreground presence at all (applicationState=${hintState.applicationState}, foregroundActiveScenes=${hintState.foregroundActiveSceneCount}, foregroundInactiveScenes=${hintState.foregroundInactiveSceneCount}) - auto-resolve's probe of every connection timed out and the read fell back to the launched app, but a step in this flow left it (e.g. a tap that opened another app), so reading its hierarchy would describe a screen that is not on screen. Transitional states are NOT refused here: an \`inactive\` app, or one still holding a foreground scene, is read as usual. If this flow's subject IS another app, give the flow a \`launch:\` step for that app; otherwise make the flow return to ${target.bundleId} before reading the UI, or \`launch\` it again.`,
|
|
144770
|
+
{
|
|
144771
|
+
error_code: FAILURE_CODES.NATIVE_TARGET_SINGLE_APP_NOT_FOREGROUND,
|
|
144772
|
+
failure_stage: "flow_tree_unpinned_hint",
|
|
144773
|
+
failure_area: "tool_server",
|
|
144774
|
+
error_kind: "validation"
|
|
144775
|
+
},
|
|
144776
|
+
err instanceof Error ? { cause: err } : void 0
|
|
144777
|
+
);
|
|
144778
|
+
}
|
|
144779
|
+
resolved = { bundleId: target.bundleId };
|
|
144780
|
+
}
|
|
144781
|
+
bundleId = resolved.bundleId;
|
|
144688
144782
|
}
|
|
144689
144783
|
const rawResult = await nativeApi.queryViewHierarchy(
|
|
144690
|
-
|
|
144784
|
+
bundleId,
|
|
144691
144785
|
"ViewHierarchy.getFullHierarchy",
|
|
144692
144786
|
{
|
|
144693
144787
|
fields: FULL_HIERARCHY_FIELDS,
|
|
@@ -144695,11 +144789,11 @@ async function queryFullHierarchyTree(registry2, device, launchedNativeApp) {
|
|
|
144695
144789
|
}
|
|
144696
144790
|
);
|
|
144697
144791
|
if (rawResult.error) {
|
|
144698
|
-
throw new Error(`getFullHierarchy failed for ${
|
|
144792
|
+
throw new Error(`getFullHierarchy failed for ${bundleId}: ${rawResult.error}`);
|
|
144699
144793
|
}
|
|
144700
144794
|
if (!Array.isArray(rawResult.windows) || rawResult.windows.length === 0) {
|
|
144701
144795
|
throw new Error(
|
|
144702
|
-
`getFullHierarchy returned no windows for ${
|
|
144796
|
+
`getFullHierarchy returned no windows for ${bundleId} - it has no window attached to read (backgrounded, or its first window not attached yet), so flows cannot resolve selectors against its view hierarchy; foreground or relaunch it, and if that bundle id is a com.apple.* system process the read resolved to a background system app rather than the app under test, so give this flow a \`launch\` step to pin reads to the right app`
|
|
144703
144797
|
);
|
|
144704
144798
|
}
|
|
144705
144799
|
const { tree, screen } = adaptFullHierarchy(rawResult);
|
|
@@ -144906,13 +145000,15 @@ async function queryVegaTree(device) {
|
|
|
144906
145000
|
}
|
|
144907
145001
|
|
|
144908
145002
|
// ../tool-server/src/tools/flows/flow-tree.ts
|
|
144909
|
-
async function fetchFlowTree(registry2, device,
|
|
145003
|
+
async function fetchFlowTree(registry2, device, target) {
|
|
144910
145004
|
const source = FLOW_TREE_SOURCES[device.platform];
|
|
144911
145005
|
if (!source) return fetchTree(registry2, device);
|
|
144912
|
-
return source(registry2, device,
|
|
145006
|
+
return source(registry2, device, target);
|
|
144913
145007
|
}
|
|
144914
145008
|
var FLOW_TREE_SOURCES = {
|
|
144915
|
-
|
|
145009
|
+
// Only iOS consumes the target: the platforms below resolve their tree
|
|
145010
|
+
// source per-device and never auto-resolve.
|
|
145011
|
+
ios: (registry2, device, target) => queryFullHierarchyTree(registry2, device, target),
|
|
144916
145012
|
android: (registry2, device) => queryAndroidFullHierarchy(registry2, device),
|
|
144917
145013
|
chromium: (registry2, device) => queryChromiumTree(registry2, device),
|
|
144918
145014
|
vega: (_registry, device) => queryVegaTree(device)
|
|
@@ -145118,7 +145214,7 @@ function provenTreeOutage(env) {
|
|
|
145118
145214
|
return proven && proven.deviceId === env.device.id ? proven.error : void 0;
|
|
145119
145215
|
}
|
|
145120
145216
|
function readFlowTree(env) {
|
|
145121
|
-
return fetchFlowTree(env.registry, env.device, env.
|
|
145217
|
+
return fetchFlowTree(env.registry, env.device, env.treeTarget).then((data) => {
|
|
145122
145218
|
if (env.treeOutage) env.treeOutage.proven = void 0;
|
|
145123
145219
|
return data;
|
|
145124
145220
|
});
|
|
@@ -146173,7 +146269,11 @@ async function captureTapSelector(registry2, session, udid, point) {
|
|
|
146173
146269
|
try {
|
|
146174
146270
|
const device = resolveDevice(udid);
|
|
146175
146271
|
const launched = recordedLaunchedApp(session, device.platform);
|
|
146176
|
-
const { tree, source } = await fetchFlowTree(
|
|
146272
|
+
const { tree, source } = await fetchFlowTree(
|
|
146273
|
+
registry2,
|
|
146274
|
+
device,
|
|
146275
|
+
launched ? { bundleId: launched, pinned: false, probeAnswered: false } : void 0
|
|
146276
|
+
);
|
|
146177
146277
|
const node = nodeAtPoint(tree, point);
|
|
146178
146278
|
if (!node) return { warning: "no element found under the tap; kept coordinates (brittle)" };
|
|
146179
146279
|
const selector = deriveSelector(node);
|
|
@@ -149111,6 +149211,7 @@ async function runLaunch(state3, app) {
|
|
|
149111
149211
|
reason: `no app id declared for platform "${device.platform}" \u2014 add a launch entry for it`
|
|
149112
149212
|
};
|
|
149113
149213
|
}
|
|
149214
|
+
state3.treeTarget = void 0;
|
|
149114
149215
|
let restart;
|
|
149115
149216
|
try {
|
|
149116
149217
|
restart = await invokeOnDevice(env, "restart-app", { bundleId });
|
|
@@ -149125,7 +149226,7 @@ async function runLaunch(state3, app) {
|
|
|
149125
149226
|
const gate = await treeSourceGate(registry2, device, bundleId, signal);
|
|
149126
149227
|
if (signal?.aborted) return ABORTED_OUTCOME;
|
|
149127
149228
|
if (gate) return { ok: false, reason: gate };
|
|
149128
|
-
state3.
|
|
149229
|
+
state3.treeTarget = { bundleId, pinned: true, probeAnswered: false };
|
|
149129
149230
|
return { ok: true };
|
|
149130
149231
|
}
|
|
149131
149232
|
async function runChromiumLaunch(state3, app) {
|
|
@@ -149312,7 +149413,11 @@ function createRunFlowTool(registry2) {
|
|
|
149312
149413
|
},
|
|
149313
149414
|
description: `Run a saved flow from the .argent/flows/ directory, or an explicit boundary-managed flow_path.
|
|
149314
149415
|
Steps run in order: \`launch\` starts an app from scratch (terminate + relaunch) and waits until it is
|
|
149315
|
-
ready
|
|
149416
|
+
ready (on iOS it also pins later element lookups to that app rather than auto-detecting the frontmost
|
|
149417
|
+
one); \`tool\` calls dispatch through the registry (a raw \`tool\` step ends that iOS pin, so lookups
|
|
149418
|
+
auto-detect again until the next \`launch\`, though a tool that cannot change the foreground app leaves the
|
|
149419
|
+
launched id as a fallback for a timed-out auto-detect, and \`launch-app\`/\`restart-app\` leave the id they
|
|
149420
|
+
started as that fallback instead); \`tap\`/\`long-press\`/\`type\` resolve a selector to an
|
|
149316
149421
|
element and act on it (\`tap: { on, times: 2 }\` double-taps; \`long-press: { on, duration }\` presses and
|
|
149317
149422
|
holds; \`tap\`/\`long-press\` alternatively take a raw normalized point \u2014 bare \`{ x, y }\` or \`on: { x, y }\`;
|
|
149318
149423
|
any selector may scope its matches geometrically, the CSS combinators read off frames: \`within: <selector>\`
|
|
@@ -150021,14 +150126,17 @@ async function execLeafStep(state3, step, index, scope) {
|
|
|
150021
150126
|
if (step.delayMs && !await sleepOrAbort(step.delayMs, signal)) {
|
|
150022
150127
|
return { ...base, status: "skip", tool: step.name, reason: "run aborted during delay" };
|
|
150023
150128
|
}
|
|
150129
|
+
if (FOREGROUND_CHANGING_TOOLS.has(step.name)) {
|
|
150130
|
+
state3.treeTarget = void 0;
|
|
150131
|
+
if (state3.treeOutage) state3.treeOutage.proven = void 0;
|
|
150132
|
+
} else if (state3.treeTarget?.pinned) {
|
|
150133
|
+
state3.treeTarget = { ...state3.treeTarget, pinned: false };
|
|
150134
|
+
if (state3.treeOutage) state3.treeOutage.proven = void 0;
|
|
150135
|
+
}
|
|
150136
|
+
if (isNestedOrchestratorTool(step.name) && state3.treeOutage) {
|
|
150137
|
+
state3.treeOutage.proven = void 0;
|
|
150138
|
+
}
|
|
150024
150139
|
try {
|
|
150025
|
-
if (FOREGROUND_CHANGING_TOOLS.has(step.name)) {
|
|
150026
|
-
state3.launchedNativeApp = void 0;
|
|
150027
|
-
if (state3.treeOutage) state3.treeOutage.proven = void 0;
|
|
150028
|
-
}
|
|
150029
|
-
if (isNestedOrchestratorTool(step.name) && state3.treeOutage) {
|
|
150030
|
-
state3.treeOutage.proven = void 0;
|
|
150031
|
-
}
|
|
150032
150140
|
const result = await invokeSubTool(registry2, ctx, step.name, args);
|
|
150033
150141
|
if (isUnmetUiWaitResult(step.name, result)) {
|
|
150034
150142
|
const note = result.note;
|
|
@@ -150075,7 +150183,9 @@ async function execLeafStep(state3, step, index, scope) {
|
|
|
150075
150183
|
}
|
|
150076
150184
|
if (step.name === "launch-app" || step.name === "restart-app") {
|
|
150077
150185
|
const launched = args.bundleId;
|
|
150078
|
-
if (typeof launched === "string")
|
|
150186
|
+
if (typeof launched === "string") {
|
|
150187
|
+
state3.treeTarget = { bundleId: launched, pinned: false, probeAnswered: false };
|
|
150188
|
+
}
|
|
150079
150189
|
}
|
|
150080
150190
|
return { ...base, status: "pass", tool: step.name, result, outputHint, args };
|
|
150081
150191
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -70,6 +70,12 @@ On iOS, Android, and Chromium, an id absent from `describe` can still resolve in
|
|
|
70
70
|
|
|
71
71
|
The recorder rechecks each successful `await-ui-element` against the runner tree. Follow any `message` warning and replay each conversion. On Vega, a mismatch usually means the screen changed. A `text` check can also select different elements from the same source. See [Live waits and checks](live-authoring.md#live-waits-and-checks).
|
|
72
72
|
|
|
73
|
+
**On iOS, a `launch:` step also decides which app the runner reads.** A successful `launch:` pins later runner-tree reads to that app, so a read probes only that app instead of fanning out over every connected one to find the frontmost. A pinned read still refuses, naming the reason, when the app has no foreground presence left, when it stops answering after an earlier read got through, when its devtools connection dropped, or when the pinned id is a `com.apple.*` system app.
|
|
74
|
+
|
|
75
|
+
Any raw `tool:` step ends the pin, because its effect on the screen is opaque to the runner, and reads auto-detect the frontmost app again until the next `launch:` re-pins. A tool that cannot change the foreground app leaves the launched id as an unpinned fallback, which takes the read only when auto-detection times out and the launched app vouches for itself with a probe of its own. `launch-app`, `restart-app`, `reinstall-app`, `open-url`, and `button` drop even that; `launch-app` and `restart-app` replace it with the app they just started, still unpinned. Nested `run:` fragments inherit both the pin and its clearing.
|
|
76
|
+
|
|
77
|
+
So on iOS recording and replay can read different apps, not only different projections: recording has no run state and always auto-detects the frontmost connected app, while a replay read between a `launch:` and the next raw `tool:` step reads the launched app.
|
|
78
|
+
|
|
73
79
|
**On iOS, never copy a `role` from `describe` into a flow selector.** The runner derives iOS roles from the UIView class name and `describe` from accessibility traits, so a React Native `Pressable` (class `RCTView`) is `AXGroup` to the runner and `AXButton` to `describe`. Select on `id`/`text`, or confirm the role against the runner's own tree.
|
|
74
80
|
|
|
75
81
|
When several nodes match, the directive decides:
|
|
@@ -99,7 +105,7 @@ Scopes can combine and nest, with at most six scope keys. Use strict selectors f
|
|
|
99
105
|
|
|
100
106
|
Directives stop the flow on failure and skip later steps. `flow-execute` documents their shapes. The available directives are `launch`, `tap`, `long-press`, `type`, `scroll-to`, `pinch`, `rotate`, `await`, `assert`, `wait`, `snapshot`, `run`, `when`, `echo`, and `tool`.
|
|
101
107
|
|
|
102
|
-
Use the launch map for cross-platform flows. A bare launch applies everywhere and becomes an app path on Chromium. The map takes `native:`, `ios:`, `android:`, `vega:`, and `chromium:`. `native:` is one id shared by iOS, Android, and Vega, and a per-platform key overrides it for that platform. `chromium:` accepts a relative or absolute app path. A launch that declares no id for the run's platform is an error, not a cue to switch platforms.
|
|
108
|
+
Use the launch map for cross-platform flows. A bare launch applies everywhere and becomes an app path on Chromium. The map takes `native:`, `ios:`, `android:`, `vega:`, and `chromium:`. `native:` is one id shared by iOS, Android, and Vega, and a per-platform key overrides it for that platform. `chromium:` accepts a relative or absolute app path. A launch that declares no id for the run's platform is an error, not a cue to switch platforms. On iOS, a successful launch also pins later tree reads to that app until the next raw `tool:` step, so read [The runner tree is not the discovery tree](#the-runner-tree-is-not-the-discovery-tree) when a read describes the wrong screen.
|
|
103
109
|
|
|
104
110
|
```yaml
|
|
105
111
|
- launch: { native: com.acme.app, chromium: ../../app }
|
|
@@ -53,9 +53,9 @@ Use the same explicit UDID throughout. Multiple booted simulators are not an inj
|
|
|
53
53
|
|
|
54
54
|
This fallback applies only to `com.apple.*` system apps. A connection failure in another app never authorizes it.
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
Argent refuses `com.apple.*` bundle ids at every native-devtools read that names one, because a system app is never the app under test. The instrumentation has been seen both loading and not loading into one, depending on the simulator runtime — either way it is no basis for a selector. `restart-app`, `launch-app`, and `describe` still work on one; it just never gets a flow tree.
|
|
57
57
|
|
|
58
|
-
Give the flow a `launch:` step as usual. On iOS the launch waits the full devtools budget out, then passes for one of these bundle ids: starting the app is all that step is for, and a coordinate-driven flow needs nothing more. The flow stays e2e; it just pays roughly sixteen seconds at the launch. Where the
|
|
58
|
+
Give the flow a `launch:` step as usual. On iOS the launch waits the full devtools budget out, then passes for one of these bundle ids: starting the app is all that step is for, and a coordinate-driven flow needs nothing more. The flow stays e2e; it just pays roughly sixteen seconds at the launch. Where the refusal bites is selector resolution, and the first selector step reports it there — terminally, naming the coordinate remedy — rather than as a launch failure. The rest of the tree-free form:
|
|
59
59
|
|
|
60
60
|
- Raw `tool: await-ui-element` accessibility checks.
|
|
61
61
|
- Point taps or long-presses derived from `describe`, each named by an echo.
|
|
@@ -66,9 +66,9 @@ Every point tap or long-press in such a flow passes **carrying a warning** for a
|
|
|
66
66
|
|
|
67
67
|
A recorded wait carries a different warning: it adds about one second and reports that the runner tree is unavailable. That warning is expected too. Keep the wait as a raw `tool:` step.
|
|
68
68
|
|
|
69
|
-
Report that the flow
|
|
69
|
+
Report that the flow has no flow tree and its coordinates are not portable. It cannot satisfy the QA contract. Report the artifact and platform blocker instead.
|
|
70
70
|
|
|
71
|
-
A normally injectable app that is broken in the environment gets the same coordinate-only treatment, but not the same launch: there the `launch:` step fails, since the gate withholds its verdict only for a bundle id
|
|
71
|
+
A normally injectable app that is broken in the environment gets the same coordinate-only treatment, but not the same launch: there the `launch:` step fails, since the gate withholds its verdict only for a bundle id argent refuses outright. Start such a flow with a raw `tool: restart-app`, which terminates and relaunches without the readiness gate, and accept that the result is a **fragment** — its first non-echo step is not `launch:`, so the runner never classifies it as e2e, and it cannot complete `argent-qa-flows`, which requires a leading `launch:`. Report the blocker rather than labeling that fallback a completed QA test.
|
|
72
72
|
|
|
73
73
|
## Tree source recovery on Android, Chromium, and Vega
|
|
74
74
|
|