appium-xcuitest-driver 11.8.0 → 11.8.1

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/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [11.8.1](https://github.com/appium/appium-xcuitest-driver/compare/v11.8.0...v11.8.1) (2026-06-03)
2
+
3
+ ### Bug Fixes
4
+
5
+ * Video attachments path for Xcode 26.5+ ([#2862](https://github.com/appium/appium-xcuitest-driver/issues/2862)) ([174ff70](https://github.com/appium/appium-xcuitest-driver/commit/174ff707cba80217197d81f9ee917e9abf66f973))
6
+
1
7
  ## [11.8.0](https://github.com/appium/appium-xcuitest-driver/compare/v11.7.7...v11.8.0) (2026-06-02)
2
8
 
3
9
  ### Features
@@ -1 +1 @@
1
- {"version":3,"file":"xctest-record-screen.d.ts","sourceRoot":"","sources":["../../../lib/commands/xctest-record-screen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,WAAW,CAAC;AAG9C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAC,yBAAyB,EAAE,qBAAqB,EAAC,MAAM,SAAS,CAAC;AAe9E;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gCAAgC,CACpD,IAAI,EAAE,cAAc,EACpB,GAAG,CAAC,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,yBAAyB,CAAC,CA2BpC;AAED;;;GAGG;AACH,wBAAsB,kCAAkC,CACtD,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAE3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,cAAc,EACpB,UAAU,CAAC,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,WAAW,EACrB,aAAa,CAAC,EAAE,MAAM,EACtB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAClD,MAAM,GAAE,KAAK,GAAG,MAAM,GAAG,OAAe,GACvC,OAAO,CAAC,qBAAqB,CAAC,CAiFhC"}
1
+ {"version":3,"file":"xctest-record-screen.d.ts","sourceRoot":"","sources":["../../../lib/commands/xctest-record-screen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,WAAW,CAAC;AAG9C,OAAO,KAAK,EAAe,WAAW,EAAC,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAC,yBAAyB,EAAE,qBAAqB,EAAC,MAAM,SAAS,CAAC;AAoH9E;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gCAAgC,CACpD,IAAI,EAAE,cAAc,EACpB,GAAG,CAAC,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,yBAAyB,CAAC,CA2BpC;AAED;;;GAGG;AACH,wBAAsB,kCAAkC,CACtD,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAE3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,cAAc,EACpB,UAAU,CAAC,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,WAAW,EACrB,aAAa,CAAC,EAAE,MAAM,EACtB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAClD,MAAM,GAAE,KAAK,GAAG,MAAM,GAAG,OAAe,GACvC,OAAO,CAAC,qBAAqB,CAAC,CAgFhC"}
@@ -8,17 +8,102 @@ exports.mobileGetXctestScreenRecordingInfo = mobileGetXctestScreenRecordingInfo;
8
8
  exports.mobileStopXctestScreenRecording = mobileStopXctestScreenRecording;
9
9
  const support_1 = require("appium/support");
10
10
  const helpers_1 = require("./helpers");
11
- const utils_1 = require("../utils");
11
+ const node_os_1 = __importDefault(require("node:os"));
12
12
  const node_path_1 = __importDefault(require("node:path"));
13
13
  const xctest_attachment_deletion_client_1 = require("../device/xctest-attachment-deletion-client");
14
14
  const remotexpc_utils_1 = require("../device/remotexpc-utils");
15
15
  const MOV_EXT = '.mov';
16
+ /**
17
+ * On simulators XCTest stores screen recording attachments under
18
+ * `InternalDaemon/<id>/Attachments/<uuid>` (legacy) or
19
+ * `InternalDaemon/<id>/tmp/Attachments/<uuid>` (Xcode 26.5+).
20
+ * Brace `{,tmp/}` matches both in a single glob.
21
+ */
22
+ const SIMULATOR_XCTEST_RECORDING_ATTACHMENT_GLOB = '*/{,tmp/}Attachments/*';
16
23
  /** Insecure feature when real-device XCTest recording is used without RemoteXPC attachment deletion. */
17
24
  const XCTEST_SCREEN_RECORD_FEATURE = 'xctest_screen_record';
18
25
  const DOMAIN_IDENTIFIER = 'com.apple.testmanagerd';
19
26
  const DOMAIN_TYPE = 'appDataContainer';
20
27
  const USERNAME = 'mobile';
21
- const SUBDIRECTORY = 'Attachments';
28
+ /** Legacy layout and Xcode 26.5+ `tmp/Attachments` within testmanagerd's app data container. */
29
+ const REAL_DEVICE_XCTEST_ATTACHMENT_SUBDIRECTORIES = ['Attachments', 'tmp/Attachments'];
30
+ class XcTestScreenRecordingRetriever {
31
+ log;
32
+ constructor(log) {
33
+ this.log = log;
34
+ }
35
+ static nameMatchesUuid(name, uuid) {
36
+ return name.toUpperCase() === uuid.toUpperCase();
37
+ }
38
+ }
39
+ class SimulatorXcTestScreenRecordingRetriever extends XcTestScreenRecordingRetriever {
40
+ device;
41
+ constructor(device, log) {
42
+ super(log);
43
+ this.device = device;
44
+ }
45
+ async retrieve(uuid) {
46
+ const dataRoot = this.device.getDir();
47
+ // e.g. .../CoreSimulator/Devices/<udid>/data/Containers/Data/InternalDaemon/<daemon-id>/Attachments/<uuid>
48
+ // or .../InternalDaemon/<daemon-id>/tmp/Attachments/<uuid> (Xcode 26.5+)
49
+ const internalDaemonRoot = node_path_1.default.resolve(dataRoot, 'Containers', 'Data', 'InternalDaemon');
50
+ const attachmentPaths = await support_1.fs.glob(SIMULATOR_XCTEST_RECORDING_ATTACHMENT_GLOB, {
51
+ cwd: internalDaemonRoot,
52
+ absolute: true,
53
+ });
54
+ const videoPath = attachmentPaths.find((fp) => XcTestScreenRecordingRetriever.nameMatchesUuid(node_path_1.default.basename(fp), uuid));
55
+ if (!videoPath) {
56
+ throw new Error(`Unable to locate XCTest screen recording identified by '${uuid}' for the Simulator ${this.device.udid}`);
57
+ }
58
+ const { size } = await support_1.fs.stat(videoPath);
59
+ this.log.debug(`Located the video at '${videoPath}' (${support_1.util.toReadableSizeString(size)})`);
60
+ return videoPath;
61
+ }
62
+ }
63
+ class RealDeviceXcTestScreenRecordingRetriever extends XcTestScreenRecordingRetriever {
64
+ device;
65
+ tmpDir;
66
+ constructor(device, tmpDir, log) {
67
+ super(log);
68
+ this.device = device;
69
+ this.tmpDir = tmpDir;
70
+ }
71
+ async retrieve(uuid) {
72
+ const attachment = await this.findAttachment(uuid);
73
+ if (!attachment) {
74
+ throw new Error(`Unable to locate XCTest screen recording identified by '${uuid}' for the device ${this.device.udid}`);
75
+ }
76
+ const videoPath = node_path_1.default.join(this.tmpDir, `${uuid}${MOV_EXT}`);
77
+ const { subdirectory, fileName } = attachment;
78
+ await this.device.devicectl.pullFile(`${subdirectory}/${fileName}`, videoPath, {
79
+ username: USERNAME,
80
+ domainIdentifier: DOMAIN_IDENTIFIER,
81
+ domainType: DOMAIN_TYPE,
82
+ });
83
+ const { size } = await support_1.fs.stat(videoPath);
84
+ this.log.debug(`Pulled the video to '${videoPath}' (${support_1.util.toReadableSizeString(size)})`);
85
+ return videoPath;
86
+ }
87
+ async findAttachment(uuid) {
88
+ for (const subdirectory of REAL_DEVICE_XCTEST_ATTACHMENT_SUBDIRECTORIES) {
89
+ let fileNames;
90
+ try {
91
+ fileNames = await this.device.devicectl.listFiles(DOMAIN_TYPE, DOMAIN_IDENTIFIER, {
92
+ username: USERNAME,
93
+ subdirectory,
94
+ });
95
+ }
96
+ catch {
97
+ continue;
98
+ }
99
+ const fileName = fileNames.find((name) => XcTestScreenRecordingRetriever.nameMatchesUuid(name, uuid));
100
+ if (fileName) {
101
+ return { subdirectory, fileName };
102
+ }
103
+ }
104
+ return null;
105
+ }
106
+ }
22
107
  /**
23
108
  * Start a new screen recording via XCTest.
24
109
  *
@@ -106,7 +191,7 @@ async function mobileStopXctestScreenRecording(remotePath, user, pass, headers,
106
191
  }
107
192
  this.log.debug(`Stopping the active screen recording: ${JSON.stringify(screenRecordingInfo)}`);
108
193
  await this.proxyCommand('/wda/video/stop', 'POST', {});
109
- const videoPath = await retrieveXcTestScreenRecording.call(this, screenRecordingInfo.uuid);
194
+ const videoPath = await createXcTestScreenRecordingRetriever(this).retrieve(screenRecordingInfo.uuid);
110
195
  const result = {
111
196
  ...screenRecordingInfo,
112
197
  payload: '', // Will be set below
@@ -163,49 +248,10 @@ async function mobileStopXctestScreenRecording(remotePath, user, pass, headers,
163
248
  }
164
249
  return result;
165
250
  }
166
- async function retrieveRecodingFromSimulator(uuid) {
167
- const device = this.device;
168
- const dataRoot = device.getDir();
169
- // On Simulators the path looks like
170
- // $HOME/Library/Developer/CoreSimulator/Devices/F8E1968A-8443-4A9A-AB86-27C54C36A2F6/data/Containers/Data/InternalDaemon/4E3FE8DF-AD0A-41DA-B6EC-C35E5798C219/Attachments/A044DAF7-4A58-4CD5-95C3-29B4FE80C377
171
- const internalDaemonRoot = node_path_1.default.resolve(dataRoot, 'Containers', 'Data', 'InternalDaemon');
172
- const videoPaths = await support_1.fs.glob(`*/Attachments/${uuid}`, {
173
- cwd: internalDaemonRoot,
174
- absolute: true,
175
- });
176
- if ((0, utils_1.isEmpty)(videoPaths)) {
177
- throw new Error(`Unable to locate XCTest screen recording identified by '${uuid}' for the Simulator ${device.udid}`);
178
- }
179
- const videoPath = videoPaths[0];
180
- const { size } = await support_1.fs.stat(videoPath);
181
- this.log.debug(`Located the video at '${videoPath}' (${support_1.util.toReadableSizeString(size)})`);
182
- return videoPath;
183
- }
184
- async function retrieveRecodingFromRealDevice(uuid) {
185
- const device = this.device;
186
- const fileNames = await device.devicectl.listFiles(DOMAIN_TYPE, DOMAIN_IDENTIFIER, {
187
- username: USERNAME,
188
- subdirectory: SUBDIRECTORY,
189
- });
190
- if (!fileNames.includes(uuid)) {
191
- throw new Error(`Unable to locate XCTest screen recording identified by '${uuid}' for the device ${this.opts.udid}`);
192
- }
193
- if (!this.opts.tmpDir) {
194
- throw new Error('tmpDir is not set in driver options');
195
- }
196
- const videoPath = node_path_1.default.join(this.opts.tmpDir, `${uuid}${MOV_EXT}`);
197
- await device.devicectl.pullFile(`${SUBDIRECTORY}/${uuid}`, videoPath, {
198
- username: USERNAME,
199
- domainIdentifier: DOMAIN_IDENTIFIER,
200
- domainType: DOMAIN_TYPE,
201
- });
202
- const { size } = await support_1.fs.stat(videoPath);
203
- this.log.debug(`Pulled the video to '${videoPath}' (${support_1.util.toReadableSizeString(size)})`);
204
- return videoPath;
205
- }
206
- async function retrieveXcTestScreenRecording(uuid) {
207
- return this.isRealDevice()
208
- ? await retrieveRecodingFromRealDevice.call(this, uuid)
209
- : await retrieveRecodingFromSimulator.call(this, uuid);
251
+ function createXcTestScreenRecordingRetriever(driver) {
252
+ if (driver.isRealDevice()) {
253
+ return new RealDeviceXcTestScreenRecordingRetriever(driver.device, driver.opts.tmpDir || node_os_1.default.tmpdir(), driver.log);
254
+ }
255
+ return new SimulatorXcTestScreenRecordingRetriever(driver.device, driver.log);
210
256
  }
211
257
  //# sourceMappingURL=xctest-record-screen.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"xctest-record-screen.js","sourceRoot":"","sources":["../../../lib/commands/xctest-record-screen.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"xctest-record-screen.js","sourceRoot":"","sources":["../../../lib/commands/xctest-record-screen.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import {fs, util} from 'appium/support';
2
2
  import {encodeBase64OrUpload} from './helpers';
3
- import {isEmpty} from '../utils';
3
+ import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import type {XCUITestDriver} from '../driver';
6
6
  import type {Simulator} from 'appium-ios-simulator';
7
7
  import type {RealDevice} from '../device/real-device-management';
8
- import type {HTTPHeaders} from '@appium/types';
8
+ import type {AppiumLogger, HTTPHeaders} from '@appium/types';
9
9
  import type {XcTestScreenRecordingInfo, XcTestScreenRecording} from './types';
10
10
  import {XctestAttachmentDeletionClient} from '../device/xctest-attachment-deletion-client';
11
11
  import {
@@ -14,12 +14,113 @@ import {
14
14
  } from '../device/remotexpc-utils';
15
15
 
16
16
  const MOV_EXT = '.mov';
17
+ /**
18
+ * On simulators XCTest stores screen recording attachments under
19
+ * `InternalDaemon/<id>/Attachments/<uuid>` (legacy) or
20
+ * `InternalDaemon/<id>/tmp/Attachments/<uuid>` (Xcode 26.5+).
21
+ * Brace `{,tmp/}` matches both in a single glob.
22
+ */
23
+ const SIMULATOR_XCTEST_RECORDING_ATTACHMENT_GLOB = '*/{,tmp/}Attachments/*';
17
24
  /** Insecure feature when real-device XCTest recording is used without RemoteXPC attachment deletion. */
18
25
  const XCTEST_SCREEN_RECORD_FEATURE = 'xctest_screen_record';
19
26
  const DOMAIN_IDENTIFIER = 'com.apple.testmanagerd';
20
27
  const DOMAIN_TYPE = 'appDataContainer';
21
28
  const USERNAME = 'mobile';
22
- const SUBDIRECTORY = 'Attachments';
29
+ /** Legacy layout and Xcode 26.5+ `tmp/Attachments` within testmanagerd's app data container. */
30
+ const REAL_DEVICE_XCTEST_ATTACHMENT_SUBDIRECTORIES = ['Attachments', 'tmp/Attachments'] as const;
31
+
32
+ abstract class XcTestScreenRecordingRetriever {
33
+ constructor(protected readonly log: AppiumLogger) {}
34
+
35
+ protected static nameMatchesUuid(name: string, uuid: string): boolean {
36
+ return name.toUpperCase() === uuid.toUpperCase();
37
+ }
38
+
39
+ abstract retrieve(uuid: string): Promise<string>;
40
+ }
41
+
42
+ class SimulatorXcTestScreenRecordingRetriever extends XcTestScreenRecordingRetriever {
43
+ constructor(
44
+ private readonly device: Simulator,
45
+ log: AppiumLogger,
46
+ ) {
47
+ super(log);
48
+ }
49
+
50
+ override async retrieve(uuid: string): Promise<string> {
51
+ const dataRoot = this.device.getDir();
52
+ // e.g. .../CoreSimulator/Devices/<udid>/data/Containers/Data/InternalDaemon/<daemon-id>/Attachments/<uuid>
53
+ // or .../InternalDaemon/<daemon-id>/tmp/Attachments/<uuid> (Xcode 26.5+)
54
+ const internalDaemonRoot = path.resolve(dataRoot, 'Containers', 'Data', 'InternalDaemon');
55
+ const attachmentPaths = await fs.glob(SIMULATOR_XCTEST_RECORDING_ATTACHMENT_GLOB, {
56
+ cwd: internalDaemonRoot,
57
+ absolute: true,
58
+ });
59
+ const videoPath = attachmentPaths.find((fp) =>
60
+ XcTestScreenRecordingRetriever.nameMatchesUuid(path.basename(fp), uuid),
61
+ );
62
+ if (!videoPath) {
63
+ throw new Error(
64
+ `Unable to locate XCTest screen recording identified by '${uuid}' for the Simulator ${this.device.udid}`,
65
+ );
66
+ }
67
+ const {size} = await fs.stat(videoPath);
68
+ this.log.debug(`Located the video at '${videoPath}' (${util.toReadableSizeString(size)})`);
69
+ return videoPath;
70
+ }
71
+ }
72
+
73
+ class RealDeviceXcTestScreenRecordingRetriever extends XcTestScreenRecordingRetriever {
74
+ constructor(
75
+ private readonly device: RealDevice,
76
+ private readonly tmpDir: string,
77
+ log: AppiumLogger,
78
+ ) {
79
+ super(log);
80
+ }
81
+
82
+ override async retrieve(uuid: string): Promise<string> {
83
+ const attachment = await this.findAttachment(uuid);
84
+ if (!attachment) {
85
+ throw new Error(
86
+ `Unable to locate XCTest screen recording identified by '${uuid}' for the device ${this.device.udid}`,
87
+ );
88
+ }
89
+ const videoPath = path.join(this.tmpDir, `${uuid}${MOV_EXT}`);
90
+ const {subdirectory, fileName} = attachment;
91
+ await this.device.devicectl.pullFile(`${subdirectory}/${fileName}`, videoPath, {
92
+ username: USERNAME,
93
+ domainIdentifier: DOMAIN_IDENTIFIER,
94
+ domainType: DOMAIN_TYPE,
95
+ });
96
+ const {size} = await fs.stat(videoPath);
97
+ this.log.debug(`Pulled the video to '${videoPath}' (${util.toReadableSizeString(size)})`);
98
+ return videoPath;
99
+ }
100
+
101
+ private async findAttachment(
102
+ uuid: string,
103
+ ): Promise<{subdirectory: string; fileName: string} | null> {
104
+ for (const subdirectory of REAL_DEVICE_XCTEST_ATTACHMENT_SUBDIRECTORIES) {
105
+ let fileNames: string[];
106
+ try {
107
+ fileNames = await this.device.devicectl.listFiles(DOMAIN_TYPE, DOMAIN_IDENTIFIER, {
108
+ username: USERNAME,
109
+ subdirectory,
110
+ });
111
+ } catch {
112
+ continue;
113
+ }
114
+ const fileName = fileNames.find((name) =>
115
+ XcTestScreenRecordingRetriever.nameMatchesUuid(name, uuid),
116
+ );
117
+ if (fileName) {
118
+ return {subdirectory, fileName};
119
+ }
120
+ }
121
+ return null;
122
+ }
123
+ }
23
124
 
24
125
  /**
25
126
  * Start a new screen recording via XCTest.
@@ -136,8 +237,7 @@ export async function mobileStopXctestScreenRecording(
136
237
 
137
238
  this.log.debug(`Stopping the active screen recording: ${JSON.stringify(screenRecordingInfo)}`);
138
239
  await this.proxyCommand('/wda/video/stop', 'POST', {});
139
- const videoPath: string = await retrieveXcTestScreenRecording.call(
140
- this,
240
+ const videoPath = await createXcTestScreenRecordingRetriever(this).retrieve(
141
241
  screenRecordingInfo.uuid,
142
242
  );
143
243
  const result: XcTestScreenRecording = {
@@ -211,55 +311,15 @@ export async function mobileStopXctestScreenRecording(
211
311
  return result;
212
312
  }
213
313
 
214
- async function retrieveRecodingFromSimulator(this: XCUITestDriver, uuid: string): Promise<string> {
215
- const device = this.device as Simulator;
216
- const dataRoot = device.getDir();
217
- // On Simulators the path looks like
218
- // $HOME/Library/Developer/CoreSimulator/Devices/F8E1968A-8443-4A9A-AB86-27C54C36A2F6/data/Containers/Data/InternalDaemon/4E3FE8DF-AD0A-41DA-B6EC-C35E5798C219/Attachments/A044DAF7-4A58-4CD5-95C3-29B4FE80C377
219
- const internalDaemonRoot = path.resolve(dataRoot, 'Containers', 'Data', 'InternalDaemon');
220
- const videoPaths = await fs.glob(`*/Attachments/${uuid}`, {
221
- cwd: internalDaemonRoot,
222
- absolute: true,
223
- });
224
- if (isEmpty(videoPaths)) {
225
- throw new Error(
226
- `Unable to locate XCTest screen recording identified by '${uuid}' for the Simulator ${device.udid}`,
314
+ function createXcTestScreenRecordingRetriever(
315
+ driver: XCUITestDriver,
316
+ ): XcTestScreenRecordingRetriever {
317
+ if (driver.isRealDevice()) {
318
+ return new RealDeviceXcTestScreenRecordingRetriever(
319
+ driver.device as RealDevice,
320
+ driver.opts.tmpDir || os.tmpdir(),
321
+ driver.log,
227
322
  );
228
323
  }
229
- const videoPath = videoPaths[0];
230
- const {size} = await fs.stat(videoPath);
231
- this.log.debug(`Located the video at '${videoPath}' (${util.toReadableSizeString(size)})`);
232
- return videoPath;
233
- }
234
-
235
- async function retrieveRecodingFromRealDevice(this: XCUITestDriver, uuid: string): Promise<string> {
236
- const device = this.device as RealDevice;
237
-
238
- const fileNames = await device.devicectl.listFiles(DOMAIN_TYPE, DOMAIN_IDENTIFIER, {
239
- username: USERNAME,
240
- subdirectory: SUBDIRECTORY,
241
- });
242
- if (!fileNames.includes(uuid)) {
243
- throw new Error(
244
- `Unable to locate XCTest screen recording identified by '${uuid}' for the device ${this.opts.udid}`,
245
- );
246
- }
247
- if (!this.opts.tmpDir) {
248
- throw new Error('tmpDir is not set in driver options');
249
- }
250
- const videoPath = path.join(this.opts.tmpDir, `${uuid}${MOV_EXT}`);
251
- await device.devicectl.pullFile(`${SUBDIRECTORY}/${uuid}`, videoPath, {
252
- username: USERNAME,
253
- domainIdentifier: DOMAIN_IDENTIFIER,
254
- domainType: DOMAIN_TYPE,
255
- });
256
- const {size} = await fs.stat(videoPath);
257
- this.log.debug(`Pulled the video to '${videoPath}' (${util.toReadableSizeString(size)})`);
258
- return videoPath;
259
- }
260
-
261
- async function retrieveXcTestScreenRecording(this: XCUITestDriver, uuid: string): Promise<string> {
262
- return this.isRealDevice()
263
- ? await retrieveRecodingFromRealDevice.call(this, uuid)
264
- : await retrieveRecodingFromSimulator.call(this, uuid);
324
+ return new SimulatorXcTestScreenRecordingRetriever(driver.device as Simulator, driver.log);
265
325
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "appium-xcuitest-driver",
3
- "version": "11.8.0",
3
+ "version": "11.8.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "appium-xcuitest-driver",
9
- "version": "11.8.0",
9
+ "version": "11.8.1",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@appium/strongbox": "^1.0.0-rc.1",
@@ -100,6 +100,18 @@
100
100
  "spdy": "4.0.2"
101
101
  }
102
102
  },
103
+ "node_modules/@appium/base-driver/node_modules/axios": {
104
+ "version": "1.16.1",
105
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz",
106
+ "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==",
107
+ "license": "MIT",
108
+ "dependencies": {
109
+ "follow-redirects": "^1.16.0",
110
+ "form-data": "^4.0.5",
111
+ "https-proxy-agent": "^5.0.1",
112
+ "proxy-from-env": "^2.1.0"
113
+ }
114
+ },
103
115
  "node_modules/@appium/base-driver/node_modules/lru-cache": {
104
116
  "version": "11.5.0",
105
117
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz",
@@ -234,6 +246,18 @@
234
246
  "sharp": "0.34.5"
235
247
  }
236
248
  },
249
+ "node_modules/@appium/support/node_modules/axios": {
250
+ "version": "1.16.1",
251
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz",
252
+ "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==",
253
+ "license": "MIT",
254
+ "dependencies": {
255
+ "follow-redirects": "^1.16.0",
256
+ "form-data": "^4.0.5",
257
+ "https-proxy-agent": "^5.0.1",
258
+ "proxy-from-env": "^2.1.0"
259
+ }
260
+ },
237
261
  "node_modules/@appium/tsconfig": {
238
262
  "version": "1.1.2",
239
263
  "resolved": "https://registry.npmjs.org/@appium/tsconfig/-/tsconfig-1.1.2.tgz",
@@ -672,9 +696,9 @@
672
696
  "license": "MIT"
673
697
  },
674
698
  "node_modules/axios": {
675
- "version": "1.16.1",
676
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz",
677
- "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==",
699
+ "version": "1.17.0",
700
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.17.0.tgz",
701
+ "integrity": "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==",
678
702
  "license": "MIT",
679
703
  "dependencies": {
680
704
  "follow-redirects": "^1.16.0",
@@ -707,9 +731,9 @@
707
731
  }
708
732
  },
709
733
  "node_modules/bare-events": {
710
- "version": "2.8.3",
711
- "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.3.tgz",
712
- "integrity": "sha512-HdUm8EMQBLaJvGUdidNNbqpA1kYkwNcb+MYxkxCLAPJGQzlv9J0C24h8V65Z4c5GLd/JEALDvpFCQgpLJqc0zw==",
734
+ "version": "2.9.1",
735
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz",
736
+ "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==",
713
737
  "license": "Apache-2.0",
714
738
  "peerDependencies": {
715
739
  "bare-abort-controller": "*"
@@ -721,9 +745,9 @@
721
745
  }
722
746
  },
723
747
  "node_modules/bare-fs": {
724
- "version": "4.7.1",
725
- "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.1.tgz",
726
- "integrity": "sha512-WDRsyVN52eAx/lBamKD6uyw8H4228h/x0sGGGegOamM2cd7Pag88GfMQalobXI+HaEUxpCkbKQUDOQqt9wawRw==",
748
+ "version": "4.7.2",
749
+ "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.2.tgz",
750
+ "integrity": "sha512-aTvMFUWkBmjzKtEQMDGGDNF8bkfpD5N1b/FCwt7A3wrU4t1o/e/85Wzkluh6JlODCjqVESYCkQCdTXqZ9G7VFg==",
727
751
  "license": "Apache-2.0",
728
752
  "dependencies": {
729
753
  "bare-events": "^2.5.4",
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "xcuitest",
9
9
  "xctest"
10
10
  ],
11
- "version": "11.8.0",
11
+ "version": "11.8.1",
12
12
  "author": "Appium Contributors",
13
13
  "license": "Apache-2.0",
14
14
  "repository": {
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Lists XCTest attachment file names (UUIDs) under **testmanagerd**'s app-data `Attachments` folder
3
+ * Lists XCTest attachment file names (UUIDs) under **testmanagerd**'s app-data `Attachments` and
4
+ * `tmp/Attachments` folders
4
5
  * and deletes them via **appium-ios-remotexpc** (`XCTestAttachment.delete`), the same mechanism the
5
6
  * driver uses for real-device XCTest screen recording cleanup.
6
7
  *
@@ -29,7 +30,8 @@ const log = logger.getLogger('cleanup-videos');
29
30
  const DOMAIN_IDENTIFIER = 'com.apple.testmanagerd';
30
31
  const DOMAIN_TYPE = 'appDataContainer';
31
32
  const USERNAME = 'mobile';
32
- const SUBDIRECTORY = 'Attachments';
33
+ /** Legacy layout and Xcode 26.5+ `tmp/Attachments` (same as xctest-record-screen.ts). */
34
+ const ATTACHMENT_SUBDIRECTORIES = ['Attachments', 'tmp/Attachments'];
33
35
 
34
36
  /** Attachment entries are UUID-shaped file names (no extension in this listing). */
35
37
  const UUID_NAME_RE = /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i;
@@ -47,24 +49,32 @@ class CleanupVideos {
47
49
  await requirePlatformVersion(udid);
48
50
 
49
51
  const devicectl = new Devicectl(udid);
50
- let fileNames;
51
- try {
52
- fileNames = await devicectl.listFiles(DOMAIN_TYPE, DOMAIN_IDENTIFIER, {
53
- username: USERNAME,
54
- subdirectory: SUBDIRECTORY,
55
- });
56
- } catch (err) {
57
- throw new Error(
58
- `Failed to list files in ${DOMAIN_TYPE}/${DOMAIN_IDENTIFIER}/${SUBDIRECTORY}. ` +
59
- `Ensure a tunnel is up and the device is reachable via devicectl.`,
60
- {cause: err},
61
- );
52
+ const fileNames = [];
53
+ const listErrors = [];
54
+ let anyListSucceeded = false;
55
+ for (const subdirectory of ATTACHMENT_SUBDIRECTORIES) {
56
+ try {
57
+ const names = await devicectl.listFiles(DOMAIN_TYPE, DOMAIN_IDENTIFIER, {
58
+ username: USERNAME,
59
+ subdirectory,
60
+ });
61
+ anyListSucceeded = true;
62
+ fileNames.push(...names);
63
+ } catch (err) {
64
+ listErrors.push(err);
65
+ log.debug(`Could not list ${subdirectory}: ${err.message ?? err}`);
66
+ }
67
+ }
68
+ if (!anyListSucceeded) {
69
+ const errorMessage = `Failed to list files in ${DOMAIN_TYPE}/${DOMAIN_IDENTIFIER}/` +
70
+ `{${ATTACHMENT_SUBDIRECTORIES.join(',')}}`;
71
+ throw new AggregateError(listErrors, errorMessage);
62
72
  }
63
73
 
64
- const uuids = filterAttachmentUuids(fileNames);
74
+ const uuids = filterAttachmentUuids([...new Set(fileNames)]);
65
75
  log.info(
66
76
  `Found ${util.pluralize('UUID-shaped attachment', uuids.length, true)} ` +
67
- `in testmanagerd Attachments (out of ${fileNames.length} listed names).`,
77
+ `in testmanagerd Attachments and tmp/Attachments (out of ${fileNames.length} listed names).`,
68
78
  );
69
79
  if (uuids.length > 0) {
70
80
  log.info(uuids.join('\n'));