@swmansion/argent 0.25.1-next.32 → 0.25.1-next.34

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
@@ -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.32";
21860
+ return "0.25.1-next.34";
21861
21861
  }
21862
21862
  return "0.0.0";
21863
21863
  }
@@ -23461,7 +23461,10 @@ async function exportFailureArtifacts(report, outputDir, flowPath, ctx) {
23461
23461
  if (dirName === null) return;
23462
23462
  dir = path14.join(outputDir, dirName);
23463
23463
  }
23464
- const dest = path14.join(dir, `${key}-${role}.png`);
23464
+ const dest = path14.join(
23465
+ dir,
23466
+ `${key}${s.snapshotRemote === true ? "-remote" : ""}-${role}.png`
23467
+ );
23465
23468
  const rel = path14.relative(outputDir, dest);
23466
23469
  if (rel.startsWith("..") || path14.isAbsolute(rel)) continue;
23467
23470
  try {
@@ -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.32";
16713
+ return "0.25.1-next.34";
16714
16714
  }
16715
16715
  return "0.0.0";
16716
16716
  }
@@ -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.32";
94707
+ return "0.25.1-next.34";
94708
94708
  }
94709
94709
  return "0.0.0";
94710
94710
  }
@@ -96676,8 +96676,8 @@ async function run(args, options) {
96676
96676
  throw new Error(`sim-remote ${args.join(" ")} failed: ${suffix}`, { cause: err });
96677
96677
  }
96678
96678
  }
