@devicelab/maestro-runner-darwin-arm64 1.1.29 → 1.1.30

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 (118) hide show
  1. package/README.md +15 -4
  2. package/bin/maestro-runner +0 -0
  3. package/drivers/android/devicelab-android-driver-test.apk +0 -0
  4. package/drivers/android/devicelab-android-driver.apk +0 -0
  5. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/ATTRIBUTION.md +16 -0
  6. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Lifecycle.swift +33 -0
  7. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Snapshot.swift +195 -88
  8. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests.swift +3 -0
  9. package/drivers/ios/WebDriverAgent/CHANGELOG.md +202 -0
  10. package/drivers/ios/WebDriverAgent/README.md +18 -9
  11. package/drivers/ios/WebDriverAgent/Scripts/ci/build-real.sh +6 -10
  12. package/drivers/ios/WebDriverAgent/Scripts/fetch-prebuilt-wda.mjs +3 -2
  13. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/project.pbxproj +119 -0
  14. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCAXClient_iOS+FBSnapshotReqParams.m +8 -0
  15. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +3 -0
  16. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +30 -1
  17. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.h +53 -0
  18. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.m +124 -0
  19. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.h +6 -0
  20. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +100 -23
  21. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHinge.h +41 -0
  22. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHinge.m +215 -0
  23. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBRotation.m +8 -3
  24. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBFind.m +20 -7
  25. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.m +110 -50
  26. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.h +7 -0
  27. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.m +20 -0
  28. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.m +14 -11
  29. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBCustomCommands.m +86 -10
  30. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBElementCommands.m +57 -7
  31. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBSessionCommands.m +3 -2
  32. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Info.plist +2 -2
  33. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBHTTPServer.m +39 -16
  34. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBScreenRecordingRequest.m +7 -2
  35. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.h +22 -0
  36. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.m +78 -12
  37. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.h +5 -1
  38. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.m +10 -1
  39. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBWebServer.m +4 -1
  40. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.h +2 -2
  41. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.m +20 -0
  42. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.h +8 -0
  43. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.m +2 -0
  44. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBHIDOperations.h +41 -0
  45. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBHIDOperations.m +244 -0
  46. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBImageProcessor.m +3 -1
  47. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.h +11 -0
  48. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.m +38 -0
  49. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMjpegServer.m +5 -3
  50. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.h +17 -0
  51. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.m +76 -1
  52. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.h +2 -2
  53. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.m +6 -2
  54. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.h +2 -0
  55. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.m +2 -0
  56. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettingsHandler.m +48 -0
  57. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.m +121 -13
  58. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXPath.m +181 -128
  59. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCache.m +8 -5
  60. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCacheNode.h +1 -1
  61. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/WebDriverAgentLib.h +1 -0
  62. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/COORDINATE_PROBE.md +3 -1
  63. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBCoordinateProbeViewController.m +18 -0
  64. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/SceneDelegate.m +33 -0
  65. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/ViewController.m +26 -7
  66. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBConfigurationTests.m +52 -0
  67. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBDisplayGestureTests.m +541 -0
  68. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBElementSwipingTests.m +95 -0
  69. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBImageProcessorTests.m +34 -0
  70. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBScreenTests.m +392 -0
  71. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBSessionIntegrationTests.m +97 -0
  72. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBTypingTest.m +33 -0
  73. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBW3CTouchActionsIntegrationTests.m +126 -0
  74. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m +111 -0
  75. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceHelperTests.m +97 -0
  76. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceRotationTests.m +53 -0
  77. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIElementFBFindTests.m +113 -0
  78. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests_tvOS/FBTVFocusIntegrationTests.m +21 -0
  79. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests_watchOS/WDAAppLifecycleIntegrationTests.swift +12 -0
  80. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBCustomCommandsTests.m +61 -0
  81. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBElementUtilitiesTests.m +40 -0
  82. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBHTTPServerSessionTests.m +22 -4
  83. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBHTTPServerTests.m +199 -5
  84. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBLRUCacheTests.m +34 -0
  85. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBMathUtilsTests.m +45 -0
  86. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBSessionTests.m +201 -0
  87. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBXPathTests.m +101 -0
  88. package/drivers/ios/WebDriverAgent/docs/hinge-angle.md +54 -0
  89. package/drivers/ios/WebDriverAgent/docs/multiple-displays.md +168 -0
  90. package/drivers/ios/WebDriverAgent/lib/webdriveragent.ts +1 -2
  91. package/drivers/ios/WebDriverAgent/lib/xcodebuild.ts +58 -10
  92. package/drivers/ios/WebDriverAgent/package.json +8 -15
  93. package/drivers/ios/WebDriverAgent/test/functional/helpers/display-settings.ts +49 -0
  94. package/drivers/ios/WebDriverAgent/test/functional/helpers/simulator.ts +7 -7
  95. package/drivers/ios/WebDriverAgent/test/functional/webdriveragent-e2e.spec.ts +4 -0
  96. package/drivers/ios/WebDriverAgent/test/unit/xcodebuild.spec.ts +230 -0
  97. package/drivers/ios/WebDriverAgent/tsconfig.json +2 -1
  98. package/drivers/ios/devicelab-ios-agent/README.md +12 -0
  99. package/drivers/ios/devicelab-ios-agent/device/DevicelabIOSAgent.app/DevicelabIOSAgent +0 -0
  100. package/drivers/ios/devicelab-ios-agent/device/DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests +0 -0
  101. package/drivers/ios/devicelab-ios-agent/device/manifest.json +3 -3
  102. package/drivers/ios/devicelab-ios-agent/simulator/DevicelabIOSAgent.app/DevicelabIOSAgent +0 -0
  103. package/drivers/ios/devicelab-ios-agent/simulator/DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests +0 -0
  104. package/drivers/ios/devicelab-ios-agent/simulator/manifest.json +3 -3
  105. package/package.json +1 -1
  106. package/drivers/ios/WebDriverAgent/.editorconfig +0 -11
  107. package/drivers/ios/WebDriverAgent/.github/ISSUE_TEMPLATE.md +0 -4
  108. package/drivers/ios/WebDriverAgent/.github/actions/xcode-setup/action.yml +0 -32
  109. package/drivers/ios/WebDriverAgent/.github/dependabot.yml +0 -22
  110. package/drivers/ios/WebDriverAgent/.github/workflows/functional-test.yml +0 -103
  111. package/drivers/ios/WebDriverAgent/.github/workflows/pr-title.yml +0 -10
  112. package/drivers/ios/WebDriverAgent/.github/workflows/publish.js.yml +0 -112
  113. package/drivers/ios/WebDriverAgent/.github/workflows/unit-test.yml +0 -32
  114. package/drivers/ios/WebDriverAgent/.github/workflows/wda-package.yml +0 -114
  115. package/drivers/ios/WebDriverAgent/.github/workflows/wda-tests.yml +0 -246
  116. package/drivers/ios/WebDriverAgent/oxfmt.config.mjs +0 -6
  117. package/drivers/ios/WebDriverAgent/oxlint.config.mjs +0 -6
  118. package/drivers/ios/WebDriverAgent/release.config.mjs +0 -14
