@swmansion/argent 0.25.1-next.20 → 0.25.1-next.22
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 +3 -2
- package/dist/installer.mjs +1 -1
- package/dist/tool-server.cjs +112 -12
- package/package.json +1 -1
package/dist/cli-cmds.mjs
CHANGED
|
@@ -21857,7 +21857,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
21857
21857
|
var SESSION_ID2 = randomUUID5();
|
|
21858
21858
|
function readCliVersion() {
|
|
21859
21859
|
if (true) {
|
|
21860
|
-
return "0.25.1-next.
|
|
21860
|
+
return "0.25.1-next.22";
|
|
21861
21861
|
}
|
|
21862
21862
|
return "0.0.0";
|
|
21863
21863
|
}
|
|
@@ -23211,7 +23211,8 @@ Subcommands:
|
|
|
23211
23211
|
|
|
23212
23212
|
Options (run):
|
|
23213
23213
|
--device <id> Device id to run against (auto-detected when omitted)
|
|
23214
|
-
--platform <p> ios | android | chromium | vega \u2014 narrow
|
|
23214
|
+
--platform <p> ios | android | chromium | vega | ios-remote \u2014 narrow
|
|
23215
|
+
auto-detection (ios never picks a remote simulator)
|
|
23215
23216
|
--update-baselines Write/refresh screenshot baselines instead of diffing
|
|
23216
23217
|
--output <dir> Also write failed snapshot images (baseline/current/diff)
|
|
23217
23218
|
under <dir>/<flow>/ \u2014 a stable path for CI artifact
|
package/dist/installer.mjs
CHANGED
|
@@ -16710,7 +16710,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
16710
16710
|
var SESSION_ID = randomUUID4();
|
|
16711
16711
|
function readCliVersion() {
|
|
16712
16712
|
if (true) {
|
|
16713
|
-
return "0.25.1-next.
|
|
16713
|
+
return "0.25.1-next.22";
|
|
16714
16714
|
}
|
|
16715
16715
|
return "0.0.0";
|
|
16716
16716
|
}
|
package/dist/tool-server.cjs
CHANGED
|
@@ -94704,7 +94704,7 @@ var _CI_VENDOR_COUNT_FOR_TEST = vendors_default.length;
|
|
|
94704
94704
|
var SESSION_ID = (0, import_node_crypto3.randomUUID)();
|
|
94705
94705
|
function readCliVersion() {
|
|
94706
94706
|
if (true) {
|
|
94707
|
-
return "0.25.1-next.
|
|
94707
|
+
return "0.25.1-next.22";
|
|
94708
94708
|
}
|
|
94709
94709
|
return "0.0.0";
|
|
94710
94710
|
}
|
|
@@ -146123,6 +146123,7 @@ function isIdleCondition(raw, kind) {
|
|
|
146123
146123
|
return true;
|
|
146124
146124
|
}
|
|
146125
146125
|
var LAUNCH_PLATFORMS = ["ios", "android", "chromium", "vega"];
|
|
146126
|
+
var SELECTABLE_PLATFORMS = [...LAUNCH_PLATFORMS, "ios-remote"];
|
|
146126
146127
|
var LAUNCH_MAP_KEYS = ["native", ...LAUNCH_PLATFORMS];
|
|
146127
146128
|
function parseChromiumLaunch(raw) {
|
|
146128
146129
|
if (typeof raw === "string" && raw.length > 0) return raw;
|
|
@@ -147375,7 +147376,7 @@ var DEVICE_BIND_KEYS = ["udid", "device_id", "device"];
|
|
|
147375
147376
|
var DEVICE_BIND_LIST_KEYS = ["devices"];
|
|
147376
147377
|
var DEVICE_ARG_KEYS = DEVICE_BIND_KEYS;
|
|
147377
147378
|
function deviceEntryId(d) {
|
|
147378
|
-
if (d.platform === "ios") return d.udid;
|
|
147379
|
+
if (d.platform === "ios" || d.platform === "ios-remote") return d.udid;
|
|
147379
147380
|
if (d.platform === "chromium") return d.id;
|
|
147380
147381
|
return d.serial;
|
|
147381
147382
|
}
|
|
@@ -147383,6 +147384,8 @@ function isBooted(d) {
|
|
|
147383
147384
|
switch (d.platform) {
|
|
147384
147385
|
case "ios":
|
|
147385
147386
|
return d.state === "Booted";
|
|
147387
|
+
case "ios-remote":
|
|
147388
|
+
return d.state === "Booted";
|
|
147386
147389
|
case "android":
|
|
147387
147390
|
return d.state === "device";
|
|
147388
147391
|
case "vega":
|
|
@@ -147677,6 +147680,7 @@ function adaptFullHierarchy(raw) {
|
|
|
147677
147680
|
});
|
|
147678
147681
|
return screenW > 0 && screenH > 0 ? { tree, screen: { width: screenW, height: screenH } } : { tree };
|
|
147679
147682
|
}
|
|
147683
|
+
var FLOW_TREE_MAX_DEPTH = 100;
|
|
147680
147684
|
var FULL_HIERARCHY_FIELDS = [
|
|
147681
147685
|
"className",
|
|
147682
147686
|
"identifier",
|
|
@@ -147708,9 +147712,9 @@ async function queryFullHierarchyTree(registry2, device, target) {
|
|
|
147708
147712
|
const ndRef = nativeDevtoolsRef(device);
|
|
147709
147713
|
nativeApi = await registry2.resolveService(ndRef.urn, ndRef.options);
|
|
147710
147714
|
} catch (err) {
|
|
147711
|
-
throw
|
|
147715
|
+
throw wrapPreservingFailure(
|
|
147712
147716
|
`native devtools is unavailable (${errMsg2(err)}) \u2014 flows resolve selectors against the full view hierarchy it serves`,
|
|
147713
|
-
|
|
147717
|
+
err
|
|
147714
147718
|
);
|
|
147715
147719
|
}
|
|
147716
147720
|
let bundleId;
|
|
@@ -147779,7 +147783,7 @@ async function queryFullHierarchyTree(registry2, device, target) {
|
|
|
147779
147783
|
resolved = await resolveNativeTargetApp(nativeApi, void 0);
|
|
147780
147784
|
} catch (err) {
|
|
147781
147785
|
const timedOut2 = getFailureSignal(err)?.error_code === FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT;
|
|
147782
|
-
if (!timedOut2 || !target) throw err;
|
|
147786
|
+
if (!timedOut2 || !target) throw await explainTargetingFailure(err, nativeApi, device, target);
|
|
147783
147787
|
if (!isInjectableBundleId(target.bundleId)) {
|
|
147784
147788
|
throw new FailureError(
|
|
147785
147789
|
systemAppFlowTargetRefusal(target.bundleId),
|
|
@@ -147792,13 +147796,15 @@ async function queryFullHierarchyTree(registry2, device, target) {
|
|
|
147792
147796
|
err instanceof Error ? { cause: err } : void 0
|
|
147793
147797
|
);
|
|
147794
147798
|
}
|
|
147795
|
-
if (!nativeApi.listConnectedBundleIds().includes(target.bundleId))
|
|
147799
|
+
if (!nativeApi.listConnectedBundleIds().includes(target.bundleId)) {
|
|
147800
|
+
throw await explainTargetingFailure(err, nativeApi, device, target);
|
|
147801
|
+
}
|
|
147796
147802
|
let hintState;
|
|
147797
147803
|
try {
|
|
147798
147804
|
hintState = await nativeApi.getAppState(target.bundleId);
|
|
147799
147805
|
} catch (probeErr) {
|
|
147800
147806
|
if (getFailureSignal(probeErr)?.error_code === FAILURE_CODES.NATIVE_DEVTOOLS_RPC_TIMEOUT) {
|
|
147801
|
-
throw err;
|
|
147807
|
+
throw await explainTargetingFailure(err, nativeApi, device, target);
|
|
147802
147808
|
}
|
|
147803
147809
|
throw probeErr;
|
|
147804
147810
|
}
|
|
@@ -147817,13 +147823,18 @@ async function queryFullHierarchyTree(registry2, device, target) {
|
|
|
147817
147823
|
resolved = { bundleId: target.bundleId };
|
|
147818
147824
|
}
|
|
147819
147825
|
bundleId = resolved.bundleId;
|
|
147826
|
+
if (!nativeApi.listConnectedBundleIds().includes(bundleId)) {
|
|
147827
|
+
throw new Error(
|
|
147828
|
+
`${bundleId} answered the target probe and then dropped its native-devtools connection before the view hierarchy could be read. It was instrumented, so a retry may ride this out; if the connection does not come back, relaunch with restart-app (or a flow \`launch\` step) \u2014 launch-app would only foreground the process that just lost it.`
|
|
147829
|
+
);
|
|
147830
|
+
}
|
|
147820
147831
|
}
|
|
147821
147832
|
const rawResult = await nativeApi.queryViewHierarchy(
|
|
147822
147833
|
bundleId,
|
|
147823
147834
|
"ViewHierarchy.getFullHierarchy",
|
|
147824
147835
|
{
|
|
147825
147836
|
fields: FULL_HIERARCHY_FIELDS,
|
|
147826
|
-
maxDepth:
|
|
147837
|
+
maxDepth: FLOW_TREE_MAX_DEPTH
|
|
147827
147838
|
}
|
|
147828
147839
|
);
|
|
147829
147840
|
if (rawResult.error) {
|
|
@@ -147840,6 +147851,86 @@ async function queryFullHierarchyTree(registry2, device, target) {
|
|
|
147840
147851
|
function errMsg2(err) {
|
|
147841
147852
|
return err instanceof Error ? err.message : String(err);
|
|
147842
147853
|
}
|
|
147854
|
+
async function explainTargetingFailure(err, nativeApi, device, launched) {
|
|
147855
|
+
const failureCode = getFailureSignal(err)?.error_code;
|
|
147856
|
+
if (failureCode === FAILURE_CODES.NATIVE_TARGET_MULTIPLE_APPS_AMBIGUOUS) {
|
|
147857
|
+
const terminate = await terminateCommand(device);
|
|
147858
|
+
const clearOthers = terminate ? `; clear the others with \`${terminate}\` (argent has no terminate tool, and restart-app would just bring that app back to the front).` : `.`;
|
|
147859
|
+
return wrapPreservingFailure(
|
|
147860
|
+
// Short header: the embedded diagnostic already says the set is
|
|
147861
|
+
// ambiguous, and the per-app entries need those 90 characters.
|
|
147862
|
+
`could not target an app to read the view hierarchy from:
|
|
147863
|
+
${cappedAppDiagnostic(withoutExplicitBundleIdAdvice(errMsg2(err)))}
|
|
147864
|
+
Flow selectors auto-target and cannot name a bundleId. Foreground the intended app with launch-app (it does not terminate), then retry${clearOthers}`,
|
|
147865
|
+
err
|
|
147866
|
+
);
|
|
147867
|
+
}
|
|
147868
|
+
if (failureCode === FAILURE_CODES.NATIVE_TARGET_SINGLE_APP_NOT_FOREGROUND) {
|
|
147869
|
+
return wrapPreservingFailure(
|
|
147870
|
+
`the only native-devtools-connected app is not foreground, so it cannot be auto-targeted:
|
|
147871
|
+
${withoutExplicitBundleIdAdvice(errMsg2(err))}
|
|
147872
|
+
Flow selector steps auto-target and cannot provide a bundleId. Bring that app to the foreground with launch-app (it does not terminate \u2014 the app is already instrumented, just not frontmost), then retry.`,
|
|
147873
|
+
err
|
|
147874
|
+
);
|
|
147875
|
+
}
|
|
147876
|
+
const stillConnected = nativeApi.listConnectedBundleIds();
|
|
147877
|
+
if (stillConnected.length > 0) {
|
|
147878
|
+
const launchedGone = launched !== void 0 && !stillConnected.includes(launched.bundleId);
|
|
147879
|
+
const terminate = stillConnected.length > (launchedGone ? 0 : 1) ? await terminateCommand(device) : void 0;
|
|
147880
|
+
const clearOthers = terminate ? ` To clear the others use \`${terminate}\` \u2014 argent exposes no terminate tool, and restart-app would bring the app you cleared back to the front instead.` : ``;
|
|
147881
|
+
return wrapPreservingFailure(
|
|
147882
|
+
`could not read the state of the native-devtools-connected apps, so none could be auto-targeted (${firstClause(err)}). Connected: ${cappedList(stillConnected)}. ` + (launchedGone ? `${launched.bundleId} \u2014 the app this flow launched \u2014 is NOT among them, so relaunch it with restart-app (or a flow \`launch\` step); launch-app does not terminate, so it would only foreground the same uninstrumented process.` : `They are instrumented \u2014 do not relaunch. A suspended app stops answering: foreground the app the flow drives with launch-app (it does not terminate), then retry.`) + clearOthers,
|
|
147883
|
+
err
|
|
147884
|
+
);
|
|
147885
|
+
}
|
|
147886
|
+
return wrapPreservingFailure(
|
|
147887
|
+
`no app is connected to native devtools, so flow selectors have no instrumented process to read the view hierarchy from (${firstClause(err)}). Relaunch with restart-app (or a flow \`launch\` step): launch-app does not terminate, so on an app already running from Metro/Expo, Xcode, or its icon it only foregrounds that uninstrumented process. Argent treats an Apple system app (com.apple.*) as non-injectable \u2014 the native-devtools feature tools refuse it too \u2014 so if one never connects, drive it with raw point taps and tool: await-ui-element steps.`,
|
|
147888
|
+
err
|
|
147889
|
+
);
|
|
147890
|
+
}
|
|
147891
|
+
async function terminateCommand(device) {
|
|
147892
|
+
try {
|
|
147893
|
+
const { prefix } = await simctlTargetForUdid(device.id);
|
|
147894
|
+
return `xcrun ${prefix.join(" ")} terminate <udid> <bundleId>`;
|
|
147895
|
+
} catch {
|
|
147896
|
+
return void 0;
|
|
147897
|
+
}
|
|
147898
|
+
}
|
|
147899
|
+
function firstClause(err) {
|
|
147900
|
+
const firstLine2 = errMsg2(err).split("\n", 1)[0];
|
|
147901
|
+
const sentenceEnd = /\.(?=\s|$)/.exec(firstLine2);
|
|
147902
|
+
return sentenceEnd === null ? firstLine2 : firstLine2.slice(0, sentenceEnd.index + 1);
|
|
147903
|
+
}
|
|
147904
|
+
var MAX_LISTED_APPS = 2;
|
|
147905
|
+
function cappedList(bundleIds) {
|
|
147906
|
+
if (bundleIds.length <= MAX_LISTED_APPS) return bundleIds.join(", ");
|
|
147907
|
+
const dropped = bundleIds.length - MAX_LISTED_APPS;
|
|
147908
|
+
return `${bundleIds.slice(0, MAX_LISTED_APPS).join(", ")} (+${dropped} more)`;
|
|
147909
|
+
}
|
|
147910
|
+
function cappedAppDiagnostic(message) {
|
|
147911
|
+
const lines = message.split("\n");
|
|
147912
|
+
const isEntry = (line) => line.startsWith("- ");
|
|
147913
|
+
const firstEntry = lines.findIndex(isEntry);
|
|
147914
|
+
if (firstEntry === -1) return message;
|
|
147915
|
+
const entries = lines.filter(isEntry);
|
|
147916
|
+
if (entries.length <= MAX_LISTED_APPS) return message;
|
|
147917
|
+
const kept = entries.slice(0, MAX_LISTED_APPS);
|
|
147918
|
+
const dropped = entries.length - MAX_LISTED_APPS;
|
|
147919
|
+
return [
|
|
147920
|
+
...lines.slice(0, firstEntry),
|
|
147921
|
+
...kept,
|
|
147922
|
+
`- (+${dropped} more connected app${dropped === 1 ? "" : "s"})`,
|
|
147923
|
+
...lines.slice(firstEntry + entries.length).filter((line) => !isEntry(line))
|
|
147924
|
+
].join("\n");
|
|
147925
|
+
}
|
|
147926
|
+
function withoutExplicitBundleIdAdvice(message) {
|
|
147927
|
+
return message.replace(/\nProvide bundleId explicitly[^\n]*$/, "");
|
|
147928
|
+
}
|
|
147929
|
+
function wrapPreservingFailure(message, err) {
|
|
147930
|
+
const cause = err instanceof Error ? err : new Error(String(err));
|
|
147931
|
+
const signal = getFailureSignal(err);
|
|
147932
|
+
return signal ? new FailureError(message, signal, { cause }) : new Error(message, { cause });
|
|
147933
|
+
}
|
|
147843
147934
|
function projectIosDeviceNode(node) {
|
|
147844
147935
|
const onScreen = node.frame.width > 0 && node.frame.height > 0;
|
|
147845
147936
|
return {
|
|
@@ -149496,7 +149587,7 @@ function unmetWaitWarningFor(cause) {
|
|
|
149496
149587
|
}
|
|
149497
149588
|
function indeterminateReasonCaveat(udid) {
|
|
149498
149589
|
if (platformOf(udid) !== "ios") return "";
|
|
149499
|
-
return ".
|
|
149590
|
+
return ". One thing that reason cannot see is this step: the probe predicts an `await:`/`assert:` directive, and no directive takes a bundleId, so neither this probe nor the runner accepts one (the `bundleId` on this step reached the live wait only)";
|
|
149500
149591
|
}
|
|
149501
149592
|
var CANCELLED_PROBE_WARNING = "recorded, but the re-probe against the tree the RUNNER reads was cancelled before it answered. The step itself ran and is written to the flow; only the verdict is missing, so whether it would convert to `await:`/`assert:` is UNKNOWN, not known-bad \u2014 record the wait again, uncancelled, before trusting the conversion";
|
|
149502
149593
|
var PROBE_MAX_TREE_READ_MS = 2500;
|
|
@@ -149570,6 +149661,11 @@ async function probeAgainstRunnerTree(registry2, ctx, args) {
|
|
|
149570
149661
|
(condition === "text" ? textTieClause(args.udid) : "") + " " + SPELLING_CLAUSE + ` ${treeDivergenceFor(args.udid, condition)} ${runnerSideReadClause(args.udid, condition)}`
|
|
149571
149662
|
};
|
|
149572
149663
|
}
|
|
149664
|
+
function roleOnlySelectorWarning(selector) {
|
|
149665
|
+
if (selector.role === void 0 || selector.identifier !== void 0) return void 0;
|
|
149666
|
+
if (selector.text !== void 0 || selector.textMatches !== void 0) return void 0;
|
|
149667
|
+
return `selector ${describeSelector(selector)} matches by role alone (the tapped element has no id or visible text) \u2014 replay takes whichever element of that role ranks first, so re-record against a labelled element if that is not reliably this one`;
|
|
149668
|
+
}
|
|
149573
149669
|
async function captureTapSelector(registry2, session, udid, point) {
|
|
149574
149670
|
try {
|
|
149575
149671
|
const device = resolveDevice(udid);
|
|
@@ -149595,7 +149691,11 @@ async function captureTapSelector(registry2, session, udid, point) {
|
|
|
149595
149691
|
warning: `selector ${describeSelector(selector)} resolves to a different element on this screen; kept coordinates (brittle)`
|
|
149596
149692
|
};
|
|
149597
149693
|
}
|
|
149598
|
-
|
|
149694
|
+
const warnings = [
|
|
149695
|
+
roleOnlySelectorWarning(selector),
|
|
149696
|
+
fallbackSourceWarning(source, device.platform)
|
|
149697
|
+
].filter((w) => w !== void 0);
|
|
149698
|
+
return { selector, ...warnings.length > 0 ? { warning: warnings.join("; ") } : {} };
|
|
149599
149699
|
} catch (err) {
|
|
149600
149700
|
return {
|
|
149601
149701
|
warning: `selector capture failed (${err instanceof Error ? err.message : String(err)}); kept coordinates`
|
|
@@ -153964,8 +154064,8 @@ var zodSchema66 = external_exports.object({
|
|
|
153964
154064
|
device: external_exports.string().optional().describe(
|
|
153965
154065
|
"Device id to run against (iOS UDID, Android/Vega serial, Chromium id) \u2014 the id list-devices reports. Auto-detected when omitted, but only when exactly one booted device matches (optionally narrowed by `platform`); with several booted the run fails and lists them, so pass this explicitly whenever more than one device is up."
|
|
153966
154066
|
),
|
|
153967
|
-
platform: external_exports.enum(
|
|
153968
|
-
"Restrict auto-detection to this platform when several devices are booted. `chromium` does more than filter: with no `device` it SELECTS the self-boot branch for an e2e flow - the runner boots an Electron instance from the `launch` step's chromium value and tears it down after the run (a single-key `launch: { chromium: \u2026 }` map selects it on its own, without this parameter). When it selects that branch it never falls back to device auto-detection (a fragment, or an e2e launch map with no `chromium` key, still does), and the launch value must be a real Electron app path on the tool-server host: a bare-string `launch:` - what the recorder writes - holds an installed-app bundle id, so passing `chromium` for one fails the whole run with `Electron boot: path does not exist`. Edit the launch to `{ chromium: <app path> }` first."
|
|
154067
|
+
platform: external_exports.enum(SELECTABLE_PLATFORMS).optional().describe(
|
|
154068
|
+
"Restrict auto-detection to this platform when several devices are booted. `ios` selects local simulators only \u2014 pass `ios-remote` to select a remote one. `chromium` does more than filter: with no `device` it SELECTS the self-boot branch for an e2e flow - the runner boots an Electron instance from the `launch` step's chromium value and tears it down after the run (a single-key `launch: { chromium: \u2026 }` map selects it on its own, without this parameter). When it selects that branch it never falls back to device auto-detection (a fragment, or an e2e launch map with no `chromium` key, still does), and the launch value must be a real Electron app path on the tool-server host: a bare-string `launch:` - what the recorder writes - holds an installed-app bundle id, so passing `chromium` for one fails the whole run with `Electron boot: path does not exist`. Edit the launch to `{ chromium: <app path> }` first."
|
|
153969
154069
|
),
|
|
153970
154070
|
updateBaselines: external_exports.boolean().optional().describe(
|
|
153971
154071
|
"Write/refresh screenshot baselines for `snapshot` steps instead of diffing against them."
|
package/package.json
CHANGED