96679
- async function simctlListDevices() {
96680
- const { stdout } = await run(["simctl", "list", "devices", "--json"]);
96679
+ async function simctlListDevices(options) {
96680
+ const { stdout } = await run(["simctl", "list", "devices", "--json"], options);
96681
96681
  try {
96682
96682
  return JSON.parse(stdout);
96683
96683
  } catch (err) {
@@ -96688,12 +96688,12 @@ async function simctlListDevices() {
96688
96688
  }
96689
96689
  }
96690
96690
  var remoteRuntimeKindCache = /* @__PURE__ */ new Map();
96691
- async function isRemoteTvOsSimulator(udid) {
96691
+ async function isRemoteTvOsSimulator(udid, options) {
96692
96692
  const id = stripRemotePrefix(udid);
96693
96693
  const cached3 = remoteRuntimeKindCache.get(id);
96694
96694
  if (cached3) return cached3 === "tv";
96695
96695
  try {
96696
- const { devices } = await simctlListDevices();
96696
+ const { devices } = await simctlListDevices(options);
96697
96697
  for (const [runtime, entries] of Object.entries(devices)) {
96698
96698
  if (!entries.some((d) => d.udid === id)) continue;
96699
96699
  const kind = runtime.includes("tvOS") ? "tv" : "mobile";
@@ -96735,6 +96735,9 @@ async function simctlOpenUrl(udid, url2) {
96735
96735
  async function simctlPrivacy(udid, action, service, bundleId) {
96736
96736
  await run(["simctl", "privacy", stripRemotePrefix(udid), action, service, bundleId]);
96737
96737
  }
96738
+ async function simctlStatusBar(udid, args, options) {
96739
+ await run(["simctl", "status_bar", stripRemotePrefix(udid), ...args], options);
96740
+ }
96738
96741
  async function simctlPbcopy(udid, text) {
96739
96742
  await run(["simctl", "pbcopy", stripRemotePrefix(udid)], { stdin: text });
96740
96743
  }
@@ -107261,6 +107264,7 @@ async function openMoqClient(udid) {
107261
107264
  async function openMoqClientFromInfo(info) {
107262
107265
  await installNodeWebTransport();
107263
107266
  const session = new MoqDeviceSession(await connectMoq(info), { publishPath: PUBLISH_PATH });
107267
+ disposeWhenTransportDies(session);
107264
107268
  return {
107265
107269
  sendControl: (payload) => session.sendControl(payload),
107266
107270
  async screenshot(opts) {
@@ -107271,6 +107275,15 @@ async function openMoqClientFromInfo(info) {
107271
107275
  }
107272
107276
  };
107273
107277
  }
107278
+ function disposeWhenTransportDies(session) {
107279
+ const dispose = () => {
107280
+ try {
107281
+ session.close();
107282
+ } catch {
107283
+ }
107284
+ };
107285
+ void session.closed.then(dispose, dispose);
107286
+ }
107274
107287
 
107275
107288
  // ../tool-server/src/utils/simulator-client.ts
107276
107289
  init_wrapper();
@@ -107339,17 +107352,26 @@ function failAllPending(conn, makeError) {
107339
107352
  conn.pending.clear();
107340
107353
  for (const entry of entries) entry.settle(makeError(entry.cmd));
107341
107354
  }
107355
+ var COMMAND_TRANSPORT_FAILURE = {
107356
+ error_code: FAILURE_CODES.SIMULATOR_COMMAND_TRANSPORT_FAILED,
107357
+ failure_stage: "simulator_command_transport",
107358
+ failure_area: "tool_server",
107359
+ error_kind: "network",
107360
+ network_failure: "connection_reset",
107361
+ failure_command: "simulator_server"
107362
+ };
107342
107363
  function transportError(cmd, apiUrl, detail) {
107343
107364
  return new FailureError(
107344
107365
  `simulator-server did not accept the '${cmd}' command: ${detail}. The command was NOT delivered to the device. Check that the simulator is still booted and the simulator-server for ${apiUrl} is running.`,
107345
- {
107346
- error_code: FAILURE_CODES.SIMULATOR_COMMAND_TRANSPORT_FAILED,
107347
- failure_stage: "simulator_command_transport",
107348
- failure_area: "tool_server",
107349
- error_kind: "network",
107350
- network_failure: "connection_reset",
107351
- failure_command: "simulator_server"
107352
- }
107366
+ COMMAND_TRANSPORT_FAILURE
107367
+ );
107368
+ }
107369
+ function remoteTransportError(cmd, cause) {
107370
+ const detail = cause instanceof Error ? cause.message : String(cause);
107371
+ return wrapFailure(
107372
+ cause,
107373
+ COMMAND_TRANSPORT_FAILURE,
107374
+ `The remote simulator did not accept the '${cmd}' command: ${detail}. The command was NOT delivered to the device. Call stop-simulator-server for this device and retry - the next call opens a new cloud session. If the machine itself was released, acquire one again before retrying.`
107353
107375
  );
107354
107376
  }
107355
107377
  function getOrCreateConnection(api) {
@@ -107405,13 +107427,10 @@ function getOrCreateConnection(api) {
107405
107427
  return conn;
107406
107428
  }
107407
107429
  function sendCommand(api, cmd) {
107408
- if (api.transport) {
107409
- routeViaTransport(api.transport, cmd);
107410
- return Promise.resolve();
107411
- }
107430
+ const cmdName = typeof cmd.cmd === "string" ? cmd.cmd : "unknown";
107431
+ if (api.transport) return sendViaTransport(api.transport, cmd, cmdName);
107412
107432
  const conn = getOrCreateConnection(api);
107413
107433
  const id = String(++cmdId);
107414
- const cmdName = typeof cmd.cmd === "string" ? cmd.cmd : "unknown";
107415
107434
  const payload = JSON.stringify({ id, ...cmd });
107416
107435
  return new Promise((resolve14, reject) => {
107417
107436
  let done = false;
@@ -107613,32 +107632,36 @@ async function httpScreenshot(api, rotation, signal, scale) {
107613
107632
  );
107614
107633
  }
107615
107634
  }
107635
+ async function sendViaTransport(transport, cmd, cmdName) {
107636
+ const sent = routeViaTransport(transport, cmd);
107637
+ try {
107638
+ await sent;
107639
+ } catch (cause) {
107640
+ throw remoteTransportError(cmdName, cause);
107641
+ }
107642
+ }
107616
107643
  function routeViaTransport(transport, cmd) {
107617
107644
  switch (cmd.cmd) {
107618
107645
  case "touch": {
107619
107646
  const sx = cmd.second_x ?? cmd.secondX;
107620
107647
  const sy = cmd.second_y ?? cmd.secondY;
107621
- transport.touch({
107648
+ return transport.touch({
107622
107649
  type: cmd.type,
107623
107650
  x: cmd.x,
107624
107651
  y: cmd.y,
107625
107652
  secondX: sx == null ? void 0 : sx,
107626
107653
  secondY: sy == null ? void 0 : sy
107627
107654
  });
107628
- return;
107629
107655
  }
107630
107656
  case "button":
107631
- transport.button({
107657
+ return transport.button({
107632
107658
  direction: cmd.direction,
107633
107659
  button: cmd.button
107634
107660
  });
107635
- return;
107636
107661
  case "rotate":
107637
- transport.rotate(cmd.direction);
107638
- return;
107662
+ return transport.rotate(cmd.direction);
107639
107663
  case "key":
107640
- transport.pressKey(cmd.direction, cmd.code);
107641
- return;
107664
+ return transport.pressKey(cmd.direction, cmd.code);
107642
107665
  default:
107643
107666
  throw new Error(`MoQ transport does not implement sendCommand cmd '${String(cmd.cmd)}'`);
107644
107667
  }
@@ -107652,8 +107675,8 @@ function createMoqTransport(moq, options) {
107652
107675
  return { url: (0, import_node_url.pathToFileURL)(file2).toString(), path: file2 };
107653
107676
  };
107654
107677
  return {
107655
- touch(opts) {
107656
- void moq.sendControl(
107678
+ async touch(opts) {
107679
+ await moq.sendControl(
107657
107680
  encodeTouch({
107658
107681
  action: opts.type,
107659
107682
  x: opts.x,
@@ -107663,17 +107686,17 @@ function createMoqTransport(moq, options) {
107663
107686
  })
107664
107687
  );
107665
107688
  },
107666
- button(opts) {
107667
- void moq.sendControl(encodeButton({ action: opts.direction, button: opts.button }));
107689
+ async button(opts) {
107690
+ await moq.sendControl(encodeButton({ action: opts.direction, button: opts.button }));
107668
107691
  },
107669
- rotate(direction) {
107670
- void moq.sendControl(encodeRotate(direction));
107692
+ async rotate(direction) {
107693
+ await moq.sendControl(encodeRotate(direction));
107671
107694
  },
107672
107695
  async paste(text) {
107673
107696
  await options.pasteText(text);
107674
107697
  },
107675
- pressKey(direction, keyCode) {
107676
- void moq.sendControl(encodeKey({ action: direction, code: keyCode }));
107698
+ async pressKey(direction, keyCode) {
107699
+ await moq.sendControl(encodeKey({ action: direction, code: keyCode }));
107677
107700
  },
107678
107701
  async screenshot(opts) {
107679
107702
  const scale = opts?.scale ?? getScreenshotScale();
@@ -107706,17 +107729,20 @@ async function buildRemoteInstance(device) {
107706
107729
  await simctlPbcopy(device.id, text);
107707
107730
  const CMD = 227;
107708
107731
  const V = 25;
107709
- await moq.sendControl(encodeKey({ action: "Down", code: CMD }));
107710
- await moq.sendControl(encodeKey({ action: "Down", code: V }));
107711
- await moq.sendControl(encodeKey({ action: "Up", code: V }));
107712
- await moq.sendControl(encodeKey({ action: "Up", code: CMD }));
107732
+ await api.pressKey("Down", CMD);
107733
+ await api.pressKey("Down", V);
107734
+ await api.pressKey("Up", V);
107735
+ await api.pressKey("Up", CMD);
107713
107736
  }
107714
107737
  });
107715
107738
  const stubUrl = `moq+remote://${device.id}`;
107716
107739
  const api = {
107717
107740
  apiUrl: stubUrl,
107718
107741
  streamUrl: stubUrl,
107719
- pressKey: (direction, keyCode) => moq.sendControl(encodeKey({ action: direction, code: keyCode })),
107742
+ // Through `sendCommand`, as the attached instance below does, so a key the
107743
+ // session refuses reports the same failure as a refused touch instead of a
107744
+ // bare SDK error the caller cannot classify.
107745
+ pressKey: (direction, keyCode) => sendCommand(api, { cmd: "key", code: keyCode, direction }),
107720
107746
  transport
107721
107747
  };
107722
107748
  return {
@@ -148203,10 +148229,14 @@ var FIRST_CAPTURE_COMPLETION_MARGIN_MS = 500;
148203
148229
  var FIRST_PIXEL_CAPTURE_TIMEOUT_MS = FIRST_FRAME_WAIT_MS + FIRST_CAPTURE_COMPLETION_MARGIN_MS;
148204
148230
  var PIXEL_CAPTURE_TIMEOUT_MS = 2e3;
148205
148231
  var IOS_DEVICE_PIXEL_CAPTURE_TIMEOUT_MS = 4e3;
148232
+ var REMOTE_PIXEL_CAPTURE_TIMEOUT_MS = 4e3;
148206
148233
  function pixelCaptureTimeoutMs(device, firstCapture) {
148207
148234
  if (isIosPhysicalDevice(device)) {
148208
148235
  return IOS_DEVICE_PIXEL_CAPTURE_TIMEOUT_MS;
148209
148236
  }
148237
+ if (device.platform === "ios-remote" && !firstCapture) {
148238
+ return REMOTE_PIXEL_CAPTURE_TIMEOUT_MS;
148239
+ }
148210
148240
  const warmFromTheStart = device.platform === "chromium" || device.platform === "vega";
148211
148241
  return firstCapture && !warmFromTheStart ? FIRST_PIXEL_CAPTURE_TIMEOUT_MS : PIXEL_CAPTURE_TIMEOUT_MS;
148212
148242
  }
@@ -153856,7 +153886,7 @@ async function runSnapshot(env, opts) {
153856
153886
  });
153857
153887
  const { w, h } = await pngDimensions(shot.image.hostPath);
153858
153888
  const cropSuffix = opts.cropOn === void 0 ? "" : `-crop-${(0, import_node_crypto12.createHash)("sha256").update(cropIdentity(opts.cropOn)).digest("hex").slice(0, 8)}`;
153859
- const snapshotKey = `${opts.name}__${env.device.platform}-${w}x${h}${cropSuffix}`;
153889
+ const snapshotKey = `${opts.name}__${authoringPlatform(env.device.platform)}-${w}x${h}${cropSuffix}`;
153860
153890
  const key2 = `${snapshotKey}.png`;
153861
153891
  const dir = baselineDir(opts.flowsDir, opts.flowName);
153862
153892
  const baselinePath = path44.join(dir, key2);
@@ -153907,9 +153937,10 @@ async function runSnapshot(env, opts) {
153907
153937
  kind: "screenshot",
153908
153938
  mimeType: "image/png"
153909
153939
  });
153940
+ const source = env.device.platform === "ios-remote" ? " from a remote simulator" : "";
153910
153941
  return {
153911
153942
  status: "pass",
153912
- reason: exists2 ? `baseline updated (${key2})` : `baseline written (${key2})`,
153943
+ reason: exists2 ? `baseline updated${source} (${key2})` : `baseline written${source} (${key2})`,
153913
153944
  snapshotKey,
153914
153945
  artifacts: { baseline }
153915
153946
  };
@@ -153995,30 +154026,41 @@ init_adb();
153995
154026
  init_device_info();
153996
154027
  var execFileAsync27 = (0, import_node_util25.promisify)(import_node_child_process35.execFile);
153997
154028
  var DEMO_BROADCAST = "am broadcast -a com.android.systemui.demo";
153998
- async function pinStatusBar(device) {
154029
+ var IOS_STATUS_BAR_OVERRIDE = [
154030
+ "override",
154031
+ "--time",
154032
+ "9:37",
154033
+ "--batteryState",
154034
+ "charged",
154035
+ "--batteryLevel",
154036
+ "100",
154037
+ "--wifiBars",
154038
+ "3",
154039
+ "--cellularBars",
154040
+ "4"
154041
+ ];
154042
+ var REMOTE_STATUS_BAR_TIMEOUT_MS = 5e3;
154043
+ function isRemoteTvOs(device) {
154044
+ return isRemoteTvOsSimulator(device.id, { timeoutMs: REMOTE_STATUS_BAR_TIMEOUT_MS });
154045
+ }
154046
+ async function pinStatusBar(device, signal) {
154047
+ if (signal?.aborted) return false;
153999
154048
  if (isIosPhysicalDevice(device)) return false;
154000
154049
  try {
154001
154050
  if (device.platform === "ios") {
154002
154051
  await execFileAsync27(
154003
154052
  "xcrun",
154004
- await simctlArgsForUdid(device.id, [
154005
- "status_bar",
154006
- device.id,
154007
- "override",
154008
- "--time",
154009
- "9:37",
154010
- "--batteryState",
154011
- "charged",
154012
- "--batteryLevel",
154013
- "100",
154014
- "--wifiBars",
154015
- "3",
154016
- "--cellularBars",
154017
- "4"
154018
- ])
154053
+ await simctlArgsForUdid(device.id, ["status_bar", device.id, ...IOS_STATUS_BAR_OVERRIDE])
154019
154054
  );
154020
154055
  return true;
154021
154056
  }
154057
+ if (device.platform === "ios-remote") {
154058
+ if (await isRemoteTvOs(device)) return false;
154059
+ await simctlStatusBar(device.id, IOS_STATUS_BAR_OVERRIDE, {
154060
+ timeoutMs: REMOTE_STATUS_BAR_TIMEOUT_MS
154061
+ });
154062
+ return true;
154063
+ }
154022
154064
  if (device.platform === "android") {
154023
154065
  await adbShell(device.id, "settings put global sysui_demo_allowed 1");
154024
154066
  await adbShell(device.id, `${DEMO_BROADCAST} -e command enter`);
@@ -154036,7 +154078,7 @@ async function pinStatusBar(device) {
154036
154078
  return false;
154037
154079
  } catch {
154038
154080
  const restored = await restoreStatusBar(device);
154039
- return device.platform === "android" && !restored;
154081
+ return (device.platform === "android" || device.platform === "ios-remote") && !restored;
154040
154082
  }
154041
154083
  }
154042
154084
  async function restoreStatusBar(device) {
@@ -154046,6 +154088,10 @@ async function restoreStatusBar(device) {
154046
154088
  "xcrun",
154047
154089
  await simctlArgsForUdid(device.id, ["status_bar", device.id, "clear"])
154048
154090
  );
154091
+ } else if (device.platform === "ios-remote") {
154092
+ if (!await isRemoteTvOs(device)) {
154093
+ await simctlStatusBar(device.id, ["clear"], { timeoutMs: REMOTE_STATUS_BAR_TIMEOUT_MS });
154094
+ }
154049
154095
  } else if (device.platform === "android") {
154050
154096
  try {
154051
154097
  await adbShell(device.id, `${DEMO_BROADCAST} -e command exit`);
@@ -154554,7 +154600,7 @@ Returns a per-step report: the first failure stops the run and the rest report a
154554
154600
  viaUpload
154555
154601
  );
154556
154602
  const device = resolved.device;
154557
- const statusBarPinned = device !== null && await pinStatusBar(device);
154603
+ const statusBarPinned = device !== null && await pinStatusBar(device, signal);
154558
154604
  if (device?.platform === "chromium") await frontChromiumPage(registry2, device);
154559
154605
  const state3 = {
154560
154606
  registry: registry2,
@@ -155080,6 +155126,7 @@ async function execLeafStep(state3, step, index, scope) {
155080
155126
  status: r.status,
155081
155127
  reason: r.reason,
155082
155128
  snapshotKey: r.snapshotKey,
155129
+ ...r.snapshotKey !== void 0 && state3.device?.platform === "ios-remote" ? { snapshotRemote: true } : {},
155083
155130
  artifacts: r.artifacts
155084
155131
  };
155085
155132
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/argent",
3
- "version": "0.25.1-next.32",
3
+ "version": "0.25.1-next.34",
4
4
  "mcpName": "io.github.software-mansion/argent",
5
5
  "description": "MCP server for iOS Simulator and Android Emulator control",
6
6
  "license": "Apache-2.0",
@@ -255,7 +255,7 @@ A screenshot is human evidence. A `snapshot:` is executable visual verification.
255
255
 
256
256
  Do not use a snapshot as the only proof of navigation, persistence, data, accessibility state, logs, or network behavior. Avoid unstable timestamps, live data, ads, animation, and device drift. First establish deterministic state, identity, and readiness.
257
257
 
258
- Baselines live under `.argent/flows/__baselines__/<flow>/` and are keyed by platform and full-capture geometry; `cropOn` also contributes its selector. Seed from a known-good state with `--update-baselines`. Inspect every baseline and require user review. Do not commit it yourself. Baseline creation or update is not a test pass. Never update a baseline only to make a diff pass. The default `maxMismatch` is 0.5 percent.
258
+ Baselines live under `.argent/flows/__baselines__/<flow>/` and are keyed by platform and full-capture geometry; `cropOn` also contributes its selector. A run on a remote simulator uses `ios` in the key, so it uses the same baseline as a local iOS run with the same capture geometry. Seed from a known-good state with `--update-baselines`. A run on a remote simulator with `--update-baselines` rewrites the same baseline file that a local run uses, and its step reason says that a remote simulator wrote it. Inspect every baseline and require user review. Do not commit it yourself. Baseline creation or update is not a test pass. Never update a baseline only to make a diff pass. The default `maxMismatch` is 0.5 percent.
259
259
 
260
260
  Pin `--platform` and `--device` for iOS, Android, or Vega. For Chromium the device class is the window's own pixel size, which the app sets and no launch argument changes: pass `--platform chromium` and omit `--device` so the runner boots the declared app path instead of attaching to a running window of another size. A window sized from host or session state produces a key CI cannot reproduce, and the step fails for a missing baseline. The runner pins mobile status bars during visual runs. `--output <dir>` writes failed baseline, current, and diff images under `<dir>/<flow>/` for CI artifact upload.
261
261