@@ -26,6 +26,9 @@ import {
26
26
 
27
27
  const DEFAULT_SIGNING_ID = 'iPhone Developer';
28
28
  const PREBUILD_DELAY = 0;
29
+ // Bounds the best-effort udid case lookup so a hung 'xcodebuild -showdestinations' cannot stall
30
+ // session startup past wdaLaunchTimeout, which does not cover this step.
31
+ const XCODE_DEVICE_UDID_RESOLUTION_TIMEOUT_MS = 15000;
29
32
 
30
33
  const ERROR_WRITING_ATTACHMENT = 'Error writing attachment data to file';
31
34
  const ERROR_COPYING_ATTACHMENT = 'Error copying testing attachment';
@@ -43,6 +46,7 @@ export class XcodeBuild {
43
46
  readonly device: AppleDevice;
44
47
  readonly realDevice: boolean;
45
48
  readonly agentPath: string;
49
+ private _xcodeDeviceUdidPromise?: Promise<string>;
46
50
  readonly bootstrapPath: string;
47
51
  readonly platformVersion?: string;
48
52
  readonly platformName?: string;
@@ -70,7 +74,6 @@ export class XcodeBuild {
70
74
  private readonly resultBundlePath?: string;
71
75
  private readonly resultBundleVersion?: string;
72
76
  private _didBuildFail: boolean;
73
- private _didProcessExit: boolean;
74
77
  private readonly _buildSettingsPromises = new Map<string, Promise<XcodeBuildSettings | undefined>>();
75
78
  private noSessionProxy?: NoSessionProxy;
76
79
  private xctestrunFilePath?: string;
@@ -126,7 +129,6 @@ export class XcodeBuild {
126
129
  this.resultBundleVersion = args.resultBundleVersion;
127
130
 
128
131
  this._didBuildFail = false;
129
- this._didProcessExit = false;
130
132
  }
131
133
 
132
134
  /**
@@ -138,6 +140,8 @@ export class XcodeBuild {
138
140
  this.noSessionProxy = noSessionProxy;
139
141
 
140
142
  if (this.useXctestrunFile) {
143
+ // Keep the caller's udid casing: the canonical case is only for '-destination id=';
144
+ // switching it here would orphan an existing .xctestrun file on case-sensitive filesystems.
141
145
  const deviceInfo = {
142
146
  isRealDevice: !!this.realDevice,
143
147
  udid: this.device.udid,
@@ -244,11 +248,13 @@ export class XcodeBuild {
244
248
  throw new Error('xcodebuild subprocess was not created');
245
249
  }
246
250
  const xcodebuild = this.xcodebuild;
251
+ // Exit state belongs to this invocation, including build-only invocations.
252
+ let didProcessExit = false;
247
253
  return await new Promise<StringRecord | void>((resolve, reject) => {
248
254
  xcodebuild.once('exit', (code, signal) => {
249
255
  xcodeLog.error(`xcodebuild exited with code '${code}' and signal '${signal}'`);
250
256
  xcodebuild.removeAllListeners();
251
- this._didProcessExit = true;
257
+ didProcessExit = true;
252
258
  if (this._didBuildFail || (!signal && code !== 0)) {
253
259
  let errorMessage =
254
260
  `xcodebuild failed with code ${code}.` +
@@ -279,7 +285,7 @@ export class XcodeBuild {
279
285
  }
280
286
  await xcodebuild.start(true);
281
287
  if (!buildOnly) {
282
- const result = await this.waitForStart(timer);
288
+ const result = await this.waitForStart(timer, () => didProcessExit);
283
289
  resolve(result ?? undefined);
284
290
  }
285
291
  } catch (err: any) {
@@ -358,7 +364,48 @@ export class XcodeBuild {
358
364
  return entry.buildSettings;
359
365
  }
360
366
 
361
- private getCommand(buildOnly: boolean = false): {cmd: string; args: string[]} {
367
+ /**
368
+ * Best-effort resolves this real device's udid to the case xcodebuild's own
369
+ * `-showdestinations` listing reports it in, so the `-destination id=` argument this class
370
+ * builds does not depend on whatever case the caller happened to pass in — different Apple
371
+ * tooling doesn't agree on udid letter case. Falls back to `device.udid` unchanged on any
372
+ * failure, timeout, or when no destination matches. A no-op for simulators, whose udid is
373
+ * already canonical by construction.
374
+ */
375
+ private resolveXcodeDeviceUdid(): Promise<string> {
376
+ if (!this.realDevice) {
377
+ return Promise.resolve(this.device.udid);
378
+ }
379
+ // Cache the in-flight promise itself (not just a "did we start" flag), so concurrent callers
380
+ // await the same resolution instead of one of them racing ahead with a value that isn't
381
+ // resolved yet.
382
+ if (!this._xcodeDeviceUdidPromise) {
383
+ this._xcodeDeviceUdidPromise = this.fetchXcodeDeviceUdid();
384
+ }
385
+ return this._xcodeDeviceUdidPromise;
386
+ }
387
+
388
+ private async fetchXcodeDeviceUdid(): Promise<string> {
389
+ const runnerScheme = `WebDriverAgentRunner${getPlatformSchemeSuffix(this.platformName || '')}`;
390
+ let stdout: string;
391
+ try {
392
+ ({stdout} = await exec('xcodebuild', ['-showdestinations', '-project', this.agentPath, '-scheme', runnerScheme], {
393
+ timeout: XCODE_DEVICE_UDID_RESOLUTION_TIMEOUT_MS,
394
+ }));
395
+ } catch (err: any) {
396
+ this.log.debug(
397
+ `Cannot list xcodebuild destinations to resolve the canonical udid case for '${this.device.udid}'. ` +
398
+ `Will use it as is. Original error: ${err.message}`,
399
+ );
400
+ return this.device.udid;
401
+ }
402
+
403
+ const udid = this.device.udid.toLowerCase();
404
+ const match = [...stdout.matchAll(/\bid:([0-9A-Fa-f-]+)/g)].find(([, id]) => id.toLowerCase() === udid);
405
+ return match?.[1] ?? this.device.udid;
406
+ }
407
+
408
+ private getCommand(buildOnly: boolean, resolvedUdid: string): {cmd: string; args: string[]} {
362
409
  const cmd = 'xcodebuild';
363
410
  const args: string[] = [];
364
411
 
@@ -396,7 +443,7 @@ export class XcodeBuild {
396
443
  args.push('-derivedDataPath', this.derivedDataPath);
397
444
  }
398
445
  }
399
- args.push('-destination', `id=${this.device.udid}`);
446
+ args.push('-destination', `id=${resolvedUdid}`);
400
447
 
401
448
  const versionMatch = this.platformVersion ? new RegExp(/^(\d+)\.(\d+)/).exec(this.platformVersion) : null;
402
449
  if (versionMatch) {
@@ -443,7 +490,8 @@ export class XcodeBuild {
443
490
  await setRealDeviceSecurity(this.keychainPath, this.keychainPassword);
444
491
  }
445
492
 
446
- const {cmd, args} = this.getCommand(buildOnly);
493
+ const resolvedUdid = await this.resolveXcodeDeviceUdid();
494
+ const {cmd, args} = this.getCommand(buildOnly, resolvedUdid);
447
495
  this.log.debug(
448
496
  `Beginning ${buildOnly ? 'build' : 'test'} with command '${cmd} ${args.join(' ')}' ` +
449
497
  `in directory '${this.bootstrapPath}'`,
@@ -505,7 +553,7 @@ export class XcodeBuild {
505
553
  return xcodebuild;
506
554
  }
507
555
 
508
- private async waitForStart(timer: timing.Timer): Promise<StringRecord | null> {
556
+ private async waitForStart(timer: timing.Timer, didProcessExit: () => boolean): Promise<StringRecord | null> {
509
557
  // try to connect once every 0.5 seconds, until `launchTimeout` is up
510
558
  const timeout = this.launchTimeout || 60000; // Default to 60 seconds if not set
511
559
  this.log.debug(`Waiting up to ${timeout}ms for WebDriverAgent to start`);
@@ -517,7 +565,7 @@ export class XcodeBuild {
517
565
  }
518
566
  const noSessionProxy = this.noSessionProxy;
519
567
  await retryInterval(retries, 1000, async () => {
520
- if (this._didProcessExit) {
568
+ if (didProcessExit()) {
521
569
  // there has been an error elsewhere and we need to short-circuit
522
570
  return currentStatus;
523
571
  }
@@ -537,7 +585,7 @@ export class XcodeBuild {
537
585
  }
538
586
  });
539
587
 
540
- if (this._didProcessExit) {
588
+ if (didProcessExit()) {
541
589
  // there has been an error elsewhere and we need to short-circuit
542
590
  return currentStatus;
543
591
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-webdriveragent",
3
- "version": "16.12.8",
3
+ "version": "16.17.5",
4
4
  "description": "Package bundling WebDriverAgent",
5
5
  "keywords": [
6
6
  "Appium",
@@ -9,15 +9,16 @@
9
9
  "WebDriverAgent",
10
10
  "iOS"
11
11
  ],
12
- "homepage": "https://github.com/appium/WebDriverAgent#readme",
12
+ "homepage": "https://github.com/appium/appium-ios/tree/main/packages/webdriveragent",
13
13
  "bugs": {
14
- "url": "https://github.com/appium/WebDriverAgent/issues"
14
+ "url": "https://github.com/appium/appium-ios/issues"
15
15
  },
16
16
  "license": "Apache-2.0",
17
17
  "author": "Appium Contributors",
18
18
  "repository": {
19
19
  "type": "git",
20
- "url": "git+https://github.com/appium/WebDriverAgent.git"
20
+ "url": "https://github.com/appium/appium-ios.git",
21
+ "directory": "packages/webdriveragent"
21
22
  },
22
23
  "files": [
23
24
  "lib",
@@ -48,10 +49,6 @@
48
49
  "build": "tsc -b",
49
50
  "dev": "npm run build -- --watch",
50
51
  "clean": "npm run build -- --clean",
51
- "lint": "oxlint -c oxlint.config.mjs .",
52
- "lint:fix": "oxlint -c oxlint.config.mjs --fix .",
53
- "format": "oxfmt -c oxfmt.config.mjs .",
54
- "format:check": "oxfmt -c oxfmt.config.mjs --check .",
55
52
  "prepare": "npm run build",
56
53
  "version": "npm run sync-wda-version",
57
54
  "test": "node --enable-source-maps --experimental-test-module-mocks --test --test-timeout=60000 \"./build/test/unit/**/*.spec.js\"",
@@ -67,22 +64,18 @@
67
64
  "@appium/base-driver": "^10.8.0",
68
65
  "@appium/strongbox": "^2.0.0",
69
66
  "@appium/support": "^7.2.6",
70
- "appium-ios-simulator": "^9.1.3",
71
- "async-lock": "^1.0.0",
72
- "asyncbox": "^6.4.3",
67
+ "asyncbox": "^6.5.0",
73
68
  "axios": "^1.18.0",
74
69
  "teen_process": "^4.2.0"
75
70
  },
76
71
  "devDependencies": {
72
+ "@appium/coresim": "^1.14.0",
77
73
  "@appium/oxc-config": "^1.1.0",
78
- "@appium/semantic-release-config": "^1.1.0",
79
74
  "@appium/tsconfig": "^1.2.0",
80
75
  "@appium/types": "^1.0.0-rc.1",
81
- "@types/async-lock": "^1.4.2",
82
76
  "@types/node": "^26.0.0",
83
77
  "@types/sinon": "^22.0.0",
84
- "appium-xcode": "^7.1.1",
85
- "node-simctl": "^9.0.0",
78
+ "appium-xcode": "^7.2.4",
86
79
  "semver": "^7.3.7",
87
80
  "sinon": "^22.0.0"
88
81
  },
@@ -0,0 +1,49 @@
1
+ import assert from 'node:assert/strict';
2
+
3
+ /** Exercise the currentDisplayId setting against a live WDA session. */
4
+ export async function assertCurrentDisplayIdSetting(baseUrl: string, sessionId: string): Promise<void> {
5
+ const screensResponse = await fetch(`${baseUrl}/wda/screens`);
6
+ assert.equal(screensResponse.status, 200);
7
+ const {value: screens} = (await screensResponse.json()) as {
8
+ value: {displayId: number; isMain: boolean}[];
9
+ };
10
+ const main = screens.find((screen) => screen.isMain);
11
+ assert.ok(main, 'expected a main display');
12
+ const sessionUrl = `${baseUrl}/session/${sessionId}`;
13
+ const setDisplay = async (value: unknown) =>
14
+ await fetch(`${sessionUrl}/appium/settings`, {
15
+ method: 'POST',
16
+ headers: {'Content-Type': 'application/json'},
17
+ body: JSON.stringify({settings: {currentDisplayId: value}}),
18
+ });
19
+ const getDisplay = async () => {
20
+ const response = await fetch(`${sessionUrl}/appium/settings`);
21
+ assert.equal(response.status, 200);
22
+ return ((await response.json()) as {value: {currentDisplayId: number}}).value.currentDisplayId;
23
+ };
24
+ const assertScreenshot = async () => {
25
+ const response = await fetch(`${sessionUrl}/screenshot`);
26
+ assert.equal(response.status, 200);
27
+ const {value} = (await response.json()) as {value: string};
28
+ assert.ok(Buffer.from(value, 'base64').length > 0, 'expected screenshot data');
29
+ };
30
+
31
+ assert.equal(await getDisplay(), main.displayId);
32
+ assert.equal((await setDisplay(main.displayId)).status, 200);
33
+ assert.equal(await getDisplay(), main.displayId);
34
+ await assertScreenshot();
35
+
36
+ const unknownId = Math.max(...screens.map((screen) => screen.displayId)) + 1;
37
+ for (const value of [true, false, unknownId, 1.5, '1']) {
38
+ const response = await setDisplay(value);
39
+ assert.equal(response.status, 400, `expected invalid argument for ${JSON.stringify(value)}`);
40
+ const {value: body} = (await response.json()) as {value: {error: string}};
41
+ assert.equal(body.error, 'invalid argument');
42
+ assert.equal(await getDisplay(), main.displayId, 'invalid input must not change the display');
43
+ }
44
+ assert.equal(await getDisplay(), main.displayId);
45
+
46
+ assert.equal((await setDisplay(null)).status, 200);
47
+ assert.equal(await getDisplay(), main.displayId);
48
+ await assertScreenshot();
49
+ }
@@ -1,4 +1,4 @@
1
- import {Simctl} from 'node-simctl';
1
+ import {NativeSimctl, SimDeviceState} from '@appium/coresim';
2
2
 
3
3
  import type {AppleDevice} from '../../../lib/types.js';
4
4
  import {DEVICE_NAME} from '../desired.js';
@@ -9,7 +9,7 @@ const LOCAL_SIM_BOOT_TIMEOUT_MS = 60 * 1000 * 5;
9
9
  * Locates the simulator these tests should run against.
10
10
  *
11
11
  * In CI, the workflow boots and settles a simulator ahead of time (via
12
- * `futureware-tech/simulator-action` + `Scripts/ci/wait-for-simulator-idle.mjs`) and passes its
12
+ * `futureware-tech/simulator-action` with `settle_timeout_seconds`) and passes its
13
13
  * UDID through `SIMULATOR_UDID` - these tests must not boot their own there, since CI applies the
14
14
  * settle-wait stability fix once per job, not once per test file.
15
15
  *
@@ -28,17 +28,17 @@ export async function getTargetDevice(): Promise<AppleDevice> {
28
28
  return {udid: process.env.SIMULATOR_UDID};
29
29
  }
30
30
 
31
- const simctl = new Simctl();
32
- const allDevices = Object.values(await simctl.getDevices()).flat();
31
+ const simctl = new NativeSimctl();
32
+ const allDevices = await simctl.getDevices();
33
33
  const device = allDevices.find((d) => d.name === DEVICE_NAME);
34
34
  if (!device) {
35
35
  const available = [...new Set(allDevices.map((d) => d.name))].sort().join(', ');
36
36
  throw new Error(`No simulator named '${DEVICE_NAME}' exists. Available simulators: ${available || '(none)'}`);
37
37
  }
38
38
 
39
- if (device.state !== 'Booted') {
40
- simctl.udid = device.udid;
41
- await simctl.startBootMonitor({shouldPreboot: true, timeout: LOCAL_SIM_BOOT_TIMEOUT_MS});
39
+ if (device.state !== SimDeviceState.Booted) {
40
+ await simctl.bootDevice(device.udid);
41
+ await simctl.waitForBoot(device.udid, {timeoutMs: LOCAL_SIM_BOOT_TIMEOUT_MS});
42
42
  }
43
43
 
44
44
  return {udid: device.udid};
@@ -5,6 +5,7 @@ import {describe, before, afterEach, it} from 'node:test';
5
5
  import type {AppleDevice} from '../../lib/types.js';
6
6
  import {WebDriverAgent} from '../../lib/webdriveragent.js';
7
7
  import {PLATFORM_NAME, PLATFORM_VERSION} from './desired.js';
8
+ import {assertCurrentDisplayIdSetting} from './helpers/display-settings.js';
8
9
  import {getTargetDevice} from './helpers/simulator.js';
9
10
 
10
11
  const WDA_BASE_URL = 'http://localhost:8100';
@@ -59,6 +60,9 @@ async function assertSessionScopedSourceWorks(): Promise<void> {
59
60
  assert.equal(sessionSourceResponse.status, 200);
60
61
  const sessionSource = (await sessionSourceResponse.json()) as {value: string};
61
62
  assert.ok(sessionSource.value.length > 0, 'expected session-scoped /source to return non-empty page source');
63
+ if (PLATFORM_NAME.toLowerCase() === 'ios') {
64
+ await assertCurrentDisplayIdSetting(WDA_BASE_URL, wdaSessionId);
65
+ }
62
66
  } finally {
63
67
  await fetch(`${WDA_BASE_URL}/session/${wdaSessionId}`, {method: 'DELETE'});
64
68
  }
@@ -0,0 +1,230 @@
1
+ import assert from 'node:assert/strict';
2
+ import {EventEmitter} from 'node:events';
3
+ import {describe, it, beforeEach, mock} from 'node:test';
4
+
5
+ import * as teenProcess from 'teen_process';
6
+
7
+ import type {XcodeBuildArgs} from '../../lib/types.js';
8
+ import * as utilsIndex from '../../lib/utils/index.js';
9
+
10
+ let currentExec: (...args: any[]) => any = async () => ({stdout: '', stderr: ''});
11
+
12
+ mock.module('teen_process', {
13
+ namedExports: {
14
+ ...teenProcess,
15
+ exec: (...args: any[]) => currentExec(...args),
16
+ },
17
+ });
18
+
19
+ let currentSetXctestrunFile: (...args: any[]) => any = async () => '/fake/WebDriverAgentRunner.xctestrun';
20
+
21
+ mock.module('../../lib/utils/index.js', {
22
+ namedExports: {
23
+ ...utilsIndex,
24
+ setXctestrunFile: (...args: any[]) => currentSetXctestrunFile(...args),
25
+ },
26
+ });
27
+
28
+ const {XcodeBuild} = await import('../../lib/xcodebuild.js');
29
+
30
+ const SHOWDESTINATIONS_OUTPUT = `
31
+ \tDestinations compatible with the "WebDriverAgentRunner" scheme:
32
+ \t\t{ platform:iOS, id:00008030-000A49391460202E, name:iPad von Galyna }
33
+ \t\t{ platform:iOS Simulator, id:BB720847-B182-40BD-BB5A-338951D32ADC, OS:27.0, name:iPad (A16) }
34
+ `;
35
+
36
+ function makeXcodeBuild(udid: string, realDevice: boolean): InstanceType<typeof XcodeBuild> {
37
+ const args: XcodeBuildArgs = {
38
+ realDevice,
39
+ agentPath: '/fake/WebDriverAgent.xcodeproj',
40
+ bootstrapPath: '/fake',
41
+ };
42
+ return new XcodeBuild({udid}, args);
43
+ }
44
+
45
+ describe('XcodeBuild real-device udid case resolution', function () {
46
+ beforeEach(function () {
47
+ currentExec = async () => ({stdout: SHOWDESTINATIONS_OUTPUT, stderr: ''});
48
+ });
49
+
50
+ it('resolves to the canonical case xcodebuild reports', async function () {
51
+ const xcodebuild = makeXcodeBuild('00008030-000a49391460202e', true);
52
+ const resolved = await (xcodebuild as any).resolveXcodeDeviceUdid();
53
+ assert.strictEqual(resolved, '00008030-000A49391460202E');
54
+ });
55
+
56
+ it('falls back to the given udid when no destination matches', async function () {
57
+ const xcodebuild = makeXcodeBuild('unknown-udid', true);
58
+ const resolved = await (xcodebuild as any).resolveXcodeDeviceUdid();
59
+ assert.strictEqual(resolved, 'unknown-udid');
60
+ });
61
+
62
+ it("falls back to the given udid when 'xcodebuild -showdestinations' fails", async function () {
63
+ currentExec = async () => {
64
+ throw new Error('boom');
65
+ };
66
+ const xcodebuild = makeXcodeBuild('some-udid', true);
67
+ const resolved = await (xcodebuild as any).resolveXcodeDeviceUdid();
68
+ assert.strictEqual(resolved, 'some-udid');
69
+ });
70
+
71
+ it('is a no-op for simulators, whose udid is already canonical', async function () {
72
+ let execCalled = false;
73
+ currentExec = async () => {
74
+ execCalled = true;
75
+ return {stdout: '', stderr: ''};
76
+ };
77
+ const xcodebuild = makeXcodeBuild('sim-udid', false);
78
+ const resolved = await (xcodebuild as any).resolveXcodeDeviceUdid();
79
+ assert.strictEqual(execCalled, false);
80
+ assert.strictEqual(resolved, 'sim-udid');
81
+ });
82
+
83
+ it("only shells out to 'xcodebuild -showdestinations' once even if resolution is triggered multiple times", async function () {
84
+ let execCallCount = 0;
85
+ currentExec = async () => {
86
+ execCallCount++;
87
+ return {stdout: SHOWDESTINATIONS_OUTPUT, stderr: ''};
88
+ };
89
+ const xcodebuild = makeXcodeBuild('00008030-000a49391460202e', true);
90
+ await (xcodebuild as any).resolveXcodeDeviceUdid();
91
+ await (xcodebuild as any).resolveXcodeDeviceUdid();
92
+ assert.strictEqual(execCallCount, 1);
93
+ });
94
+
95
+ it('lets truly concurrent callers await the same in-flight resolution instead of racing ahead', async function () {
96
+ let execCallCount = 0;
97
+ let releaseExec: () => void = () => {};
98
+ const execGate = new Promise<void>((resolve) => {
99
+ releaseExec = resolve;
100
+ });
101
+ currentExec = async () => {
102
+ execCallCount++;
103
+ await execGate;
104
+ return {stdout: SHOWDESTINATIONS_OUTPUT, stderr: ''};
105
+ };
106
+ const xcodebuild = makeXcodeBuild('00008030-000a49391460202e', true);
107
+
108
+ // Both calls start before the (gated) exec() resolves — a naive "already started" boolean
109
+ // would let the second one return immediately with a value that isn't resolved yet.
110
+ const first = (xcodebuild as any).resolveXcodeDeviceUdid();
111
+ const second = (xcodebuild as any).resolveXcodeDeviceUdid();
112
+ releaseExec();
113
+ const [firstResolved, secondResolved] = await Promise.all([first, second]);
114
+
115
+ assert.strictEqual(execCallCount, 1);
116
+ assert.strictEqual(firstResolved, '00008030-000A49391460202E');
117
+ assert.strictEqual(secondResolved, '00008030-000A49391460202E');
118
+ });
119
+
120
+ it("builds the '-destination id=' argument from the resolved udid", async function () {
121
+ const xcodebuild = makeXcodeBuild('00008030-000a49391460202e', true);
122
+ const resolved = await (xcodebuild as any).resolveXcodeDeviceUdid();
123
+ const {args} = (xcodebuild as any).getCommand(false, resolved);
124
+ assert.ok(args.includes('id=00008030-000A49391460202E'));
125
+ });
126
+
127
+ it("bounds 'xcodebuild -showdestinations' with a timeout", async function () {
128
+ let capturedOpts: any;
129
+ currentExec = async (_cmd: string, _args: string[], opts: any) => {
130
+ capturedOpts = opts;
131
+ return {stdout: SHOWDESTINATIONS_OUTPUT, stderr: ''};
132
+ };
133
+ const xcodebuild = makeXcodeBuild('00008030-000a49391460202e', true);
134
+ await (xcodebuild as any).resolveXcodeDeviceUdid();
135
+ assert.ok(typeof capturedOpts?.timeout === 'number' && capturedOpts.timeout > 0);
136
+ });
137
+
138
+ it('falls back to the given udid when resolution times out', async function () {
139
+ currentExec = async () => {
140
+ throw new Error("Command 'xcodebuild ...' timed out after 15000ms");
141
+ };
142
+ const xcodebuild = makeXcodeBuild('some-udid', true);
143
+ const resolved = await (xcodebuild as any).resolveXcodeDeviceUdid();
144
+ assert.strictEqual(resolved, 'some-udid');
145
+ });
146
+
147
+ it("keeps the caller's udid casing for the .xctestrun file lookup, unaffected by resolution", async function () {
148
+ let capturedDeviceInfo: any;
149
+ currentSetXctestrunFile = async ({deviceInfo}: any) => {
150
+ capturedDeviceInfo = deviceInfo;
151
+ return '/fake/WebDriverAgentRunner.xctestrun';
152
+ };
153
+ const xcodebuild = new XcodeBuild(
154
+ {udid: '00008030-000a49391460202e'},
155
+ {
156
+ realDevice: true,
157
+ agentPath: '/fake/WebDriverAgent.xcodeproj',
158
+ bootstrapPath: '/fake',
159
+ useXctestrunFile: true,
160
+ },
161
+ );
162
+ await xcodebuild.init({} as any);
163
+ assert.strictEqual(capturedDeviceInfo.udid, '00008030-000a49391460202e');
164
+ });
165
+ });
166
+
167
+ describe('XcodeBuild readiness after a previous process exits', function () {
168
+ function setup() {
169
+ const build = new XcodeBuild(
170
+ {udid: 'sim-udid'},
171
+ {
172
+ realDevice: false,
173
+ agentPath: '/fake',
174
+ bootstrapPath: '/fake',
175
+ prebuildDelay: 0,
176
+ },
177
+ );
178
+ const processes: EventEmitter[] = [];
179
+ let statusCalls = 0;
180
+ const status = {ready: true};
181
+ (build as any).noSessionProxy = {
182
+ timeout: 5000,
183
+ command: async () => {
184
+ statusCalls++;
185
+ return status;
186
+ },
187
+ };
188
+ (build as any).createSubProcess = async (buildOnly: boolean) => {
189
+ const process = new EventEmitter();
190
+ processes.push(process);
191
+ (process as any).start = async () => {
192
+ if (buildOnly) {
193
+ process.emit('exit', 0, null);
194
+ }
195
+ };
196
+ return process;
197
+ };
198
+ return {build, processes, status, statusCalls: () => statusCalls};
199
+ }
200
+
201
+ it('checks status after a successful prebuild', async function () {
202
+ const fixture = setup();
203
+ await fixture.build.prebuild();
204
+ assert.deepStrictEqual(await fixture.build.start(), fixture.status);
205
+ assert.strictEqual(fixture.statusCalls(), 1);
206
+ });
207
+
208
+ it('checks status again when restarting the same instance', async function () {
209
+ const fixture = setup();
210
+ await fixture.build.start();
211
+ fixture.processes[0].emit('exit', 0, null);
212
+ assert.deepStrictEqual(await fixture.build.start(), fixture.status);
213
+ assert.strictEqual(fixture.statusCalls(), 2);
214
+ });
215
+
216
+ it('does not let an old process exit mark the new process as exited', async function () {
217
+ const fixture = setup();
218
+ await fixture.build.start();
219
+ const create = (fixture.build as any).createSubProcess;
220
+ (fixture.build as any).createSubProcess = async (buildOnly: boolean) => {
221
+ const process = await create(buildOnly);
222
+ process.start = async () => {
223
+ fixture.processes[0].emit('exit', 0, null);
224
+ };
225
+ return process;
226
+ };
227
+ assert.deepStrictEqual(await fixture.build.start(), fixture.status);
228
+ assert.strictEqual(fixture.statusCalls(), 2);
229
+ });
230
+ });
@@ -10,5 +10,6 @@
10
10
  "module": "NodeNext",
11
11
  "moduleResolution": "NodeNext"
12
12
  },
13
- "include": ["lib", "test"]
13
+ "include": ["lib", "test"],
14
+ "references": [{"path": "../coresim"}, {"path": "../xcode"}]
14
15
  }
@@ -38,6 +38,16 @@ maestro-runner --platform ios --driver devicelab --team-id <TEAM_ID> \
38
38
  Regenerate `SigningStub.xcodeproj` from `signing-stub/project.yml` with
39
39
  `xcodegen generate`.
40
40
 
41
+ ## Access
42
+
43
+ Each run starts the agent with a random token (`DL_AGENT_TOKEN`, through
44
+ `SIMCTL_CHILD_` or the xctestrun's environment) and sends it on every request
45
+ as `X-DeviceLab-Token`; an agent built with token support answers a request
46
+ without it 401 and runs nothing. The token is saved, readable by the user only,
47
+ in `~/.maestro-runner/cache/devicelab-ios-agent/<udid>/agent.json` so a later
48
+ run can re-attach to a kept agent. The agent listens on 127.0.0.1 only: on a
49
+ simulator that is the Mac, on a device the end of the usbmux forward.
50
+
41
51
  Overrides:
42
52
 
43
53
  - `DEVICELAB_IOS_AGENT_DIR` — use an agent build from another directory
@@ -49,3 +59,5 @@ Overrides:
49
59
  - `DEVICELAB_IOS_AGENT_BUNDLE_ID` — sign the agent on a device as another
50
60
  bundle id (a personal team cannot use the team wildcard App ID, and
51
61
  `dev.devicelab.agent` may already be registered elsewhere)
62
+ - `DEVICELAB_IOS_AGENT_BIND=any` — on a device, have the agent listen on every
63
+ interface instead of loopback (the token is still required)
@@ -1,18 +1,18 @@
1
1
  {
2
- "version": "a2f0bc8",
2
+ "version": "ff2be7b",
3
3
  "protocol": "1.0",
4
4
  "xcode": "Xcode 27.0",
5
5
  "runnerBundleId": "dev.devicelab.agent.uitests.xctrunner",
6
6
  "hostBundleId": "dev.devicelab.agent",
7
7
  "files": {
8
8
  "agent.xctestrun": "93df94d1ffdc3f93a8cbe7d9b453096bde1436e8ed7cafd88436dfbc210d3f98",
9
- "DevicelabIOSAgent.app/DevicelabIOSAgent": "398d638b732d9dd5e088c9a1fb75abe231adc6ea506227c3c9ac9c53e48c088d",
9
+ "DevicelabIOSAgent.app/DevicelabIOSAgent": "98a49ac56496fd30dfec399c6c20b6070f478ee7b3e2f84ad0df7ebab4574864",
10
10
  "DevicelabIOSAgent.app/Info.plist": "cba8d7ca7542f6c6a0bf195b957a692fa5a924f0c4ef3c547edd5100399b8b7c",
11
11
  "DevicelabIOSAgent.app/PkgInfo": "82502191c9484b04d685374f9879a0066069c49b8acae7a04b01d38d07e8eca0",
12
12
  "DevicelabIOSAgentUITests-Runner.app/DevicelabIOSAgentUITests-Runner": "26d1a9837de9735a1c9dcaef554144297f03d6291c8421aa04d6e8f6c9bc5833",
13
13
  "DevicelabIOSAgentUITests-Runner.app/Info.plist": "ffc0b193115180aa4af30f183fe4bf67eb821c79c763f363fdd7b41e82262498",
14
14
  "DevicelabIOSAgentUITests-Runner.app/PkgInfo": "82502191c9484b04d685374f9879a0066069c49b8acae7a04b01d38d07e8eca0",
15
- "DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests": "c546096e04e0f747d661a8ce09bc8bcf1a23a25ea0dc6787187c699ae54e271d",
15
+ "DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests": "9a33ba998859cacc4c34d7fda9393aea445ec6f9c60d0dddc055ed35e767153c",
16
16
  "DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/Info.plist": "82c7c5c76ae134228a1cbc5235630dc5a63964bf5d3721b660790b08bec534f7"
17
17
  }
18
18
  }
@@ -1,12 +1,12 @@
1
1
  {
2
- "version": "a2f0bc8",
2
+ "version": "ff2be7b",
3
3
  "protocol": "1.0",
4
4
  "xcode": "Xcode 27.0",
5
5
  "runnerBundleId": "dev.devicelab.agent.uitests.xctrunner",
6
6
  "hostBundleId": "dev.devicelab.agent",
7
7
  "files": {
8
8
  "agent.xctestrun": "45b704ea6a432fef69461223a1de91e381743cb9413e082db7f2966283beea75",
9
- "DevicelabIOSAgent.app/DevicelabIOSAgent": "95d73c9fb94710f32f39d68956388c9ca249cd6595e21689691abbc4631d8a87",
9
+ "DevicelabIOSAgent.app/DevicelabIOSAgent": "6b660b46764d4052dd1fb7323b7dece22c9febdeb530d1149613f3eba2d5ec36",
10
10
  "DevicelabIOSAgent.app/Info.plist": "1de91d903f985c153b06d9ce6d2086812e2d8f49e657b17f6e58f4a8733af93e",
11
11
  "DevicelabIOSAgent.app/PkgInfo": "82502191c9484b04d685374f9879a0066069c49b8acae7a04b01d38d07e8eca0",
12
12
  "DevicelabIOSAgentUITests-Runner.app/DevicelabIOSAgentUITests-Runner": "d776d95c3da519cf802b9e58794802128cd158160db68471c703005522b37337",
@@ -156,7 +156,7 @@
156
156
  "DevicelabIOSAgentUITests-Runner.app/Frameworks/XCUnit.framework/XCUnit": "ca2ce84e479758c3f2ab3c95964de49938d917b3ed3fa3d98038a6c4ef5ddc33",
157
157
  "DevicelabIOSAgentUITests-Runner.app/Info.plist": "db45280229a22ebbda188c44e0b97d8ba049d65c2373a1b11b4c5b61525014a0",
158
158
  "DevicelabIOSAgentUITests-Runner.app/PkgInfo": "82502191c9484b04d685374f9879a0066069c49b8acae7a04b01d38d07e8eca0",
159
- "DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests": "67607c905d8889ebbda54d782dd47c4b9135e3a3dc63f84ce07b78dd485834f8",
159
+ "DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/DevicelabIOSAgentUITests": "ad228f590c4ed2713fe457128979ec023c933bd9d65dff7c926a129f46930a10",
160
160
  "DevicelabIOSAgentUITests-Runner.app/PlugIns/DevicelabIOSAgentUITests.xctest/Info.plist": "b4b1ff08ff01133dd8e3664807675a48476c073d5eba8dee0afa29f0c1bf46d5"
161
161
  }
162
162
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devicelab/maestro-runner-darwin-arm64",
3
- "version": "1.1.29",
3
+ "version": "1.1.30",
4
4
  "description": "maestro-runner binary for darwin arm64. Installed automatically as an optional dependency of maestro-runner.",
5
5
  "homepage": "https://open.devicelab.dev/maestro-runner",
6
6
  "repository": {